OSDN Git Service

[Refactor] COLD_BLOOD を新定義に合わせた
[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 #include <memory>
112 #include <span>
113 #include <string>
114
115 /*
116  * Available graphic modes
117  */
118 // clang-format off
119 #define GRAPHICS_NONE       0
120 #define GRAPHICS_ORIGINAL   1
121 #define GRAPHICS_ADAM_BOLT  2
122 #define GRAPHICS_HENGBAND   3
123 // clang-format on
124
125 #ifdef USE_X11
126 #ifndef __MAKEDEPEND__
127 #include <X11/Xlib.h>
128 #include <X11/Xutil.h>
129 #include <X11/keysym.h>
130 #include <X11/keysymdef.h>
131 #ifdef USE_LOCALE
132 #include <X11/Xlocale.h>
133 #endif
134 #include <X11/Xatom.h>
135 #endif /* __MAKEDEPEND__ */
136
137 #ifdef USE_XFT
138 #include <X11/Xft/Xft.h>
139 #endif
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 px, int py, const XftColor &fg, 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, &fg, Infofnt->info, px + Infofnt->wid * offset, py, (const FcChar8 *)str, byte_len);
902         offset += (byte_len > 1 ? 2 : 1);
903         str += byte_len;
904     }
905 }
906
907 static void Infofnt_text_std_xft(int x, int y, int len, const XftColor &fg, const XftColor &bg, const char *str, int utf8_len)
908 {
909     auto *draw = Infowin->draw;
910
911     const auto py = (y * Infofnt->hgt) + Infofnt->asc + Infowin->oy;
912     const auto px = (x * Infofnt->wid) + Infowin->ox;
913
914     XRectangle r{ 0, 0, static_cast<unsigned short>(Infofnt->wid * len), static_cast<unsigned short>(Infofnt->hgt) };
915     XftDrawSetClipRectangles(draw, px, py - Infofnt->asc, &r, 1);
916     XftDrawRect(draw, &bg, px, py - Infofnt->asc, r.width, r.height);
917     Infofnt_text_std_xft_draw_str(px, py, fg, str, str + utf8_len);
918     XftDrawSetClip(draw, 0);
919 }
920 #endif
921
922 /*
923  * Standard Text
924  */
925 static errr Infofnt_text_std(int x, int y, concptr str, int len)
926 {
927     if (!str || !*str) {
928         return -1;
929     }
930
931     if (len < 0) {
932         len = strlen(str);
933     }
934
935 #ifndef USE_XFT
936     y = (y * Infofnt->hgt) + Infofnt->asc + Infowin->oy;
937     x = (x * Infofnt->wid) + Infowin->ox;
938 #endif
939
940     if (Infofnt->mono) {
941 #ifndef USE_XFT
942         int i;
943         for (i = 0; i < len; ++i) {
944             XDrawImageString(Metadpy->dpy, Infowin->win, Infoclr->gc, x + i * Infofnt->wid + Infofnt->off, y, str + i, 1);
945         }
946 #endif
947     } else {
948 #ifdef JP
949         char utf8_buf[1024];
950         int utf8_len = euc_to_utf8(str, len, utf8_buf, sizeof(utf8_buf));
951         if (utf8_len < 0) {
952             return -1;
953         }
954 #endif
955
956 #ifdef USE_XFT
957         Infofnt_text_std_xft(x, y, len, Infoclr->fg, Infoclr->bg, _(utf8_buf, str), _(utf8_len, len));
958 #else
959         XmbDrawImageString(Metadpy->dpy, Infowin->win, Infofnt->info, Infoclr->gc, x, y, _(utf8_buf, str), _(utf8_len, len));
960 #endif
961     }
962
963     return 0;
964 }
965
966 /*
967  * Painting where text would be
968  */
969 static errr Infofnt_text_non(int x, int y, concptr str, int len)
970 {
971     int w, h;
972     if (len < 0) {
973         len = strlen(str);
974     }
975
976     w = len * Infofnt->wid;
977     x = x * Infofnt->wid + Infowin->ox;
978     h = Infofnt->hgt;
979     y = y * h + Infowin->oy;
980
981 #ifdef USE_XFT
982     XftDrawRect(Infowin->draw, &Infoclr->fg, x, y, w, h);
983 #else
984     XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, w, h);
985 #endif
986
987     return 0;
988 }
989
990 /*
991  * Angband specific code follows... (ANGBAND)
992  */
993
994 /*
995  * Hack -- cursor color
996  */
997 static std::unique_ptr<infoclr> xor_;
998
999 /*
1000  * Actual color table
1001  */
1002 static std::unique_ptr<infoclr> clr[256];
1003
1004 /*
1005  * Color info (unused, red, green, blue).
1006  */
1007 static byte color_table[256][4];
1008
1009 namespace {
1010 /*
1011  * A structure for each "term"
1012  */
1013 struct term_data {
1014     term_type t;
1015     std::unique_ptr<infofnt> fnt;
1016     std::unique_ptr<infowin> win;
1017 #ifndef USE_XFT
1018     XImage *tiles;
1019     XImage *TmpImage;
1020 #endif
1021 };
1022 }
1023
1024 /*
1025  * The number of term data structures
1026  */
1027 #define MAX_TERM_DATA 8
1028
1029 /*
1030  * The array of term data structures
1031  */
1032 static term_data data[MAX_TERM_DATA];
1033
1034 /* Use short names for the most commonly used elements of various structures. */
1035 #define DPY (Metadpy->dpy)
1036 #define WIN (Infowin->win)
1037
1038 /* Describe a set of co-ordinates. */
1039 struct co_ord {
1040     int x;
1041     int y;
1042 };
1043
1044 /*
1045  * A special structure to store information about the text currently
1046  * selected.
1047  */
1048 struct x11_selection_type {
1049     bool select; /* The selection is currently in use. */
1050     bool drawn; /* The selection is currently displayed. */
1051     term_type *t; /* The window where the selection is found. */
1052     co_ord init; /* The starting co-ordinates. */
1053     co_ord cur; /* The end co-ordinates (the current ones if still copying). */
1054     co_ord old; /* The previous end co-ordinates. */
1055     Time time; /* The time at which the selection was finalised. */
1056 };
1057
1058 static x11_selection_type s_ptr[1];
1059
1060 // ゲーム側へキーを送る
1061 static void send_key(const char key)
1062 {
1063     // Windows ドライバと同様、自前でキューを操作する。
1064     // 逆順に term_key_push() する方法だと長い日本語を入力したときにテキストの
1065     // 順序が入れ替わってしまう。
1066
1067     // キーバッファが一杯なら入力を捨てる
1068     const int head_nxt = game_term->key_head + 1 == game_term->key_size ? 0 : game_term->key_head + 1;
1069     if (head_nxt == game_term->key_tail) {
1070         plog_fmt("key buffer overflow, ignoring key 0x%02X", key);
1071         return;
1072     }
1073
1074     game_term->key_queue[game_term->key_head] = key;
1075     game_term->key_head = head_nxt;
1076 }
1077
1078 // ゲーム側へキー列を送る
1079 static void send_keys(const char *const keys)
1080 {
1081     for (const char *p = keys; *p != '\0'; ++p) {
1082         send_key(*p);
1083     }
1084 }
1085
1086 /*
1087  * Process a keypress event
1088  */
1089 static void react_keypress(XKeyEvent *xev)
1090 {
1091     int n, mc, ms, mo, mx;
1092     uint ks1;
1093     XKeyEvent *ev = (XKeyEvent *)(xev);
1094     KeySym ks;
1095     char buf[128];
1096     char msg[128];
1097
1098 #ifdef USE_XIM
1099     int valid_keysym = true;
1100 #endif
1101
1102 #ifdef USE_XIM
1103     if (Focuswin && Focuswin->xic) {
1104         Status status;
1105         n = XmbLookupString(Focuswin->xic, ev, buf, 125, &ks, &status);
1106         if (status == XBufferOverflow) {
1107             printf("Input is too long, and dropped\n");
1108             return;
1109         }
1110         if (status != XLookupKeySym && status != XLookupBoth) {
1111             valid_keysym = false;
1112         }
1113     } else {
1114         n = XLookupString(ev, buf, 125, &ks, nullptr);
1115     }
1116 #else
1117     n = XLookupString(ev, buf, 125, &ks, nullptr);
1118 #endif
1119
1120     buf[n] = '\0';
1121
1122 #ifdef USE_XIM
1123     if (!valid_keysym) { /* XIMからの入力時のみ false になる */
1124 #ifdef JP
1125         char euc_buf[sizeof(buf)];
1126         /* strlen + 1 を渡して文字列終端('\0')を含めて変換する */
1127         if (utf8_to_euc(buf, strlen(buf) + 1, euc_buf, sizeof(euc_buf)) < 0) {
1128             return;
1129         }
1130 #endif
1131         send_keys(_(euc_buf, buf));
1132         return;
1133     }
1134 #endif
1135
1136     if (IsModifierKey(ks)) {
1137         return;
1138     }
1139
1140     ks1 = (uint)(ks);
1141     mc = any_bits(ev->state, ControlMask);
1142     ms = any_bits(ev->state, ShiftMask);
1143     mo = any_bits(ev->state, Mod1Mask);
1144     mx = any_bits(ev->state, Mod2Mask);
1145     if (n && !mo && !mx && !IsSpecialKey(ks)) {
1146         send_keys(buf);
1147         return;
1148     }
1149
1150     switch (ks1) {
1151     case XK_Escape: {
1152         send_key(ESCAPE);
1153         return;
1154     }
1155
1156     case XK_Return: {
1157         send_key('\r');
1158         return;
1159     }
1160
1161     case XK_Tab: {
1162         send_key('\t');
1163         return;
1164     }
1165
1166     case XK_Delete: {
1167         send_key(0x7f);
1168         return;
1169     }
1170     case XK_BackSpace: {
1171         send_key('\010');
1172         return;
1173     }
1174     }
1175
1176     if (ks) {
1177         strnfmt(msg, sizeof(msg), "%c%s%s%s%s_%lX%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", (unsigned long)(ks), 13);
1178     } else {
1179         strnfmt(msg, sizeof(msg), "%c%s%s%s%sK_%X%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", ev->keycode, 13);
1180     }
1181
1182     send_keys(msg);
1183
1184     if (n && !macro_patterns.empty() && (macro_find_exact(msg) < 0)) {
1185         macro_add(msg, buf);
1186     }
1187 }
1188
1189 /*
1190  * Find the square a particular pixel is part of.
1191  */
1192 static void pixel_to_square(int *const x, int *const y, const int ox, const int oy)
1193 {
1194     (*x) = (ox - Infowin->ox) / Infofnt->wid;
1195     (*y) = (oy - Infowin->oy) / Infofnt->hgt;
1196 }
1197
1198 /*
1199  * Find the pixel at the top-left corner of a square.
1200  */
1201 static void square_to_pixel(int *const x, int *const y, const int ox, const int oy)
1202 {
1203     (*x) = ox * Infofnt->wid + Infowin->ox;
1204     (*y) = oy * Infofnt->hgt + Infowin->oy;
1205 }
1206
1207 /*
1208  * Convert co-ordinates from starting corner/opposite corner to minimum/maximum.
1209  */
1210 static void sort_co_ord(co_ord *min, co_ord *max, const co_ord *b, const co_ord *a)
1211 {
1212     min->x = std::min(a->x, b->x);
1213     min->y = std::min(a->y, b->y);
1214     max->x = std::max(a->x, b->x);
1215     max->y = std::max(a->y, b->y);
1216 }
1217
1218 #ifdef USE_XFT
1219 template <class T, class D>
1220 auto make_unique_ptr_with_deleter(T *p, D d) noexcept
1221 {
1222     return std::unique_ptr<T, D>(p, std::move(d));
1223 }
1224
1225 /*!
1226  * @brief 矩形領域の枠を描画する
1227  *
1228  * ドラッグ時の選択範囲の表示に使用する。
1229  *
1230  * @param x 矩形領域の左上のX座標(ピクセル単位)
1231  * @param y 矩形領域の左上のY座標(ピクセル単位)
1232  * @param widht 矩形領域の幅(ピクセル単位)
1233  * @param height 矩形領域の高さ(ピクセル単位)
1234  */
1235 static void draw_rectangle_frame(int x, int y, int width, int height)
1236 {
1237     auto gc = make_unique_ptr_with_deleter(XCreateGC(Metadpy->dpy, Infowin->win, 0, NULL),
1238         [dpy = Metadpy->dpy](GC gc) { XFreeGC(dpy, gc); });
1239
1240     XSetForeground(Metadpy->dpy, gc.get(), WhitePixel(Metadpy->dpy, DefaultScreen(Metadpy->dpy)));
1241     XDrawLine(Metadpy->dpy, Infowin->win, gc.get(), x, y, x + width, y);
1242     XDrawLine(Metadpy->dpy, Infowin->win, gc.get(), x, y, x, y + height);
1243     XDrawLine(Metadpy->dpy, Infowin->win, gc.get(), x + width, y, x + width, y + height);
1244     XDrawLine(Metadpy->dpy, Infowin->win, gc.get(), x, y + height, x + width, y + height);
1245 }
1246 #endif
1247
1248 #ifdef USE_XFT
1249 static void draw_cursor_xft(int x, int y, int len)
1250 {
1251     // term_what() では中央寄せ時に座標がずれるので直接取得
1252     const std::span<const char> cursor_chars(&game_term->scr->c[y][x], len);
1253
1254 #ifdef JP
1255     char utf8_buf[16];
1256     const auto utf8_len = euc_to_utf8(cursor_chars.data(), cursor_chars.size(), utf8_buf, sizeof(utf8_buf));
1257     if (utf8_len < 0) {
1258         return;
1259     }
1260 #endif
1261     Infofnt_text_std_xft(x, y, len, Infoclr->bg, Infoclr->fg, _(utf8_buf, cursor_chars.data()), _(utf8_len, len));
1262 }
1263 #endif
1264
1265 static void draw_cursor(int x, int y, int len)
1266 {
1267 #ifdef USE_XFT
1268     draw_cursor_xft(x, y, len);
1269 #else
1270     square_to_pixel(&x, &y, x, y);
1271     const auto width = Infofnt->wid * len;
1272     const auto height = Infofnt->hgt;
1273     XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, width, height);
1274 #endif
1275 }
1276
1277 /*
1278  * Remove the selection by redrawing it.
1279  */
1280 static void mark_selection_clear(int x1, int y1, int x2, int y2)
1281 {
1282     term_redraw_section(x1, y1, x2, y2);
1283 }
1284
1285 /*
1286  * Select an area by drawing a grey box around it.
1287  * NB. These two functions can cause flicker as the selection is modified,
1288  * as the game redraws the entire marked section.
1289  */
1290 static void mark_selection_mark(int x1, int y1, int x2, int y2)
1291 {
1292     square_to_pixel(&x1, &y1, x1, y1);
1293     square_to_pixel(&x2, &y2, x2, y2);
1294 #ifdef USE_XFT
1295     draw_rectangle_frame(x1, y1, x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
1296 #else
1297     XDrawRectangle(Metadpy->dpy, Infowin->win, clr[2]->gc, x1, y1, x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
1298 #endif
1299 }
1300
1301 /*
1302  * Mark a selection by drawing boxes around it (for now).
1303  */
1304 static void mark_selection(void)
1305 {
1306     co_ord min, max;
1307     term_type *old = game_term;
1308     bool draw = s_ptr->select;
1309     bool clear = s_ptr->drawn;
1310     if (s_ptr->t != old) {
1311         term_activate(s_ptr->t);
1312     }
1313
1314     if (clear) {
1315         sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->old);
1316         mark_selection_clear(min.x, min.y, max.x, max.y);
1317     }
1318     if (draw) {
1319         sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
1320         mark_selection_mark(min.x, min.y, max.x, max.y);
1321     }
1322
1323     if (s_ptr->t != old) {
1324         term_activate(old);
1325     }
1326
1327     s_ptr->old.x = s_ptr->cur.x;
1328     s_ptr->old.y = s_ptr->cur.y;
1329     s_ptr->drawn = s_ptr->select;
1330 }
1331
1332 /*
1333  * Forget a selection for one reason or another.
1334  */
1335 static void copy_x11_release(void)
1336 {
1337     s_ptr->select = false;
1338     mark_selection();
1339 }
1340
1341 /*
1342  * Start to select some text on the screen.
1343  */
1344 static void copy_x11_start(int x, int y)
1345 {
1346     if (s_ptr->select) {
1347         copy_x11_release();
1348     }
1349
1350     s_ptr->t = game_term;
1351     s_ptr->init.x = s_ptr->cur.x = s_ptr->old.x = x;
1352     s_ptr->init.y = s_ptr->cur.y = s_ptr->old.y = y;
1353 }
1354
1355 /*
1356  * Respond to movement of the mouse when selecting text.
1357  */
1358 static void copy_x11_cont(int x, int y, unsigned int buttons)
1359 {
1360     x = MIN(MAX(x, 0), game_term->wid - 1);
1361     y = MIN(MAX(y, 0), game_term->hgt - 1);
1362     if (~buttons & Button1Mask) {
1363         return;
1364     }
1365     if (s_ptr->t != game_term) {
1366         return;
1367     }
1368     if (x == s_ptr->old.x && y == s_ptr->old.y && s_ptr->select) {
1369         return;
1370     }
1371
1372     s_ptr->select = true;
1373     s_ptr->cur.x = x;
1374     s_ptr->cur.y = y;
1375     mark_selection();
1376 }
1377
1378 /*
1379  * Respond to release of the left mouse button by putting the selected text in
1380  * the primary buffer.
1381  */
1382 static void copy_x11_end(const Time time)
1383 {
1384     if (!s_ptr->select) {
1385         return;
1386     }
1387     if (s_ptr->t != game_term) {
1388         return;
1389     }
1390
1391     s_ptr->time = time;
1392     XSetSelectionOwner(Metadpy->dpy, XA_PRIMARY, Infowin->win, time);
1393     if (XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY) != Infowin->win) {
1394         s_ptr->select = false;
1395         mark_selection();
1396     }
1397 }
1398
1399 static Atom xa_targets, xa_timestamp, xa_text, xa_compound_text, xa_utf8;
1400
1401 /*
1402  * Set the required variable atoms at start-up to avoid errors later.
1403  */
1404 static void set_atoms(void)
1405 {
1406     xa_targets = XInternAtom(DPY, "TARGETS", False);
1407     xa_timestamp = XInternAtom(DPY, "TIMESTAMP", False);
1408     xa_text = XInternAtom(DPY, "TEXT", False);
1409     xa_compound_text = XInternAtom(DPY, "COMPOUND_TEXT", False);
1410     xa_utf8 = XInternAtom(DPY, "UTF8_STRING", False);
1411 }
1412
1413 static Atom request_target = 0;
1414
1415 /*
1416  * Send a message to request that the PRIMARY buffer be sent here.
1417  */
1418 static void paste_x11_request(Atom target, const Time time)
1419 {
1420     Atom property = XInternAtom(DPY, "__COPY_TEXT", False);
1421     if (XGetSelectionOwner(DPY, XA_PRIMARY) == None) {
1422         /* No selection. */
1423         /* bell("No selection found."); */
1424         return;
1425     }
1426
1427     request_target = target;
1428     XConvertSelection(DPY, XA_PRIMARY, target, property, WIN, time);
1429 }
1430
1431 /*
1432  * Add the contents of the PRIMARY buffer to the input queue.
1433  *
1434  * Hack - This doesn't use the "time" of the event, and so accepts anything a
1435  * client tries to send it.
1436  */
1437 static void paste_x11_accept(const XSelectionEvent *ptr)
1438 {
1439     unsigned long left;
1440     const long offset = 0;
1441     const long length = 32000;
1442     XTextProperty xtextproperty;
1443     errr err = 0;
1444     Atom property = XInternAtom(DPY, "__COPY_TEXT", False);
1445     if (ptr->property == None) {
1446         if (request_target == xa_compound_text) {
1447             paste_x11_request(XA_STRING, ptr->time);
1448         } else {
1449             request_target = 0;
1450             plog("Paste failure (remote client could not send).");
1451         }
1452
1453         return;
1454     }
1455
1456     if (ptr->selection != XA_PRIMARY) {
1457         plog("Paste failure (remote client did not send primary selection).");
1458         return;
1459     }
1460
1461     if (ptr->target != request_target) {
1462         plog("Paste failure (selection in unknown format).");
1463         return;
1464     }
1465
1466     if (XGetWindowProperty(Metadpy->dpy, Infowin->win, property, offset, length, true, request_target, &xtextproperty.encoding, &xtextproperty.format,
1467             &xtextproperty.nitems, &left, &xtextproperty.value) != Success) {
1468         return;
1469     }
1470
1471     if (request_target == xa_compound_text) {
1472         char **list;
1473         int count;
1474
1475         XmbTextPropertyToTextList(DPY, &xtextproperty, &list, &count);
1476
1477         if (list) {
1478             int i;
1479
1480             for (i = 0; i < count; i++) {
1481                 err = type_string(list[i], 0);
1482                 if (err) {
1483                     break;
1484                 }
1485             }
1486
1487             XFreeStringList(list);
1488         }
1489     } else {
1490         err = type_string((char *)xtextproperty.value, xtextproperty.nitems);
1491     }
1492
1493     XFree(xtextproperty.value);
1494     if (err) {
1495         plog("Paste failure (too much text selected).");
1496     }
1497 }
1498
1499 /*
1500  * Add a character to a string in preparation for sending it to another
1501  * client as a STRING.
1502  * This doesn't change anything, as clients tend not to have difficulty in
1503  * receiving this format (although the standard specifies a restricted set).
1504  * Strings do not have a colour.
1505  */
1506 static bool paste_x11_send_text(XSelectionRequestEvent *rq)
1507 {
1508     char buf[1024];
1509     co_ord max, min;
1510     TERM_LEN x, y;
1511     int l;
1512     TERM_COLOR a;
1513     char c;
1514
1515     sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
1516     if (XGetSelectionOwner(DPY, XA_PRIMARY) != WIN) {
1517         return false;
1518     }
1519
1520     for (y = 0; y < game_term->hgt; y++) {
1521 #ifdef JP
1522         int kanji = 0;
1523 #endif
1524         if (y < min.y) {
1525             continue;
1526         }
1527         if (y > max.y) {
1528             break;
1529         }
1530
1531         for (l = 0, x = 0; x < game_term->wid; x++) {
1532 #ifdef JP
1533             if (x > max.x) {
1534                 break;
1535             }
1536
1537             term_what(x, y, &a, &c);
1538             if (1 == kanji) {
1539                 kanji = 2;
1540             } else if (iskanji(c)) {
1541                 kanji = 1;
1542             } else {
1543                 kanji = 0;
1544             }
1545
1546             if (x < min.x) {
1547                 continue;
1548             }
1549
1550             /*
1551              * A single kanji character was divided in two...
1552              * Delete the garbage.
1553              */
1554             if ((2 == kanji && x == min.x) || (1 == kanji && x == max.x)) {
1555                 c = ' ';
1556             }
1557 #else
1558             if (x > max.x) {
1559                 break;
1560             }
1561             if (x < min.x) {
1562                 continue;
1563             }
1564
1565             term_what(x, y, &a, &c);
1566 #endif
1567
1568             buf[l] = c;
1569             l++;
1570         }
1571
1572         // 末尾の空白を削る。
1573         while (l >= 1 && buf[l - 1] == ' ') {
1574             l--;
1575         }
1576
1577         // 複数行の場合、各行末に改行を付加。
1578         if (min.y != max.y) {
1579             buf[l] = '\n';
1580             l++;
1581         }
1582
1583         buf[l] = '\0';
1584
1585 #ifdef JP
1586         char utf8_buf[2048];
1587         const int len = euc_to_utf8(buf, l, utf8_buf, sizeof(utf8_buf));
1588 #endif
1589         if (_(len, l) > 0) {
1590             XChangeProperty(DPY, rq->requestor, rq->property, xa_utf8, 8, PropModeAppend, (unsigned char *)_(utf8_buf, buf), _(len, l));
1591         }
1592     }
1593
1594     return true;
1595 }
1596
1597 /*
1598  * Send some text requested by another X client.
1599  */
1600 static void paste_x11_send(XSelectionRequestEvent *rq)
1601 {
1602     XEvent event;
1603     XSelectionEvent *ptr = &(event.xselection);
1604
1605     ptr->type = SelectionNotify;
1606     ptr->property = rq->property;
1607     ptr->display = rq->display;
1608     ptr->requestor = rq->requestor;
1609     ptr->selection = rq->selection;
1610     ptr->target = rq->target;
1611     ptr->time = rq->time;
1612
1613     /*
1614      * Paste the appropriate information for each target type.
1615      * Note that this currently rejects MULTIPLE targets.
1616      */
1617
1618     if (rq->target == xa_utf8) {
1619         if (!paste_x11_send_text(rq)) {
1620             ptr->property = None;
1621         }
1622     } else if (rq->target == xa_targets) {
1623         Atom target_list[] = { xa_targets, xa_utf8 };
1624         XChangeProperty(
1625             DPY, rq->requestor, rq->property, XA_ATOM, 32, PropModeReplace, (unsigned char *)target_list, (sizeof(target_list) / sizeof(target_list[0])));
1626     } else {
1627         ptr->property = None;
1628     }
1629
1630     XSendEvent(DPY, rq->requestor, false, NoEventMask, &event);
1631 }
1632
1633 /*
1634  * Handle various events conditional on presses of a mouse button.
1635  */
1636 static void handle_button(Time time, int x, int y, int button, bool press)
1637 {
1638     pixel_to_square(&x, &y, x, y);
1639
1640     if (press && button == 1) {
1641         copy_x11_start(x, y);
1642     }
1643     if (!press && button == 1) {
1644         copy_x11_end(time);
1645     }
1646     if (!press && button == 2) {
1647         paste_x11_request(xa_compound_text, time);
1648     }
1649 }
1650
1651 /*
1652  * Process events
1653  */
1654 static errr CheckEvent(bool wait)
1655 {
1656     term_data *old_td = (term_data *)(game_term->data);
1657
1658     XEvent xev_body, *xev = &xev_body;
1659
1660     term_data *td = nullptr;
1661     infowin *iwin = nullptr;
1662
1663     int i;
1664
1665 #ifdef USE_XIM
1666     do {
1667 #endif
1668
1669         if (!wait && !XPending(Metadpy->dpy)) {
1670             return 1;
1671         }
1672
1673         if (s_ptr->select && !s_ptr->drawn) {
1674             mark_selection();
1675         }
1676
1677         XNextEvent(Metadpy->dpy, xev);
1678
1679 #ifdef USE_XIM
1680     } while (XFilterEvent(xev, xev->xany.window));
1681 #endif
1682
1683     if (xev->type == MappingNotify) {
1684         XRefreshKeyboardMapping(&xev->xmapping);
1685         return 0;
1686     }
1687
1688     for (i = 0; i < MAX_TERM_DATA; i++) {
1689         if (!data[i].win) {
1690             continue;
1691         }
1692         if (xev->xany.window == data[i].win->win) {
1693             td = &data[i];
1694             iwin = td->win.get();
1695             break;
1696         }
1697     }
1698
1699     if (!td || !iwin) {
1700         return 0;
1701     }
1702
1703     term_activate(&td->t);
1704     Infowin_set(iwin);
1705     switch (xev->type) {
1706     case ButtonPress:
1707     case ButtonRelease: {
1708         bool press = (xev->type == ButtonPress);
1709         int x = xev->xbutton.x;
1710         int y = xev->xbutton.y;
1711         int z;
1712         if (xev->xbutton.button == Button1) {
1713             z = 1;
1714         } else if (xev->xbutton.button == Button2) {
1715             z = 2;
1716         } else if (xev->xbutton.button == Button3) {
1717             z = 3;
1718         } else if (xev->xbutton.button == Button4) {
1719             z = 4;
1720         } else if (xev->xbutton.button == Button5) {
1721             z = 5;
1722         } else {
1723             z = 0;
1724         }
1725
1726         handle_button(xev->xbutton.time, x, y, z, press);
1727         break;
1728     }
1729     case EnterNotify:
1730     case LeaveNotify: {
1731         break;
1732     }
1733     case MotionNotify: {
1734         int x = xev->xmotion.x;
1735         int y = xev->xmotion.y;
1736         unsigned int z = xev->xmotion.state;
1737         pixel_to_square(&x, &y, x, y);
1738         copy_x11_cont(x, y, z);
1739         break;
1740     }
1741     case SelectionNotify: {
1742         paste_x11_accept(&(xev->xselection));
1743         break;
1744     }
1745     case SelectionRequest: {
1746         paste_x11_send(&(xev->xselectionrequest));
1747         break;
1748     }
1749     case SelectionClear: {
1750         s_ptr->select = false;
1751         mark_selection();
1752         break;
1753     }
1754     case KeyRelease: {
1755         break;
1756     }
1757     case KeyPress: {
1758         term_activate(&old_td->t);
1759         react_keypress(&(xev->xkey));
1760         break;
1761     }
1762     case Expose: {
1763         int x1, x2, y1, y2;
1764         x1 = (xev->xexpose.x - Infowin->ox) / Infofnt->wid;
1765         x2 = (xev->xexpose.x + xev->xexpose.width - Infowin->ox) / Infofnt->wid;
1766
1767         y1 = (xev->xexpose.y - Infowin->oy) / Infofnt->hgt;
1768         y2 = (xev->xexpose.y + xev->xexpose.height - Infowin->oy) / Infofnt->hgt;
1769
1770         term_redraw_section(x1, y1, x2, y2);
1771         break;
1772     }
1773     case MapNotify: {
1774         Infowin->mapped = 1;
1775         game_term->mapped_flag = true;
1776         break;
1777     }
1778     case UnmapNotify: {
1779         Infowin->mapped = 0;
1780         game_term->mapped_flag = false;
1781         break;
1782     }
1783     case ConfigureNotify: {
1784         int cols, rows, wid, hgt;
1785         int ox = Infowin->ox;
1786         int oy = Infowin->oy;
1787         Infowin->x = xev->xconfigure.x;
1788         Infowin->y = xev->xconfigure.y;
1789         Infowin->w = xev->xconfigure.width;
1790         Infowin->h = xev->xconfigure.height;
1791         cols = ((Infowin->w - (ox + ox)) / td->fnt->wid);
1792         rows = ((Infowin->h - (oy + oy)) / td->fnt->hgt);
1793         if (cols < 1) {
1794             cols = 1;
1795         }
1796         if (rows < 1) {
1797             rows = 1;
1798         }
1799
1800         if (td == &data[0]) {
1801             if (cols < MAIN_TERM_MIN_COLS) {
1802                 cols = MAIN_TERM_MIN_COLS;
1803             }
1804             if (rows < MAIN_TERM_MIN_ROWS) {
1805                 rows = MAIN_TERM_MIN_ROWS;
1806             }
1807         }
1808
1809         wid = cols * td->fnt->wid + (ox + ox);
1810         hgt = rows * td->fnt->hgt + (oy + oy);
1811         term_resize(cols, rows);
1812         if ((Infowin->w != wid) || (Infowin->h != hgt)) {
1813             Infowin_set(td->win.get());
1814             Infowin_resize(wid, hgt);
1815         }
1816
1817         break;
1818     }
1819 #ifdef USE_XIM
1820     case FocusIn: {
1821         if (iwin->xic) {
1822             XSetICFocus(iwin->xic);
1823         }
1824         Focuswin = iwin;
1825         break;
1826     }
1827     case FocusOut: {
1828         if (iwin->xic) {
1829             XUnsetICFocus(iwin->xic);
1830         }
1831
1832         break;
1833     }
1834 #endif
1835     }
1836
1837     term_activate(&old_td->t);
1838     Infowin_set(old_td->win.get());
1839     return 0;
1840 }
1841
1842 /*
1843  * An array of sound file names
1844  */
1845 static concptr sound_file[SOUND_MAX];
1846
1847 /*
1848  * Check for existance of a file
1849  */
1850 static bool check_file(concptr s)
1851 {
1852     FILE *fff;
1853
1854     fff = fopen(s, "r");
1855     if (!fff) {
1856         return false;
1857     }
1858
1859     fclose(fff);
1860     return true;
1861 }
1862
1863 /*
1864  * Initialize sound
1865  */
1866 static void init_sound(void)
1867 {
1868     const auto &dir_xtra_sound = path_build(ANGBAND_DIR_XTRA, "sound");
1869     for (auto i = 1; i < SOUND_MAX; i++) {
1870         std::string wav = angband_sound_name[i];
1871         wav.append(".wav");
1872         const auto &path = path_build(dir_xtra_sound, wav);
1873         const auto &filename = path.string();
1874         if (check_file(filename.data())) {
1875             sound_file[i] = string_make(filename.data());
1876         }
1877     }
1878
1879     use_sound = true;
1880     return;
1881 }
1882
1883 /*
1884  * Hack -- make a sound
1885  */
1886 static errr game_term_xtra_x11_sound(int v)
1887 {
1888     if (!use_sound) {
1889         return 1;
1890     }
1891     if ((v < 0) || (v >= SOUND_MAX)) {
1892         return 1;
1893     }
1894     if (!sound_file[v]) {
1895         return 1;
1896     }
1897
1898     std::string buf = "./playwave.sh ";
1899     buf.append(sound_file[v]).append("\n");
1900     return system(buf.data()) < 0;
1901 }
1902
1903 /*
1904  * Handle "activation" of a term
1905  */
1906 static errr game_term_xtra_x11_level(int v)
1907 {
1908     term_data *td = (term_data *)(game_term->data);
1909     if (v) {
1910         Infowin_set(td->win.get());
1911         Infofnt_set(td->fnt.get());
1912     }
1913
1914     return 0;
1915 }
1916
1917 /*
1918  * React to changes
1919  */
1920 static errr game_term_xtra_x11_react(void)
1921 {
1922     int i;
1923
1924     if (Metadpy->color) {
1925         for (i = 0; i < 256; i++) {
1926             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])) {
1927                 Pixell pixel;
1928                 color_table[i][0] = angband_color_table[i][0];
1929                 color_table[i][1] = angband_color_table[i][1];
1930                 color_table[i][2] = angband_color_table[i][2];
1931                 color_table[i][3] = angband_color_table[i][3];
1932                 pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]);
1933                 Infoclr_set(clr[i].get());
1934                 Infoclr_change_fg(pixel);
1935             }
1936         }
1937     }
1938
1939     return 0;
1940 }
1941
1942 /*
1943  * Handle a "special request"
1944  */
1945 static errr game_term_xtra_x11(int n, int v)
1946 {
1947     switch (n) {
1948     case TERM_XTRA_NOISE:
1949         Metadpy_do_beep();
1950         return 0;
1951     case TERM_XTRA_SOUND:
1952         return game_term_xtra_x11_sound(v);
1953 #ifdef USE_XFT
1954     case TERM_XTRA_FRESH:
1955         Metadpy_update(1, 1, 0);
1956         return 0;
1957 #else
1958     case TERM_XTRA_FRESH:
1959         Metadpy_update(1, 0, 0);
1960         return 0;
1961 #endif
1962     case TERM_XTRA_BORED:
1963         return CheckEvent(0);
1964     case TERM_XTRA_EVENT:
1965         return CheckEvent(v);
1966     case TERM_XTRA_FLUSH:
1967         while (!CheckEvent(false)) {
1968             ;
1969         }
1970         return 0;
1971     case TERM_XTRA_LEVEL:
1972         return game_term_xtra_x11_level(v);
1973     case TERM_XTRA_CLEAR:
1974         Infowin_wipe();
1975         s_ptr->drawn = false;
1976         return 0;
1977     case TERM_XTRA_DELAY:
1978         usleep(1000 * v);
1979         return 0;
1980     case TERM_XTRA_REACT:
1981         return game_term_xtra_x11_react();
1982     }
1983
1984     return 1;
1985 }
1986
1987 /*
1988  * Draw the cursor as an inverted rectangle.
1989  *
1990  * Consider a rectangular outline like "main-mac.c".  XXX XXX
1991  */
1992 static errr game_term_curs_x11(int x, int y)
1993 {
1994     if (use_graphics) {
1995 #ifdef USE_XFT
1996         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->wid - 1, Infofnt->hgt - 1);
1997         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->wid - 3, Infofnt->hgt - 3);
1998 #else
1999         XDrawRectangle(
2000             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->wid - 1, Infofnt->hgt - 1);
2001         XDrawRectangle(
2002             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->wid - 3, Infofnt->hgt - 3);
2003 #endif
2004     } else {
2005         Infoclr_set(xor_.get());
2006         draw_cursor(x, y, 1);
2007     }
2008
2009     return 0;
2010 }
2011
2012 /*
2013  * Draw the double width cursor
2014  */
2015 static errr game_term_bigcurs_x11(int x, int y)
2016 {
2017     if (use_graphics) {
2018 #ifdef USE_XFT
2019         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->twid - 1, Infofnt->hgt - 1);
2020         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->twid - 3, Infofnt->hgt - 3);
2021 #else
2022         XDrawRectangle(
2023             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->twid - 1, Infofnt->hgt - 1);
2024         XDrawRectangle(
2025             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->twid - 3, Infofnt->hgt - 3);
2026 #endif
2027     } else {
2028         Infoclr_set(xor_.get());
2029         draw_cursor(x, y, 2);
2030     }
2031
2032     return 0;
2033 }
2034
2035 /*
2036  * Erase some characters.
2037  */
2038 static errr game_term_wipe_x11(int x, int y, int n)
2039 {
2040     Infoclr_set(clr[TERM_DARK].get());
2041     Infofnt_text_non(x, y, "", n);
2042     s_ptr->drawn = false;
2043     return 0;
2044 }
2045
2046 /*
2047  * Draw some textual characters.
2048  */
2049 static errr game_term_text_x11(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
2050 {
2051     Infoclr_set(clr[a].get());
2052     Infofnt_text_std(x, y, s, n);
2053     s_ptr->drawn = false;
2054     return 0;
2055 }
2056
2057 #ifndef USE_XFT
2058 /*
2059  * Draw some graphical characters.
2060  */
2061 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)
2062 {
2063     int i, x1, y1;
2064
2065     TERM_COLOR a;
2066     char c;
2067
2068     TERM_COLOR ta;
2069     char tc;
2070
2071     int x2, y2;
2072     int k, l;
2073
2074     unsigned long pixel, blank;
2075
2076     term_data *td = (term_data *)(game_term->data);
2077
2078     y *= Infofnt->hgt;
2079     x *= Infofnt->wid;
2080
2081     y += Infowin->oy;
2082     x += Infowin->ox;
2083     for (i = 0; i < n; ++i, x += td->fnt->wid) {
2084         a = *ap++;
2085         c = *cp++;
2086         x1 = (c & 0x7F) * td->fnt->twid;
2087         y1 = (a & 0x7F) * td->fnt->hgt;
2088         if (td->tiles->width < x1 + td->fnt->wid || td->tiles->height < y1 + td->fnt->hgt) {
2089             XFillRectangle(Metadpy->dpy, td->win->win, clr[0]->gc, x, y, td->fnt->twid, td->fnt->hgt);
2090             continue;
2091         }
2092
2093         ta = *tap++;
2094         tc = *tcp++;
2095
2096         x2 = (tc & 0x7F) * td->fnt->twid;
2097         y2 = (ta & 0x7F) * td->fnt->hgt;
2098
2099         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) {
2100             XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->tiles, x1, y1, x, y, td->fnt->twid, td->fnt->hgt);
2101         } else {
2102             blank = XGetPixel(td->tiles, 0, td->fnt->hgt * 6);
2103             for (k = 0; k < td->fnt->twid; k++) {
2104                 for (l = 0; l < td->fnt->hgt; l++) {
2105                     if ((pixel = XGetPixel(td->tiles, x1 + k, y1 + l)) == blank) {
2106                         pixel = XGetPixel(td->tiles, x2 + k, y2 + l);
2107                     }
2108
2109                     XPutPixel(td->TmpImage, k, l, pixel);
2110                 }
2111             }
2112
2113             XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->TmpImage, 0, 0, x, y, td->fnt->twid, td->fnt->hgt);
2114         }
2115     }
2116
2117     s_ptr->drawn = false;
2118     return 0;
2119 }
2120 #endif
2121
2122 #ifdef USE_XIM
2123 static void IMDestroyCallback(XIM, XPointer, XPointer);
2124
2125 static void IMInstantiateCallback(Display *display, XPointer unused1, XPointer unused2)
2126 {
2127     XIM xim;
2128     XIMCallback ximcallback;
2129     XIMStyles *xim_styles = nullptr;
2130     int i;
2131
2132     (void)unused1;
2133     (void)unused2;
2134
2135     xim = XOpenIM(display, nullptr, nullptr, nullptr);
2136     if (!xim) {
2137         printf("can't open IM\n");
2138         return;
2139     }
2140
2141     ximcallback.callback = IMDestroyCallback;
2142     ximcallback.client_data = nullptr;
2143     XSetIMValues(xim, XNDestroyCallback, &ximcallback, nullptr);
2144     XGetIMValues(xim, XNQueryInputStyle, &xim_styles, nullptr);
2145     for (i = 0; i < xim_styles->count_styles; i++) {
2146         if (xim_styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) {
2147             break;
2148         }
2149     }
2150     if (i >= xim_styles->count_styles) {
2151         printf("Sorry, your IM does not support 'Root' preedit style...\n");
2152         XCloseIM(xim);
2153         return;
2154     }
2155     XFree(xim_styles);
2156
2157     Metadpy->xim = xim;
2158
2159     for (i = 0; i < MAX_TERM_DATA; i++) {
2160         infowin *iwin = data[i].win.get();
2161         if (!iwin) {
2162             continue;
2163         }
2164         iwin->xic = XCreateIC(xim, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing), XNClientWindow, iwin->win, XNFocusWindow, iwin->win, nullptr);
2165         if (!iwin->xic) {
2166             printf("Can't create input context for Term%d\n", i);
2167             continue;
2168         }
2169
2170         if (XGetICValues(iwin->xic, XNFilterEvents, &iwin->xic_mask, nullptr) != nullptr) {
2171             iwin->xic_mask = 0L;
2172         }
2173
2174         XSelectInput(Metadpy->dpy, iwin->win, iwin->mask | iwin->xic_mask);
2175     }
2176
2177     return;
2178 }
2179
2180 static void IMDestroyCallback(XIM xim, XPointer client_data, XPointer call_data)
2181 {
2182     int i;
2183     (void)xim;
2184     (void)client_data;
2185
2186     if (call_data == nullptr) {
2187         XRegisterIMInstantiateCallback(Metadpy->dpy, nullptr, nullptr, nullptr, IMInstantiateCallback, nullptr);
2188     }
2189
2190     for (i = 0; i < MAX_TERM_DATA; i++) {
2191         infowin *iwin = data[i].win.get();
2192         if (!iwin) {
2193             continue;
2194         }
2195         if (iwin->xic_mask) {
2196             XSelectInput(Metadpy->dpy, iwin->win, iwin->mask);
2197             iwin->xic_mask = 0L;
2198         }
2199         iwin->xic = nullptr;
2200     }
2201
2202     Metadpy->xim = nullptr;
2203 }
2204 #endif
2205
2206 static char force_lower(char a)
2207 {
2208     return isupper(a) ? tolower(a) : a;
2209 }
2210
2211 static void game_term_nuke_x11(term_type *)
2212 {
2213     for (auto i = 0; i < MAX_TERM_DATA; i++) {
2214         infofnt *ifnt = data[i].fnt.get();
2215         infowin *iwin = data[i].win.get();
2216         if (ifnt && ifnt->info)
2217 #ifdef USE_XFT
2218             XftFontClose(Metadpy->dpy, ifnt->info);
2219 #else
2220             XFreeFontSet(Metadpy->dpy, ifnt->info);
2221 #endif
2222         if (iwin && iwin->xic) {
2223             XDestroyIC(iwin->xic);
2224         }
2225 #ifdef USE_XFT
2226         if (iwin && iwin->draw) {
2227             XftDrawDestroy(iwin->draw);
2228         }
2229 #endif
2230         angband_terms[i] = nullptr;
2231     }
2232
2233     if (Metadpy->xim) {
2234         XCloseIM(Metadpy->xim);
2235     }
2236     XUnregisterIMInstantiateCallback(Metadpy->dpy, NULL, NULL, NULL, IMInstantiateCallback, NULL);
2237     XCloseDisplay(Metadpy->dpy);
2238 }
2239
2240 /*
2241  * Initialize a term_data
2242  */
2243 static errr term_data_init(term_data *td, int i)
2244 {
2245     term_type *t = &td->t;
2246
2247     concptr name = angband_term_name[i];
2248
2249     concptr font;
2250     int x = 0;
2251     int y = 0;
2252
2253     int cols = TERM_DEFAULT_COLS;
2254     int rows = TERM_DEFAULT_ROWS;
2255
2256     int ox = 1;
2257     int oy = 1;
2258
2259     int wid, hgt, num;
2260
2261     concptr str;
2262
2263     int val;
2264
2265     XClassHint *ch;
2266
2267     char res_name[20];
2268     char res_class[20];
2269
2270     XSizeHints *sh;
2271 #ifdef USE_XIM
2272     XWMHints *wh;
2273 #endif
2274
2275     font = getenv(format("ANGBAND_X11_FONT_%d", i).data());
2276     if (!font) {
2277         font = getenv("ANGBAND_X11_FONT");
2278     }
2279
2280     if (!font) {
2281         switch (i) {
2282         case 0: {
2283             font = DEFAULT_X11_FONT_0;
2284         } break;
2285         case 1: {
2286             font = DEFAULT_X11_FONT_1;
2287         } break;
2288         case 2: {
2289             font = DEFAULT_X11_FONT_2;
2290         } break;
2291         case 3: {
2292             font = DEFAULT_X11_FONT_3;
2293         } break;
2294         case 4: {
2295             font = DEFAULT_X11_FONT_4;
2296         } break;
2297         case 5: {
2298             font = DEFAULT_X11_FONT_5;
2299         } break;
2300         case 6: {
2301             font = DEFAULT_X11_FONT_6;
2302         } break;
2303         case 7: {
2304             font = DEFAULT_X11_FONT_7;
2305         } break;
2306         default: {
2307             font = DEFAULT_X11_FONT;
2308         }
2309         }
2310     }
2311
2312     str = getenv(format("ANGBAND_X11_AT_X_%d", i).data());
2313     x = (str != nullptr) ? atoi(str) : -1;
2314
2315     str = getenv(format("ANGBAND_X11_AT_Y_%d", i).data());
2316     y = (str != nullptr) ? atoi(str) : -1;
2317
2318     str = getenv(format("ANGBAND_X11_COLS_%d", i).data());
2319     val = (str != nullptr) ? atoi(str) : -1;
2320     if (val > 0) {
2321         cols = val;
2322     }
2323
2324     str = getenv(format("ANGBAND_X11_ROWS_%d", i).data());
2325     val = (str != nullptr) ? atoi(str) : -1;
2326     if (val > 0) {
2327         rows = val;
2328     }
2329
2330     if (!i) {
2331         if (cols < MAIN_TERM_MIN_COLS) {
2332             cols = MAIN_TERM_MIN_COLS;
2333         }
2334         if (rows < MAIN_TERM_MIN_ROWS) {
2335             rows = MAIN_TERM_MIN_ROWS;
2336         }
2337     }
2338
2339     str = getenv(format("ANGBAND_X11_IBOX_%d", i).data());
2340     val = (str != nullptr) ? atoi(str) : -1;
2341     if (val > 0) {
2342         ox = val;
2343     }
2344
2345     str = getenv(format("ANGBAND_X11_IBOY_%d", i).data());
2346     val = (str != nullptr) ? atoi(str) : -1;
2347     if (val > 0) {
2348         oy = val;
2349     }
2350
2351     td->fnt = std::make_unique<infofnt>();
2352     Infofnt_set(td->fnt.get());
2353     Infofnt_init_data(font);
2354
2355     num = ((i == 0) ? 1024 : 16);
2356     wid = cols * td->fnt->wid + (ox + ox);
2357     hgt = rows * td->fnt->hgt + (oy + oy);
2358     td->win = std::make_unique<infowin>();
2359     Infowin_set(td->win.get());
2360     Infowin_init_top(x, y, wid, hgt, 0, Metadpy->fg, Metadpy->bg);
2361
2362 #if defined(USE_XIM)
2363     Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask);
2364 #else
2365     Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
2366 #endif
2367
2368     Infowin_set_name(name);
2369     Infowin->ox = ox;
2370     Infowin->oy = oy;
2371     ch = XAllocClassHint();
2372
2373     if (ch == nullptr) {
2374         quit("XAllocClassHint failed");
2375     }
2376
2377     strcpy(res_name, name);
2378     res_name[0] = force_lower(res_name[0]);
2379     ch->res_name = res_name;
2380
2381     strcpy(res_class, "Angband");
2382     ch->res_class = res_class;
2383
2384     XSetClassHint(Metadpy->dpy, Infowin->win, ch);
2385     XFree(ch);
2386     sh = XAllocSizeHints();
2387     if (sh == nullptr) {
2388         quit("XAllocSizeHints failed");
2389     }
2390
2391     if (i == 0) {
2392         sh->flags = PMinSize | PMaxSize;
2393         sh->min_width = MAIN_TERM_MIN_COLS * td->fnt->wid + (ox + ox);
2394         sh->min_height = MAIN_TERM_MIN_ROWS * td->fnt->hgt + (oy + oy);
2395         sh->max_width = 255 * td->fnt->wid + (ox + ox);
2396         sh->max_height = 255 * td->fnt->hgt + (oy + oy);
2397     } else {
2398         sh->flags = PMinSize | PMaxSize;
2399         sh->min_width = td->fnt->wid + (ox + ox);
2400         sh->min_height = td->fnt->hgt + (oy + oy);
2401         sh->max_width = 256 * td->fnt->wid + (ox + ox);
2402         sh->max_height = 256 * td->fnt->hgt + (oy + oy);
2403     }
2404
2405     sh->flags |= PResizeInc;
2406     sh->width_inc = td->fnt->wid;
2407     sh->height_inc = td->fnt->hgt;
2408     sh->flags |= PBaseSize;
2409     sh->base_width = (ox + ox);
2410     sh->base_height = (oy + oy);
2411     XSetWMNormalHints(Metadpy->dpy, Infowin->win, sh);
2412     XFree(sh);
2413     Infowin_map();
2414
2415 #ifdef USE_XIM
2416     wh = XAllocWMHints();
2417     if (wh == nullptr) {
2418         quit("XAllocWMHints failed");
2419     }
2420     wh->flags = InputHint;
2421     wh->input = True;
2422     XSetWMHints(Metadpy->dpy, Infowin->win, wh);
2423     XFree(wh);
2424 #endif
2425
2426     if ((x >= 0) && (y >= 0)) {
2427         Infowin_impell(x, y);
2428     }
2429
2430     term_init(t, cols, rows, num);
2431     t->soft_cursor = true;
2432     t->attr_blank = TERM_WHITE;
2433     t->char_blank = ' ';
2434     t->xtra_hook = game_term_xtra_x11;
2435     t->curs_hook = game_term_curs_x11;
2436     t->bigcurs_hook = game_term_bigcurs_x11;
2437     t->wipe_hook = game_term_wipe_x11;
2438     t->text_hook = game_term_text_x11;
2439     t->nuke_hook = game_term_nuke_x11;
2440     t->data = td;
2441     term_activate(t);
2442     return 0;
2443 }
2444
2445 /*
2446  * Initialization function for an "X11" module to Angband
2447  */
2448 errr init_x11(int argc, char *argv[])
2449 {
2450     int i;
2451     concptr dpy_name = "";
2452     int num_term = 3;
2453
2454 #ifndef USE_XFT
2455     int pict_wid = 0;
2456     int pict_hgt = 0;
2457
2458     char *TmpData;
2459 #endif
2460
2461     for (i = 1; i < argc; i++) {
2462         if (prefix(argv[i], "-d")) {
2463             dpy_name = &argv[i][2];
2464             continue;
2465         }
2466
2467 #ifndef USE_XFT
2468         if (prefix(argv[i], "-s")) {
2469             smoothRescaling = false;
2470             continue;
2471         }
2472
2473         if (prefix(argv[i], "-a")) {
2474             arg_graphics = GRAPHICS_ADAM_BOLT;
2475             continue;
2476         }
2477
2478         if (prefix(argv[i], "-o")) {
2479             arg_graphics = GRAPHICS_ORIGINAL;
2480             continue;
2481         }
2482 #endif
2483
2484         if (prefix(argv[i], "-b")) {
2485             arg_bigtile = use_bigtile = true;
2486             continue;
2487         }
2488
2489         if (prefix(argv[i], "-n")) {
2490             num_term = atoi(&argv[i][2]);
2491             if (num_term > MAX_TERM_DATA) {
2492                 num_term = MAX_TERM_DATA;
2493             } else if (num_term < 1) {
2494                 num_term = 1;
2495             }
2496             continue;
2497         }
2498
2499         if (prefix(argv[i], "--")) {
2500             continue;
2501         }
2502
2503         plog_fmt("Ignoring option: %s", argv[i]);
2504     }
2505
2506 #ifdef USE_LOCALE
2507
2508 #ifdef JP
2509     setlocale(LC_ALL, "");
2510
2511 #ifdef DEFAULT_LOCALE
2512     if (!strcmp(setlocale(LC_ALL, nullptr), "C")) {
2513         printf("try default locale \"%s\"\n", DEFAULT_LOCALE);
2514         setlocale(LC_ALL, DEFAULT_LOCALE);
2515     }
2516 #endif
2517
2518     if (!strcmp(setlocale(LC_ALL, nullptr), "C")) {
2519         printf("WARNING: Locale is not supported. Non-english font may be displayed incorrectly.\n");
2520     }
2521
2522     if (!XSupportsLocale()) {
2523         printf("can't support locale in X\n");
2524         setlocale(LC_ALL, "C");
2525     }
2526 #else
2527     setlocale(LC_ALL, "C");
2528 #endif /* JP */
2529
2530 #endif /* USE_LOCALE */
2531
2532     if (Metadpy_init_name(dpy_name)) {
2533         return -1;
2534     }
2535
2536     xor_ = std::make_unique<infoclr>();
2537     Infoclr_set(xor_.get());
2538     Infoclr_init_ppn(Metadpy->fg, Metadpy->bg, "xor", 0);
2539     for (i = 0; i < 256; ++i) {
2540         Pixell pixel;
2541         clr[i] = std::make_unique<infoclr>();
2542         Infoclr_set(clr[i].get());
2543         color_table[i][0] = angband_color_table[i][0];
2544         color_table[i][1] = angband_color_table[i][1];
2545         color_table[i][2] = angband_color_table[i][2];
2546         color_table[i][3] = angband_color_table[i][3];
2547         pixel = ((i == 0) ? Metadpy->bg : Metadpy->fg);
2548         if (Metadpy->color) {
2549             pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]);
2550         }
2551
2552         Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0);
2553     }
2554
2555     set_atoms();
2556     for (i = 0; i < num_term; i++) {
2557         term_data *td = &data[i];
2558         term_data_init(td, i);
2559         angband_terms[i] = game_term;
2560     }
2561
2562     Infowin_set(data[0].win.get());
2563     Infowin_raise();
2564     term_activate(&data[0].t);
2565
2566 #ifdef USE_XIM
2567     {
2568         char *p;
2569         p = XSetLocaleModifiers("");
2570         if (!p || !*p) {
2571             p = XSetLocaleModifiers("@im=");
2572         }
2573     }
2574     XRegisterIMInstantiateCallback(Metadpy->dpy, nullptr, nullptr, nullptr, IMInstantiateCallback, nullptr);
2575 #endif
2576
2577     if (arg_sound) {
2578         init_sound();
2579     }
2580
2581 #ifndef USE_XFT
2582     char filename[1024]{};
2583     switch (arg_graphics) {
2584     case GRAPHICS_ORIGINAL: {
2585         const auto &path = path_build(ANGBAND_DIR_XTRA, "graf/8x8.bmp");
2586         if (0 == fd_close(fd_open(path, O_RDONLY))) {
2587             use_graphics = true;
2588             pict_wid = pict_hgt = 8;
2589             ANGBAND_GRAF = "old";
2590             angband_strcpy(filename, path.string().data(), sizeof(filename));
2591         }
2592         break;
2593     }
2594     case GRAPHICS_ADAM_BOLT: {
2595         const auto &path = path_build(ANGBAND_DIR_XTRA, "graf/16x16.bmp");
2596         if (0 == fd_close(fd_open(path, O_RDONLY))) {
2597             use_graphics = true;
2598             pict_wid = pict_hgt = 16;
2599             ANGBAND_GRAF = "new";
2600             angband_strcpy(filename, path.string().data(), sizeof(filename));
2601         }
2602         break;
2603     }
2604     }
2605
2606     if (use_graphics) {
2607         Display *dpy = Metadpy->dpy;
2608         XImage *tiles_raw;
2609         tiles_raw = ReadBMP(dpy, filename);
2610         for (i = 0; i < num_term; i++) {
2611             term_data *td = &data[i];
2612             term_type *t = &td->t;
2613             t->pict_hook = game_term_pict_x11;
2614             t->higher_pict = true;
2615             td->tiles = ResizeImage(dpy, tiles_raw, pict_wid, pict_hgt, td->fnt->twid, td->fnt->hgt);
2616         }
2617
2618         for (i = 0; i < num_term; i++) {
2619             term_data *td = &data[i];
2620             int ii, jj;
2621             int depth = DefaultDepth(dpy, DefaultScreen(dpy));
2622             Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
2623             int total;
2624             ii = 1;
2625             jj = (depth - 1) >> 2;
2626             while (jj >>= 1) {
2627                 ii <<= 1;
2628             }
2629             total = td->fnt->twid * td->fnt->hgt * ii;
2630             TmpData = (char *)malloc(total);
2631             td->TmpImage = XCreateImage(dpy, visual, depth, ZPixmap, 0, TmpData, td->fnt->twid, td->fnt->hgt, 8, 0);
2632         }
2633     }
2634 #endif /* ! USE_XFT */
2635     return 0;
2636 }
2637
2638 #endif /* USE_X11 */