OSDN Git Service

[Refactor] ダンジョンのモンスター出現フィルターに適用
[hengbandforosx/hengbandosx.git] / src / main-x11.cpp
1 /* File: main-x11.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 /*
12  * This file helps Angband work with UNIX/X11 computers.
13  *
14  * To use this file, compile with "USE_X11" defined, and link against all
15  * the various "X11" libraries which may be needed.
16  *
17  * Part of this file provides a user interface package composed of several
18  * pseudo-objects, including "metadpy" (a display), "infowin" (a window),
19  * "infoclr" (a color), and "infofnt" (a font).  Actually, the package was
20  * originally much more interesting, but it was bastardized to keep this
21  * file simple.
22  *
23  * The rest of this file is an implementation of "main-xxx.c" for X11.
24  *
25  * Most of this file is by Ben Harrison (benh@phial.com).
26  */
27
28 /*
29  * The following shell script can be used to launch Angband, assuming that
30  * it was extracted into "~/Angband", and compiled using "USE_X11", on a
31  * Linux machine, with a 1280x1024 screen, using 6 windows (with the given
32  * characteristics), with gamma correction of 1.8 -> (1 / 1.8) * 256 = 142,
33  * and without graphics (add "-g" for graphics).  Just copy this comment
34  * into a file, remove the leading " * " characters (and the head/tail of
35  * this comment), and make the file executable.
36  *
37  *
38  * #!/bin/csh
39  *
40  * # Describe attempt
41  * echo "Launching angband..."
42  * sleep 2
43  *
44  * # Main window
45  * setenv ANGBAND_X11_FONT_0 10x20
46  * setenv ANGBAND_X11_AT_X_0 5
47  * setenv ANGBAND_X11_AT_Y_0 510
48  *
49  * # Message window
50  * setenv ANGBAND_X11_FONT_1 8x13
51  * setenv ANGBAND_X11_AT_X_1 5
52  * setenv ANGBAND_X11_AT_Y_1 22
53  * setenv ANGBAND_X11_ROWS_1 35
54  *
55  * # Inventory window
56  * setenv ANGBAND_X11_FONT_2 8x13
57  * setenv ANGBAND_X11_AT_X_2 635
58  * setenv ANGBAND_X11_AT_Y_2 182
59  * setenv ANGBAND_X11_ROWS_3 23
60  *
61  * # Equipment window
62  * setenv ANGBAND_X11_FONT_3 8x13
63  * setenv ANGBAND_X11_AT_X_3 635
64  * setenv ANGBAND_X11_AT_Y_3 22
65  * setenv ANGBAND_X11_ROWS_3 12
66  *
67  * # Monster recall window
68  * setenv ANGBAND_X11_FONT_4 6x13
69  * setenv ANGBAND_X11_AT_X_4 817
70  * setenv ANGBAND_X11_AT_Y_4 847
71  * setenv ANGBAND_X11_COLS_4 76
72  * setenv ANGBAND_X11_ROWS_4 11
73  *
74  * # Object recall window
75  * setenv ANGBAND_X11_FONT_5 6x13
76  * setenv ANGBAND_X11_AT_X_5 817
77  * setenv ANGBAND_X11_AT_Y_5 520
78  * setenv ANGBAND_X11_COLS_5 76
79  * setenv ANGBAND_X11_ROWS_5 24
80  *
81  * # The build directory
82  * cd ~/Angband
83  *
84  * # Gamma correction
85  * setenv ANGBAND_X11_GAMMA 142
86  *
87  * # Launch Angband
88  * ./src/angband -mx11 -- -n6 &
89  *
90  */
91
92 #include "cmd-io/macro-util.h"
93 #include "game-option/runtime-arguments.h"
94 #include "game-option/special-options.h"
95 #include "io/files-util.h"
96 #include "locale/japanese.h"
97 #include "locale/utf-8.h"
98 #include "main/sound-definitions-table.h"
99 #include "main/sound-of-music.h"
100 #include "main/x11-type-string.h"
101 #include "system/angband.h"
102 #include "system/system-variables.h"
103 #include "term/gameterm.h"
104 #include "term/term-color-types.h"
105 #include "util/angband-files.h"
106 #include "util/bit-flags-calculator.h"
107 #include "util/int-char-converter.h"
108 #include "util/string-processor.h"
109 #include <algorithm>
110
111 /*
112  * Available graphic modes
113  */
114 // clang-format off
115 #define GRAPHICS_NONE       0
116 #define GRAPHICS_ORIGINAL   1
117 #define GRAPHICS_ADAM_BOLT  2
118 #define GRAPHICS_HENGBAND   3
119 // clang-format on
120
121 #ifdef USE_X11
122 #ifndef __MAKEDEPEND__
123 #include <X11/Xlib.h>
124 #include <X11/Xutil.h>
125 #include <X11/keysym.h>
126 #include <X11/keysymdef.h>
127 #ifdef USE_LOCALE
128 #include <X11/Xlocale.h>
129 #endif
130 #include <X11/Xatom.h>
131 #endif /* __MAKEDEPEND__ */
132
133 #ifdef USE_XFT
134 #include <X11/Xft/Xft.h>
135 #endif
136
137 #include <memory>
138 #include <string>
139
140 /*
141  * Include some helpful X11 code.
142  */
143 #include "maid-x11.cpp"
144
145 /*
146  * Notes on Colors:
147  *
148  *   1) On a monochrome (or "fake-monochrome") display, all colors
149  *   will be "cast" to "fg," except for the bg color, which is,
150  *   obviously, cast to "bg".  Thus, one can ignore this setting.
151  *
152  *   2) Because of the inner functioning of the color allocation
153  *   routines, colors may be specified as (a) a typical color name,
154  *   (b) a hexidecimal color specification (preceded by a pound sign),
155  *   or (c) by strings such as "fg", "bg", "zg".
156  *
157  *   3) Due to the workings of the init routines, many colors
158  *   may also be dealt with by their actual pixel values.  Note that
159  *   the pixel with all bits set is "zg = (1<<metadpy->depth)-1", which
160  *   is not necessarily either black or white.
161  */
162
163 /**** Generic Types ****/
164
165 /*
166  * An X11 pixell specifier
167  */
168 #ifdef USE_XFT
169 typedef XftColor Pixell;
170 #else
171 typedef unsigned long Pixell;
172 #endif
173
174 /*
175  * A structure summarizing a given Display.
176  *
177  *      - The Display itself
178  *      - The default Screen for the display
179  *      - The virtual root (usually just the root)
180  *      - The default colormap (from a macro)
181  *
182  *      - The "name" of the display
183  *
184  *      - The socket to listen to for events
185  *
186  *      - The width of the display screen (from a macro)
187  *      - The height of the display screen (from a macro)
188  *      - The bit depth of the display screen (from a macro)
189  *
190  *      - The black Pixell (from a macro)
191  *      - The white Pixell (from a macro)
192  *
193  *      - The background Pixell (default: black)
194  *      - The foreground Pixell (default: white)
195  *      - The maximal Pixell (Equals: ((2 ^ depth)-1), is usually ugly)
196  *
197  *      - Bit Flag: Force all colors to black and white (default: !color)
198  *      - Bit Flag: Allow the use of color (default: depth > 1)
199  *      - Bit Flag: We created 'dpy', and so should nuke it when done.
200  */
201 struct metadpy {
202     Display *dpy;
203     Screen *screen;
204     Window root;
205     Colormap cmap;
206 #ifdef USE_XIM
207     XIM xim;
208 #endif
209
210     char *name;
211
212     int fd;
213
214     uint width;
215     uint height;
216     uint depth;
217
218     Pixell black;
219     Pixell white;
220
221     Pixell bg;
222     Pixell fg;
223 #ifndef USE_XFT
224     Pixell zg;
225 #endif
226
227     uint mono : 1;
228     uint color : 1;
229     uint nuke : 1;
230 };
231
232 /*
233  * A Structure summarizing Window Information.
234  *
235  * I assume that a window is at most 30000 pixels on a side.
236  * I assume that the root windw is also at most 30000 square.
237  *
238  *      - The Window
239  *      - The current Input Event Mask
240  *
241  *      - The location of the window
242  *      - The width, height of the window
243  *      - The border width of this window
244  *
245  *      - Byte: 1st Extra byte
246  *
247  *      - Bit Flag: This window is currently Mapped
248  *      - Bit Flag: This window needs to be redrawn
249  *      - Bit Flag: This window has been resized
250  *
251  *      - Bit Flag: We should nuke 'win' when done with it
252  *
253  *      - Bit Flag: 1st extra flag
254  *      - Bit Flag: 2nd extra flag
255  *      - Bit Flag: 3rd extra flag
256  *      - Bit Flag: 4th extra flag
257  */
258 struct infowin {
259     Window win;
260 #ifdef USE_XIM
261     XIC xic;
262     long xic_mask;
263 #endif
264 #ifdef USE_XFT
265     XftDraw *draw;
266 #endif
267
268     long mask;
269
270     int16_t ox, oy;
271     int16_t x, y;
272     int16_t w, h;
273     uint16_t b;
274
275     byte byte1;
276
277     uint mapped : 1;
278     uint redraw : 1;
279     uint resize : 1;
280
281     uint nuke : 1;
282
283     uint flag1 : 1;
284     uint flag2 : 1;
285     uint flag3 : 1;
286     uint flag4 : 1;
287 };
288
289 /*
290  * A Structure summarizing Operation+Color Information
291  *
292  *      - The actual GC corresponding to this info
293  *
294  *      - The Foreground Pixell Value
295  *      - The Background Pixell Value
296  *
297  *      - Num (0-15): The operation code (As in Clear, Xor, etc)
298  *      - Bit Flag: The GC is in stipple mode
299  *      - Bit Flag: Destroy 'gc' at Nuke time.
300  */
301 struct infoclr {
302 #ifndef USE_XFT
303     GC gc;
304 #endif
305
306     Pixell fg;
307     Pixell bg;
308
309     uint code : 4;
310     uint stip : 1;
311     uint nuke : 1;
312 };
313
314 /*
315  * A Structure to Hold Font Information
316  *
317  *      - The 'XFontStruct*' (yields the 'Font')
318  *
319  *      - The font name
320  *
321  *      - The default character width
322  *      - The default character height
323  *      - The default character ascent
324  *
325  *      - Byte: Pixel offset used during fake mono
326  *
327  *      - Flag: Force monospacing via 'wid'
328  *      - Flag: Nuke info when done
329  */
330 struct infofnt {
331 #ifdef USE_XFT
332     XftFont *info;
333 #else
334     XFontSet info;
335 #endif
336     std::string name;
337
338     int16_t wid;
339     int16_t twid;
340     int16_t hgt;
341     int16_t asc;
342
343     byte off;
344
345     uint mono : 1;
346     uint nuke : 1;
347 };
348
349 /* Set current metadpy (Metadpy) to 'M' */
350 #define Metadpy_set(M) Metadpy = M
351
352 /* Initialize 'M' using Display 'D' */
353 #define Metadpy_init_dpy(D) Metadpy_init_2(D, cnullptr)
354
355 /* Initialize 'M' using a Display named 'N' */
356 #define Metadpy_init_name(N) Metadpy_init_2((Display *)(nullptr), N)
357
358 /* Initialize 'M' using the standard Display */
359 #define Metadpy_init() Metadpy_init_name("")
360
361 /* Init an infowin by giving father as an (info_win*) (or nullptr), and data */
362 #define Infowin_init_dad(D, X, Y, W, H, B, FG, BG) Infowin_init_data(((D) ? ((D)->win) : (Window)(None)), X, Y, W, H, B, FG, BG)
363
364 /* Init a top level infowin by pos,size,bord,Colors */
365 #define Infowin_init_top(X, Y, W, H, B, FG, BG) Infowin_init_data(None, X, Y, W, H, B, FG, BG)
366
367 /* Request a new standard window by giving Dad infowin and X,Y,W,H */
368 #define Infowin_init_std(D, X, Y, W, H, B) Infowin_init_dad(D, X, Y, W, H, B, Metadpy->fg, Metadpy->bg)
369
370 /* Set the current Infowin */
371 #define Infowin_set(I) (Infowin = (I))
372
373 /* Set the current Infoclr */
374 #define Infoclr_set(C) (Infoclr = (C))
375
376 #define Infoclr_init_ppo(F, B, O, M) Infoclr_init_data(F, B, O, M)
377
378 #define Infoclr_init_cco(F, B, O, M) Infoclr_init_ppo(Infoclr_Pixell(F), Infoclr_Pixell(B), O, M)
379
380 #define Infoclr_init_ppn(F, B, O, M) Infoclr_init_ppo(F, B, Infoclr_Opcode(O), M)
381
382 #define Infoclr_init_ccn(F, B, O, M) Infoclr_init_cco(F, B, Infoclr_Opcode(O), M)
383
384 /* Set the current infofnt */
385 #define Infofnt_set(I) (Infofnt = (I))
386
387 /* Errr: Expose Infowin */
388 #define Infowin_expose() (!(Infowin->redraw = 1))
389
390 /* Errr: Unxpose Infowin */
391 #define Infowin_unexpose() (Infowin->redraw = 0)
392
393 /*
394  * The "default" values
395  */
396 static metadpy metadpy_default;
397
398 /*
399  * The "current" variables
400  */
401 static metadpy *Metadpy = &metadpy_default;
402 static infowin *Infowin = (infowin *)(nullptr);
403 #ifdef USE_XIM
404 static infowin *Focuswin = (infowin *)(nullptr);
405 #endif
406 static infoclr *Infoclr = (infoclr *)(nullptr);
407 static infofnt *Infofnt = (infofnt *)(nullptr);
408
409 /*
410  * Init the current metadpy, with various initialization stuff.
411  *
412  * Inputs:
413  *      dpy:  The Display* to use (if nullptr, create it)
414  *      name: The name of the Display (if nullptr, the current)
415  *
416  * Notes:
417  *      If 'name' is nullptr, but 'dpy' is set, extract name from dpy
418  *      If 'dpy' is nullptr, then Create the named Display
419  *      If 'name' is nullptr, and so is 'dpy', use current Display
420  *
421  * Return -1 if no Display given, and none can be opened.
422  */
423 static errr Metadpy_init_2(Display *dpy, concptr name)
424 {
425     metadpy *m = Metadpy;
426     if (!dpy) {
427         dpy = XOpenDisplay(name);
428         if (!dpy) {
429             return -1;
430         }
431
432         m->nuke = 1;
433     } else {
434         m->nuke = 0;
435     }
436
437     m->dpy = dpy;
438     m->screen = DefaultScreenOfDisplay(dpy);
439     m->root = RootWindowOfScreen(m->screen);
440     m->cmap = DefaultColormapOfScreen(m->screen);
441     m->name = DisplayString(dpy);
442     m->fd = ConnectionNumber(Metadpy->dpy);
443     m->width = WidthOfScreen(m->screen);
444     m->height = HeightOfScreen(m->screen);
445     m->depth = DefaultDepthOfScreen(m->screen);
446
447 #ifdef USE_XFT
448     Visual *vis = DefaultVisual(dpy, 0);
449     XftColorAllocName(dpy, vis, m->cmap, "black", &m->black);
450     XftColorAllocName(dpy, vis, m->cmap, "white", &m->white);
451 #else
452     m->black = BlackPixelOfScreen(m->screen);
453     m->white = WhitePixelOfScreen(m->screen);
454 #endif
455
456     m->bg = m->black;
457     m->fg = m->white;
458
459 #ifndef USE_XFT
460     m->zg = (1 << m->depth) - 1;
461 #endif
462
463     m->color = ((m->depth > 1) ? 1 : 0);
464     m->mono = ((m->color) ? 0 : 1);
465     return 0;
466 }
467
468 /*
469  * General Flush/ Sync/ Discard routine
470  */
471 static errr Metadpy_update(int flush, int sync, int discard)
472 {
473     if (flush) {
474         XFlush(Metadpy->dpy);
475     }
476     if (sync) {
477         XSync(Metadpy->dpy, discard);
478     }
479
480     return 0;
481 }
482
483 /*
484  * Make a simple beep
485  */
486 static errr Metadpy_do_beep(void)
487 {
488     XBell(Metadpy->dpy, 100);
489     return 0;
490 }
491
492 /*
493  * Set the name (in the title bar) of Infowin
494  */
495 static errr Infowin_set_name(concptr name)
496 {
497     Status st;
498     XTextProperty tp;
499     char buf[128];
500     char *bp = buf;
501     strcpy(buf, name);
502     st = XStringListToTextProperty(&bp, 1, &tp);
503     if (st) {
504         XSetWMName(Metadpy->dpy, Infowin->win, &tp);
505         XFree(tp.value);
506     }
507     return 0;
508 }
509
510 /*
511  * Prepare a new 'infowin'.
512  */
513 static errr Infowin_prepare(Window xid)
514 {
515     infowin *iwin = Infowin;
516     Window tmp_win;
517     XWindowAttributes xwa;
518     int x, y;
519     unsigned int w, h, b, d;
520     iwin->win = xid;
521     XGetGeometry(Metadpy->dpy, xid, &tmp_win, &x, &y, &w, &h, &b, &d);
522
523 #ifdef USE_XFT
524     Visual *vis = DefaultVisual(Metadpy->dpy, 0);
525     if (vis->c_class != TrueColor) {
526         quit_fmt("Display does not support truecolor.\n");
527     }
528     iwin->draw = XftDrawCreate(Metadpy->dpy, iwin->win, vis, Metadpy->cmap);
529 #endif
530
531     iwin->x = x;
532     iwin->y = y;
533     iwin->w = w;
534     iwin->h = h;
535     iwin->b = b;
536
537     XGetWindowAttributes(Metadpy->dpy, xid, &xwa);
538     iwin->mask = xwa.your_event_mask;
539     iwin->mapped = ((xwa.map_state == IsUnmapped) ? 0 : 1);
540     iwin->redraw = 1;
541     return 0;
542 }
543
544 /*
545  * Init an infowin by giving some data.
546  *
547  * Inputs:
548  *      dad: The Window that should own this Window (if any)
549  *      x,y: The position of this Window
550  *      w,h: The size of this Window
551  *      b,d: The border width and pixel depth
552  *
553  * Notes:
554  *      If 'dad == None' assume 'dad == root'
555  */
556 static errr Infowin_init_data(Window dad, int x, int y, int w, int h, int b, Pixell fg, Pixell bg)
557 {
558     Window xid;
559     *Infowin = {};
560     if (dad == None) {
561         dad = Metadpy->root;
562     }
563
564 #ifdef USE_XFT
565     xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg.pixel, bg.pixel);
566 #else
567     xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg, bg);
568 #endif
569
570     XSelectInput(Metadpy->dpy, xid, 0L);
571     Infowin->nuke = 1;
572     return Infowin_prepare(xid);
573 }
574
575 /*
576  * Modify the event mask of an Infowin
577  */
578 static errr Infowin_set_mask(long mask)
579 {
580     Infowin->mask = mask;
581     XSelectInput(Metadpy->dpy, Infowin->win, Infowin->mask);
582     return 0;
583 }
584
585 /*
586  * Request that Infowin be mapped
587  */
588 static errr Infowin_map(void)
589 {
590     XMapWindow(Metadpy->dpy, Infowin->win);
591     return 0;
592 }
593
594 /*
595  * Request that Infowin be raised
596  */
597 static errr Infowin_raise(void)
598 {
599     XRaiseWindow(Metadpy->dpy, Infowin->win);
600     return 0;
601 }
602
603 /*
604  * Request that Infowin be moved to a new location
605  */
606 static errr Infowin_impell(int x, int y)
607 {
608     XMoveWindow(Metadpy->dpy, Infowin->win, x, y);
609     return 0;
610 }
611
612 /*
613  * Resize an infowin
614  */
615 static errr Infowin_resize(int w, int h)
616 {
617     XResizeWindow(Metadpy->dpy, Infowin->win, w, h);
618     return 0;
619 }
620
621 /*
622  * Visually clear Infowin
623  */
624 static errr Infowin_wipe(void)
625 {
626     XClearWindow(Metadpy->dpy, Infowin->win);
627     return 0;
628 }
629
630 /*
631  * A nullptr terminated pair list of legal "operation names"
632  *
633  * Pairs of values, first is texttual name, second is the string
634  * holding the decimal value that the operation corresponds to.
635  */
636 // clang-format off
637 static concptr opcode_pairs[] =
638 {
639     "cpy", "3",
640     "xor", "6",
641     "and", "1",
642     "ior", "7",
643     "nor", "8",
644     "inv", "10",
645     "clr", "0",
646     "set", "15",
647
648     "src", "3",
649     "dst", "5",
650
651     "+andReverse", "2",
652     "+andInverted", "4",
653     "+noop", "5",
654     "+equiv", "9",
655     "+orReverse", "11",
656     "+copyInverted", "12",
657     "+orInverted", "13",
658     "+nand", "14",
659     nullptr
660 };
661 // clang-format on
662
663 /*
664  * Parse a word into an operation "code"
665  *
666  * Inputs:
667  *      str: A string, hopefully representing an Operation
668  *
669  * Output:
670  *      0-15: if 'str' is a valid Operation
671  *      -1:   if 'str' could not be parsed
672  */
673 static int Infoclr_Opcode(concptr str)
674 {
675     int i;
676     for (i = 0; opcode_pairs[i * 2]; ++i) {
677         if (streq(opcode_pairs[i * 2], str)) {
678             return atoi(opcode_pairs[i * 2 + 1]);
679         }
680     }
681
682     return -1;
683 }
684
685 /*
686  * Initialize an infoclr with some data
687  *
688  * Inputs:
689  *      fg:   The Pixell for the requested Foreground (see above)
690  *      bg:   The Pixell for the requested Background (see above)
691  *      op:   The Opcode for the requested Operation (see above)
692  *      stip: The stipple mode
693  */
694 static errr Infoclr_init_data(Pixell fg, Pixell bg, int op, int stip)
695 {
696     infoclr *iclr = Infoclr;
697
698 #ifndef USE_XFT
699     GC gc;
700     XGCValues gcv;
701     unsigned long gc_mask;
702 #endif
703
704 #ifndef USE_XFT
705     if (bg > Metadpy->zg) {
706         return -1;
707     }
708     if (fg > Metadpy->zg) {
709         return -1;
710     }
711     if ((op < 0) || (op > 15)) {
712         return -1;
713     }
714
715     gcv.function = op;
716     gcv.background = bg;
717     gcv.foreground = fg;
718     if (op == 6) {
719         gcv.background = 0;
720     }
721     if (op == 6) {
722         gcv.foreground = (bg ^ fg);
723     }
724
725     gcv.fill_style = (stip ? FillStippled : FillSolid);
726     gcv.graphics_exposures = False;
727     gc_mask = (GCFunction | GCBackground | GCForeground | GCFillStyle | GCGraphicsExposures);
728     gc = XCreateGC(Metadpy->dpy, Metadpy->root, gc_mask, &gcv);
729 #endif
730
731     *iclr = {};
732
733 #ifndef USE_XFT
734     iclr->gc = gc;
735 #endif
736
737     iclr->nuke = 1;
738     iclr->fg = fg;
739     iclr->bg = bg;
740     iclr->code = op;
741     iclr->stip = stip ? 1 : 0;
742     return 0;
743 }
744
745 /*
746  * Change the 'fg' for an infoclr
747  *
748  * Inputs:
749  *      fg:   The Pixell for the requested Foreground (see above)
750  */
751 static errr Infoclr_change_fg(Pixell fg)
752 {
753     infoclr *iclr = Infoclr;
754
755 #ifdef USE_XFT
756     iclr->fg = fg;
757 #else
758     if (fg > Metadpy->zg) {
759         return -1;
760     }
761
762     XSetForeground(Metadpy->dpy, iclr->gc, fg);
763 #endif
764
765     return 0;
766 }
767
768 /*
769  * Prepare a new 'infofnt'
770  */
771 #ifdef USE_XFT
772 static errr Infofnt_prepare(XftFont *info)
773 #else
774 static errr Infofnt_prepare(XFontSet info)
775 #endif
776 {
777     infofnt *ifnt = Infofnt;
778
779 #ifndef USE_XFT
780     XCharStruct *cs;
781     XFontStruct **fontinfo;
782     char **fontname;
783     int n_fonts;
784     int ascent, descent, width;
785 #endif
786
787     ifnt->info = info;
788
789 #ifdef USE_XFT
790     ifnt->asc = info->ascent;
791     ifnt->hgt = info->height;
792     const char *text = "A";
793     XGlyphInfo extent;
794     XftTextExtentsUtf8(Metadpy->dpy, info, (FcChar8 *)text, strlen(text), &extent);
795     ifnt->wid = extent.xOff;
796 #else
797     n_fonts = XFontsOfFontSet(info, &fontinfo, &fontname);
798
799     ascent = descent = width = 0;
800     while (n_fonts-- > 0) {
801         cs = &((*fontinfo)->max_bounds);
802         if (ascent < (*fontinfo)->ascent) {
803             ascent = (*fontinfo)->ascent;
804         }
805         if (descent < (*fontinfo)->descent) {
806             descent = (*fontinfo)->descent;
807         }
808         if (((*fontinfo)->max_byte1) > 0) {
809             /* 多バイト文字の場合は幅半分(端数切り上げ)で評価する */
810             if (width < (cs->width + 1) / 2) {
811                 width = (cs->width + 1) / 2;
812             }
813         } else {
814             if (width < cs->width) {
815                 width = cs->width;
816             }
817         }
818         fontinfo++;
819         fontname++;
820     }
821     ifnt->asc = ascent;
822     ifnt->hgt = ascent + descent;
823     ifnt->wid = width;
824 #endif
825
826     if (use_bigtile) {
827         ifnt->twid = 2 * ifnt->wid;
828     } else {
829         ifnt->twid = ifnt->wid;
830     }
831
832     return 0;
833 }
834
835 /*
836  * Init an infofnt by its Name
837  *
838  * Inputs:
839  *      name: The name of the requested Font
840  */
841 static void Infofnt_init_data(concptr name)
842
843 {
844 #ifdef USE_XFT
845     XftFont *info;
846 #else
847     XFontSet info;
848     char **missing_list;
849     int missing_count;
850     char *default_font;
851 #endif
852
853     if (!name || !*name) {
854         quit("Missing font!");
855     }
856
857 #ifdef USE_XFT
858     info = XftFontOpenName(Metadpy->dpy, 0, name);
859     /* TODO: error handling */
860 #else
861     info = XCreateFontSet(Metadpy->dpy, name, &missing_list, &missing_count, &default_font);
862     if (missing_count > 0) {
863         printf("missing font(s): \n");
864         while (missing_count-- > 0) {
865             printf("\t%s\n", missing_list[missing_count]);
866         }
867         XFreeStringList(missing_list);
868     }
869 #endif
870
871     if (!info) {
872         quit_fmt("Failed to find font:\"%s\"", name);
873     }
874
875     *Infofnt = {};
876     if (Infofnt_prepare(info)) {
877 #ifdef USE_XFT
878         XftFontClose(Metadpy->dpy, info);
879 #else
880         XFreeFontSet(Metadpy->dpy, info);
881 #endif
882         quit_fmt("Failed to prepare font:\"%s\"", name);
883     }
884
885     Infofnt->name = name;
886     Infofnt->nuke = 1;
887 }
888
889 #ifdef USE_XFT
890 static void Infofnt_text_std_xft_draw_str(int x, int y, concptr str, concptr str_end)
891 {
892     int offset = 0;
893     while (str < str_end) {
894         const int byte_len = utf8_next_char_byte_length(str);
895
896         if (byte_len == 0 || str + byte_len > str_end) {
897             return;
898         }
899
900         XftDrawStringUtf8(Infowin->draw, &Infoclr->fg, Infofnt->info, x + Infofnt->wid * offset, y, (const FcChar8 *)str, byte_len);
901         offset += (byte_len > 1 ? 2 : 1);
902         str += byte_len;
903     }
904 }
905 #endif
906
907 /*
908  * Standard Text
909  */
910 static errr Infofnt_text_std(int x, int y, concptr str, int len)
911 {
912     if (!str || !*str) {
913         return -1;
914     }
915
916     if (len < 0) {
917         len = strlen(str);
918     }
919
920     y = (y * Infofnt->hgt) + Infofnt->asc + Infowin->oy;
921     x = (x * Infofnt->wid) + Infowin->ox;
922     if (Infofnt->mono) {
923 #ifndef USE_XFT
924         int i;
925         for (i = 0; i < len; ++i) {
926             XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc, x + i * Infofnt->wid + Infofnt->off, y, str + i, 1);
927         }
928 #endif
929     } else {
930 #ifdef JP
931         char utf8_buf[1024];
932         int utf8_len = euc_to_utf8(str, len, utf8_buf, sizeof(utf8_buf));
933         if (utf8_len < 0) {
934             return -1;
935         }
936 #endif
937
938 #ifdef USE_XFT
939         XftDraw *draw = Infowin->draw;
940
941         XRectangle r;
942         r.x = 0;
943         r.y = 0;
944         r.width = Infofnt->wid * len;
945         r.height = Infofnt->hgt;
946         XftDrawSetClipRectangles(draw, x, y - Infofnt->asc, &r, 1);
947         XftDrawRect(draw, &Infoclr->bg, x, y - Infofnt->asc, Infofnt->wid * len, Infofnt->hgt);
948         Infofnt_text_std_xft_draw_str(x, y, _(utf8_buf, str), _(utf8_buf + utf8_len, str + len));
949         XftDrawSetClip(draw, 0);
950 #else
951         XmbDrawImageString(Metadpy->dpy, Infowin->win, Infofnt->info, Infoclr->gc, x, y, _(utf8_buf, str), _(utf8_len, len));
952 #endif
953     }
954
955     return 0;
956 }
957
958 /*
959  * Painting where text would be
960  */
961 static errr Infofnt_text_non(int x, int y, concptr str, int len)
962 {
963     int w, h;
964     if (len < 0) {
965         len = strlen(str);
966     }
967
968     w = len * Infofnt->wid;
969     x = x * Infofnt->wid + Infowin->ox;
970     h = Infofnt->hgt;
971     y = y * h + Infowin->oy;
972
973 #ifdef USE_XFT
974     XftDrawRect(Infowin->draw, &Infoclr->fg, x, y, w, h);
975 #else
976     XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, w, h);
977 #endif
978
979     return 0;
980 }
981
982 /*
983  * Angband specific code follows... (ANGBAND)
984  */
985
986 /*
987  * Hack -- cursor color
988  */
989 static std::unique_ptr<infoclr> xor_;
990
991 /*
992  * Actual color table
993  */
994 static std::unique_ptr<infoclr> clr[256];
995
996 /*
997  * Color info (unused, red, green, blue).
998  */
999 static byte color_table[256][4];
1000
1001 namespace {
1002 /*
1003  * A structure for each "term"
1004  */
1005 struct term_data {
1006     term_type t;
1007     std::unique_ptr<infofnt> fnt;
1008     std::unique_ptr<infowin> win;
1009 #ifndef USE_XFT
1010     XImage *tiles;
1011     XImage *TmpImage;
1012 #endif
1013 };
1014 }
1015
1016 /*
1017  * The number of term data structures
1018  */
1019 #define MAX_TERM_DATA 8
1020
1021 /*
1022  * The array of term data structures
1023  */
1024 static term_data data[MAX_TERM_DATA];
1025
1026 /* Use short names for the most commonly used elements of various structures. */
1027 #define DPY (Metadpy->dpy)
1028 #define WIN (Infowin->win)
1029
1030 /* Describe a set of co-ordinates. */
1031 struct co_ord {
1032     int x;
1033     int y;
1034 };
1035
1036 /*
1037  * A special structure to store information about the text currently
1038  * selected.
1039  */
1040 struct x11_selection_type {
1041     bool select; /* The selection is currently in use. */
1042     bool drawn; /* The selection is currently displayed. */
1043     term_type *t; /* The window where the selection is found. */
1044     co_ord init; /* The starting co-ordinates. */
1045     co_ord cur; /* The end co-ordinates (the current ones if still copying). */
1046     co_ord old; /* The previous end co-ordinates. */
1047     Time time; /* The time at which the selection was finalised. */
1048 };
1049
1050 static x11_selection_type s_ptr[1];
1051
1052 // ゲーム側へキーを送る
1053 static void send_key(const char key)
1054 {
1055     // Windows ドライバと同様、自前でキューを操作する。
1056     // 逆順に term_key_push() する方法だと長い日本語を入力したときにテキストの
1057     // 順序が入れ替わってしまう。
1058
1059     // キーバッファが一杯なら入力を捨てる
1060     const int head_nxt = game_term->key_head + 1 == game_term->key_size ? 0 : game_term->key_head + 1;
1061     if (head_nxt == game_term->key_tail) {
1062         plog_fmt("key buffer overflow, ignoring key 0x%02X", key);
1063         return;
1064     }
1065
1066     game_term->key_queue[game_term->key_head] = key;
1067     game_term->key_head = head_nxt;
1068 }
1069
1070 // ゲーム側へキー列を送る
1071 static void send_keys(const char *const keys)
1072 {
1073     for (const char *p = keys; *p != '\0'; ++p) {
1074         send_key(*p);
1075     }
1076 }
1077
1078 /*
1079  * Process a keypress event
1080  */
1081 static void react_keypress(XKeyEvent *xev)
1082 {
1083     int n, mc, ms, mo, mx;
1084     uint ks1;
1085     XKeyEvent *ev = (XKeyEvent *)(xev);
1086     KeySym ks;
1087     char buf[128];
1088     char msg[128];
1089
1090 #ifdef USE_XIM
1091     int valid_keysym = true;
1092 #endif
1093
1094 #ifdef USE_XIM
1095     if (Focuswin && Focuswin->xic) {
1096         Status status;
1097         n = XmbLookupString(Focuswin->xic, ev, buf, 125, &ks, &status);
1098         if (status == XBufferOverflow) {
1099             printf("Input is too long, and dropped\n");
1100             return;
1101         }
1102         if (status != XLookupKeySym && status != XLookupBoth) {
1103             valid_keysym = false;
1104         }
1105     } else {
1106         n = XLookupString(ev, buf, 125, &ks, nullptr);
1107     }
1108 #else
1109     n = XLookupString(ev, buf, 125, &ks, nullptr);
1110 #endif
1111
1112     buf[n] = '\0';
1113
1114 #ifdef USE_XIM
1115     if (!valid_keysym) { /* XIMからの入力時のみ false になる */
1116 #ifdef JP
1117         char euc_buf[sizeof(buf)];
1118         /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
1119         if (utf8_to_euc(buf, strlen(buf) + 1, euc_buf, sizeof(euc_buf)) < 0) {
1120             return;
1121         }
1122 #endif
1123         send_keys(_(euc_buf, buf));
1124         return;
1125     }
1126 #endif
1127
1128     if (IsModifierKey(ks)) {
1129         return;
1130     }
1131
1132     ks1 = (uint)(ks);
1133     mc = any_bits(ev->state, ControlMask);
1134     ms = any_bits(ev->state, ShiftMask);
1135     mo = any_bits(ev->state, Mod1Mask);
1136     mx = any_bits(ev->state, Mod2Mask);
1137     if (n && !mo && !mx && !IsSpecialKey(ks)) {
1138         send_keys(buf);
1139         return;
1140     }
1141
1142     switch (ks1) {
1143     case XK_Escape: {
1144         send_key(ESCAPE);
1145         return;
1146     }
1147
1148     case XK_Return: {
1149         send_key('\r');
1150         return;
1151     }
1152
1153     case XK_Tab: {
1154         send_key('\t');
1155         return;
1156     }
1157
1158     case XK_Delete: {
1159         send_key(0x7f);
1160         return;
1161     }
1162     case XK_BackSpace: {
1163         send_key('\010');
1164         return;
1165     }
1166     }
1167
1168     if (ks) {
1169         sprintf(msg, "%c%s%s%s%s_%lX%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", (unsigned long)(ks), 13);
1170     } else {
1171         sprintf(msg, "%c%s%s%s%sK_%X%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", ev->keycode, 13);
1172     }
1173
1174     send_keys(msg);
1175
1176     if (n && (macro_find_exact(msg) < 0)) {
1177         macro_add(msg, buf);
1178     }
1179 }
1180
1181 /*
1182  * Find the square a particular pixel is part of.
1183  */
1184 static void pixel_to_square(int *const x, int *const y, const int ox, const int oy)
1185 {
1186     (*x) = (ox - Infowin->ox) / Infofnt->wid;
1187     (*y) = (oy - Infowin->oy) / Infofnt->hgt;
1188 }
1189
1190 /*
1191  * Find the pixel at the top-left corner of a square.
1192  */
1193 static void square_to_pixel(int *const x, int *const y, const int ox, const int oy)
1194 {
1195     (*x) = ox * Infofnt->wid + Infowin->ox;
1196     (*y) = oy * Infofnt->hgt + Infowin->oy;
1197 }
1198
1199 /*
1200  * Convert co-ordinates from starting corner/opposite corner to minimum/maximum.
1201  */
1202 static void sort_co_ord(co_ord *min, co_ord *max, const co_ord *b, const co_ord *a)
1203 {
1204     min->x = std::min(a->x, b->x);
1205     min->y = std::min(a->y, b->y);
1206     max->x = std::max(a->x, b->x);
1207     max->y = std::max(a->y, b->y);
1208 }
1209
1210 /*
1211  * Remove the selection by redrawing it.
1212  */
1213 static void mark_selection_clear(int x1, int y1, int x2, int y2)
1214 {
1215     term_redraw_section(x1, y1, x2, y2);
1216 }
1217
1218 /*
1219  * Select an area by drawing a grey box around it.
1220  * NB. These two functions can cause flicker as the selection is modified,
1221  * as the game redraws the entire marked section.
1222  */
1223 static void mark_selection_mark(int x1, int y1, int x2, int y2)
1224 {
1225     square_to_pixel(&x1, &y1, x1, y1);
1226     square_to_pixel(&x2, &y2, x2, y2);
1227 #ifdef USE_XFT
1228     XftDrawRect(Infowin->draw, &clr[2]->fg, x1, y1, x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
1229 #else
1230     XDrawRectangle(Metadpy->dpy, Infowin->win, clr[2]->gc, x1, y1, x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
1231 #endif
1232 }
1233
1234 /*
1235  * Mark a selection by drawing boxes around it (for now).
1236  */
1237 static void mark_selection(void)
1238 {
1239     co_ord min, max;
1240     term_type *old = game_term;
1241     bool draw = s_ptr->select;
1242     bool clear = s_ptr->drawn;
1243     if (s_ptr->t != old) {
1244         term_activate(s_ptr->t);
1245     }
1246
1247     if (clear) {
1248         sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->old);
1249         mark_selection_clear(min.x, min.y, max.x, max.y);
1250     }
1251     if (draw) {
1252         sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
1253         mark_selection_mark(min.x, min.y, max.x, max.y);
1254     }
1255
1256     if (s_ptr->t != old) {
1257         term_activate(old);
1258     }
1259
1260     s_ptr->old.x = s_ptr->cur.x;
1261     s_ptr->old.y = s_ptr->cur.y;
1262     s_ptr->drawn = s_ptr->select;
1263 }
1264
1265 /*
1266  * Forget a selection for one reason or another.
1267  */
1268 static void copy_x11_release(void)
1269 {
1270     s_ptr->select = false;
1271     mark_selection();
1272 }
1273
1274 /*
1275  * Start to select some text on the screen.
1276  */
1277 static void copy_x11_start(int x, int y)
1278 {
1279     if (s_ptr->select) {
1280         copy_x11_release();
1281     }
1282
1283     s_ptr->t = game_term;
1284     s_ptr->init.x = s_ptr->cur.x = s_ptr->old.x = x;
1285     s_ptr->init.y = s_ptr->cur.y = s_ptr->old.y = y;
1286 }
1287
1288 /*
1289  * Respond to movement of the mouse when selecting text.
1290  */
1291 static void copy_x11_cont(int x, int y, unsigned int buttons)
1292 {
1293     x = MIN(MAX(x, 0), game_term->wid - 1);
1294     y = MIN(MAX(y, 0), game_term->hgt - 1);
1295     if (~buttons & Button1Mask) {
1296         return;
1297     }
1298     if (s_ptr->t != game_term) {
1299         return;
1300     }
1301     if (x == s_ptr->old.x && y == s_ptr->old.y && s_ptr->select) {
1302         return;
1303     }
1304
1305     s_ptr->select = true;
1306     s_ptr->cur.x = x;
1307     s_ptr->cur.y = y;
1308     mark_selection();
1309 }
1310
1311 /*
1312  * Respond to release of the left mouse button by putting the selected text in
1313  * the primary buffer.
1314  */
1315 static void copy_x11_end(const Time time)
1316 {
1317     if (!s_ptr->select) {
1318         return;
1319     }
1320     if (s_ptr->t != game_term) {
1321         return;
1322     }
1323
1324     s_ptr->time = time;
1325     XSetSelectionOwner(Metadpy->dpy, XA_PRIMARY, Infowin->win, time);
1326     if (XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY) != Infowin->win) {
1327         s_ptr->select = false;
1328         mark_selection();
1329     }
1330 }
1331
1332 static Atom xa_targets, xa_timestamp, xa_text, xa_compound_text, xa_utf8;
1333
1334 /*
1335  * Set the required variable atoms at start-up to avoid errors later.
1336  */
1337 static void set_atoms(void)
1338 {
1339     xa_targets = XInternAtom(DPY, "TARGETS", False);
1340     xa_timestamp = XInternAtom(DPY, "TIMESTAMP", False);
1341     xa_text = XInternAtom(DPY, "TEXT", False);
1342     xa_compound_text = XInternAtom(DPY, "COMPOUND_TEXT", False);
1343     xa_utf8 = XInternAtom(DPY, "UTF8_STRING", False);
1344 }
1345
1346 static Atom request_target = 0;
1347
1348 /*
1349  * Send a message to request that the PRIMARY buffer be sent here.
1350  */
1351 static void paste_x11_request(Atom target, const Time time)
1352 {
1353     Atom property = XInternAtom(DPY, "__COPY_TEXT", False);
1354     if (XGetSelectionOwner(DPY, XA_PRIMARY) == None) {
1355         /* No selection. */
1356         /* bell("No selection found."); */
1357         return;
1358     }
1359
1360     request_target = target;
1361     XConvertSelection(DPY, XA_PRIMARY, target, property, WIN, time);
1362 }
1363
1364 /*
1365  * Add the contents of the PRIMARY buffer to the input queue.
1366  *
1367  * Hack - This doesn't use the "time" of the event, and so accepts anything a
1368  * client tries to send it.
1369  */
1370 static void paste_x11_accept(const XSelectionEvent *ptr)
1371 {
1372     unsigned long left;
1373     const long offset = 0;
1374     const long length = 32000;
1375     XTextProperty xtextproperty;
1376     errr err = 0;
1377     Atom property = XInternAtom(DPY, "__COPY_TEXT", False);
1378     if (ptr->property == None) {
1379         if (request_target == xa_compound_text) {
1380             paste_x11_request(XA_STRING, ptr->time);
1381         } else {
1382             request_target = 0;
1383             plog("Paste failure (remote client could not send).");
1384         }
1385
1386         return;
1387     }
1388
1389     if (ptr->selection != XA_PRIMARY) {
1390         plog("Paste failure (remote client did not send primary selection).");
1391         return;
1392     }
1393
1394     if (ptr->target != request_target) {
1395         plog("Paste failure (selection in unknown format).");
1396         return;
1397     }
1398
1399     if (XGetWindowProperty(Metadpy->dpy, Infowin->win, property, offset, length, true, request_target, &xtextproperty.encoding, &xtextproperty.format,
1400             &xtextproperty.nitems, &left, &xtextproperty.value) != Success) {
1401         return;
1402     }
1403
1404     if (request_target == xa_compound_text) {
1405         char **list;
1406         int count;
1407
1408         XmbTextPropertyToTextList(DPY, &xtextproperty, &list, &count);
1409
1410         if (list) {
1411             int i;
1412
1413             for (i = 0; i < count; i++) {
1414                 err = type_string(list[i], 0);
1415                 if (err) {
1416                     break;
1417                 }
1418             }
1419
1420             XFreeStringList(list);
1421         }
1422     } else {
1423         err = type_string((char *)xtextproperty.value, xtextproperty.nitems);
1424     }
1425
1426     XFree(xtextproperty.value);
1427     if (err) {
1428         plog("Paste failure (too much text selected).");
1429     }
1430 }
1431
1432 /*
1433  * Add a character to a string in preparation for sending it to another
1434  * client as a STRING.
1435  * This doesn't change anything, as clients tend not to have difficulty in
1436  * receiving this format (although the standard specifies a restricted set).
1437  * Strings do not have a colour.
1438  */
1439 static bool paste_x11_send_text(XSelectionRequestEvent *rq)
1440 {
1441     char buf[1024];
1442     co_ord max, min;
1443     TERM_LEN x, y;
1444     int l;
1445     TERM_COLOR a;
1446     char c;
1447
1448     sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
1449     if (XGetSelectionOwner(DPY, XA_PRIMARY) != WIN) {
1450         return false;
1451     }
1452
1453     for (y = 0; y < game_term->hgt; y++) {
1454 #ifdef JP
1455         int kanji = 0;
1456 #endif
1457         if (y < min.y) {
1458             continue;
1459         }
1460         if (y > max.y) {
1461             break;
1462         }
1463
1464         for (l = 0, x = 0; x < game_term->wid; x++) {
1465 #ifdef JP
1466             if (x > max.x) {
1467                 break;
1468             }
1469
1470             term_what(x, y, &a, &c);
1471             if (1 == kanji) {
1472                 kanji = 2;
1473             } else if (iskanji(c)) {
1474                 kanji = 1;
1475             } else {
1476                 kanji = 0;
1477             }
1478
1479             if (x < min.x) {
1480                 continue;
1481             }
1482
1483             /*
1484              * A single kanji character was divided in two...
1485              * Delete the garbage.
1486              */
1487             if ((2 == kanji && x == min.x) || (1 == kanji && x == max.x)) {
1488                 c = ' ';
1489             }
1490 #else
1491             if (x > max.x) {
1492                 break;
1493             }
1494             if (x < min.x) {
1495                 continue;
1496             }
1497
1498             term_what(x, y, &a, &c);
1499 #endif
1500
1501             buf[l] = c;
1502             l++;
1503         }
1504
1505         // 末尾の空白を削る。
1506         while (l >= 1 && buf[l - 1] == ' ') {
1507             l--;
1508         }
1509
1510         // 複数行の場合、各行末に改行を付加。
1511         if (min.y != max.y) {
1512             buf[l] = '\n';
1513             l++;
1514         }
1515
1516         buf[l] = '\0';
1517
1518 #ifdef JP
1519         char utf8_buf[2048];
1520         const int len = euc_to_utf8(buf, l, utf8_buf, sizeof(utf8_buf));
1521 #endif
1522         if (_(len, l) > 0) {
1523             XChangeProperty(DPY, rq->requestor, rq->property, xa_utf8, 8, PropModeAppend, (unsigned char *)_(utf8_buf, buf), _(len, l));
1524         }
1525     }
1526
1527     return true;
1528 }
1529
1530 /*
1531  * Send some text requested by another X client.
1532  */
1533 static void paste_x11_send(XSelectionRequestEvent *rq)
1534 {
1535     XEvent event;
1536     XSelectionEvent *ptr = &(event.xselection);
1537
1538     ptr->type = SelectionNotify;
1539     ptr->property = rq->property;
1540     ptr->display = rq->display;
1541     ptr->requestor = rq->requestor;
1542     ptr->selection = rq->selection;
1543     ptr->target = rq->target;
1544     ptr->time = rq->time;
1545
1546     /*
1547      * Paste the appropriate information for each target type.
1548      * Note that this currently rejects MULTIPLE targets.
1549      */
1550
1551     if (rq->target == xa_utf8) {
1552         if (!paste_x11_send_text(rq)) {
1553             ptr->property = None;
1554         }
1555     } else if (rq->target == xa_targets) {
1556         Atom target_list[] = { xa_targets, xa_utf8 };
1557         XChangeProperty(
1558             DPY, rq->requestor, rq->property, XA_ATOM, 32, PropModeReplace, (unsigned char *)target_list, (sizeof(target_list) / sizeof(target_list[0])));
1559     } else {
1560         ptr->property = None;
1561     }
1562
1563     XSendEvent(DPY, rq->requestor, false, NoEventMask, &event);
1564 }
1565
1566 /*
1567  * Handle various events conditional on presses of a mouse button.
1568  */
1569 static void handle_button(Time time, int x, int y, int button, bool press)
1570 {
1571     pixel_to_square(&x, &y, x, y);
1572
1573     if (press && button == 1) {
1574         copy_x11_start(x, y);
1575     }
1576     if (!press && button == 1) {
1577         copy_x11_end(time);
1578     }
1579     if (!press && button == 2) {
1580         paste_x11_request(xa_compound_text, time);
1581     }
1582 }
1583
1584 /*
1585  * Process events
1586  */
1587 static errr CheckEvent(bool wait)
1588 {
1589     term_data *old_td = (term_data *)(game_term->data);
1590
1591     XEvent xev_body, *xev = &xev_body;
1592
1593     term_data *td = nullptr;
1594     infowin *iwin = nullptr;
1595
1596     int i;
1597
1598 #ifdef USE_XIM
1599     do {
1600 #endif
1601
1602         if (!wait && !XPending(Metadpy->dpy)) {
1603             return 1;
1604         }
1605
1606         if (s_ptr->select && !s_ptr->drawn) {
1607             mark_selection();
1608         }
1609
1610         XNextEvent(Metadpy->dpy, xev);
1611
1612 #ifdef USE_XIM
1613     } while (XFilterEvent(xev, xev->xany.window));
1614 #endif
1615
1616     if (xev->type == MappingNotify) {
1617         XRefreshKeyboardMapping(&xev->xmapping);
1618         return 0;
1619     }
1620
1621     for (i = 0; i < MAX_TERM_DATA; i++) {
1622         if (!data[i].win) {
1623             continue;
1624         }
1625         if (xev->xany.window == data[i].win->win) {
1626             td = &data[i];
1627             iwin = td->win.get();
1628             break;
1629         }
1630     }
1631
1632     if (!td || !iwin) {
1633         return 0;
1634     }
1635
1636     term_activate(&td->t);
1637     Infowin_set(iwin);
1638     switch (xev->type) {
1639     case ButtonPress:
1640     case ButtonRelease: {
1641         bool press = (xev->type == ButtonPress);
1642         int x = xev->xbutton.x;
1643         int y = xev->xbutton.y;
1644         int z;
1645         if (xev->xbutton.button == Button1) {
1646             z = 1;
1647         } else if (xev->xbutton.button == Button2) {
1648             z = 2;
1649         } else if (xev->xbutton.button == Button3) {
1650             z = 3;
1651         } else if (xev->xbutton.button == Button4) {
1652             z = 4;
1653         } else if (xev->xbutton.button == Button5) {
1654             z = 5;
1655         } else {
1656             z = 0;
1657         }
1658
1659         handle_button(xev->xbutton.time, x, y, z, press);
1660         break;
1661     }
1662     case EnterNotify:
1663     case LeaveNotify: {
1664         break;
1665     }
1666     case MotionNotify: {
1667         int x = xev->xmotion.x;
1668         int y = xev->xmotion.y;
1669         unsigned int z = xev->xmotion.state;
1670         pixel_to_square(&x, &y, x, y);
1671         copy_x11_cont(x, y, z);
1672         break;
1673     }
1674     case SelectionNotify: {
1675         paste_x11_accept(&(xev->xselection));
1676         break;
1677     }
1678     case SelectionRequest: {
1679         paste_x11_send(&(xev->xselectionrequest));
1680         break;
1681     }
1682     case SelectionClear: {
1683         s_ptr->select = false;
1684         mark_selection();
1685         break;
1686     }
1687     case KeyRelease: {
1688         break;
1689     }
1690     case KeyPress: {
1691         term_activate(&old_td->t);
1692         react_keypress(&(xev->xkey));
1693         break;
1694     }
1695     case Expose: {
1696         int x1, x2, y1, y2;
1697         x1 = (xev->xexpose.x - Infowin->ox) / Infofnt->wid;
1698         x2 = (xev->xexpose.x + xev->xexpose.width - Infowin->ox) / Infofnt->wid;
1699
1700         y1 = (xev->xexpose.y - Infowin->oy) / Infofnt->hgt;
1701         y2 = (xev->xexpose.y + xev->xexpose.height - Infowin->oy) / Infofnt->hgt;
1702
1703         term_redraw_section(x1, y1, x2, y2);
1704         break;
1705     }
1706     case MapNotify: {
1707         Infowin->mapped = 1;
1708         game_term->mapped_flag = true;
1709         break;
1710     }
1711     case UnmapNotify: {
1712         Infowin->mapped = 0;
1713         game_term->mapped_flag = false;
1714         break;
1715     }
1716     case ConfigureNotify: {
1717         int cols, rows, wid, hgt;
1718         int ox = Infowin->ox;
1719         int oy = Infowin->oy;
1720         Infowin->x = xev->xconfigure.x;
1721         Infowin->y = xev->xconfigure.y;
1722         Infowin->w = xev->xconfigure.width;
1723         Infowin->h = xev->xconfigure.height;
1724         cols = ((Infowin->w - (ox + ox)) / td->fnt->wid);
1725         rows = ((Infowin->h - (oy + oy)) / td->fnt->hgt);
1726         if (cols < 1) {
1727             cols = 1;
1728         }
1729         if (rows < 1) {
1730             rows = 1;
1731         }
1732
1733         if (td == &data[0]) {
1734             if (cols < 80) {
1735                 cols = 80;
1736             }
1737             if (rows < 24) {
1738                 rows = 24;
1739             }
1740         }
1741
1742         wid = cols * td->fnt->wid + (ox + ox);
1743         hgt = rows * td->fnt->hgt + (oy + oy);
1744         term_resize(cols, rows);
1745         if ((Infowin->w != wid) || (Infowin->h != hgt)) {
1746             Infowin_set(td->win.get());
1747             Infowin_resize(wid, hgt);
1748         }
1749
1750         break;
1751     }
1752 #ifdef USE_XIM
1753     case FocusIn: {
1754         if (iwin->xic) {
1755             XSetICFocus(iwin->xic);
1756         }
1757         Focuswin = iwin;
1758         break;
1759     }
1760     case FocusOut: {
1761         if (iwin->xic) {
1762             XUnsetICFocus(iwin->xic);
1763         }
1764
1765         break;
1766     }
1767 #endif
1768     }
1769
1770     term_activate(&old_td->t);
1771     Infowin_set(old_td->win.get());
1772     return 0;
1773 }
1774
1775 /*
1776  * An array of sound file names
1777  */
1778 static concptr sound_file[SOUND_MAX];
1779
1780 /*
1781  * Check for existance of a file
1782  */
1783 static bool check_file(concptr s)
1784 {
1785     FILE *fff;
1786
1787     fff = fopen(s, "r");
1788     if (!fff) {
1789         return false;
1790     }
1791
1792     fclose(fff);
1793     return true;
1794 }
1795
1796 /*
1797  * Initialize sound
1798  */
1799 static void init_sound(void)
1800 {
1801     int i;
1802     char wav[128];
1803     char buf[1024];
1804     char dir_xtra_sound[1024];
1805     path_build(dir_xtra_sound, sizeof(dir_xtra_sound), ANGBAND_DIR_XTRA, "sound");
1806     for (i = 1; i < SOUND_MAX; i++) {
1807         sprintf(wav, "%s.wav", angband_sound_name[i]);
1808         path_build(buf, sizeof(buf), dir_xtra_sound, wav);
1809         if (check_file(buf)) {
1810             sound_file[i] = string_make(buf);
1811         }
1812     }
1813
1814     use_sound = true;
1815     return;
1816 }
1817
1818 /*
1819  * Hack -- make a sound
1820  */
1821 static errr game_term_xtra_x11_sound(int v)
1822 {
1823     char buf[1024];
1824     if (!use_sound) {
1825         return 1;
1826     }
1827     if ((v < 0) || (v >= SOUND_MAX)) {
1828         return 1;
1829     }
1830     if (!sound_file[v]) {
1831         return 1;
1832     }
1833
1834     sprintf(buf, "./playwave.sh %s\n", sound_file[v]);
1835     return system(buf) < 0;
1836 }
1837
1838 /*
1839  * Handle "activation" of a term
1840  */
1841 static errr game_term_xtra_x11_level(int v)
1842 {
1843     term_data *td = (term_data *)(game_term->data);
1844     if (v) {
1845         Infowin_set(td->win.get());
1846         Infofnt_set(td->fnt.get());
1847     }
1848
1849     return 0;
1850 }
1851
1852 /*
1853  * React to changes
1854  */
1855 static errr game_term_xtra_x11_react(void)
1856 {
1857     int i;
1858
1859     if (Metadpy->color) {
1860         for (i = 0; i < 256; i++) {
1861             if ((color_table[i][0] != angband_color_table[i][0]) || (color_table[i][1] != angband_color_table[i][1]) || (color_table[i][2] != angband_color_table[i][2]) || (color_table[i][3] != angband_color_table[i][3])) {
1862                 Pixell pixel;
1863                 color_table[i][0] = angband_color_table[i][0];
1864                 color_table[i][1] = angband_color_table[i][1];
1865                 color_table[i][2] = angband_color_table[i][2];
1866                 color_table[i][3] = angband_color_table[i][3];
1867                 pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]);
1868                 Infoclr_set(clr[i].get());
1869                 Infoclr_change_fg(pixel);
1870             }
1871         }
1872     }
1873
1874     return 0;
1875 }
1876
1877 /*
1878  * Handle a "special request"
1879  */
1880 static errr game_term_xtra_x11(int n, int v)
1881 {
1882     switch (n) {
1883     case TERM_XTRA_NOISE:
1884         Metadpy_do_beep();
1885         return 0;
1886     case TERM_XTRA_SOUND:
1887         return game_term_xtra_x11_sound(v);
1888 #ifdef USE_XFT
1889     case TERM_XTRA_FRESH:
1890         Metadpy_update(1, 1, 0);
1891         return 0;
1892 #else
1893     case TERM_XTRA_FRESH:
1894         Metadpy_update(1, 0, 0);
1895         return 0;
1896 #endif
1897     case TERM_XTRA_BORED:
1898         return CheckEvent(0);
1899     case TERM_XTRA_EVENT:
1900         return CheckEvent(v);
1901     case TERM_XTRA_FLUSH:
1902         while (!CheckEvent(false)) {
1903             ;
1904         }
1905         return 0;
1906     case TERM_XTRA_LEVEL:
1907         return game_term_xtra_x11_level(v);
1908     case TERM_XTRA_CLEAR:
1909         Infowin_wipe();
1910         s_ptr->drawn = false;
1911         return 0;
1912     case TERM_XTRA_DELAY:
1913         usleep(1000 * v);
1914         return 0;
1915     case TERM_XTRA_REACT:
1916         return game_term_xtra_x11_react();
1917     }
1918
1919     return 1;
1920 }
1921
1922 /*
1923  * Draw the cursor as an inverted rectangle.
1924  *
1925  * Consider a rectangular outline like "main-mac.c".  XXX XXX
1926  */
1927 static errr game_term_curs_x11(int x, int y)
1928 {
1929     if (use_graphics) {
1930 #ifdef USE_XFT
1931         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->wid - 1, Infofnt->hgt - 1);
1932         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->wid - 3, Infofnt->hgt - 3);
1933 #else
1934         XDrawRectangle(
1935             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->wid - 1, Infofnt->hgt - 1);
1936         XDrawRectangle(
1937             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->wid - 3, Infofnt->hgt - 3);
1938 #endif
1939     } else {
1940         Infoclr_set(xor_.get());
1941         Infofnt_text_non(x, y, " ", 1);
1942     }
1943
1944     return 0;
1945 }
1946
1947 /*
1948  * Draw the double width cursor
1949  */
1950 static errr game_term_bigcurs_x11(int x, int y)
1951 {
1952     if (use_graphics) {
1953 #ifdef USE_XFT
1954         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->twid - 1, Infofnt->hgt - 1);
1955         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->twid - 3, Infofnt->hgt - 3);
1956 #else
1957         XDrawRectangle(
1958             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->twid - 1, Infofnt->hgt - 1);
1959         XDrawRectangle(
1960             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->twid - 3, Infofnt->hgt - 3);
1961 #endif
1962     } else {
1963         Infoclr_set(xor_.get());
1964         Infofnt_text_non(x, y, "  ", 2);
1965     }
1966
1967     return 0;
1968 }
1969
1970 /*
1971  * Erase some characters.
1972  */
1973 static errr game_term_wipe_x11(int x, int y, int n)
1974 {
1975     Infoclr_set(clr[TERM_DARK].get());
1976     Infofnt_text_non(x, y, "", n);
1977     s_ptr->drawn = false;
1978     return 0;
1979 }
1980
1981 /*
1982  * Draw some textual characters.
1983  */
1984 static errr game_term_text_x11(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
1985 {
1986     Infoclr_set(clr[a].get());
1987     Infofnt_text_std(x, y, s, n);
1988     s_ptr->drawn = false;
1989     return 0;
1990 }
1991
1992 #ifndef USE_XFT
1993 /*
1994  * Draw some graphical characters.
1995  */
1996 static errr game_term_pict_x11(TERM_LEN x, TERM_LEN y, int n, const TERM_COLOR *ap, const char *cp, const TERM_COLOR *tap, const char *tcp)
1997 {
1998     int i, x1, y1;
1999
2000     TERM_COLOR a;
2001     char c;
2002
2003     TERM_COLOR ta;
2004     char tc;
2005
2006     int x2, y2;
2007     int k, l;
2008
2009     unsigned long pixel, blank;
2010
2011     term_data *td = (term_data *)(game_term->data);
2012
2013     y *= Infofnt->hgt;
2014     x *= Infofnt->wid;
2015
2016     y += Infowin->oy;
2017     x += Infowin->ox;
2018     for (i = 0; i < n; ++i, x += td->fnt->wid) {
2019         a = *ap++;
2020         c = *cp++;
2021         x1 = (c & 0x7F) * td->fnt->twid;
2022         y1 = (a & 0x7F) * td->fnt->hgt;
2023         if (td->tiles->width < x1 + td->fnt->wid || td->tiles->height < y1 + td->fnt->hgt) {
2024             XFillRectangle(Metadpy->dpy, td->win->win, clr[0]->gc, x, y, td->fnt->twid, td->fnt->hgt);
2025             continue;
2026         }
2027
2028         ta = *tap++;
2029         tc = *tcp++;
2030
2031         x2 = (tc & 0x7F) * td->fnt->twid;
2032         y2 = (ta & 0x7F) * td->fnt->hgt;
2033
2034         if (((x1 == x2) && (y1 == y2)) || !(((byte)ta & 0x80) && ((byte)tc & 0x80)) || td->tiles->width < x2 + td->fnt->wid || td->tiles->height < y2 + td->fnt->hgt) {
2035             XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->tiles, x1, y1, x, y, td->fnt->twid, td->fnt->hgt);
2036         } else {
2037             blank = XGetPixel(td->tiles, 0, td->fnt->hgt * 6);
2038             for (k = 0; k < td->fnt->twid; k++) {
2039                 for (l = 0; l < td->fnt->hgt; l++) {
2040                     if ((pixel = XGetPixel(td->tiles, x1 + k, y1 + l)) == blank) {
2041                         pixel = XGetPixel(td->tiles, x2 + k, y2 + l);
2042                     }
2043
2044                     XPutPixel(td->TmpImage, k, l, pixel);
2045                 }
2046             }
2047
2048             XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->TmpImage, 0, 0, x, y, td->fnt->twid, td->fnt->hgt);
2049         }
2050     }
2051
2052     s_ptr->drawn = false;
2053     return 0;
2054 }
2055 #endif
2056
2057 #ifdef USE_XIM
2058 static void IMDestroyCallback(XIM, XPointer, XPointer);
2059
2060 static void IMInstantiateCallback(Display *display, XPointer unused1, XPointer unused2)
2061 {
2062     XIM xim;
2063     XIMCallback ximcallback;
2064     XIMStyles *xim_styles = nullptr;
2065     int i;
2066
2067     (void)unused1;
2068     (void)unused2;
2069
2070     xim = XOpenIM(display, nullptr, nullptr, nullptr);
2071     if (!xim) {
2072         printf("can't open IM\n");
2073         return;
2074     }
2075
2076     ximcallback.callback = IMDestroyCallback;
2077     ximcallback.client_data = nullptr;
2078     XSetIMValues(xim, XNDestroyCallback, &ximcallback, nullptr);
2079     XGetIMValues(xim, XNQueryInputStyle, &xim_styles, nullptr);
2080     for (i = 0; i < xim_styles->count_styles; i++) {
2081         if (xim_styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) {
2082             break;
2083         }
2084     }
2085     if (i >= xim_styles->count_styles) {
2086         printf("Sorry, your IM does not support 'Root' preedit style...\n");
2087         XCloseIM(xim);
2088         return;
2089     }
2090     XFree(xim_styles);
2091
2092     Metadpy->xim = xim;
2093
2094     for (i = 0; i < MAX_TERM_DATA; i++) {
2095         infowin *iwin = data[i].win.get();
2096         if (!iwin) {
2097             continue;
2098         }
2099         iwin->xic = XCreateIC(xim, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing), XNClientWindow, iwin->win, XNFocusWindow, iwin->win, nullptr);
2100         if (!iwin->xic) {
2101             printf("Can't create input context for Term%d\n", i);
2102             continue;
2103         }
2104
2105         if (XGetICValues(iwin->xic, XNFilterEvents, &iwin->xic_mask, nullptr) != nullptr) {
2106             iwin->xic_mask = 0L;
2107         }
2108
2109         XSelectInput(Metadpy->dpy, iwin->win, iwin->mask | iwin->xic_mask);
2110     }
2111
2112     return;
2113 }
2114
2115 static void IMDestroyCallback(XIM xim, XPointer client_data, XPointer call_data)
2116 {
2117     int i;
2118     (void)xim;
2119     (void)client_data;
2120
2121     if (call_data == nullptr) {
2122         XRegisterIMInstantiateCallback(Metadpy->dpy, nullptr, nullptr, nullptr, IMInstantiateCallback, nullptr);
2123     }
2124
2125     for (i = 0; i < MAX_TERM_DATA; i++) {
2126         infowin *iwin = data[i].win.get();
2127         if (!iwin) {
2128             continue;
2129         }
2130         if (iwin->xic_mask) {
2131             XSelectInput(Metadpy->dpy, iwin->win, iwin->mask);
2132             iwin->xic_mask = 0L;
2133         }
2134         iwin->xic = nullptr;
2135     }
2136
2137     Metadpy->xim = nullptr;
2138 }
2139 #endif
2140
2141 static char force_lower(char a)
2142 {
2143     return isupper(a) ? tolower(a) : a;
2144 }
2145
2146 static void game_term_nuke_x11(term_type *)
2147 {
2148     for (auto i = 0; i < MAX_TERM_DATA; i++) {
2149         infofnt *ifnt = data[i].fnt.get();
2150         infowin *iwin = data[i].win.get();
2151         if (ifnt && ifnt->info)
2152 #ifdef USE_XFT
2153             XftFontClose(Metadpy->dpy, ifnt->info);
2154 #else
2155             XFreeFontSet(Metadpy->dpy, ifnt->info);
2156 #endif
2157         if (iwin && iwin->xic) {
2158             XDestroyIC(iwin->xic);
2159         }
2160 #ifdef USE_XFT
2161         if (iwin && iwin->draw) {
2162             XftDrawDestroy(iwin->draw);
2163         }
2164 #endif
2165         angband_term[i] = nullptr;
2166     }
2167
2168     if (Metadpy->xim) {
2169         XCloseIM(Metadpy->xim);
2170     }
2171     XUnregisterIMInstantiateCallback(Metadpy->dpy, NULL, NULL, NULL, IMInstantiateCallback, NULL);
2172     XCloseDisplay(Metadpy->dpy);
2173 }
2174
2175 /*
2176  * Initialize a term_data
2177  */
2178 static errr term_data_init(term_data *td, int i)
2179 {
2180     term_type *t = &td->t;
2181
2182     concptr name = angband_term_name[i];
2183
2184     concptr font;
2185     int x = 0;
2186     int y = 0;
2187
2188     int cols = 80;
2189     int rows = 24;
2190
2191     int ox = 1;
2192     int oy = 1;
2193
2194     int wid, hgt, num;
2195
2196     char buf[80];
2197
2198     concptr str;
2199
2200     int val;
2201
2202     XClassHint *ch;
2203
2204     char res_name[20];
2205     char res_class[20];
2206
2207     XSizeHints *sh;
2208 #ifdef USE_XIM
2209     XWMHints *wh;
2210 #endif
2211
2212     sprintf(buf, "ANGBAND_X11_FONT_%d", i);
2213     font = getenv(buf);
2214     if (!font) {
2215         font = getenv("ANGBAND_X11_FONT");
2216     }
2217
2218     if (!font) {
2219         switch (i) {
2220         case 0: {
2221             font = DEFAULT_X11_FONT_0;
2222         } break;
2223         case 1: {
2224             font = DEFAULT_X11_FONT_1;
2225         } break;
2226         case 2: {
2227             font = DEFAULT_X11_FONT_2;
2228         } break;
2229         case 3: {
2230             font = DEFAULT_X11_FONT_3;
2231         } break;
2232         case 4: {
2233             font = DEFAULT_X11_FONT_4;
2234         } break;
2235         case 5: {
2236             font = DEFAULT_X11_FONT_5;
2237         } break;
2238         case 6: {
2239             font = DEFAULT_X11_FONT_6;
2240         } break;
2241         case 7: {
2242             font = DEFAULT_X11_FONT_7;
2243         } break;
2244         default: {
2245             font = DEFAULT_X11_FONT;
2246         }
2247         }
2248     }
2249
2250     sprintf(buf, "ANGBAND_X11_AT_X_%d", i);
2251     str = getenv(buf);
2252     x = (str != nullptr) ? atoi(str) : -1;
2253
2254     sprintf(buf, "ANGBAND_X11_AT_Y_%d", i);
2255     str = getenv(buf);
2256     y = (str != nullptr) ? atoi(str) : -1;
2257
2258     sprintf(buf, "ANGBAND_X11_COLS_%d", i);
2259     str = getenv(buf);
2260     val = (str != nullptr) ? atoi(str) : -1;
2261     if (val > 0) {
2262         cols = val;
2263     }
2264
2265     sprintf(buf, "ANGBAND_X11_ROWS_%d", i);
2266     str = getenv(buf);
2267     val = (str != nullptr) ? atoi(str) : -1;
2268     if (val > 0) {
2269         rows = val;
2270     }
2271
2272     if (!i) {
2273         if (cols < 80) {
2274             cols = 80;
2275         }
2276         if (rows < 24) {
2277             rows = 24;
2278         }
2279     }
2280
2281     sprintf(buf, "ANGBAND_X11_IBOX_%d", i);
2282     str = getenv(buf);
2283     val = (str != nullptr) ? atoi(str) : -1;
2284     if (val > 0) {
2285         ox = val;
2286     }
2287
2288     sprintf(buf, "ANGBAND_X11_IBOY_%d", i);
2289     str = getenv(buf);
2290     val = (str != nullptr) ? atoi(str) : -1;
2291     if (val > 0) {
2292         oy = val;
2293     }
2294
2295     td->fnt = std::make_unique<infofnt>();
2296     Infofnt_set(td->fnt.get());
2297     Infofnt_init_data(font);
2298
2299     num = ((i == 0) ? 1024 : 16);
2300     wid = cols * td->fnt->wid + (ox + ox);
2301     hgt = rows * td->fnt->hgt + (oy + oy);
2302     td->win = std::make_unique<infowin>();
2303     Infowin_set(td->win.get());
2304     Infowin_init_top(x, y, wid, hgt, 0, Metadpy->fg, Metadpy->bg);
2305
2306 #if defined(USE_XIM)
2307     Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask);
2308 #else
2309     Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
2310 #endif
2311
2312     Infowin_set_name(name);
2313     Infowin->ox = ox;
2314     Infowin->oy = oy;
2315     ch = XAllocClassHint();
2316
2317     if (ch == nullptr) {
2318         quit("XAllocClassHint failed");
2319     }
2320
2321     strcpy(res_name, name);
2322     res_name[0] = force_lower(res_name[0]);
2323     ch->res_name = res_name;
2324
2325     strcpy(res_class, "Angband");
2326     ch->res_class = res_class;
2327
2328     XSetClassHint(Metadpy->dpy, Infowin->win, ch);
2329     XFree(ch);
2330     sh = XAllocSizeHints();
2331     if (sh == nullptr) {
2332         quit("XAllocSizeHints failed");
2333     }
2334
2335     if (i == 0) {
2336         sh->flags = PMinSize | PMaxSize;
2337         sh->min_width = 80 * td->fnt->wid + (ox + ox);
2338         sh->min_height = 24 * td->fnt->hgt + (oy + oy);
2339         sh->max_width = 255 * td->fnt->wid + (ox + ox);
2340         sh->max_height = 255 * td->fnt->hgt + (oy + oy);
2341     } else {
2342         sh->flags = PMinSize | PMaxSize;
2343         sh->min_width = td->fnt->wid + (ox + ox);
2344         sh->min_height = td->fnt->hgt + (oy + oy);
2345         sh->max_width = 256 * td->fnt->wid + (ox + ox);
2346         sh->max_height = 256 * td->fnt->hgt + (oy + oy);
2347     }
2348
2349     sh->flags |= PResizeInc;
2350     sh->width_inc = td->fnt->wid;
2351     sh->height_inc = td->fnt->hgt;
2352     sh->flags |= PBaseSize;
2353     sh->base_width = (ox + ox);
2354     sh->base_height = (oy + oy);
2355     XSetWMNormalHints(Metadpy->dpy, Infowin->win, sh);
2356     XFree(sh);
2357     Infowin_map();
2358
2359 #ifdef USE_XIM
2360     wh = XAllocWMHints();
2361     if (wh == nullptr) {
2362         quit("XAllocWMHints failed");
2363     }
2364     wh->flags = InputHint;
2365     wh->input = True;
2366     XSetWMHints(Metadpy->dpy, Infowin->win, wh);
2367     XFree(wh);
2368 #endif
2369
2370     if ((x >= 0) && (y >= 0)) {
2371         Infowin_impell(x, y);
2372     }
2373
2374     term_init(t, cols, rows, num);
2375     t->soft_cursor = true;
2376     t->attr_blank = TERM_WHITE;
2377     t->char_blank = ' ';
2378     t->xtra_hook = game_term_xtra_x11;
2379     t->curs_hook = game_term_curs_x11;
2380     t->bigcurs_hook = game_term_bigcurs_x11;
2381     t->wipe_hook = game_term_wipe_x11;
2382     t->text_hook = game_term_text_x11;
2383     t->nuke_hook = game_term_nuke_x11;
2384     t->data = td;
2385     term_activate(t);
2386     return 0;
2387 }
2388
2389 /*
2390  * Initialization function for an "X11" module to Angband
2391  */
2392 errr init_x11(int argc, char *argv[])
2393 {
2394     int i;
2395     concptr dpy_name = "";
2396     int num_term = 3;
2397
2398 #ifndef USE_XFT
2399     char filename[1024];
2400
2401     int pict_wid = 0;
2402     int pict_hgt = 0;
2403
2404     char *TmpData;
2405 #endif
2406
2407     for (i = 1; i < argc; i++) {
2408         if (prefix(argv[i], "-d")) {
2409             dpy_name = &argv[i][2];
2410             continue;
2411         }
2412
2413 #ifndef USE_XFT
2414         if (prefix(argv[i], "-s")) {
2415             smoothRescaling = false;
2416             continue;
2417         }
2418
2419         if (prefix(argv[i], "-a")) {
2420             arg_graphics = GRAPHICS_ADAM_BOLT;
2421             continue;
2422         }
2423
2424         if (prefix(argv[i], "-o")) {
2425             arg_graphics = GRAPHICS_ORIGINAL;
2426             continue;
2427         }
2428 #endif
2429
2430         if (prefix(argv[i], "-b")) {
2431             arg_bigtile = use_bigtile = true;
2432             continue;
2433         }
2434
2435         if (prefix(argv[i], "-n")) {
2436             num_term = atoi(&argv[i][2]);
2437             if (num_term > MAX_TERM_DATA) {
2438                 num_term = MAX_TERM_DATA;
2439             } else if (num_term < 1) {
2440                 num_term = 1;
2441             }
2442             continue;
2443         }
2444
2445         if (prefix(argv[i], "--")) {
2446             continue;
2447         }
2448
2449         plog_fmt("Ignoring option: %s", argv[i]);
2450     }
2451
2452 #ifdef USE_LOCALE
2453
2454 #ifdef JP
2455     setlocale(LC_ALL, "");
2456
2457 #ifdef DEFAULT_LOCALE
2458     if (!strcmp(setlocale(LC_ALL, nullptr), "C")) {
2459         printf("try default locale \"%s\"\n", DEFAULT_LOCALE);
2460         setlocale(LC_ALL, DEFAULT_LOCALE);
2461     }
2462 #endif
2463
2464     if (!strcmp(setlocale(LC_ALL, nullptr), "C")) {
2465         printf("WARNING: Locale is not supported. Non-english font may be displayed incorrectly.\n");
2466     }
2467
2468     if (!XSupportsLocale()) {
2469         printf("can't support locale in X\n");
2470         setlocale(LC_ALL, "C");
2471     }
2472 #else
2473     setlocale(LC_ALL, "C");
2474 #endif /* JP */
2475
2476 #endif /* USE_LOCALE */
2477
2478     if (Metadpy_init_name(dpy_name)) {
2479         return -1;
2480     }
2481
2482     xor_ = std::make_unique<infoclr>();
2483     Infoclr_set(xor_.get());
2484     Infoclr_init_ppn(Metadpy->fg, Metadpy->bg, "xor", 0);
2485     for (i = 0; i < 256; ++i) {
2486         Pixell pixel;
2487         clr[i] = std::make_unique<infoclr>();
2488         Infoclr_set(clr[i].get());
2489         color_table[i][0] = angband_color_table[i][0];
2490         color_table[i][1] = angband_color_table[i][1];
2491         color_table[i][2] = angband_color_table[i][2];
2492         color_table[i][3] = angband_color_table[i][3];
2493         pixel = ((i == 0) ? Metadpy->bg : Metadpy->fg);
2494         if (Metadpy->color) {
2495             pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]);
2496         }
2497
2498         Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0);
2499     }
2500
2501     set_atoms();
2502     for (i = 0; i < num_term; i++) {
2503         term_data *td = &data[i];
2504         term_data_init(td, i);
2505         angband_term[i] = game_term;
2506     }
2507
2508     Infowin_set(data[0].win.get());
2509     Infowin_raise();
2510     term_activate(&data[0].t);
2511
2512 #ifdef USE_XIM
2513     {
2514         char *p;
2515         p = XSetLocaleModifiers("");
2516         if (!p || !*p) {
2517             p = XSetLocaleModifiers("@im=");
2518         }
2519     }
2520     XRegisterIMInstantiateCallback(Metadpy->dpy, nullptr, nullptr, nullptr, IMInstantiateCallback, nullptr);
2521 #endif
2522
2523     if (arg_sound) {
2524         init_sound();
2525     }
2526
2527 #ifndef USE_XFT
2528     switch (arg_graphics) {
2529     case GRAPHICS_ORIGINAL:
2530         path_build(filename, sizeof(filename), ANGBAND_DIR_XTRA, "graf/8x8.bmp");
2531         if (0 == fd_close(fd_open(filename, O_RDONLY))) {
2532             use_graphics = true;
2533             pict_wid = pict_hgt = 8;
2534             ANGBAND_GRAF = "old";
2535         }
2536         break;
2537     case GRAPHICS_ADAM_BOLT:
2538         path_build(filename, sizeof(filename), ANGBAND_DIR_XTRA, "graf/16x16.bmp");
2539         if (0 == fd_close(fd_open(filename, O_RDONLY))) {
2540             use_graphics = true;
2541             pict_wid = pict_hgt = 16;
2542             ANGBAND_GRAF = "new";
2543         }
2544         break;
2545     }
2546
2547     if (use_graphics) {
2548         Display *dpy = Metadpy->dpy;
2549         XImage *tiles_raw;
2550         tiles_raw = ReadBMP(dpy, filename);
2551         for (i = 0; i < num_term; i++) {
2552             term_data *td = &data[i];
2553             term_type *t = &td->t;
2554             t->pict_hook = game_term_pict_x11;
2555             t->higher_pict = true;
2556             td->tiles = ResizeImage(dpy, tiles_raw, pict_wid, pict_hgt, td->fnt->twid, td->fnt->hgt);
2557         }
2558
2559         for (i = 0; i < num_term; i++) {
2560             term_data *td = &data[i];
2561             int ii, jj;
2562             int depth = DefaultDepth(dpy, DefaultScreen(dpy));
2563             Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
2564             int total;
2565             ii = 1;
2566             jj = (depth - 1) >> 2;
2567             while (jj >>= 1) {
2568                 ii <<= 1;
2569             }
2570             total = td->fnt->twid * td->fnt->hgt * ii;
2571             TmpData = (char *)malloc(total);
2572             td->TmpImage = XCreateImage(dpy, visual, depth, ZPixmap, 0, TmpData, td->fnt->twid, td->fnt->hgt, 8, 0);
2573         }
2574     }
2575 #endif /* ! USE_XFT */
2576     return 0;
2577 }
2578
2579 #endif /* USE_X11 */