4 * Copyright (c) 1997 Ben Harrison, and others
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.
12 * This file helps Angband work with UNIX/X11 computers.
14 * To use this file, compile with "USE_X11" defined, and link against all
15 * the various "X11" libraries which may be needed.
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
23 * The rest of this file is an implementation of "main-xxx.c" for X11.
25 * Most of this file is by Ben Harrison (benh@phial.com).
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.
41 * echo "Launching angband..."
45 * setenv ANGBAND_X11_FONT_0 10x20
46 * setenv ANGBAND_X11_AT_X_0 5
47 * setenv ANGBAND_X11_AT_Y_0 510
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
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
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
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
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
81 * # The build directory
85 * setenv ANGBAND_X11_GAMMA 142
88 * ./src/angband -mx11 -- -n6 &
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"
116 * Available graphic modes
119 #define GRAPHICS_NONE 0
120 #define GRAPHICS_ORIGINAL 1
121 #define GRAPHICS_ADAM_BOLT 2
122 #define GRAPHICS_HENGBAND 3
126 #ifndef __MAKEDEPEND__
127 #include <X11/Xlib.h>
128 #include <X11/Xutil.h>
129 #include <X11/keysym.h>
130 #include <X11/keysymdef.h>
132 #include <X11/Xlocale.h>
134 #include <X11/Xatom.h>
135 #endif /* __MAKEDEPEND__ */
138 #include <X11/Xft/Xft.h>
142 * Include some helpful X11 code.
144 #include "maid-x11.cpp"
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.
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".
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.
164 /**** Generic Types ****/
167 * An X11 pixell specifier
170 typedef XftColor Pixell;
172 typedef unsigned long Pixell;
176 * A structure summarizing a given Display.
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)
183 * - The "name" of the display
185 * - The socket to listen to for events
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)
191 * - The black Pixell (from a macro)
192 * - The white Pixell (from a macro)
194 * - The background Pixell (default: black)
195 * - The foreground Pixell (default: white)
196 * - The maximal Pixell (Equals: ((2 ^ depth)-1), is usually ugly)
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.
234 * A Structure summarizing Window Information.
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.
240 * - The current Input Event Mask
242 * - The location of the window
243 * - The width, height of the window
244 * - The border width of this window
246 * - Byte: 1st Extra byte
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
252 * - Bit Flag: We should nuke 'win' when done with it
254 * - Bit Flag: 1st extra flag
255 * - Bit Flag: 2nd extra flag
256 * - Bit Flag: 3rd extra flag
257 * - Bit Flag: 4th extra flag
291 * A Structure summarizing Operation+Color Information
293 * - The actual GC corresponding to this info
295 * - The Foreground Pixell Value
296 * - The Background Pixell Value
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.
316 * A Structure to Hold Font Information
318 * - The 'XFontStruct*' (yields the 'Font')
322 * - The default character width
323 * - The default character height
324 * - The default character ascent
326 * - Byte: Pixel offset used during fake mono
328 * - Flag: Force monospacing via 'wid'
329 * - Flag: Nuke info when done
350 /* Set current metadpy (Metadpy) to 'M' */
351 #define Metadpy_set(M) Metadpy = M
353 /* Initialize 'M' using Display 'D' */
354 #define Metadpy_init_dpy(D) Metadpy_init_2(D, cnullptr)
356 /* Initialize 'M' using a Display named 'N' */
357 #define Metadpy_init_name(N) Metadpy_init_2((Display *)(nullptr), N)
359 /* Initialize 'M' using the standard Display */
360 #define Metadpy_init() Metadpy_init_name("")
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)
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)
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)
371 /* Set the current Infowin */
372 #define Infowin_set(I) (Infowin = (I))
374 /* Set the current Infoclr */
375 #define Infoclr_set(C) (Infoclr = (C))
377 #define Infoclr_init_ppo(F, B, O, M) Infoclr_init_data(F, B, O, M)
379 #define Infoclr_init_cco(F, B, O, M) Infoclr_init_ppo(Infoclr_Pixell(F), Infoclr_Pixell(B), O, M)
381 #define Infoclr_init_ppn(F, B, O, M) Infoclr_init_ppo(F, B, Infoclr_Opcode(O), M)
383 #define Infoclr_init_ccn(F, B, O, M) Infoclr_init_cco(F, B, Infoclr_Opcode(O), M)
385 /* Set the current infofnt */
386 #define Infofnt_set(I) (Infofnt = (I))
388 /* Errr: Expose Infowin */
389 #define Infowin_expose() (!(Infowin->redraw = 1))
391 /* Errr: Unxpose Infowin */
392 #define Infowin_unexpose() (Infowin->redraw = 0)
395 * The "default" values
397 static metadpy metadpy_default;
400 * The "current" variables
402 static metadpy *Metadpy = &metadpy_default;
403 static infowin *Infowin = (infowin *)(nullptr);
405 static infowin *Focuswin = (infowin *)(nullptr);
407 static infoclr *Infoclr = (infoclr *)(nullptr);
408 static infofnt *Infofnt = (infofnt *)(nullptr);
411 * Init the current metadpy, with various initialization stuff.
414 * dpy: The Display* to use (if nullptr, create it)
415 * name: The name of the Display (if nullptr, the current)
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
422 * Return -1 if no Display given, and none can be opened.
424 static errr Metadpy_init_2(Display *dpy, concptr name)
426 metadpy *m = Metadpy;
428 dpy = XOpenDisplay(name);
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);
449 Visual *vis = DefaultVisual(dpy, 0);
450 XftColorAllocName(dpy, vis, m->cmap, "black", &m->black);
451 XftColorAllocName(dpy, vis, m->cmap, "white", &m->white);
453 m->black = BlackPixelOfScreen(m->screen);
454 m->white = WhitePixelOfScreen(m->screen);
461 m->zg = (1 << m->depth) - 1;
464 m->color = ((m->depth > 1) ? 1 : 0);
465 m->mono = ((m->color) ? 0 : 1);
470 * General Flush/ Sync/ Discard routine
472 static errr Metadpy_update(int flush, int sync, int discard)
475 XFlush(Metadpy->dpy);
478 XSync(Metadpy->dpy, discard);
487 static errr Metadpy_do_beep(void)
489 XBell(Metadpy->dpy, 100);
494 * Set the name (in the title bar) of Infowin
496 static errr Infowin_set_name(concptr name)
503 st = XStringListToTextProperty(&bp, 1, &tp);
505 XSetWMName(Metadpy->dpy, Infowin->win, &tp);
512 * Prepare a new 'infowin'.
514 static errr Infowin_prepare(Window xid)
516 infowin *iwin = Infowin;
518 XWindowAttributes xwa;
520 unsigned int w, h, b, d;
522 XGetGeometry(Metadpy->dpy, xid, &tmp_win, &x, &y, &w, &h, &b, &d);
525 Visual *vis = DefaultVisual(Metadpy->dpy, 0);
526 if (vis->c_class != TrueColor) {
527 quit_fmt("Display does not support truecolor.\n");
529 iwin->draw = XftDrawCreate(Metadpy->dpy, iwin->win, vis, Metadpy->cmap);
538 XGetWindowAttributes(Metadpy->dpy, xid, &xwa);
539 iwin->mask = xwa.your_event_mask;
540 iwin->mapped = ((xwa.map_state == IsUnmapped) ? 0 : 1);
546 * Init an infowin by giving some data.
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
555 * If 'dad == None' assume 'dad == root'
557 static errr Infowin_init_data(Window dad, int x, int y, int w, int h, int b, Pixell fg, Pixell bg)
566 xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg.pixel, bg.pixel);
568 xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg, bg);
571 XSelectInput(Metadpy->dpy, xid, 0L);
573 return Infowin_prepare(xid);
577 * Modify the event mask of an Infowin
579 static errr Infowin_set_mask(long mask)
581 Infowin->mask = mask;
582 XSelectInput(Metadpy->dpy, Infowin->win, Infowin->mask);
587 * Request that Infowin be mapped
589 static errr Infowin_map(void)
591 XMapWindow(Metadpy->dpy, Infowin->win);
596 * Request that Infowin be raised
598 static errr Infowin_raise(void)
600 XRaiseWindow(Metadpy->dpy, Infowin->win);
605 * Request that Infowin be moved to a new location
607 static errr Infowin_impell(int x, int y)
609 XMoveWindow(Metadpy->dpy, Infowin->win, x, y);
616 static errr Infowin_resize(int w, int h)
618 XResizeWindow(Metadpy->dpy, Infowin->win, w, h);
623 * Visually clear Infowin
625 static errr Infowin_wipe(void)
627 XClearWindow(Metadpy->dpy, Infowin->win);
632 * A nullptr terminated pair list of legal "operation names"
634 * Pairs of values, first is texttual name, second is the string
635 * holding the decimal value that the operation corresponds to.
638 static concptr opcode_pairs[] =
657 "+copyInverted", "12",
665 * Parse a word into an operation "code"
668 * str: A string, hopefully representing an Operation
671 * 0-15: if 'str' is a valid Operation
672 * -1: if 'str' could not be parsed
674 static int Infoclr_Opcode(concptr str)
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]);
687 * Initialize an infoclr with some data
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
695 static errr Infoclr_init_data(Pixell fg, Pixell bg, int op, int stip)
697 infoclr *iclr = Infoclr;
702 unsigned long gc_mask;
706 if (bg > Metadpy->zg) {
709 if (fg > Metadpy->zg) {
712 if ((op < 0) || (op > 15)) {
723 gcv.foreground = (bg ^ fg);
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);
742 iclr->stip = stip ? 1 : 0;
747 * Change the 'fg' for an infoclr
750 * fg: The Pixell for the requested Foreground (see above)
752 static errr Infoclr_change_fg(Pixell fg)
754 infoclr *iclr = Infoclr;
759 if (fg > Metadpy->zg) {
763 XSetForeground(Metadpy->dpy, iclr->gc, fg);
770 * Prepare a new 'infofnt'
773 static errr Infofnt_prepare(XftFont *info)
775 static errr Infofnt_prepare(XFontSet info)
778 infofnt *ifnt = Infofnt;
782 XFontStruct **fontinfo;
785 int ascent, descent, width;
791 ifnt->asc = info->ascent;
792 ifnt->hgt = info->height;
793 const char *text = "A";
795 XftTextExtentsUtf8(Metadpy->dpy, info, (FcChar8 *)text, strlen(text), &extent);
796 ifnt->wid = extent.xOff;
798 n_fonts = XFontsOfFontSet(info, &fontinfo, &fontname);
800 ascent = descent = width = 0;
801 while (n_fonts-- > 0) {
802 cs = &((*fontinfo)->max_bounds);
803 if (ascent < (*fontinfo)->ascent) {
804 ascent = (*fontinfo)->ascent;
806 if (descent < (*fontinfo)->descent) {
807 descent = (*fontinfo)->descent;
809 if (((*fontinfo)->max_byte1) > 0) {
810 /* 多バイト文字の場合は幅半分(端数切り上げ)で評価する */
811 if (width < (cs->width + 1) / 2) {
812 width = (cs->width + 1) / 2;
815 if (width < cs->width) {
823 ifnt->hgt = ascent + descent;
828 ifnt->twid = 2 * ifnt->wid;
830 ifnt->twid = ifnt->wid;
837 * Init an infofnt by its Name
840 * name: The name of the requested Font
842 static void Infofnt_init_data(concptr name)
854 if (!name || !*name) {
855 quit("Missing font!");
859 info = XftFontOpenName(Metadpy->dpy, 0, name);
860 /* TODO: error handling */
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]);
868 XFreeStringList(missing_list);
873 quit_fmt("Failed to find font:\"%s\"", name);
877 if (Infofnt_prepare(info)) {
879 XftFontClose(Metadpy->dpy, info);
881 XFreeFontSet(Metadpy->dpy, info);
883 quit_fmt("Failed to prepare font:\"%s\"", name);
886 Infofnt->name = name;
891 static void Infofnt_text_std_xft_draw_str(int px, int py, const XftColor &fg, concptr str, concptr str_end)
894 while (str < str_end) {
895 const int byte_len = utf8_next_char_byte_length(str);
897 if (byte_len == 0 || str + byte_len > str_end) {
901 XftDrawStringUtf8(Infowin->draw, &fg, Infofnt->info, px + Infofnt->wid * offset, py, (const FcChar8 *)str, byte_len);
902 offset += (byte_len > 1 ? 2 : 1);
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)
909 auto *draw = Infowin->draw;
911 const auto py = (y * Infofnt->hgt) + Infofnt->asc + Infowin->oy;
912 const auto px = (x * Infofnt->wid) + Infowin->ox;
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);
925 static errr Infofnt_text_std(int x, int y, concptr str, int len)
936 y = (y * Infofnt->hgt) + Infofnt->asc + Infowin->oy;
937 x = (x * Infofnt->wid) + Infowin->ox;
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);
950 int utf8_len = euc_to_utf8(str, len, utf8_buf, sizeof(utf8_buf));
957 Infofnt_text_std_xft(x, y, len, Infoclr->fg, Infoclr->bg, _(utf8_buf, str), _(utf8_len, len));
959 XmbDrawImageString(Metadpy->dpy, Infowin->win, Infofnt->info, Infoclr->gc, x, y, _(utf8_buf, str), _(utf8_len, len));
967 * Painting where text would be
969 static errr Infofnt_text_non(int x, int y, concptr str, int len)
976 w = len * Infofnt->wid;
977 x = x * Infofnt->wid + Infowin->ox;
979 y = y * h + Infowin->oy;
982 XftDrawRect(Infowin->draw, &Infoclr->fg, x, y, w, h);
984 XFillRectangle(Metadpy->dpy, Infowin->win, Infoclr->gc, x, y, w, h);
991 * Angband specific code follows... (ANGBAND)
995 * Hack -- cursor color
997 static std::unique_ptr<infoclr> xor_;
1000 * Actual color table
1002 static std::unique_ptr<infoclr> clr[256];
1005 * Color info (unused, red, green, blue).
1007 static byte color_table[256][4];
1011 * A structure for each "term"
1015 std::unique_ptr<infofnt> fnt;
1016 std::unique_ptr<infowin> win;
1025 * The number of term data structures
1027 #define MAX_TERM_DATA 8
1030 * The array of term data structures
1032 static term_data data[MAX_TERM_DATA];
1034 /* Use short names for the most commonly used elements of various structures. */
1035 #define DPY (Metadpy->dpy)
1036 #define WIN (Infowin->win)
1038 /* Describe a set of co-ordinates. */
1045 * A special structure to store information about the text currently
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. */
1058 static x11_selection_type s_ptr[1];
1061 static void send_key(const char key)
1063 // Windows ドライバと同様、自前でキューを操作する。
1064 // 逆順に term_key_push() する方法だと長い日本語を入力したときにテキストの
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);
1074 game_term->key_queue[game_term->key_head] = key;
1075 game_term->key_head = head_nxt;
1079 static void send_keys(const char *const keys)
1081 for (const char *p = keys; *p != '\0'; ++p) {
1087 * Process a keypress event
1089 static void react_keypress(XKeyEvent *xev)
1091 int n, mc, ms, mo, mx;
1093 XKeyEvent *ev = (XKeyEvent *)(xev);
1099 int valid_keysym = true;
1103 if (Focuswin && Focuswin->xic) {
1105 n = XmbLookupString(Focuswin->xic, ev, buf, 125, &ks, &status);
1106 if (status == XBufferOverflow) {
1107 printf("Input is too long, and dropped\n");
1110 if (status != XLookupKeySym && status != XLookupBoth) {
1111 valid_keysym = false;
1114 n = XLookupString(ev, buf, 125, &ks, nullptr);
1117 n = XLookupString(ev, buf, 125, &ks, nullptr);
1123 if (!valid_keysym) { /* XIMからの入力時のみ false になる */
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) {
1131 send_keys(_(euc_buf, buf));
1136 if (IsModifierKey(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)) {
1170 case XK_BackSpace: {
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);
1179 strnfmt(msg, sizeof(msg), "%c%s%s%s%sK_%X%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", ev->keycode, 13);
1184 if (n && !macro_patterns.empty() && (macro_find_exact(msg) < 0)) {
1185 macro_add(msg, buf);
1190 * Find the square a particular pixel is part of.
1192 static void pixel_to_square(int *const x, int *const y, const int ox, const int oy)
1194 (*x) = (ox - Infowin->ox) / Infofnt->wid;
1195 (*y) = (oy - Infowin->oy) / Infofnt->hgt;
1199 * Find the pixel at the top-left corner of a square.
1201 static void square_to_pixel(int *const x, int *const y, const int ox, const int oy)
1203 (*x) = ox * Infofnt->wid + Infowin->ox;
1204 (*y) = oy * Infofnt->hgt + Infowin->oy;
1208 * Convert co-ordinates from starting corner/opposite corner to minimum/maximum.
1210 static void sort_co_ord(co_ord *min, co_ord *max, const co_ord *b, const co_ord *a)
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);
1219 template <class T, class D>
1220 auto make_unique_ptr_with_deleter(T *p, D d) noexcept
1222 return std::unique_ptr<T, D>(p, std::move(d));
1226 * @brief 矩形領域の枠を描画する
1228 * ドラッグ時の選択範囲の表示に使用する。
1230 * @param x 矩形領域の左上のX座標(ピクセル単位)
1231 * @param y 矩形領域の左上のY座標(ピクセル単位)
1232 * @param widht 矩形領域の幅(ピクセル単位)
1233 * @param height 矩形領域の高さ(ピクセル単位)
1235 static void draw_rectangle_frame(int x, int y, int width, int height)
1237 auto gc = make_unique_ptr_with_deleter(XCreateGC(Metadpy->dpy, Infowin->win, 0, NULL),
1238 [dpy = Metadpy->dpy](GC gc) { XFreeGC(dpy, gc); });
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);
1249 static void draw_cursor_xft(int x, int y, int len)
1251 // term_what() では中央寄せ時に座標がずれるので直接取得
1252 const std::span<const char> cursor_chars(&game_term->scr->c[y][x], len);
1256 const auto utf8_len = euc_to_utf8(cursor_chars.data(), cursor_chars.size(), utf8_buf, sizeof(utf8_buf));
1261 Infofnt_text_std_xft(x, y, len, Infoclr->bg, Infoclr->fg, _(utf8_buf, cursor_chars.data()), _(utf8_len, len));
1265 static void draw_cursor(int x, int y, int len)
1268 draw_cursor_xft(x, y, len);
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);
1278 * Remove the selection by redrawing it.
1280 static void mark_selection_clear(int x1, int y1, int x2, int y2)
1282 term_redraw_section(x1, y1, x2, y2);
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.
1290 static void mark_selection_mark(int x1, int y1, int x2, int y2)
1292 square_to_pixel(&x1, &y1, x1, y1);
1293 square_to_pixel(&x2, &y2, x2, y2);
1295 draw_rectangle_frame(x1, y1, x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
1297 XDrawRectangle(Metadpy->dpy, Infowin->win, clr[2]->gc, x1, y1, x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
1302 * Mark a selection by drawing boxes around it (for now).
1304 static void mark_selection(void)
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);
1315 sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->old);
1316 mark_selection_clear(min.x, min.y, max.x, max.y);
1319 sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
1320 mark_selection_mark(min.x, min.y, max.x, max.y);
1323 if (s_ptr->t != old) {
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;
1333 * Forget a selection for one reason or another.
1335 static void copy_x11_release(void)
1337 s_ptr->select = false;
1342 * Start to select some text on the screen.
1344 static void copy_x11_start(int x, int y)
1346 if (s_ptr->select) {
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;
1356 * Respond to movement of the mouse when selecting text.
1358 static void copy_x11_cont(int x, int y, unsigned int buttons)
1360 x = MIN(MAX(x, 0), game_term->wid - 1);
1361 y = MIN(MAX(y, 0), game_term->hgt - 1);
1362 if (~buttons & Button1Mask) {
1365 if (s_ptr->t != game_term) {
1368 if (x == s_ptr->old.x && y == s_ptr->old.y && s_ptr->select) {
1372 s_ptr->select = true;
1379 * Respond to release of the left mouse button by putting the selected text in
1380 * the primary buffer.
1382 static void copy_x11_end(const Time time)
1384 if (!s_ptr->select) {
1387 if (s_ptr->t != game_term) {
1392 XSetSelectionOwner(Metadpy->dpy, XA_PRIMARY, Infowin->win, time);
1393 if (XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY) != Infowin->win) {
1394 s_ptr->select = false;
1399 static Atom xa_targets, xa_timestamp, xa_text, xa_compound_text, xa_utf8;
1402 * Set the required variable atoms at start-up to avoid errors later.
1404 static void set_atoms(void)
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);
1413 static Atom request_target = 0;
1416 * Send a message to request that the PRIMARY buffer be sent here.
1418 static void paste_x11_request(Atom target, const Time time)
1420 Atom property = XInternAtom(DPY, "__COPY_TEXT", False);
1421 if (XGetSelectionOwner(DPY, XA_PRIMARY) == None) {
1423 /* bell("No selection found."); */
1427 request_target = target;
1428 XConvertSelection(DPY, XA_PRIMARY, target, property, WIN, time);
1432 * Add the contents of the PRIMARY buffer to the input queue.
1434 * Hack - This doesn't use the "time" of the event, and so accepts anything a
1435 * client tries to send it.
1437 static void paste_x11_accept(const XSelectionEvent *ptr)
1440 const long offset = 0;
1441 const long length = 32000;
1442 XTextProperty xtextproperty;
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);
1450 plog("Paste failure (remote client could not send).");
1456 if (ptr->selection != XA_PRIMARY) {
1457 plog("Paste failure (remote client did not send primary selection).");
1461 if (ptr->target != request_target) {
1462 plog("Paste failure (selection in unknown format).");
1466 if (XGetWindowProperty(Metadpy->dpy, Infowin->win, property, offset, length, true, request_target, &xtextproperty.encoding, &xtextproperty.format,
1467 &xtextproperty.nitems, &left, &xtextproperty.value) != Success) {
1471 if (request_target == xa_compound_text) {
1475 XmbTextPropertyToTextList(DPY, &xtextproperty, &list, &count);
1480 for (i = 0; i < count; i++) {
1481 err = type_string(list[i], 0);
1487 XFreeStringList(list);
1490 err = type_string((char *)xtextproperty.value, xtextproperty.nitems);
1493 XFree(xtextproperty.value);
1495 plog("Paste failure (too much text selected).");
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.
1506 static bool paste_x11_send_text(XSelectionRequestEvent *rq)
1515 sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
1516 if (XGetSelectionOwner(DPY, XA_PRIMARY) != WIN) {
1520 for (y = 0; y < game_term->hgt; y++) {
1531 for (l = 0, x = 0; x < game_term->wid; x++) {
1537 term_what(x, y, &a, &c);
1540 } else if (iskanji(c)) {
1551 * A single kanji character was divided in two...
1552 * Delete the garbage.
1554 if ((2 == kanji && x == min.x) || (1 == kanji && x == max.x)) {
1565 term_what(x, y, &a, &c);
1573 while (l >= 1 && buf[l - 1] == ' ') {
1577 // 複数行の場合、各行末に改行を付加。
1578 if (min.y != max.y) {
1586 char utf8_buf[2048];
1587 const int len = euc_to_utf8(buf, l, utf8_buf, sizeof(utf8_buf));
1589 if (_(len, l) > 0) {
1590 XChangeProperty(DPY, rq->requestor, rq->property, xa_utf8, 8, PropModeAppend, (unsigned char *)_(utf8_buf, buf), _(len, l));
1598 * Send some text requested by another X client.
1600 static void paste_x11_send(XSelectionRequestEvent *rq)
1603 XSelectionEvent *ptr = &(event.xselection);
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;
1614 * Paste the appropriate information for each target type.
1615 * Note that this currently rejects MULTIPLE targets.
1618 if (rq->target == xa_utf8) {
1619 if (!paste_x11_send_text(rq)) {
1620 ptr->property = None;
1622 } else if (rq->target == xa_targets) {
1623 Atom target_list[] = { xa_targets, xa_utf8 };
1625 DPY, rq->requestor, rq->property, XA_ATOM, 32, PropModeReplace, (unsigned char *)target_list, (sizeof(target_list) / sizeof(target_list[0])));
1627 ptr->property = None;
1630 XSendEvent(DPY, rq->requestor, false, NoEventMask, &event);
1634 * Handle various events conditional on presses of a mouse button.
1636 static void handle_button(Time time, int x, int y, int button, bool press)
1638 pixel_to_square(&x, &y, x, y);
1640 if (press && button == 1) {
1641 copy_x11_start(x, y);
1643 if (!press && button == 1) {
1646 if (!press && button == 2) {
1647 paste_x11_request(xa_compound_text, time);
1654 static errr CheckEvent(bool wait)
1656 term_data *old_td = (term_data *)(game_term->data);
1658 XEvent xev_body, *xev = &xev_body;
1660 term_data *td = nullptr;
1661 infowin *iwin = nullptr;
1669 if (!wait && !XPending(Metadpy->dpy)) {
1673 if (s_ptr->select && !s_ptr->drawn) {
1677 XNextEvent(Metadpy->dpy, xev);
1680 } while (XFilterEvent(xev, xev->xany.window));
1683 if (xev->type == MappingNotify) {
1684 XRefreshKeyboardMapping(&xev->xmapping);
1688 for (i = 0; i < MAX_TERM_DATA; i++) {
1692 if (xev->xany.window == data[i].win->win) {
1694 iwin = td->win.get();
1703 term_activate(&td->t);
1705 switch (xev->type) {
1707 case ButtonRelease: {
1708 bool press = (xev->type == ButtonPress);
1709 int x = xev->xbutton.x;
1710 int y = xev->xbutton.y;
1712 if (xev->xbutton.button == Button1) {
1714 } else if (xev->xbutton.button == Button2) {
1716 } else if (xev->xbutton.button == Button3) {
1718 } else if (xev->xbutton.button == Button4) {
1720 } else if (xev->xbutton.button == Button5) {
1726 handle_button(xev->xbutton.time, x, y, z, press);
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);
1741 case SelectionNotify: {
1742 paste_x11_accept(&(xev->xselection));
1745 case SelectionRequest: {
1746 paste_x11_send(&(xev->xselectionrequest));
1749 case SelectionClear: {
1750 s_ptr->select = false;
1758 term_activate(&old_td->t);
1759 react_keypress(&(xev->xkey));
1764 x1 = (xev->xexpose.x - Infowin->ox) / Infofnt->wid;
1765 x2 = (xev->xexpose.x + xev->xexpose.width - Infowin->ox) / Infofnt->wid;
1767 y1 = (xev->xexpose.y - Infowin->oy) / Infofnt->hgt;
1768 y2 = (xev->xexpose.y + xev->xexpose.height - Infowin->oy) / Infofnt->hgt;
1770 term_redraw_section(x1, y1, x2, y2);
1774 Infowin->mapped = 1;
1775 game_term->mapped_flag = true;
1779 Infowin->mapped = 0;
1780 game_term->mapped_flag = false;
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);
1800 if (td == &data[0]) {
1801 if (cols < MAIN_TERM_MIN_COLS) {
1802 cols = MAIN_TERM_MIN_COLS;
1804 if (rows < MAIN_TERM_MIN_ROWS) {
1805 rows = MAIN_TERM_MIN_ROWS;
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);
1822 XSetICFocus(iwin->xic);
1829 XUnsetICFocus(iwin->xic);
1837 term_activate(&old_td->t);
1838 Infowin_set(old_td->win.get());
1843 * An array of sound file names
1845 static concptr sound_file[SOUND_MAX];
1848 * Check for existance of a file
1850 static bool check_file(concptr s)
1854 fff = fopen(s, "r");
1866 static void init_sound(void)
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];
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());
1884 * Hack -- make a sound
1886 static errr game_term_xtra_x11_sound(int v)
1891 if ((v < 0) || (v >= SOUND_MAX)) {
1894 if (!sound_file[v]) {
1898 std::string buf = "./playwave.sh ";
1899 buf.append(sound_file[v]).append("\n");
1900 return system(buf.data()) < 0;
1904 * Handle "activation" of a term
1906 static errr game_term_xtra_x11_level(int v)
1908 term_data *td = (term_data *)(game_term->data);
1910 Infowin_set(td->win.get());
1911 Infofnt_set(td->fnt.get());
1920 static errr game_term_xtra_x11_react(void)
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])) {
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);
1943 * Handle a "special request"
1945 static errr game_term_xtra_x11(int n, int v)
1948 case TERM_XTRA_NOISE:
1951 case TERM_XTRA_SOUND:
1952 return game_term_xtra_x11_sound(v);
1954 case TERM_XTRA_FRESH:
1955 Metadpy_update(1, 1, 0);
1958 case TERM_XTRA_FRESH:
1959 Metadpy_update(1, 0, 0);
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)) {
1971 case TERM_XTRA_LEVEL:
1972 return game_term_xtra_x11_level(v);
1973 case TERM_XTRA_CLEAR:
1975 s_ptr->drawn = false;
1977 case TERM_XTRA_DELAY:
1980 case TERM_XTRA_REACT:
1981 return game_term_xtra_x11_react();
1988 * Draw the cursor as an inverted rectangle.
1990 * Consider a rectangular outline like "main-mac.c". XXX XXX
1992 static errr game_term_curs_x11(int x, int y)
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);
2000 Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->wid - 1, Infofnt->hgt - 1);
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);
2005 Infoclr_set(xor_.get());
2006 draw_cursor(x, y, 1);
2013 * Draw the double width cursor
2015 static errr game_term_bigcurs_x11(int x, int y)
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);
2023 Metadpy->dpy, Infowin->win, xor_->gc, x * Infofnt->wid + Infowin->ox, y * Infofnt->hgt + Infowin->oy, Infofnt->twid - 1, Infofnt->hgt - 1);
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);
2028 Infoclr_set(xor_.get());
2029 draw_cursor(x, y, 2);
2036 * Erase some characters.
2038 static errr game_term_wipe_x11(int x, int y, int n)
2040 Infoclr_set(clr[TERM_DARK].get());
2041 Infofnt_text_non(x, y, "", n);
2042 s_ptr->drawn = false;
2047 * Draw some textual characters.
2049 static errr game_term_text_x11(TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr s)
2051 Infoclr_set(clr[a].get());
2052 Infofnt_text_std(x, y, s, n);
2053 s_ptr->drawn = false;
2059 * Draw some graphical characters.
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)
2074 unsigned long pixel, blank;
2076 term_data *td = (term_data *)(game_term->data);
2083 for (i = 0; i < n; ++i, x += td->fnt->wid) {
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);
2096 x2 = (tc & 0x7F) * td->fnt->twid;
2097 y2 = (ta & 0x7F) * td->fnt->hgt;
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);
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);
2109 XPutPixel(td->TmpImage, k, l, pixel);
2113 XPutImage(Metadpy->dpy, td->win->win, clr[0]->gc, td->TmpImage, 0, 0, x, y, td->fnt->twid, td->fnt->hgt);
2117 s_ptr->drawn = false;
2123 static void IMDestroyCallback(XIM, XPointer, XPointer);
2125 static void IMInstantiateCallback(Display *display, XPointer unused1, XPointer unused2)
2128 XIMCallback ximcallback;
2129 XIMStyles *xim_styles = nullptr;
2135 xim = XOpenIM(display, nullptr, nullptr, nullptr);
2137 printf("can't open IM\n");
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)) {
2150 if (i >= xim_styles->count_styles) {
2151 printf("Sorry, your IM does not support 'Root' preedit style...\n");
2159 for (i = 0; i < MAX_TERM_DATA; i++) {
2160 infowin *iwin = data[i].win.get();
2164 iwin->xic = XCreateIC(xim, XNInputStyle, (XIMPreeditNothing | XIMStatusNothing), XNClientWindow, iwin->win, XNFocusWindow, iwin->win, nullptr);
2166 printf("Can't create input context for Term%d\n", i);
2170 if (XGetICValues(iwin->xic, XNFilterEvents, &iwin->xic_mask, nullptr) != nullptr) {
2171 iwin->xic_mask = 0L;
2174 XSelectInput(Metadpy->dpy, iwin->win, iwin->mask | iwin->xic_mask);
2180 static void IMDestroyCallback(XIM xim, XPointer client_data, XPointer call_data)
2186 if (call_data == nullptr) {
2187 XRegisterIMInstantiateCallback(Metadpy->dpy, nullptr, nullptr, nullptr, IMInstantiateCallback, nullptr);
2190 for (i = 0; i < MAX_TERM_DATA; i++) {
2191 infowin *iwin = data[i].win.get();
2195 if (iwin->xic_mask) {
2196 XSelectInput(Metadpy->dpy, iwin->win, iwin->mask);
2197 iwin->xic_mask = 0L;
2199 iwin->xic = nullptr;
2202 Metadpy->xim = nullptr;
2206 static char force_lower(char a)
2208 return isupper(a) ? tolower(a) : a;
2211 static void game_term_nuke_x11(term_type *)
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)
2218 XftFontClose(Metadpy->dpy, ifnt->info);
2220 XFreeFontSet(Metadpy->dpy, ifnt->info);
2222 if (iwin && iwin->xic) {
2223 XDestroyIC(iwin->xic);
2226 if (iwin && iwin->draw) {
2227 XftDrawDestroy(iwin->draw);
2230 angband_terms[i] = nullptr;
2234 XCloseIM(Metadpy->xim);
2236 XUnregisterIMInstantiateCallback(Metadpy->dpy, NULL, NULL, NULL, IMInstantiateCallback, NULL);
2237 XCloseDisplay(Metadpy->dpy);
2241 * Initialize a term_data
2243 static errr term_data_init(term_data *td, int i)
2245 term_type *t = &td->t;
2247 concptr name = angband_term_name[i];
2253 int cols = TERM_DEFAULT_COLS;
2254 int rows = TERM_DEFAULT_ROWS;
2275 font = getenv(format("ANGBAND_X11_FONT_%d", i).data());
2277 font = getenv("ANGBAND_X11_FONT");
2283 font = DEFAULT_X11_FONT_0;
2286 font = DEFAULT_X11_FONT_1;
2289 font = DEFAULT_X11_FONT_2;
2292 font = DEFAULT_X11_FONT_3;
2295 font = DEFAULT_X11_FONT_4;
2298 font = DEFAULT_X11_FONT_5;
2301 font = DEFAULT_X11_FONT_6;
2304 font = DEFAULT_X11_FONT_7;
2307 font = DEFAULT_X11_FONT;
2312 str = getenv(format("ANGBAND_X11_AT_X_%d", i).data());
2313 x = (str != nullptr) ? atoi(str) : -1;
2315 str = getenv(format("ANGBAND_X11_AT_Y_%d", i).data());
2316 y = (str != nullptr) ? atoi(str) : -1;
2318 str = getenv(format("ANGBAND_X11_COLS_%d", i).data());
2319 val = (str != nullptr) ? atoi(str) : -1;
2324 str = getenv(format("ANGBAND_X11_ROWS_%d", i).data());
2325 val = (str != nullptr) ? atoi(str) : -1;
2331 if (cols < MAIN_TERM_MIN_COLS) {
2332 cols = MAIN_TERM_MIN_COLS;
2334 if (rows < MAIN_TERM_MIN_ROWS) {
2335 rows = MAIN_TERM_MIN_ROWS;
2339 str = getenv(format("ANGBAND_X11_IBOX_%d", i).data());
2340 val = (str != nullptr) ? atoi(str) : -1;
2345 str = getenv(format("ANGBAND_X11_IBOY_%d", i).data());
2346 val = (str != nullptr) ? atoi(str) : -1;
2351 td->fnt = std::make_unique<infofnt>();
2352 Infofnt_set(td->fnt.get());
2353 Infofnt_init_data(font);
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);
2362 #if defined(USE_XIM)
2363 Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | FocusChangeMask);
2365 Infowin_set_mask(ExposureMask | StructureNotifyMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
2368 Infowin_set_name(name);
2371 ch = XAllocClassHint();
2373 if (ch == nullptr) {
2374 quit("XAllocClassHint failed");
2377 strcpy(res_name, name);
2378 res_name[0] = force_lower(res_name[0]);
2379 ch->res_name = res_name;
2381 strcpy(res_class, "Angband");
2382 ch->res_class = res_class;
2384 XSetClassHint(Metadpy->dpy, Infowin->win, ch);
2386 sh = XAllocSizeHints();
2387 if (sh == nullptr) {
2388 quit("XAllocSizeHints failed");
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);
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);
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);
2416 wh = XAllocWMHints();
2417 if (wh == nullptr) {
2418 quit("XAllocWMHints failed");
2420 wh->flags = InputHint;
2422 XSetWMHints(Metadpy->dpy, Infowin->win, wh);
2426 if ((x >= 0) && (y >= 0)) {
2427 Infowin_impell(x, y);
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;
2446 * Initialization function for an "X11" module to Angband
2448 errr init_x11(int argc, char *argv[])
2451 concptr dpy_name = "";
2461 for (i = 1; i < argc; i++) {
2462 if (prefix(argv[i], "-d")) {
2463 dpy_name = &argv[i][2];
2468 if (prefix(argv[i], "-s")) {
2469 smoothRescaling = false;
2473 if (prefix(argv[i], "-a")) {
2474 arg_graphics = GRAPHICS_ADAM_BOLT;
2478 if (prefix(argv[i], "-o")) {
2479 arg_graphics = GRAPHICS_ORIGINAL;
2484 if (prefix(argv[i], "-b")) {
2485 arg_bigtile = use_bigtile = true;
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) {
2499 if (prefix(argv[i], "--")) {
2503 plog_fmt("Ignoring option: %s", argv[i]);
2509 setlocale(LC_ALL, "");
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);
2518 if (!strcmp(setlocale(LC_ALL, nullptr), "C")) {
2519 printf("WARNING: Locale is not supported. Non-english font may be displayed incorrectly.\n");
2522 if (!XSupportsLocale()) {
2523 printf("can't support locale in X\n");
2524 setlocale(LC_ALL, "C");
2527 setlocale(LC_ALL, "C");
2530 #endif /* USE_LOCALE */
2532 if (Metadpy_init_name(dpy_name)) {
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) {
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]);
2552 Infoclr_init_ppn(pixel, Metadpy->bg, "cpy", 0);
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;
2562 Infowin_set(data[0].win.get());
2564 term_activate(&data[0].t);
2569 p = XSetLocaleModifiers("");
2571 p = XSetLocaleModifiers("@im=");
2574 XRegisterIMInstantiateCallback(Metadpy->dpy, nullptr, nullptr, nullptr, IMInstantiateCallback, nullptr);
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));
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));
2607 Display *dpy = Metadpy->dpy;
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);
2618 for (i = 0; i < num_term; i++) {
2619 term_data *td = &data[i];
2621 int depth = DefaultDepth(dpy, DefaultScreen(dpy));
2622 Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
2625 jj = (depth - 1) >> 2;
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);
2634 #endif /* ! USE_XFT */
2638 #endif /* USE_X11 */