/* File: main-x11.c */ /* * Copyright (c) 1997 Ben Harrison, and others * * This software may be copied and distributed for educational, research, * and not for profit purposes provided that this copyright and statement * are included in all such copies. */ /* * This file helps Angband work with UNIX/X11 computers. * * To use this file, compile with "USE_X11" defined, and link against all * the various "X11" libraries which may be needed. * * See also "main-xaw.c". * * Part of this file provides a user interface package composed of several * pseudo-objects, including "metadpy" (a display), "infowin" (a window), * "infoclr" (a color), and "infofnt" (a font). Actually, the package was * originally much more interesting, but it was bastardized to keep this * file simple. * * The rest of this file is an implementation of "main-xxx.c" for X11. * * Most of this file is by Ben Harrison (benh@phial.com). */ /* * The following shell script can be used to launch Angband, assuming that * it was extracted into "~/Angband", and compiled using "USE_X11", on a * Linux machine, with a 1280x1024 screen, using 6 windows (with the given * characteristics), with gamma correction of 1.8 -> (1 / 1.8) * 256 = 142, * and without graphics (add "-g" for graphics). Just copy this comment * into a file, remove the leading " * " characters (and the head/tail of * this comment), and make the file executable. * * * #!/bin/csh * * # Describe attempt * echo "Launching angband..." * sleep 2 * * # Main window * setenv ANGBAND_X11_FONT_0 10x20 * setenv ANGBAND_X11_AT_X_0 5 * setenv ANGBAND_X11_AT_Y_0 510 * * # Message window * setenv ANGBAND_X11_FONT_1 8x13 * setenv ANGBAND_X11_AT_X_1 5 * setenv ANGBAND_X11_AT_Y_1 22 * setenv ANGBAND_X11_ROWS_1 35 * * # Inventory window * setenv ANGBAND_X11_FONT_2 8x13 * setenv ANGBAND_X11_AT_X_2 635 * setenv ANGBAND_X11_AT_Y_2 182 * setenv ANGBAND_X11_ROWS_3 23 * * # Equipment window * setenv ANGBAND_X11_FONT_3 8x13 * setenv ANGBAND_X11_AT_X_3 635 * setenv ANGBAND_X11_AT_Y_3 22 * setenv ANGBAND_X11_ROWS_3 12 * * # Monster recall window * setenv ANGBAND_X11_FONT_4 6x13 * setenv ANGBAND_X11_AT_X_4 817 * setenv ANGBAND_X11_AT_Y_4 847 * setenv ANGBAND_X11_COLS_4 76 * setenv ANGBAND_X11_ROWS_4 11 * * # Object recall window * setenv ANGBAND_X11_FONT_5 6x13 * setenv ANGBAND_X11_AT_X_5 817 * setenv ANGBAND_X11_AT_Y_5 520 * setenv ANGBAND_X11_COLS_5 76 * setenv ANGBAND_X11_ROWS_5 24 * * # The build directory * cd ~/Angband * * # Gamma correction * setenv ANGBAND_X11_GAMMA 142 * * # Launch Angband * ./src/angband -mx11 -- -n6 & * */ #include "config.hpp" #include "defines.hpp" #include "frontend.hpp" #include "loadsave.hpp" #include "main.hpp" #include "util.hpp" #include "variable.hpp" #include "z-form.hpp" #include "z-util.hpp" #ifndef __MAKEDEPEND__ #include #include #include #include #include #endif /* __MAKEDEPEND__ */ #include #include #include #include #include #include #include using boost::algorithm::equals; using boost::algorithm::starts_with; #define FORCELOWER(A) ((isupper((A))) ? tolower((A)) : (A)) /* * This file is designed to be "included" by "main-x11.c" or "main-xaw.c", * which will have already "included" several relevant header files. */ #ifndef IsModifierKey /* * Keysym macros, used on Keysyms to test for classes of symbols * These were stolen from one of the X11 header files * * Also appears in "main-x11.c". */ #define IsKeypadKey(keysym) \ (((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal)) #define IsCursorKey(keysym) \ (((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select)) #define IsPFKey(keysym) \ (((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4)) #define IsFunctionKey(keysym) \ (((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35)) #define IsMiscFunctionKey(keysym) \ (((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space)) #define IsModifierKey(keysym) \ (((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R)) #endif /* IsModifierKey */ /* * Checks if the keysym is a special key or a normal key * Assume that XK_MISCELLANY keysyms are special * * Also appears in "main-x11.c". */ #define IsSpecialKey(keysym) \ ((unsigned)(keysym) >= 0xFF00) /* * Hack -- Convert an RGB value to an X11 Pixel, or die. * * Original code by Desvignes Sebastien (desvigne@solar12.eerie.fr). * * BMP format support by Denis Eropkin (denis@dream.homepage.ru). * * Major fixes and cleanup by Ben Harrison (benh@phial.com). */ static unsigned long create_pixel(Display *dpy, byte red, byte green, byte blue) { Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)); char cname[8]; XColor xcolour; /* Build the color */ xcolour.red = red * 255 + red; xcolour.green = green * 255 + green; xcolour.blue = blue * 255 + blue; xcolour.flags = DoRed | DoGreen | DoBlue; /* Attempt to Allocate the Parsed color */ if (!(XAllocColor(dpy, cmap, &xcolour))) { quit_fmt("Couldn't allocate bitmap color '%s'\n", cname); } return (xcolour.pixel); } /* * Notes on Colors: * * 1) On a monochrome (or "fake-monochrome") display, all colors * will be "cast" to "fg," except for the bg color, which is, * obviously, cast to "bg". Thus, one can ignore this setting. * * 2) Because of the inner functioning of the color allocation * routines, colors may be specified as (a) a typical color name, * (b) a hexidecimal color specification (preceded by a pound sign), * or (c) by strings such as "fg", "bg", "zg". * * 3) Due to the workings of the init routines, many colors * may also be dealt with by their actual pixel values. Note that * the pixel with all bits set is "zg = (1<depth)-1", which * is not necessarily either black or white. */ /**** Generic Types ****/ /* * An X11 pixell specifier */ typedef unsigned long Pixell; /* * The structures defined below */ typedef struct metadpy metadpy; typedef struct infowin infowin; typedef struct infoclr infoclr; typedef struct infofnt infofnt; /* * A structure summarizing a given Display. * * - The Display itself * - The default Screen for the display * - The virtual root (usually just the root) * - The default colormap (from a macro) * * - The "name" of the display * * - The socket to listen to for events * * - The width of the display screen (from a macro) * - The height of the display screen (from a macro) * - The bit depth of the display screen (from a macro) * * - The black Pixell (from a macro) * - The white Pixell (from a macro) * * - The background Pixell (default: black) * - The foreground Pixell (default: white) * - The maximal Pixell (Equals: ((2 ^ depth)-1), is usually ugly) * * - Bit Flag: Force all colors to black and white (default: !color) * - Bit Flag: Allow the use of color (default: depth > 1) * - Bit Flag: We created 'dpy', and so should nuke it when done. */ struct metadpy { Display *dpy; Screen *screen; Window root; Colormap cmap; char *name; int fd; unsigned int width; unsigned int height; unsigned int depth; Pixell black; Pixell white; Pixell bg; Pixell fg; Pixell zg; unsigned int mono: 1; unsigned int color: 1; unsigned int nuke: 1; }; /* * A Structure summarizing Window Information. * * I assume that a window is at most 30000 pixels on a side. * I assume that the root windw is also at most 30000 square. * * - The Window * - The current Input Event Mask * * - The location of the window * - The width, height of the window * - The border width of this window * * - Byte: 1st Extra byte * * - Bit Flag: This window is currently Mapped * - Bit Flag: This window needs to be redrawn * - Bit Flag: This window has been resized * * - Bit Flag: We should nuke 'win' when done with it * * - Bit Flag: 1st extra flag * - Bit Flag: 2nd extra flag * - Bit Flag: 3rd extra flag * - Bit Flag: 4th extra flag */ struct infowin { Window win; long mask; s16b ox, oy; s16b x, y; s16b w, h; u16b b; byte byte1; unsigned int mapped: 1; unsigned int redraw: 1; unsigned int resize: 1; unsigned int nuke: 1; unsigned int flag1: 1; unsigned int flag2: 1; unsigned int flag3: 1; unsigned int flag4: 1; }; /* * A Structure summarizing Operation+Color Information * * - The actual GC corresponding to this info * * - The Foreground Pixell Value * - The Background Pixell Value * * - Num (0-15): The operation code (As in Clear, Xor, etc) * - Bit Flag: The GC is in stipple mode * - Bit Flag: Destroy 'gc' at Nuke time. */ struct infoclr { GC gc; Pixell fg; Pixell bg; unsigned int code: 4; unsigned int nuke: 1; }; /* * A Structure to Hold Font Information * * - The 'XFontStruct*' (yields the 'Font') * * - The font name * * - The default character width * - The default character height * - The default character ascent * * - Byte: Pixel offset used during fake mono * * - Flag: Force monospacing via 'wid' * - Flag: Nuke info when done */ struct infofnt { XFontStruct *info; const char *name; s16b wid; s16b twid; s16b hgt; s16b asc; byte off; unsigned int mono: 1; unsigned int nuke: 1; }; /**** Generic Globals ****/ /* * The "default" values */ static metadpy metadpy_default; /* * The "current" variables */ static metadpy *Metadpy = &metadpy_default; /**** Generic code ****/ /* * Simple routine to save the state of the game when the display connection * is broken. Remember, you cannot do anything in this function that will * generate X protocol requests. */ int x_io_error_handler(Display *d) { /* We have nothing to save */ if (!character_generated) return 0; save_dungeon(); save_player(); return 0; } /* * Create a new metadpy and initialize it. * * Inputs: * name: The name of the Display * * Notes: * If 'name' is NULL, but 'dpy' is set, extract name from dpy * If 'dpy' is NULL, then Create the named Display * If 'name' is NULL, and so is 'dpy', use current Display * * Return -1 on error. */ static errr Metadpy_new(const char *name) { metadpy *m = Metadpy; /*** Create the display ***/ Display *dpy = XOpenDisplay(name); if (!dpy) return ( -1); m->nuke = 1; XSetIOErrorHandler(x_io_error_handler); /*** Save some information ***/ /* Save the Display itself */ m->dpy = dpy; /* Get the Screen and Virtual Root Window */ m->screen = DefaultScreenOfDisplay(dpy); m->root = RootWindowOfScreen(m->screen); /* Get the default colormap */ m->cmap = DefaultColormapOfScreen(m->screen); /* Extract the true name of the display */ m->name = DisplayString(dpy); /* Extract the fd */ m->fd = ConnectionNumber(Metadpy->dpy); /* Save the Size and Depth of the screen */ m->width = WidthOfScreen(m->screen); m->height = HeightOfScreen(m->screen); m->depth = DefaultDepthOfScreen(m->screen); /* Save the Standard Colors */ m->black = BlackPixelOfScreen(m->screen); m->white = WhitePixelOfScreen(m->screen); /*** Make some clever Guesses ***/ /* Guess at the desired 'fg' and 'bg' Pixell's */ m->bg = m->black; m->fg = m->white; /* Calculate the Maximum allowed Pixel value. */ m->zg = (1 << m->depth) - 1; /* Save various default Flag Settings */ m->color = ((m->depth > 1) ? 1 : 0); m->mono = ((m->color) ? 0 : 1); /* Return "success" */ return (0); } /* * Set the name (in the title bar) of Infowin */ static void Infowin_set_name(infowin *iwin, std::string_view name_sv) { char buf[128]; // Trim to the size of the buffer - 1 so that // strncpy is guaranteed to NUL-terminate. auto name = name_sv.substr(0, sizeof(buf) - 1); // Copy strncpy(buf, name.begin(), name.size()); buf[name.size()] = '\0'; char *bp = buf; // Set XTextProperty tp; Status st = XStringListToTextProperty(&bp, 1, &tp); if (st) { XSetWMName(Metadpy->dpy, iwin->win, &tp); } } /* * Prepare a new 'infowin'. */ static errr Infowin_prepare(infowin *iwin, Window xid) { Window tmp_win; XWindowAttributes xwa; int x, y; unsigned int w, h, b, d; /* Assign stuff */ iwin->win = xid; /* Check For Error XXX Extract some ACTUAL data from 'xid' */ XGetGeometry(Metadpy->dpy, xid, &tmp_win, &x, &y, &w, &h, &b, &d); /* Apply the above info */ iwin->x = x; iwin->y = y; iwin->w = w; iwin->h = h; iwin->b = b; /* Check Error XXX Extract some more ACTUAL data */ XGetWindowAttributes(Metadpy->dpy, xid, &xwa); /* Apply the above info */ iwin->mask = xwa.your_event_mask; iwin->mapped = ((xwa.map_state == IsUnmapped) ? 0 : 1); /* And assume that we are exposed */ iwin->redraw = 1; /* Success */ return (0); } /* * Init an infowin by giving some data. * * Inputs: * dad: The Window that should own this Window (if any) * x,y: The position of this Window * w,h: The size of this Window * b,d: The border width and pixel depth * * Notes: * If 'dad == None' assume 'dad == root' */ static errr Infowin_init_data(infowin *iwin, Window dad, int x, int y, int w, int h, int b, Pixell fg, Pixell bg) { Window xid; /* Wipe it clean */ memset(iwin, 0, sizeof(struct infowin)); /*** Error Check XXX ***/ /*** Create the Window 'xid' from data ***/ /* What happened here? XXX XXX XXX */ /* If no parent given, depend on root */ if (dad == None) /* #ifdef USE_GRAPHICS xid = XCreateWindow(Metadpy->dpy, Metadpy->root, x, y, w, h, b, 8, InputOutput, CopyFromParent, 0, 0); else */ /* #else */ dad = Metadpy->root; /* #endif */ /* Create the Window XXX Error Check */ xid = XCreateSimpleWindow(Metadpy->dpy, dad, x, y, w, h, b, fg, bg); /* Start out selecting No events */ XSelectInput(Metadpy->dpy, xid, 0L); /*** Prepare the new infowin ***/ /* Mark it as nukable */ iwin->nuke = 1; /* Attempt to Initialize the infowin */ return Infowin_prepare(iwin, xid); } /* * Modify the event mask of an Infowin */ static void Infowin_set_mask(infowin *iwin, long mask) { /* Save the new setting */ iwin->mask = mask; /* Execute the Mapping */ XSelectInput(Metadpy->dpy, iwin->win, iwin->mask); } /* * Request that Infowin be mapped */ static void Infowin_map(infowin *iwin) { /* Execute the Mapping */ XMapWindow(Metadpy->dpy, iwin->win); } /* * Request that Infowin be raised */ static void Infowin_raise(infowin *iwin) { /* Raise towards visibility */ XRaiseWindow(Metadpy->dpy, iwin->win); } /* * Request that Infowin be moved to a new location */ static void Infowin_impell(infowin *iwin, int x, int y) { XMoveWindow(Metadpy->dpy, iwin->win, x, y); } /* * Visually clear Infowin */ static void Infowin_wipe(infowin *iwin) { XClearWindow(Metadpy->dpy, iwin->win); } /* * Opcodes for Infoclr_init_data(). */ static constexpr int Infoclr_Opcode_CPY = 3; static constexpr int Infoclr_Opcode_XOR = 6; /* * Initialize an infoclr with some data * * Inputs: * fg: The Pixell for the requested Foreground (see above) * bg: The Pixell for the requested Background (see above) * op: The Opcode for the requested Operation (see above) * stip: The stipple mode */ static errr Infoclr_init_data(infoclr *iclr, Pixell fg, Pixell bg, int op) { GC gc; XGCValues gcv; unsigned long gc_mask; /*** Simple error checking of opr and clr ***/ /* Check the 'Pixells' for realism */ if (bg > Metadpy->zg) return ( -1); if (fg > Metadpy->zg) return ( -1); /* Check the data for trueness */ if ((op < 0) || (op > 15)) return ( -1); /*** Create the requested 'GC' ***/ /* Assign the proper GC function */ gcv.function = op; /* Assign the proper GC background */ gcv.background = bg; /* Assign the proper GC foreground */ gcv.foreground = fg; /* Hack -- Handle XOR (xor is code 6) by hacking bg and fg */ if (op == Infoclr_Opcode_XOR) { gcv.background = 0; gcv.foreground = (bg ^ fg); } /* Assign the proper GC Fill Style */ gcv.fill_style = FillSolid; /* Turn off 'Give exposure events for pixmap copying' */ gcv.graphics_exposures = False; /* Set up the GC mask */ gc_mask = (GCFunction | GCBackground | GCForeground | GCFillStyle | GCGraphicsExposures); /* Create the GC detailed above */ gc = XCreateGC(Metadpy->dpy, Metadpy->root, gc_mask, &gcv); /*** Initialize ***/ /* Wipe the iclr clean */ memset(iclr, 0, sizeof(struct infoclr)); /* Assign the GC */ iclr->gc = gc; /* Nuke it when done */ iclr->nuke = 1; /* Assign the parms */ iclr->fg = fg; iclr->bg = bg; iclr->code = op; /* Success */ return (0); } /* * Change the 'fg' for an infoclr * * Inputs: * fg: The Pixell for the requested Foreground (see above) */ static errr Infoclr_change_fg(infoclr *iclr, Pixell fg) { /*** Simple error checking of opr and clr ***/ /* Check the 'Pixells' for realism */ if (fg > Metadpy->zg) return ( -1); /*** Change ***/ /* Change */ XSetForeground(Metadpy->dpy, iclr->gc, fg); /* Success */ return (0); } /* * Prepare a new 'infofnt' */ static errr Infofnt_prepare(infofnt *ifnt, XFontStruct *info) { /* Assign the struct */ ifnt->info = info; /* Jump into the max bouonds thing */ auto cs = &(info->max_bounds); /* Extract default sizing info */ ifnt->asc = info->ascent; ifnt->hgt = info->ascent + info->descent; ifnt->wid = cs->width; ifnt->twid = ifnt->wid; /* Success */ return (0); } /* * Init an infofnt by its Name * * Inputs: * name: The name of the requested Font */ static errr Infofnt_init_data(infofnt *ifnt, const char *name) { XFontStruct *info; /*** Load the info Fresh, using the name ***/ /* If the name is not given, report an error */ if (!name) return ( -1); /* Attempt to load the font */ info = XLoadQueryFont(Metadpy->dpy, name); /* The load failed, try to recover */ if (!info) return ( -1); /*** Init the font ***/ /* Wipe the thing */ memset(ifnt, 0, sizeof(struct infofnt)); /* Attempt to prepare it */ if (Infofnt_prepare(ifnt, info)) { /* Free the font */ XFreeFont(Metadpy->dpy, info); /* Fail */ return ( -1); } /* Save a copy of the font name */ ifnt->name = strdup(name); /* Mark it as nukable */ ifnt->nuke = 1; /* Success */ return (0); } /* * Standard Text */ static void Infofnt_text_std(infowin *iwin, infoclr *iclr, infofnt *ifnt, int x, int y, const char *str, int len) { /*** Do a brief info analysis ***/ /* Do nothing if the string is null */ if (!str || !*str) { return; } /* Get the length of the string */ if (len < 0) len = strlen(str); /*** Decide where to place the string, vertically ***/ /* Ignore Vertical Justifications */ y = (y * ifnt->hgt) + ifnt->asc + iwin->oy; /*** Decide where to place the string, horizontally ***/ /* Line up with x at left edge of column 'x' */ x = (x * ifnt->wid) + iwin->ox; /*** Actually draw 'str' onto the infowin ***/ /* Be sure the correct font is ready */ XSetFont(Metadpy->dpy, iclr->gc, ifnt->info->fid); /*** Handle the fake mono we can enforce on fonts ***/ /* Monotize the font */ if (ifnt->mono) { /* Do each character */ for (int i = 0; i < len; ++i) { /* Note that the Infoclr is set up to contain the Infofnt */ XDrawImageString(Metadpy->dpy, iwin->win, iclr->gc, x + i * ifnt->wid + ifnt->off, y, str + i, 1); } } /* Assume monoospaced font */ else { /* Note that the Infoclr is set up to contain the Infofnt */ XDrawImageString(Metadpy->dpy, iwin->win, iclr->gc, x, y, str, len); } } /* * Painting where text would be */ static void Infofnt_text_non(infowin *iwin, infoclr *iclr, infofnt *ifnt, int x, int y, const char *str, int len) { /*** Find the width ***/ /* Negative length is a flag to count the characters in str */ if (len < 0) len = strlen(str); /* The total width will be 'len' chars * standard width */ int const w = len * ifnt->wid; /*** Find the X dimensions ***/ /* Line up with x at left edge of column 'x' */ x = x * ifnt->wid + iwin->ox; /*** Find other dimensions ***/ /* Simply do 'Infofnt->hgt' (a single row) high */ int const h = ifnt->hgt; /* Simply do "at top" in row 'y' */ y = y * h + iwin->oy; /*** Actually 'paint' the area ***/ /* Just do a Fill Rectangle */ XFillRectangle(Metadpy->dpy, iwin->win, iclr->gc, x, y, w, h); } /*************************************************************************/ /* * Angband specific code follows... (ANGBAND) */ /* * Hack -- cursor color */ static infoclr *cursor_clr; /* * Actual color table */ static infoclr *clr[256]; /* * Color info (unused, red, green, blue). */ static byte color_table[256][4]; /* * Forward declare */ typedef struct term_data term_data; /* * A structure for each "term" */ struct term_data { term *term_ptr; infofnt *fnt; infowin *win; }; /* * The number of term data structures */ #define MAX_TERM_DATA 8 /* * The array of term data structures */ static term_data data[MAX_TERM_DATA]; /* * Process a keypress event * * Also appears in "main-xaw.c". */ static void react_keypress(XKeyEvent *xev) { int i, n, mc, ms, mo, mx; unsigned int ks1; XKeyEvent *ev = (XKeyEvent*)(xev); KeySym ks; char buf[128]; char msg[128]; /* Check for "normal" keypresses */ n = XLookupString(ev, buf, 125, &ks, NULL); /* Terminate */ buf[n] = '\0'; /* Hack -- Ignore "modifier keys" */ if (IsModifierKey(ks)) return; /* Hack -- convert into an unsigned int */ ks1 = (unsigned int)(ks); /* Extract four "modifier flags" */ mc = (ev->state & ControlMask) ? true : false; ms = (ev->state & ShiftMask) ? true : false; mo = (ev->state & Mod1Mask) ? true : false; mx = (ev->state & Mod2Mask) ? true : false; /* Normal keys with no modifiers */ if (n && !mo && !mx && !IsSpecialKey(ks)) { /* Enqueue the normal key(s) */ for (i = 0; buf[i]; i++) Term_keypress(buf[i]); /* All done */ return; } /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */ switch (ks1) { case XK_Escape: { Term_keypress(ESCAPE); return; } case XK_Return: { Term_keypress('\r'); return; } case XK_Tab: { Term_keypress('\t'); return; } case XK_Delete: case XK_BackSpace: { Term_keypress('\010'); return; } } /* Hack -- Use the KeySym */ if (ks) { sprintf(msg, "%c%s%s%s%s_%lX%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", (unsigned long)(ks), 13); } /* Hack -- Use the Keycode */ else { sprintf(msg, "%c%s%s%s%sK_%X%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", ev->keycode, 13); } /* Enqueue the "macro trigger" string */ for (i = 0; msg[i]; i++) Term_keypress(msg[i]); /* Hack -- auto-define macros as needed */ if (n && (macro_find_exact(msg) < 0)) { /* Create a macro */ macro_add(msg, buf); } } /* * Process events */ static errr CheckEvent(term_data *old_td, bool wait) { XEvent xev_body, *xev = &xev_body; term_data *td = NULL; infowin *iwin = NULL; int i; /* Do not wait unless requested */ if (!wait && !XPending(Metadpy->dpy)) return (1); /* Load the Event */ XNextEvent(Metadpy->dpy, xev); /* Notice new keymaps */ if (xev->type == MappingNotify) { XRefreshKeyboardMapping(&xev->xmapping); return 0; } /* Scan the windows */ for (i = 0; i < MAX_TERM_DATA; i++) { if (xev->xany.window == data[i].win->win) { td = &data[i]; iwin = td->win; break; } } /* Unknown window */ if (!td || !iwin) return (0); /* Hack -- activate the Term */ Term_activate(td->term_ptr); /* Switch on the Type */ switch (xev->type) { case KeyPress: { /* Hack -- use "old" term */ Term_activate(old_td->term_ptr); /* Process the key */ react_keypress(&(xev->xkey)); break; } case Expose: { /* Ignore "extra" exposes */ if (xev->xexpose.count) break; /* Clear the window */ Infowin_wipe(iwin); /* Redraw */ Term_redraw(); break; } case MapNotify: { iwin->mapped = 1; Term_mapped(); break; } case UnmapNotify: { iwin->mapped = 0; Term_unmapped(); break; } /* Move and/or Resize */ case ConfigureNotify: { int cols, rows; int ox = iwin->ox; int oy = iwin->oy; /* Save the new Window Parms */ iwin->x = xev->xconfigure.x; iwin->y = xev->xconfigure.y; iwin->w = xev->xconfigure.width; iwin->h = xev->xconfigure.height; /* Determine "proper" number of rows/cols */ cols = ((iwin->w - (ox + ox)) / td->fnt->wid); rows = ((iwin->h - (oy + oy)) / td->fnt->hgt); /* Hack -- minimal size */ if (td == &data[0]) { if (cols < 80) cols = 80; if (rows < 24) rows = 24; } else { if (cols < 1) cols = 1; if (rows < 1) rows = 1; } /* Paranoia */ if (cols > 255) cols = 255; if (rows > 255) rows = 255; /* Resize the Term (if needed) */ Term_resize(cols, rows); break; } } /* Hack -- Activate the old term */ Term_activate(old_td->term_ptr); /* Success */ return (0); } /** * UserInterace for X11 */ class X11Frontend final : public Frontend { private: term_data *m_term_data; public: explicit X11Frontend(term_data *term_data) : m_term_data(term_data) { } void init() final { // No action necessary } bool soft_cursor() const final { return true; } bool icky_corner() const final { return false; } void nuke() final { // No action necessary } void process_event(bool wait) final { CheckEvent(m_term_data, wait); } void flush_events() final { while (!CheckEvent(m_term_data, false)) { // Keep flushing } } void clear() final { Infowin_wipe(m_term_data->win); } void flush_output() final { XFlush(Metadpy->dpy); } void noise() final { XBell(Metadpy->dpy, 100); } void process_queued_events() final { CheckEvent(m_term_data, false); } void react() final { if (Metadpy->color) { /* Check the colors */ for (int i = 0; i < 256; i++) { 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])) { Pixell pixel; /* Save new values */ 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]; /* Create pixel */ pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]); /* Change the foreground */ Infoclr_change_fg(clr[i], pixel); } } } } void rename_main_window(std::string_view sv) final { Infowin_set_name(m_term_data->win, sv); } void draw_cursor(int x, int y) final { Infofnt_text_non(m_term_data->win, cursor_clr, m_term_data->fnt, x, y, " ", 1); } void draw_text(int x, int y, int n, byte a, const char *s) final { Infofnt_text_std(m_term_data->win, clr[a], m_term_data->fnt, x, y, s, n); } }; /* * Initialize a term_data */ static term *term_data_init(term_data *td, int i) { const char *name = angband_term_name[i]; const char *font; int x = 0; int y = 0; int cols = 80; int rows = 24; int ox = 1; int oy = 1; int wid, hgt, num; char buf[80]; const char *str; int val; XClassHint *ch; char res_name[20]; char res_class[20]; XSizeHints *sh; /* Window specific font name */ sprintf(buf, "ANGBAND_X11_FONT_%d", i); /* Check environment for that font */ font = getenv(buf); /* Check environment for "base" font */ if (!font) font = getenv("ANGBAND_X11_FONT"); /* No environment variables, use default font */ if (!font) { switch (i) { case 0: { font = DEFAULT_X11_FONT; } break; case 1: { font = DEFAULT_X11_FONT; } break; case 2: { font = DEFAULT_X11_FONT; } break; case 3: { font = DEFAULT_X11_FONT; } break; case 4: { font = DEFAULT_X11_FONT; } break; case 5: { font = DEFAULT_X11_FONT; } break; case 6: { font = DEFAULT_X11_FONT; } break; case 7: { font = DEFAULT_X11_FONT; } break; default: { font = DEFAULT_X11_FONT; } } } /* Window specific location (x) */ sprintf(buf, "ANGBAND_X11_AT_X_%d", i); str = getenv(buf); x = (str != NULL) ? atoi(str) : -1; /* Window specific location (y) */ sprintf(buf, "ANGBAND_X11_AT_Y_%d", i); str = getenv(buf); y = (str != NULL) ? atoi(str) : -1; /* Window specific cols */ sprintf(buf, "ANGBAND_X11_COLS_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) cols = val; /* Window specific rows */ sprintf(buf, "ANGBAND_X11_ROWS_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) rows = val; /* Window specific inner border offset (ox) */ sprintf(buf, "ANGBAND_X11_IBOX_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) ox = val; /* Window specific inner border offset (oy) */ sprintf(buf, "ANGBAND_X11_IBOY_%d", i); str = getenv(buf); val = (str != NULL) ? atoi(str) : -1; if (val > 0) oy = val; /* Prepare the standard font */ td->fnt = (infofnt *) calloc(1, sizeof(struct infofnt)); if (td->fnt == NULL) { abort(); } Infofnt_init_data(td->fnt, font); /* Hack -- key buffer size */ num = (i == 0 ? 1024 : 16); /* Assume full size windows */ wid = cols * td->fnt->wid + (ox + ox); hgt = rows * td->fnt->hgt + (oy + oy); /* Create a top-window */ td->win = (infowin *) calloc(1, sizeof(struct infowin)); if (td->win == NULL) { abort(); } Infowin_init_data(td->win, None, x, y, wid, hgt, 0, Metadpy->fg, Metadpy->bg); /* Ask for certain events */ Infowin_set_mask(td->win, ExposureMask | StructureNotifyMask | KeyPressMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask); /* Set the window name */ Infowin_set_name(td->win, name); /* Save the inner border */ td->win->ox = ox; td->win->oy = oy; /* Make Class Hints */ ch = XAllocClassHint(); if (ch == NULL) quit("XAllocClassHint failed"); strcpy(res_name, name); res_name[0] = FORCELOWER(res_name[0]); ch->res_name = res_name; strcpy(res_class, "Angband"); ch->res_class = res_class; XSetClassHint(Metadpy->dpy, td->win->win, ch); /* Make Size Hints */ sh = XAllocSizeHints(); /* Oops */ if (sh == NULL) quit("XAllocSizeHints failed"); /* Fixed window size */ if (i == 0) { /* Main window: 80x24 -- 255x255 */ sh->flags = PMinSize | PMaxSize; sh->min_width = 80 * td->fnt->wid + (ox + ox); sh->min_height = 24 * td->fnt->hgt + (oy + oy); sh->max_width = 255 * td->fnt->wid + (ox + ox); sh->max_height = 255 * td->fnt->hgt + (oy + oy); } /* Variable window size */ else { /* Subwindows: 1x1 -- 255x255 */ sh->flags = PMinSize | PMaxSize; sh->min_width = td->fnt->wid + (ox + ox); sh->min_height = td->fnt->hgt + (oy + oy); sh->max_width = 255 * td->fnt->wid + (ox + ox); sh->max_height = 255 * td->fnt->hgt + (oy + oy); } /* Resize increment */ sh->flags |= PResizeInc; sh->width_inc = td->fnt->wid; sh->height_inc = td->fnt->hgt; /* Base window size */ sh->flags |= PBaseSize; sh->base_width = (ox + ox); sh->base_height = (oy + oy); /* Use the size hints */ XSetWMNormalHints(Metadpy->dpy, td->win->win, sh); /* Map the window */ Infowin_map(td->win); /* Move the window to requested location */ if ((x >= 0) && (y >= 0)) { Infowin_impell(td->win, x, y); } /* Initialize the term */ td->term_ptr = term_init(cols, rows, num, std::make_shared(td)); /* Activate (important) */ Term_activate(td->term_ptr); /* Success */ return td->term_ptr; } /* * Initialization function for an "X11" module to Angband */ errr init_x11(int argc, char *argv[]) { int i; const char *dpy_name = ""; int num_term = 1; /* Parse args */ for (i = 1; i < argc; i++) { if (starts_with(argv[i], "-d")) { dpy_name = &argv[i][2]; continue; } if (starts_with(argv[i], "-n")) { num_term = atoi(&argv[i][2]); if (num_term > MAX_TERM_DATA) num_term = MAX_TERM_DATA; else if (num_term < 1) num_term = 1; continue; } fprintf(stderr, "Ignoring option: %s", argv[i]); } /* Init the Metadpy if possible */ if (Metadpy_new(dpy_name)) return ( -1); /* Prepare cursor color */ cursor_clr = (infoclr *) calloc(1, sizeof(struct infoclr)); if (cursor_clr == NULL) { abort(); } Infoclr_init_data(cursor_clr, Metadpy->fg, Metadpy->bg, Infoclr_Opcode_XOR); /* Prepare normal colors */ for (i = 0; i < 256; ++i) { Pixell pixel; clr[i] = (infoclr *) calloc(1, sizeof(struct infoclr)); if (clr[i] == NULL) { abort(); } /* Acquire Angband colors */ 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]; /* Default to monochrome */ pixel = ((i == 0) ? Metadpy->bg : Metadpy->fg); /* Handle color */ if (Metadpy->color) { /* Create pixel */ pixel = create_pixel(Metadpy->dpy, color_table[i][1], color_table[i][2], color_table[i][3]); } /* Initialize the color */ Infoclr_init_data(clr[i], pixel, Metadpy->bg, Infoclr_Opcode_CPY); } /* Initialize the windows */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; /* Initialize the term_data */ term *t = term_data_init(td, i); /* Save global entry */ angband_term[i] = t; } /* Raise the "Angband" window */ Infowin_raise(data[0].win); /* Activate the "Angband" window screen */ Term_activate(data[0].term_ptr); /* Success */ return (0); } int main(int argc, char *argv[]) { return main_real( argc, argv, "x11", init_x11, " -- -n# Number of terms to use\n" " -- -d Display to use\n"); }