OSDN Git Service

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