OSDN Git Service

Merge pull request #2940 from backwardsEric/sprintf-string-format
[hengbandforosx/hengbandosx.git] / src / main-x11.cpp
1 /* File: main-x11.c */
2
3 /*
4  * Copyright (c) 1997 Ben Harrison, and others
5  *
6  * This software may be copied and distributed for educational, research,
7  * and not for profit purposes provided that this copyright and statement
8  * are included in all such copies.
9  */
10
11 /*
12  * This file helps Angband work with UNIX/X11 computers.
13  *
14  * To use this file, compile with "USE_X11" defined, and link against all
15  * the various "X11" libraries which may be needed.
16  *
17  * Part of this file provides a user interface package composed of several
18  * pseudo-objects, including "metadpy" (a display), "infowin" (a window),
19  * "infoclr" (a color), and "infofnt" (a font).  Actually, the package was
20  * originally much more interesting, but it was bastardized to keep this
21  * file simple.
22  *
23  * The rest of this file is an implementation of "main-xxx.c" for X11.
24  *
25  * Most of this file is by Ben Harrison (benh@phial.com).
26  */
27
28 /*
29  * The following shell script can be used to launch Angband, assuming that
30  * it was extracted into "~/Angband", and compiled using "USE_X11", on a
31  * Linux machine, with a 1280x1024 screen, using 6 windows (with the given
32  * characteristics), with gamma correction of 1.8 -> (1 / 1.8) * 256 = 142,
33  * and without graphics (add "-g" for graphics).  Just copy this comment
34  * into a file, remove the leading " * " characters (and the head/tail of
35  * this comment), and make the file executable.
36  *
37  *
38  * #!/bin/csh
39  *
40  * # Describe attempt
41  * echo "Launching angband..."
42  * sleep 2
43  *
44  * # Main window
45  * setenv ANGBAND_X11_FONT_0 10x20
46  * setenv ANGBAND_X11_AT_X_0 5
47  * setenv ANGBAND_X11_AT_Y_0 510
48  *
49  * # Message window
50  * setenv ANGBAND_X11_FONT_1 8x13
51  * setenv ANGBAND_X11_AT_X_1 5
52  * setenv ANGBAND_X11_AT_Y_1 22
53  * setenv ANGBAND_X11_ROWS_1 35
54  *
55  * # Inventory window
56  * setenv ANGBAND_X11_FONT_2 8x13
57  * setenv ANGBAND_X11_AT_X_2 635
58  * setenv ANGBAND_X11_AT_Y_2 182
59  * setenv ANGBAND_X11_ROWS_3 23
60  *
61  * # Equipment window
62  * setenv ANGBAND_X11_FONT_3 8x13
63  * setenv ANGBAND_X11_AT_X_3 635
64  * setenv ANGBAND_X11_AT_Y_3 22
65  * setenv ANGBAND_X11_ROWS_3 12
66  *
67  * # Monster recall window
68  * setenv ANGBAND_X11_FONT_4 6x13
69  * setenv ANGBAND_X11_AT_X_4 817
70  * setenv ANGBAND_X11_AT_Y_4 847
71  * setenv ANGBAND_X11_COLS_4 76
72  * setenv ANGBAND_X11_ROWS_4 11
73  *
74  * # Object recall window
75  * setenv ANGBAND_X11_FONT_5 6x13
76  * setenv ANGBAND_X11_AT_X_5 817
77  * setenv ANGBAND_X11_AT_Y_5 520
78  * setenv ANGBAND_X11_COLS_5 76
79  * setenv ANGBAND_X11_ROWS_5 24
80  *
81  * # The build directory
82  * cd ~/Angband
83  *
84  * # Gamma correction
85  * setenv ANGBAND_X11_GAMMA 142
86  *
87  * # Launch Angband
88  * ./src/angband -mx11 -- -n6 &
89  *
90  */
91
92 #include "cmd-io/macro-util.h"
93 #include "game-option/runtime-arguments.h"
94 #include "game-option/special-options.h"
95 #include "io/files-util.h"
96 #include "locale/japanese.h"
97 #include "locale/utf-8.h"
98 #include "main/sound-definitions-table.h"
99 #include "main/sound-of-music.h"
100 #include "main/x11-type-string.h"
101 #include "system/angband.h"
102 #include "system/system-variables.h"
103 #include "term/gameterm.h"
104 #include "term/term-color-types.h"
105 #include "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 < 80) {
1736                 cols = 80;
1737             }
1738             if (rows < 24) {
1739                 rows = 24;
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     int i;
1803     char buf[1024];
1804     char dir_xtra_sound[1024];
1805     path_build(dir_xtra_sound, sizeof(dir_xtra_sound), ANGBAND_DIR_XTRA, "sound");
1806     for (i = 1; i < SOUND_MAX; i++) {
1807         std::string wav = angband_sound_name[i];
1808         wav.append(".wav");
1809         path_build(buf, sizeof(buf), dir_xtra_sound, wav.data());
1810         if (check_file(buf)) {
1811             sound_file[i] = string_make(buf);
1812         }
1813     }
1814
1815     use_sound = true;
1816     return;
1817 }
1818
1819 /*
1820  * Hack -- make a sound
1821  */
1822 static errr game_term_xtra_x11_sound(int v)
1823 {
1824     if (!use_sound) {
1825         return 1;
1826     }
1827     if ((v < 0) || (v >= SOUND_MAX)) {
1828         return 1;
1829     }
1830     if (!sound_file[v]) {
1831         return 1;
1832     }
1833
1834     std::string buf = "./playwave.sh ";
1835     buf.append(sound_file[v]).append("\n");
1836     return system(buf.data()) < 0;
1837 }
1838
1839 /*
1840  * Handle "activation" of a term
1841  */
1842 static errr game_term_xtra_x11_level(int v)
1843 {
1844     term_data *td = (term_data *)(game_term->data);
1845     if (v) {
1846         Infowin_set(td->win.get());
1847         Infofnt_set(td->fnt.get());
1848     }
1849
1850     return 0;
1851 }
1852
1853 /*
1854  * React to changes
1855  */
1856 static errr game_term_xtra_x11_react(void)
1857 {
1858     int i;
1859
1860     if (Metadpy->color) {
1861         for (i = 0; i < 256; i++) {
1862             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])) {
1863                 Pixell pixel;
1864                 color_table[i][0] = angband_color_table[i][0];
1865                 color_table[i][1] = angband_color_table[i][1];
1866                 color_table[i][2] = angband_color_table[i][2];
1867                 color_table[i][3] = angband_color_table[i][3];
1868                 pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]);
1869                 Infoclr_set(clr[i].get());
1870                 Infoclr_change_fg(pixel);
1871             }
1872         }
1873     }
1874
1875     return 0;
1876 }
1877
1878 /*
1879  * Handle a "special request"
1880  */
1881 static errr game_term_xtra_x11(int n, int v)
1882 {
1883     switch (n) {
1884     case TERM_XTRA_NOISE:
1885         Metadpy_do_beep();
1886         return 0;
1887     case TERM_XTRA_SOUND:
1888         return game_term_xtra_x11_sound(v);
1889 #ifdef USE_XFT
1890     case TERM_XTRA_FRESH:
1891         Metadpy_update(1, 1, 0);
1892         return 0;
1893 #else
1894     case TERM_XTRA_FRESH:
1895         Metadpy_update(1, 0, 0);
1896         return 0;
1897 #endif
1898     case TERM_XTRA_BORED:
1899         return CheckEvent(0);
1900     case TERM_XTRA_EVENT:
1901         return CheckEvent(v);
1902     case TERM_XTRA_FLUSH:
1903         while (!CheckEvent(false)) {
1904             ;
1905         }
1906         return 0;
1907     case TERM_XTRA_LEVEL:
1908         return game_term_xtra_x11_level(v);
1909     case TERM_XTRA_CLEAR:
1910         Infowin_wipe();
1911         s_ptr->drawn = false;
1912         return 0;
1913     case TERM_XTRA_DELAY:
1914         usleep(1000 * v);
1915         return 0;
1916     case TERM_XTRA_REACT:
1917         return game_term_xtra_x11_react();
1918     }
1919
1920     return 1;
1921 }
1922
1923 /*
1924  * Draw the cursor as an inverted rectangle.
1925  *
1926  * Consider a rectangular outline like "main-mac.c".  XXX XXX
1927  */
1928 static errr game_term_curs_x11(int x, int y)
1929 {
1930     if (use_graphics) {
1931 #ifdef USE_XFT
1932         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->wid - 1, Infofnt->hgt - 1);
1933         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->wid - 3, Infofnt->hgt - 3);
1934 #else
1935         XDrawRectangle(
1936             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->wid - 1, Infofnt->hgt - 1);
1937         XDrawRectangle(
1938             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->wid - 3, Infofnt->hgt - 3);
1939 #endif
1940     } else {
1941         Infoclr_set(xor_.get());
1942         Infofnt_text_non(x, y, " ", 1);
1943     }
1944
1945     return 0;
1946 }
1947
1948 /*
1949  * Draw the double width cursor
1950  */
1951 static errr game_term_bigcurs_x11(int x, int y)
1952 {
1953     if (use_graphics) {
1954 #ifdef USE_XFT
1955         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->twid - 1, Infofnt->hgt - 1);
1956         XftDrawRect(Infowin->draw, &xor_->fg, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->twid - 3, Infofnt->hgt - 3);
1957 #else
1958         XDrawRectangle(
1959             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->twid - 1, Infofnt->hgt - 1);
1960         XDrawRectangle(
1961             Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox + 1, y * Infofnt->hgt + Infowin->oy + 1, Infofnt->twid - 3, Infofnt->hgt - 3);
1962 #endif
1963     } else {
1964         Infoclr_set(xor_.get());
1965         Infofnt_text_non(x, y, "  ", 2);
1966     }
1967
1968     return 0;
1969 }
1970
1971 /*
1972  * Erase some characters.
1973  */
1974 static errr game_term_wipe_x11(int x, int y, int n)
1975 {
1976     Infoclr_set(clr[TERM_DARK].get());
1977     Infofnt_text_non(x, y, "", n);
1978     s_ptr->drawn = false;
1979     return 0;
1980 }
1981
1982 /*
1983  * Draw some textual characters.
1984  */
1985 static errr game_term_text_x11(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
1986 {
1987     Infoclr_set(clr[a].get());
1988     Infofnt_text_std(x, y, s, n);
1989     s_ptr->drawn = false;
1990     return 0;
1991 }
1992
1993 #ifndef USE_XFT
1994 /*
1995  * Draw some graphical characters.
1996  */
1997 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)
1998 {
1999     int i, x1, y1;
2000
2001     TERM_COLOR a;
2002     char c;
2003
2004     TERM_COLOR ta;
2005     char tc;
2006
2007     int x2, y2;
2008     int k, l;
2009
2010     unsigned long pixel, blank;
2011
2012     term_data *td = (term_data *)(game_term->data);
2013
2014     y *= Infofnt->hgt;
2015     x *= Infofnt->wid;
2016
2017     y += Infowin->oy;
2018     x += Infowin->ox;
2019     for (i = 0; i < n; ++i, x += td->fnt->wid) {
2020         a = *ap++;
2021         c = *cp++;
2022         x1 = (c & 0x7F) * td->fnt->twid;
2023         y1 = (a & 0x7F) * td->fnt->hgt;
2024         if (td->tiles->width < x1 + td->fnt->wid || td->tiles->height < y1 + td->fnt->hgt) {
2025             XFillRectangle(Metadpy->dpy, td->win->win, clr[0]->gc, x, y, td->fnt->twid, td->fnt->hgt);
2026             continue;
2027         }
2028
2029         ta = *tap++;
2030         tc = *tcp++;
2031
2032         x2 = (tc & 0x7F) * td->fnt->twid;
2033         y2 = (ta & 0x7F) * td->fnt->hgt;
2034
2035         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) {
2036             XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->tiles, x1, y1, x, y, td->fnt->twid, td->fnt->hgt);
2037         } else {
2038             blank = XGetPixel(td->tiles, 0, td->fnt->hgt * 6);
2039             for (k = 0; k < td->fnt->twid; k++) {
2040                 for (l = 0; l < td->fnt->hgt; l++) {
2041                     if ((pixel = XGetPixel(td->tiles, x1 + k, y1 + l)) == blank) {
2042                         pixel = XGetPixel(td->tiles, x2 + k, y2 + l);
2043                     }
2044
2045                     XPutPixel(td->TmpImage, k, l, pixel);
2046                 }
2047             }
2048
2049             XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->TmpImage, 0, 0, x, y, td->fnt->twid, td->fnt->hgt);
2050         }
2051     }
2052
2053     s_ptr->drawn = false;
2054     return 0;
2055 }
2056 #endif
2057
2058 #ifdef USE_XIM
2059 static void IMDestroyCallback(XIM, XPointer, XPointer);
2060
2061 static void IMInstantiateCallback(Display *display, XPointer unused1, XPointer unused2)
2062 {
2063     XIM xim;
2064     XIMCallback ximcallback;
2065     XIMStyles *xim_styles = nullptr;
2066     int i;
2067
2068     (void)unused1;
2069     (void)unused2;
2070
2071     xim = XOpenIM(display, nullptr, nullptr, nullptr);
2072     if (!xim) {
2073         printf("can't open IM\n");
2074         return;
2075     }
2076
2077     ximcallback.callback = IMDestroyCallback;
2078     ximcallback.client_data = nullptr;
2079     XSetIMValues(xim, XNDestroyCallback, &ximcallback, nullptr);
2080     XGetIMValues(xim, XNQueryInputStyle, &xim_styles, nullptr);
2081     for (i = 0; i < xim_styles->count_styles; i++) {
2082         if (xim_styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) {
2083             break;
2084         }
2085     }
2086     if (i >= xim_styles->count_styles) {
2087         printf("Sorry, your IM does not support 'Root' preedit style...\n");
2088         XCloseIM(xim);
2089         return;
2090     }
2091     XFree(xim_styles);
2092
2093     Metadpy->xim = xim;
2094
2095     for (i = 0; i < MAX_TERM_DATA; i++) {
2096         infowin *iwin = data[i].win.get();
2097         if (!iwin) {
2098             continue;
2099         }
2100         iwin->xic = XCreateIC(xim, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing), XNClientWindow, iwin->win, XNFocusWindow, iwin->win, nullptr);
2101         if (!iwin->xic) {
2102             printf("Can't create input context for Term%d\n", i);
2103             continue;
2104         }
2105
2106         if (XGetICValues(iwin->xic, XNFilterEvents, &iwin->xic_mask, nullptr) != nullptr) {
2107             iwin->xic_mask = 0L;
2108         }
2109
2110         XSelectInput(Metadpy->dpy, iwin->win, iwin->mask | iwin->xic_mask);
2111     }
2112
2113     return;
2114 }
2115
2116 static void IMDestroyCallback(XIM xim, XPointer client_data, XPointer call_data)
2117 {
2118     int i;
2119     (void)xim;
2120     (void)client_data;
2121
2122     if (call_data == nullptr) {
2123         XRegisterIMInstantiateCallback(Metadpy->dpy, nullptr, nullptr, nullptr, IMInstantiateCallback, nullptr);
2124     }
2125
2126     for (i = 0; i < MAX_TERM_DATA; i++) {
2127         infowin *iwin = data[i].win.get();
2128         if (!iwin) {
2129             continue;
2130         }
2131         if (iwin->xic_mask) {
2132             XSelectInput(Metadpy->dpy, iwin->win, iwin->mask);
2133             iwin->xic_mask = 0L;
2134         }
2135         iwin->xic = nullptr;
2136     }
2137
2138     Metadpy->xim = nullptr;
2139 }
2140 #endif
2141
2142 static char force_lower(char a)
2143 {
2144     return isupper(a) ? tolower(a) : a;
2145 }
2146
2147 static void game_term_nuke_x11(term_type *)
2148 {
2149     for (auto i = 0; i < MAX_TERM_DATA; i++) {
2150         infofnt *ifnt = data[i].fnt.get();
2151         infowin *iwin = data[i].win.get();
2152         if (ifnt && ifnt->info)
2153 #ifdef USE_XFT
2154             XftFontClose(Metadpy->dpy, ifnt->info);
2155 #else
2156             XFreeFontSet(Metadpy->dpy, ifnt->info);
2157 #endif
2158         if (iwin && iwin->xic) {
2159             XDestroyIC(iwin->xic);
2160         }
2161 #ifdef USE_XFT
2162         if (iwin && iwin->draw) {
2163             XftDrawDestroy(iwin->draw);
2164         }
2165 #endif
2166         angband_terms[i] = nullptr;
2167     }
2168
2169     if (Metadpy->xim) {
2170         XCloseIM(Metadpy->xim);
2171     }
2172     XUnregisterIMInstantiateCallback(Metadpy->dpy, NULL, NULL, NULL, IMInstantiateCallback, NULL);
2173     XCloseDisplay(Metadpy->dpy);
2174 }
2175
2176 /*
2177  * Initialize a term_data
2178  */
2179 static errr term_data_init(term_data *td, int i)
2180 {
2181     term_type *t = &td->t;
2182
2183     concptr name = angband_term_name[i];
2184
2185     concptr font;
2186     int x = 0;
2187     int y = 0;
2188
2189     int cols = 80;
2190     int rows = 24;
2191
2192     int ox = 1;
2193     int oy = 1;
2194
2195     int wid, hgt, num;
2196
2197     concptr str;
2198
2199     int val;
2200
2201     XClassHint *ch;
2202
2203     char res_name[20];
2204     char res_class[20];
2205
2206     XSizeHints *sh;
2207 #ifdef USE_XIM
2208     XWMHints *wh;
2209 #endif
2210
2211     font = getenv(format("ANGBAND_X11_FONT_%d", i));
2212     if (!font) {
2213         font = getenv("ANGBAND_X11_FONT");
2214     }
2215
2216     if (!font) {
2217         switch (i) {
2218         case 0: {
2219             font = DEFAULT_X11_FONT_0;
2220         } break;
2221         case 1: {
2222             font = DEFAULT_X11_FONT_1;
2223         } break;
2224         case 2: {
2225             font = DEFAULT_X11_FONT_2;
2226         } break;
2227         case 3: {
2228             font = DEFAULT_X11_FONT_3;
2229         } break;
2230         case 4: {
2231             font = DEFAULT_X11_FONT_4;
2232         } break;
2233         case 5: {
2234             font = DEFAULT_X11_FONT_5;
2235         } break;
2236         case 6: {
2237             font = DEFAULT_X11_FONT_6;
2238         } break;
2239         case 7: {
2240             font = DEFAULT_X11_FONT_7;
2241         } break;
2242         default: {
2243             font = DEFAULT_X11_FONT;
2244         }
2245         }
2246     }
2247
2248     str = getenv(format("ANGBAND_X11_AT_X_%d", i));
2249     x = (str != nullptr) ? atoi(str) : -1;
2250
2251     str = getenv(format("ANGBAND_X11_AT_Y_%d", i));
2252     y = (str != nullptr) ? atoi(str) : -1;
2253
2254     str = getenv(format("ANGBAND_X11_COLS_%d", i));
2255     val = (str != nullptr) ? atoi(str) : -1;
2256     if (val > 0) {
2257         cols = val;
2258     }
2259
2260     str = getenv(format("ANGBAND_X11_ROWS_%d", i));
2261     val = (str != nullptr) ? atoi(str) : -1;
2262     if (val > 0) {
2263         rows = val;
2264     }
2265
2266     if (!i) {
2267         if (cols < 80) {
2268             cols = 80;
2269         }
2270         if (rows < 24) {
2271             rows = 24;
2272         }
2273     }
2274
2275     str = getenv(format("ANGBAND_X11_IBOX_%d", i));
2276     val = (str != nullptr) ? atoi(str) : -1;
2277     if (val > 0) {
2278         ox = val;
2279     }
2280
2281     str = getenv(format("ANGBAND_X11_IBOY_%d", i));
2282     val = (str != nullptr) ? atoi(str) : -1;
2283     if (val > 0) {
2284         oy = val;
2285     }
2286
2287     td->fnt = std::make_unique<infofnt>();
2288     Infofnt_set(td->fnt.get());
2289     Infofnt_init_data(font);
2290
2291     num = ((i == 0) ? 1024 : 16);
2292     wid = cols * td->fnt->wid + (ox + ox);
2293     hgt = rows * td->fnt->hgt + (oy + oy);
2294     td->win = std::make_unique<infowin>();
2295     Infowin_set(td->win.get());
2296     Infowin_init_top(x, y, wid, hgt, 0, Metadpy->fg, Metadpy->bg);
2297
2298 #if defined(USE_XIM)
2299     Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask);
2300 #else
2301     Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
2302 #endif
2303
2304     Infowin_set_name(name);
2305     Infowin->ox = ox;
2306     Infowin->oy = oy;
2307     ch = XAllocClassHint();
2308
2309     if (ch == nullptr) {
2310         quit("XAllocClassHint failed");
2311     }
2312
2313     strcpy(res_name, name);
2314     res_name[0] = force_lower(res_name[0]);
2315     ch->res_name = res_name;
2316
2317     strcpy(res_class, "Angband");
2318     ch->res_class = res_class;
2319
2320     XSetClassHint(Metadpy->dpy, Infowin->win, ch);
2321     XFree(ch);
2322     sh = XAllocSizeHints();
2323     if (sh == nullptr) {
2324         quit("XAllocSizeHints failed");
2325     }
2326
2327     if (i == 0) {
2328         sh->flags = PMinSize | PMaxSize;
2329         sh->min_width = 80 * td->fnt->wid + (ox + ox);
2330         sh->min_height = 24 * td->fnt->hgt + (oy + oy);
2331         sh->max_width = 255 * td->fnt->wid + (ox + ox);
2332         sh->max_height = 255 * td->fnt->hgt + (oy + oy);
2333     } else {
2334         sh->flags = PMinSize | PMaxSize;
2335         sh->min_width = td->fnt->wid + (ox + ox);
2336         sh->min_height = td->fnt->hgt + (oy + oy);
2337         sh->max_width = 256 * td->fnt->wid + (ox + ox);
2338         sh->max_height = 256 * td->fnt->hgt + (oy + oy);
2339     }
2340
2341     sh->flags |= PResizeInc;
2342     sh->width_inc = td->fnt->wid;
2343     sh->height_inc = td->fnt->hgt;
2344     sh->flags |= PBaseSize;
2345     sh->base_width = (ox + ox);
2346     sh->base_height = (oy + oy);
2347     XSetWMNormalHints(Metadpy->dpy, Infowin->win, sh);
2348     XFree(sh);
2349     Infowin_map();
2350
2351 #ifdef USE_XIM
2352     wh = XAllocWMHints();
2353     if (wh == nullptr) {
2354         quit("XAllocWMHints failed");
2355     }
2356     wh->flags = InputHint;
2357     wh->input = True;
2358     XSetWMHints(Metadpy->dpy, Infowin->win, wh);
2359     XFree(wh);
2360 #endif
2361
2362     if ((x >= 0) && (y >= 0)) {
2363         Infowin_impell(x, y);
2364     }
2365
2366     term_init(t, cols, rows, num);
2367     t->soft_cursor = true;
2368     t->attr_blank = TERM_WHITE;
2369     t->char_blank = ' ';
2370     t->xtra_hook = game_term_xtra_x11;
2371     t->curs_hook = game_term_curs_x11;
2372     t->bigcurs_hook = game_term_bigcurs_x11;
2373     t->wipe_hook = game_term_wipe_x11;
2374     t->text_hook = game_term_text_x11;
2375     t->nuke_hook = game_term_nuke_x11;
2376     t->data = td;
2377     term_activate(t);
2378     return 0;
2379 }
2380
2381 /*
2382  * Initialization function for an "X11" module to Angband
2383  */
2384 errr init_x11(int argc, char *argv[])
2385 {
2386     int i;
2387     concptr dpy_name = "";
2388     int num_term = 3;
2389
2390 #ifndef USE_XFT
2391     char filename[1024];
2392
2393     int pict_wid = 0;
2394     int pict_hgt = 0;
2395
2396     char *TmpData;
2397 #endif
2398
2399     for (i = 1; i < argc; i++) {
2400         if (prefix(argv[i], "-d")) {
2401             dpy_name = &argv[i][2];
2402             continue;
2403         }
2404
2405 #ifndef USE_XFT
2406         if (prefix(argv[i], "-s")) {
2407             smoothRescaling = false;
2408             continue;
2409         }
2410
2411         if (prefix(argv[i], "-a")) {
2412             arg_graphics = GRAPHICS_ADAM_BOLT;
2413             continue;
2414         }
2415
2416         if (prefix(argv[i], "-o")) {
2417             arg_graphics = GRAPHICS_ORIGINAL;
2418             continue;
2419         }
2420 #endif
2421
2422         if (prefix(argv[i], "-b")) {
2423             arg_bigtile = use_bigtile = true;
2424             continue;
2425         }
2426
2427         if (prefix(argv[i], "-n")) {
2428             num_term = atoi(&argv[i][2]);
2429             if (num_term > MAX_TERM_DATA) {
2430                 num_term = MAX_TERM_DATA;
2431             } else if (num_term < 1) {
2432                 num_term = 1;
2433             }
2434             continue;
2435         }
2436
2437         if (prefix(argv[i], "--")) {
2438             continue;
2439         }
2440
2441         plog_fmt("Ignoring option: %s", argv[i]);
2442     }
2443
2444 #ifdef USE_LOCALE
2445
2446 #ifdef JP
2447     setlocale(LC_ALL, "");
2448
2449 #ifdef DEFAULT_LOCALE
2450     if (!strcmp(setlocale(LC_ALL, nullptr), "C")) {
2451         printf("try default locale \"%s\"\n", DEFAULT_LOCALE);
2452         setlocale(LC_ALL, DEFAULT_LOCALE);
2453     }
2454 #endif
2455
2456     if (!strcmp(setlocale(LC_ALL, nullptr), "C")) {
2457         printf("WARNING: Locale is not supported. Non-english font may be displayed incorrectly.\n");
2458     }
2459
2460     if (!XSupportsLocale()) {
2461         printf("can't support locale in X\n");
2462         setlocale(LC_ALL, "C");
2463     }
2464 #else
2465     setlocale(LC_ALL, "C");
2466 #endif /* JP */
2467
2468 #endif /* USE_LOCALE */
2469
2470     if (Metadpy_init_name(dpy_name)) {
2471         return -1;
2472     }
2473
2474     xor_ = std::make_unique<infoclr>();
2475     Infoclr_set(xor_.get());
2476     Infoclr_init_ppn(Metadpy->fg, Metadpy->bg, "xor", 0);
2477     for (i = 0; i < 256; ++i) {
2478         Pixell pixel;
2479         clr[i] = std::make_unique<infoclr>();
2480         Infoclr_set(clr[i].get());
2481         color_table[i][0] = angband_color_table[i][0];
2482         color_table[i][1] = angband_color_table[i][1];
2483         color_table[i][2] = angband_color_table[i][2];
2484         color_table[i][3] = angband_color_table[i][3];
2485         pixel = ((i == 0) ? Metadpy->bg : Metadpy->fg);
2486         if (Metadpy->color) {
2487             pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]);
2488         }
2489
2490         Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0);
2491     }
2492
2493     set_atoms();
2494     for (i = 0; i < num_term; i++) {
2495         term_data *td = &data[i];
2496         term_data_init(td, i);
2497         angband_terms[i] = game_term;
2498     }
2499
2500     Infowin_set(data[0].win.get());
2501     Infowin_raise();
2502     term_activate(&data[0].t);
2503
2504 #ifdef USE_XIM
2505     {
2506         char *p;
2507         p = XSetLocaleModifiers("");
2508         if (!p || !*p) {
2509             p = XSetLocaleModifiers("@im=");
2510         }
2511     }
2512     XRegisterIMInstantiateCallback(Metadpy->dpy, nullptr, nullptr, nullptr, IMInstantiateCallback, nullptr);
2513 #endif
2514
2515     if (arg_sound) {
2516         init_sound();
2517     }
2518
2519 #ifndef USE_XFT
2520     switch (arg_graphics) {
2521     case GRAPHICS_ORIGINAL:
2522         path_build(filename, sizeof(filename), ANGBAND_DIR_XTRA, "graf/8x8.bmp");
2523         if (0 == fd_close(fd_open(filename, O_RDONLY))) {
2524             use_graphics = true;
2525             pict_wid = pict_hgt = 8;
2526             ANGBAND_GRAF = "old";
2527         }
2528         break;
2529     case GRAPHICS_ADAM_BOLT:
2530         path_build(filename, sizeof(filename), ANGBAND_DIR_XTRA, "graf/16x16.bmp");
2531         if (0 == fd_close(fd_open(filename, O_RDONLY))) {
2532             use_graphics = true;
2533             pict_wid = pict_hgt = 16;
2534             ANGBAND_GRAF = "new";
2535         }
2536         break;
2537     }
2538
2539     if (use_graphics) {
2540         Display *dpy = Metadpy->dpy;
2541         XImage *tiles_raw;
2542         tiles_raw = ReadBMP(dpy, filename);
2543         for (i = 0; i < num_term; i++) {
2544             term_data *td = &data[i];
2545             term_type *t = &td->t;
2546             t->pict_hook = game_term_pict_x11;
2547             t->higher_pict = true;
2548             td->tiles = ResizeImage(dpy, tiles_raw, pict_wid, pict_hgt, td->fnt->twid, td->fnt->hgt);
2549         }
2550
2551         for (i = 0; i < num_term; i++) {
2552             term_data *td = &data[i];
2553             int ii, jj;
2554             int depth = DefaultDepth(dpy, DefaultScreen(dpy));
2555             Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
2556             int total;
2557             ii = 1;
2558             jj = (depth - 1) >> 2;
2559             while (jj >>= 1) {
2560                 ii <<= 1;
2561             }
2562             total = td->fnt->twid * td->fnt->hgt * ii;
2563             TmpData = (char *)malloc(total);
2564             td->TmpImage = XCreateImage(dpy, visual, depth, ZPixmap, 0, TmpData, td->fnt->twid, td->fnt->hgt, 8, 0);
2565         }
2566     }
2567 #endif /* ! USE_XFT */
2568     return 0;
2569 }
2570
2571 #endif /* USE_X11 */