/* File: main-xaw.c */ /* * Copyright (c) 1997 Ben Harrison, Torbjorn Lindgren, 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_XAW" defined, and link against all * the various "X11" libraries which may be needed. * * See also "main-x11.c". * * The Angband widget is not as self-contained as it really should be. * Originally everything was output to a Pixmap which was later copied * to the screen when necessary. The idea was abandoned since Pixmaps * create big performance problems for some really old X terminals (such * as 3/50's running Xkernel). * * Initial framework (and some code) by Ben Harrison (benh@phial.com). * * Most of this file is by Torbjorn Lindgren (tl@cd.chalmers.se). * * Major modifications by Ben Harrison (benh@phial.com). */ #include "angband.h" #ifdef USE_XAW #ifndef __MAKEDEPEND__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* __MAKEDEPEND__ */ #include #include #include #include /* /me pffts Solaris */ #ifndef NAME_MAX #define NAME_MAX _POSIX_NAME_MAX #endif /* * Include some helpful X11 code. */ #include "maid-x11.c" /**** Resources ****/ /* Name Class RepType Default Value ---- ----- ------- ------------- background Background Pixel XtDefaultBackground border BorderColor Pixel XtDefaultForeground borderWidth BorderWidth Dimension 1 cursor Cursor Cursor None cursorName Cursor String NULL destroyCallback Callback Pointer NULL height Height Dimension 0 insensitiveBorder Insensitive Pixmap Gray mappedWhenManaged MappedWhenManaged Boolean True pointerColor Foreground Pixel XtDefaultForeground pointerColorBackground Background Pixel XtDefaultBackground sensitive Sensitive Boolean True width Width Dimension 0 x Position Position 0 y Position Position 0 The colors can be changed using the standard Angband user pref files, which can also be used to provide black text on a white background, by setting color zero to "#FFFFFF" and color one to "#000000", since the other colors are unused. */ /* * New resource names */ #define XtNstartRows "startRows" #define XtNstartColumns "startColumns" #define XtNminRows "minRows" #define XtNminColumns "minColumns" #define XtNmaxRows "maxRows" #define XtNmaxColumns "maxColumns" #define XtNinternalBorder "internalBorder" #define XtNredrawCallback "redrawCallback" /* * Total normal colors */ #define NUM_COLORS 256 /* * Special "XOR" color */ #define COLOR_XOR 256 /**** The Widget Code ****/ /* * Forward declarations */ typedef struct AngbandPart AngbandPart; typedef struct AngbandRec *AngbandWidget; typedef struct AngbandRec AngbandRec; typedef struct AngbandClassRec *AngbandWidgetClass; typedef struct AngbandClassPart AngbandClassPart; typedef struct AngbandClassRec AngbandClassRec; typedef struct term_data term_data; /* * A structure for each "term" */ struct term_data { term t; AngbandWidget widget; }; /* * Maximum number of windows */ #define MAX_TERM_DATA 8 /* * An array of term_data's */ static term_data data[MAX_TERM_DATA]; /* * Current number of windows open */ static int num_term = MAX_TERM_DATA; /* * New fields for the Angband widget record */ struct AngbandPart { /* Settable resources */ int start_rows; int start_columns; int min_rows; int min_columns; int max_rows; int max_columns; int internal_border; String font; XtCallbackList redraw_callbacks; #ifdef USE_GRAPHICS /* Tiles */ XImage *tiles; /* Tempory storage for overlaying tiles. */ XImage *TmpImage; #endif /* USE_GRAPHICS */ /* Private state */ XFontStruct *fnt; Dimension fontheight; Dimension fontwidth; Dimension fontascent; /* Color info for GC's */ byte color[NUM_COLORS][4]; /* GC's (including "xor") */ GC gc[NUM_COLORS + 1]; }; /* * Full instance record declaration */ struct AngbandRec { CorePart core; SimplePart simple; AngbandPart angband; }; /* * New fields for the Angband widget class record */ struct AngbandClassPart { int dummy; }; /* * Full class record declaration */ struct AngbandClassRec { CoreClassPart core_class; SimpleClassPart simple_class; AngbandClassPart angband_class; }; /* * Hack -- see below */ #define offset(field) XtOffsetOf(AngbandRec, angband.field) /* * Fallback resources for Angband widget */ static XtResource resources[] = { { XtNstartRows, XtCValue, XtRInt, sizeof(int), offset(start_rows), XtRImmediate, (XtPointer) 24 }, { XtNstartColumns, XtCValue, XtRInt, sizeof(int), offset(start_columns), XtRImmediate, (XtPointer) 80 }, { XtNminRows, XtCValue, XtRInt, sizeof(int), offset(min_rows), XtRImmediate, (XtPointer) 1 }, { XtNminColumns, XtCValue, XtRInt, sizeof(int), offset(min_columns), XtRImmediate, (XtPointer) 1 }, { XtNmaxRows, XtCValue, XtRInt, sizeof(int), offset(max_rows), XtRImmediate, (XtPointer) 255 }, { XtNmaxColumns, XtCValue, XtRInt, sizeof(int), offset(max_columns), XtRImmediate, (XtPointer) 255 }, { XtNinternalBorder, XtCValue, XtRInt, sizeof(int), offset(internal_border), XtRImmediate, (XtPointer) 2 }, { XtNfont, XtCFont, XtRString, sizeof(char *), offset(font), XtRString, DEFAULT_X11_FONT }, { XtNredrawCallback, XtCCallback, XtRCallback, sizeof(XtPointer), offset(redraw_callbacks), XtRCallback, (XtPointer)NULL } }; /* * Hack -- see above */ #undef offset /* * Forward declarations for Widget functions */ static void Initialize(AngbandWidget request, AngbandWidget wnew); static void Redisplay(AngbandWidget w, XEvent *event, Region region); static Boolean SetValues(AngbandWidget current, AngbandWidget request, AngbandWidget wnew, ArgList args, Cardinal *num_args); static void Destroy(AngbandWidget widget); static void Resize_term(AngbandWidget wnew); /* * Forward declaration for internal functions */ static void calculateSizeHints(AngbandWidget wnew); static XFontStruct *getFont(AngbandWidget widget, String font, Boolean fallback); /* * Hack -- see below */ #define superclass (&simpleClassRec) /* * Class record constanst */ AngbandClassRec angbandClassRec = { { /* Core class fields initialization */ /* superclass */ (WidgetClass) superclass, /* class_name */ "Angband", /* widget_size */ sizeof(AngbandRec), /* class_initialize */ NULL, /* class_part_initialize*/ NULL, /* class_inited */ FALSE, /* initialize */ (XtInitProc) Initialize, /* initialize_hook */ NULL, /* realize */ XtInheritRealize, /* actions */ NULL, /* num_actions */ 0, /* resources */ resources, /* num_resources */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ TRUE, /* compress_exposure */ XtExposeCompressMultiple, /* compress_enterleave */ TRUE, /* visible_interest */ FALSE, /* destroy */ (XtWidgetProc) Destroy, /* resize */ (XtWidgetProc) Resize_term, /* expose */ (XtExposeProc) Redisplay, /* set_values */ (XtSetValuesFunc) SetValues, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ NULL, /* version */ XtVersion, /* callback_private */ NULL, /* tm_table */ NULL, /* query_geometry */ NULL, /* display_accelerator */ XtInheritDisplayAccelerator, /* extension */ NULL }, /* Simple class fields initialization */ { /* change_sensitive */ XtInheritChangeSensitive #ifndef OLDXAW , NULL #endif }, /* Angband class fields initialization */ { /* nothing */ 0 } }; /* * Hack -- see above */ #undef superclass /* * Class record pointer */ WidgetClass angbandWidgetClass = (WidgetClass) &angbandClassRec; /* * Public procedures */ /* * 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. */ static int x_io_error_handler(Display *d) { /* We have nothing to save */ if (!character_generated) return 0; save_dungeon(); save_player(); return 0; } /* * Clear an area */ static void AngbandClearArea(AngbandWidget widget, int x, int y, int w, int h, int a) { /* Figure out which area to clear */ y = y * widget->angband.fontheight + widget->angband.internal_border; x = x * widget->angband.fontwidth + widget->angband.internal_border; /* Clear the area */ XFillRectangle(XtDisplay(widget), XtWindow(widget), widget->angband.gc[a], x, y, widget->angband.fontwidth * w, widget->angband.fontheight * h); } /* * Output some text */ static void AngbandOutputText(AngbandWidget widget, int x, int y, String txt, int len, int a) { /* Do nothing if the string is null */ if (!txt || !*txt) return; /* Check the length, and fix it if it's below zero */ if (len < 0) len = strlen(txt); /* Figure out where to place the text */ y = (y * widget->angband.fontheight + widget->angband.fontascent + widget->angband.internal_border); x = (x * widget->angband.fontwidth + widget->angband.internal_border); /* Place the string */ XDrawImageString(XtDisplay(widget), XtWindow(widget), widget->angband.gc[a], x, y, txt, len); } #ifdef USE_GRAPHICS /* * Draw some graphical characters. */ static void AngbandOutputPict(AngbandWidget widget, int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp) { int i, x1, y1; byte a; char c; byte ta; char tc; int x2, y2; byte ea; char ec; int x3, y3; bool_ has_overlay; int k, l; unsigned long pixel, blank; /* Figure out where to place the text */ y = (y * widget->angband.fontheight + widget->angband.internal_border); x = (x * widget->angband.fontwidth + widget->angband.internal_border); for (i = 0; i < n; ++i) { a = *ap++; c = *cp++; /* For extra speed - cache these values */ x1 = (c & 0x7F) * widget->angband.fontwidth; y1 = (a & 0x7F) * widget->angband.fontheight; ta = *tap++; tc = *tcp++; /* For extra speed - cache these values */ x2 = (tc & 0x7F) * widget->angband.fontwidth; y2 = (ta & 0x7F) * widget->angband.fontheight; ea = *eap++; ec = *ecp++; has_overlay = (ea && ec); /* For extra speed -- cache these values */ x3 = (ec & 0x7F) * widget->angband.fontwidth; y3 = (ea & 0x7F) * widget->angband.fontheight; /* Optimise the common case */ if ((x1 == x2) && (y1 == y2)) { /* No overlay */ if (!has_overlay) { /* Draw object / terrain */ XPutImage(XtDisplay(widget), XtWindow(widget), widget->angband.gc[0], widget->angband.tiles, x1, y1, x, y, widget->angband.fontwidth, widget->angband.fontheight); } /* Terrain overlay */ else { /* Mega Hack^2 - assume the top left corner is "black" */ blank = XGetPixel(widget->angband.tiles, 0, widget->angband.fontheight * 6); for (k = 0; k < widget->angband.fontwidth; k++) { for (l = 0; l < widget->angband.fontheight; l++) { /* If mask set... */ if ((pixel = XGetPixel(widget->angband.tiles, x3 + k, y3 + l)) == blank) { /* Output from the terrain */ pixel = XGetPixel(widget->angband.tiles, x1 + k, y1 + l); } /* Store into the temp storage */ XPutPixel(widget->angband.TmpImage, k, l, pixel); } } /* Draw terrain + overlay */ XPutImage(XtDisplay(widget), XtWindow(widget), widget->angband.gc[0], widget->angband.TmpImage, 0, 0, x, y, widget->angband.fontwidth, widget->angband.fontheight); } } else { /* Mega Hack^2 - assume the top left corner is "black" */ blank = XGetPixel(widget->angband.tiles, 0, widget->angband.fontheight * 6); for (k = 0; k < widget->angband.fontwidth; k++) { for (l = 0; l < widget->angband.fontheight; l++) { /* Get overlay pixel */ if (has_overlay) { pixel = XGetPixel(widget->angband.tiles, x3 + k, y3 + l); } /* Hack -- no overlay */ else { pixel = blank; } /* If it's blank */ if (pixel == blank) { /* Use obj/mon */ pixel = XGetPixel(widget->angband.tiles, x1 + k, y1 + l); } /* Use terrain if it's blank too */ if (pixel == blank) { pixel = XGetPixel(widget->angband.tiles, x2 + k, y2 + l); } /* Store into the temp storage. */ XPutPixel(widget->angband.TmpImage, k, l, pixel); } } /* Draw to screen */ /* Draw object / terrain */ XPutImage(XtDisplay(widget), XtWindow(widget), widget->angband.gc[0], widget->angband.TmpImage, 0, 0, x, y, widget->angband.fontwidth, widget->angband.fontheight); } x += widget->angband.fontwidth; } } #endif /* USE_GRAPHICS */ /* * Private procedures */ /* * Procedure Initialize() is called during the widget creation * process. Initialize() load fonts and calculates window geometry. * The request parameter is filled in by parents to this widget. The * wnew parameter is the request parameter plus data filled in by this * widget. All changes should be done to the wnew parameter. */ static void Initialize(AngbandWidget request, AngbandWidget wnew) { Display *dpy = XtDisplay(wnew); int depth = DefaultDepthOfScreen(XtScreen((Widget) wnew)); XGCValues gcv; TopLevelShellWidget parent = (TopLevelShellWidget)XtParent((Widget) wnew); int i; /* Default background pixel */ unsigned long bg = create_pixel(dpy, angband_color_table[0][1], angband_color_table[0][2], angband_color_table[0][3]); /* Default foreground pixel */ unsigned long fg = create_pixel(dpy, angband_color_table[1][1], angband_color_table[1][2], angband_color_table[1][3]); /* Fix the background color */ wnew->core.background_pixel = bg; /* Get some information about the font */ wnew->angband.fnt = getFont(wnew, wnew->angband.font, TRUE); wnew->angband.fontheight = wnew->angband.fnt->ascent + wnew->angband.fnt->descent; wnew->angband.fontwidth = wnew->angband.fnt->max_bounds.width; wnew->angband.fontascent = wnew->angband.fnt->ascent; /* Create and initialize the graphics contexts */ /* GXset? */ gcv.font = wnew->angband.fnt->fid; gcv.graphics_exposures = FALSE; gcv.background = bg; for (i = 0; i < NUM_COLORS; i++) { unsigned long pixel; /* Acquire Angband colors */ wnew->angband.color[i][0] = angband_color_table[i][0]; wnew->angband.color[i][1] = angband_color_table[i][1]; wnew->angband.color[i][2] = angband_color_table[i][2]; wnew->angband.color[i][3] = angband_color_table[i][3]; if (depth > 1) { /* Create pixel */ pixel = create_pixel(dpy, wnew->angband.color[i][1], wnew->angband.color[i][2], wnew->angband.color[i][3]); } else { /* Use background or foreground */ pixel = ((i == 0) ? bg : fg); } gcv.foreground = pixel; /* Copy */ gcv.function = 3; wnew->angband.gc[i] = XtGetGC((Widget)wnew, (GCFont | GCForeground | GCFunction | GCBackground | GCGraphicsExposures), &gcv); } /* Create a special GC for highlighting */ gcv.foreground = (BlackPixelOfScreen(XtScreen((Widget)wnew)) ^ WhitePixelOfScreen(XtScreen((Widget)wnew))); gcv.background = 0; gcv.function = GXxor; wnew->angband.gc[COLOR_XOR] = XtGetGC((Widget)wnew, (GCFunction | GCForeground | GCBackground | GCGraphicsExposures), &gcv); /* Calculate window geometry */ wnew->core.height = (wnew->angband.start_rows * wnew->angband.fontheight + 2 * wnew->angband.internal_border); wnew->core.width = (wnew->angband.start_columns * wnew->angband.fontwidth + 2 * wnew->angband.internal_border); /* We need to be able to resize the Widget if the user wants to */ /* change font on the fly! */ parent->shell.allow_shell_resize = TRUE; /* Calculates all the size hints */ calculateSizeHints(wnew); } /* * Procedure Destroy() is called during the destruction of the widget. * Destroy() releases and frees GCs, frees the pixmaps and frees the * fonts. */ static void Destroy(AngbandWidget widget) { int n; /* Free all GC's */ for (n = 0; n < NUM_COLORS + 1; n++) { XtReleaseGC((Widget)widget, widget->angband.gc[n]); } /* Free the font */ XFreeFont(XtDisplay((Widget)widget), widget->angband.fnt); } static void Resize_term(AngbandWidget wnew) { int cols, rows, wid, hgt; int ox = wnew->angband.internal_border; int oy = wnew->angband.internal_border; int i; term_data *old_td; term_data *td = &data[0]; /* * Mega-Hack -- avoid calling this before the terms package is * initialised */ if (!Term) return; old_td = (term_data*)(Term->data); /* Hack - Find the term to activate */ for (i = 0; i < num_term; i++) { td = &data[i]; /* Paranoia: none of the widgets matched */ if (!td) return; /* Have we found it? */ if (td->widget == wnew) break; } /* Paranoia -- No matches */ if (i == num_term) return; /* Activate the proper Term */ Term_activate(&td->t); /* Determine "proper" number of rows/cols */ cols = ((wnew->core.width - (ox + ox)) / wnew->angband.fontwidth); rows = ((wnew->core.height - (oy + oy)) / wnew->angband.fontheight); /* Hack -- minimal size */ if (cols < 1) cols = 1; if (rows < 1) rows = 1; if (i == 0) { /* Hack the main window must be at least 80x24 */ if (cols < 80) cols = 80; if (rows < 24) rows = 24; } /* Desired size of window */ wid = cols * wnew->angband.fontwidth + (ox + ox); hgt = rows * wnew->angband.fontheight + (oy + oy); /* Resize the Term (if needed) */ (void) Term_resize(cols, rows); /* Activate the old term */ Term_activate(&old_td->t); } /* * Procedure Redisplay() is called as the result of an Expose event. * Use the redraw callback to do a full redraw */ static void Redisplay(AngbandWidget wnew, XEvent *xev, Region region) { int x1, x2, y1, y2; int i; term_data *old_td = (term_data*)(Term->data); term_data *td = &data[0]; /* Hack - Find the term to activate */ for (i = 0; i < num_term; i++) { td = &data[i]; /* Have we found it? */ if (td->widget == wnew) break; /* Paranoia: none of the widgets matched */ if (!td) return; } /* Activate the proper Term */ Term_activate(&td->t); /* Find the bounds of the exposed region */ /* * This probably could be obtained from the Region parameter - * but I don't know anything about XAW. */ x1 = (xev->xexpose.x - wnew->angband.internal_border) / wnew->angband.fontwidth; x2 = (xev->xexpose.x + xev->xexpose.width - wnew->angband.internal_border) / wnew->angband.fontwidth; y1 = (xev->xexpose.y - wnew->angband.internal_border) / wnew->angband.fontheight; y2 = (xev->xexpose.y + xev->xexpose.height - wnew->angband.internal_border) / wnew->angband.fontheight; Term_redraw_section(x1, y1, x2, y2); /* Activate the old term */ Term_activate(&old_td->t); #if 0 if (XtHasCallbacks((Widget)widget, XtNredrawCallback) == XtCallbackHasSome) { XtCallCallbacks((Widget)widget, XtNredrawCallback, NULL); } #endif /* 0 */ } /* * Font and internal_border can be changed on the fly. * * The entire widget is redrawn if any of those parameters change (all * can potentially have effects that spans the whole widget). * * Color changes are handled elsewhere. * * This function is very underspecified, in terms of how these changes can * occur, and what is true about the various AngbandWidget's passed in. It * is very likely that this code no longer works. */ static Boolean SetValues(AngbandWidget current, AngbandWidget request, AngbandWidget wnew, ArgList args, Cardinal *num_args) { Display *dpy = XtDisplay(wnew); Boolean font_changed = FALSE; Boolean border_changed = FALSE; int height, width; int i; /* Handle font change */ if (wnew->angband.font != current->angband.font) { /* Check if the font exists */ wnew->angband.fnt = getFont(wnew, wnew->angband.font, FALSE); /* The font didn't exist */ if (wnew->angband.fnt == NULL) { wnew->angband.fnt = current->angband.fnt; wnew->angband.font = current->angband.font; XtWarning("Couldn't find the requested font!"); } else { font_changed = TRUE; /* Free the old font */ XFreeFont(XtDisplay((Widget)wnew), current->angband.fnt); /* Update font information */ wnew->angband.fontheight = wnew->angband.fnt->ascent + wnew->angband.fnt->descent; wnew->angband.fontwidth = wnew->angband.fnt->max_bounds.width; wnew->angband.fontascent = wnew->angband.fnt->ascent; } } /* Handle font change */ if (font_changed) { /* Update all GC's */ for (i = 0; i < NUM_COLORS; i++) { /* Steal the old GC */ wnew->angband.gc[i] = current->angband.gc[i]; current->angband.gc[i] = NULL; /* Be sure the correct font is ready */ XSetFont(dpy, wnew->angband.gc[i], wnew->angband.fnt->fid); } /* Steal the old GC */ wnew->angband.gc[NUM_COLORS] = current->angband.gc[NUM_COLORS]; current->angband.gc[NUM_COLORS] = NULL; } /* Check if internal border width has changed, used later */ if (current->angband.internal_border != wnew->angband.internal_border) { border_changed = TRUE; } /* If the font or the internal border has changed, all geometry */ /* has to be recalculated */ if (font_changed || border_changed) { /* Change window size */ height = ((current->core.height - 2 * current->angband.internal_border) / current->angband.fontheight * wnew->angband.fontheight + 2 * current->angband.internal_border); width = ((current->core.width - 2 * current->angband.internal_border) / current->angband.fontwidth * wnew->angband.fontwidth + 2 * wnew->angband.internal_border); /* Get the new width */ if (XtMakeResizeRequest((Widget)wnew, width, height, NULL, NULL) == XtGeometryNo) { /* Not allowed */ XtWarning("Size change denied!"); } else { /* Recalculate size hints */ calculateSizeHints(wnew); } } /* Tell it to redraw the widget if anything has changed */ return (font_changed || border_changed); } /* * Calculate size hints */ static void calculateSizeHints(AngbandWidget wnew) { TopLevelShellWidget parent = (TopLevelShellWidget)XtParent((Widget) wnew); /* Calculate minimum size */ parent->wm.size_hints.min_height = (wnew->angband.min_rows * wnew->angband.fontheight + 2 * wnew->angband.internal_border); /* Calculate minimum size */ parent->wm.size_hints.min_width = (wnew->angband.min_columns * wnew->angband.fontwidth + 2 * wnew->angband.internal_border); /* Calculate minimum size */ parent->wm.size_hints.flags |= PMinSize; /* Calculate maximum size */ parent->wm.size_hints.max_height = (wnew->angband.max_rows * wnew->angband.fontheight + 2 * wnew->angband.internal_border); /* Calculate maximum size */ parent->wm.size_hints.max_width = (wnew->angband.max_columns * wnew->angband.fontwidth + 2 * wnew->angband.internal_border); /* Calculate maximum size */ parent->wm.size_hints.flags |= PMaxSize; /* Calculate increment size */ parent->wm.size_hints.height_inc = wnew->angband.fontheight; parent->wm.size_hints.width_inc = wnew->angband.fontwidth; parent->wm.size_hints.flags |= PResizeInc; /* Calculate base size */ parent->wm.base_height = 2 * wnew->angband.internal_border; parent->wm.base_width = 2 * wnew->angband.internal_border; parent->wm.size_hints.flags |= PBaseSize; } /* * Load a font */ static XFontStruct *getFont(AngbandWidget widget, String font, Boolean fallback) { Display *dpy = XtDisplay((Widget) widget); char buf[256]; XFontStruct *fnt = NULL; if (!(fnt = XLoadQueryFont(dpy, font)) && fallback) { sprintf(buf, "Can't find the font \"%s\", trying fixed\n", font); XtWarning(buf); if (!(fnt = XLoadQueryFont(dpy, "fixed"))) { XtError("Can't fint the font \"fixed\"!, bailing out\n"); } } return fnt; } /*** The Angband code ****/ /* * Number of fallback resources per window */ #define TERM_FALLBACKS 6 /* * The names of the term_data's */ char *termNames[MAX_TERM_DATA] = { "angband", "mirror", "recall", "choice", "term-4", "term-5", "term-6", "term-7" }; /* * The special Arg's */ Arg specialArgs[TERM_FALLBACKS] = { { XtNstartRows, 24}, { XtNstartColumns, 80}, { XtNminRows, 24}, { XtNminColumns, 80}, { XtNmaxRows, 255}, { XtNmaxColumns, 255} }; /* * The default Arg's */ Arg defaultArgs[TERM_FALLBACKS] = { { XtNstartRows, 24}, { XtNstartColumns, 80}, { XtNminRows, 1}, { XtNminColumns, 1}, { XtNmaxRows, 255}, { XtNmaxColumns, 255} }; /* * The application context */ XtAppContext appcon; /* * User changable information about widgets */ static String fallback[] = { "Angband.angband.iconName: ToME", "Angband.angband.title: ToME", "Angband.term-1.iconName: Mirror", "Angband.term-1.title: Mirror", "Angband.term-2.iconName: Recall", "Angband.term-2.title: Recall", "Angband.term-3.iconName: Choice", "Angband.term-3.title: Choice", "Angband.term-4.iconName: Term 4", "Angband.term-4.title: Term 4", "Angband.term-5.iconName: Term 5", "Angband.term-5.title: Term 5", "Angband.term-6.iconName: Term 6", "Angband.term-6.title: Term 6", "Angband.term-7.iconName: Term 7", "Angband.term-7.title: Term 7", NULL }; /* * Do a redraw */ static void react_redraw(Widget widget, XtPointer client_data, XtPointer call_data) { term_data *old_td = (term_data*)(Term->data); term_data *td = (term_data*)client_data; /* Activate the proper Term */ Term_activate(&td->t); /* Request a redraw */ Term_redraw(); /* Activate the old Term */ Term_activate(&old_td->t); } /* * Process a keypress event * * Also appears in "main-x11.c". */ static void react_keypress(XKeyEvent *xev) { int i, n, mc, ms, mo, mx; uint 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 = (uint)(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); } } /* * Handle an event */ static void handle_event(Widget widget, XtPointer client_data, XEvent *event, Boolean *continue_to_dispatch) { term_data *old_td = (term_data*)(Term->data); term_data *td = (term_data *)client_data; /* Continue to process the event by default */ *continue_to_dispatch = TRUE; /* Activate the Term */ Term_activate(&td->t); switch (event->type) { case KeyPress: { /* Hack -- use old term */ Term_activate(&old_td->t); /* Handle the keypress */ react_keypress(&(event->xkey)); /* We took care of the event */ *continue_to_dispatch = FALSE; break; } /* Oops */ default: { break; } } /* Activate the old term */ Term_activate(&old_td->t); return; } /* * Process an event (or just check for one) */ errr CheckEvent(bool_ wait) { XEvent event; /* No events ready, and told to just check */ if (!wait && !XtAppPending(appcon)) return 1; /* Process */ while (1) { XtAppNextEvent(appcon, &event); XtDispatchEvent(&event); if (!XtAppPending(appcon)) break; } return (0); } /* * Monstrous hack. */ static void Term_xtra_xaw_react_aux(term_data *td) { AngbandWidget wnew = td->widget; Display *dpy = XtDisplay((Widget) wnew); int depth = DefaultDepthOfScreen(XtScreen((Widget) wnew)); int i; /* See if any colors need to be changed */ for (i = 0; i < NUM_COLORS; i++) { if (depth > 1) { if ((wnew->angband.color[i][0] != angband_color_table[i][0]) || (wnew->angband.color[i][1] != angband_color_table[i][1]) || (wnew->angband.color[i][2] != angband_color_table[i][2]) || (wnew->angband.color[i][3] != angband_color_table[i][3])) { unsigned long pixel; /* Save new values */ wnew->angband.color[i][0] = angband_color_table[i][0]; wnew->angband.color[i][1] = angband_color_table[i][1]; wnew->angband.color[i][2] = angband_color_table[i][2]; wnew->angband.color[i][3] = angband_color_table[i][3]; /* Create pixel */ pixel = create_pixel(dpy, wnew->angband.color[i][1], wnew->angband.color[i][2], wnew->angband.color[i][3]); /* Change */ XSetForeground(dpy, wnew->angband.gc[i], pixel); } } } } /* * Monstrous hack. */ static errr Term_xtra_xaw_react(void) { int i; /* Initialize the windows */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; if (!td) break; Term_xtra_xaw_react_aux(td); } return (0); } /* * Handle a "special request" */ static errr Term_xtra_xaw(int n, int v) { term_data *td = (term_data*)(Term->data); Widget widget = (Widget)(td->widget); Display *dpy = XtDisplay(widget); /* Handle a subset of the legal requests */ switch (n) { /* Make a noise */ case TERM_XTRA_NOISE: XBell(dpy, 100); return (0); /* Flush the output */ case TERM_XTRA_FRESH: XFlush(dpy); /* Allow flushed events to be showed */ CheckEvent(FALSE); return (0); /* Process random events */ case TERM_XTRA_BORED: return (CheckEvent(0)); /* Process events */ case TERM_XTRA_EVENT: return (CheckEvent(v)); /* Flush events */ case TERM_XTRA_FLUSH: while (!CheckEvent(FALSE)); return (0); /* Clear the window */ case TERM_XTRA_CLEAR: XClearWindow(dpy, XtWindow(widget)); return (0); /* Delay */ case TERM_XTRA_DELAY: usleep(1000 * v); return (0); /* Get Delay of some milliseconds */ case TERM_XTRA_GET_DELAY: { int ret; struct timeval tv; ret = gettimeofday(&tv, NULL); Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); return ret; } /* Subdirectory scan */ case TERM_XTRA_SCANSUBDIR: { DIR *directory; struct dirent *entry; scansubdir_max = 0; directory = opendir(scansubdir_dir); if (!directory) return 1; while (entry = readdir(directory)) { char file[PATH_MAX + NAME_MAX + 2]; struct stat filedata; file[PATH_MAX + NAME_MAX] = 0; strncpy(file, scansubdir_dir, PATH_MAX); strncat(file, "/", 2); strncat(file, entry->d_name, NAME_MAX); if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode))) { string_free(scansubdir_result[scansubdir_max]); scansubdir_result[scansubdir_max] = string_make(entry->d_name); ++scansubdir_max; } } } case TERM_XTRA_REACT: return (Term_xtra_xaw_react()); } /* Unknown */ return (1); } /* * Erase a number of characters */ static errr Term_wipe_xaw(int x, int y, int n) { term_data *td = (term_data*)(Term->data); /* Erase using color 0 */ AngbandClearArea(td->widget, x, y, n, 1, 0); /* Success */ return (0); } /* * Draw the cursor, by hiliting with XOR * * Should perhaps use rectangle outline, ala "main-mac.c". XXX XXX XXX */ static errr Term_curs_xaw(int x, int y) { term_data *td = (term_data*)(Term->data); /* Hilite the cursor character */ AngbandClearArea(td->widget, x, y, 1, 1, COLOR_XOR); /* Success */ return (0); } /* * Draw a number of characters */ static errr Term_text_xaw(int x, int y, int n, byte a, cptr s) { term_data *td = (term_data*)(Term->data); /* Draw the text */ AngbandOutputText(td->widget, x, y, (String)s, n, a); /* Success */ return (0); } #ifdef USE_GRAPHICS /* * Draw some graphical characters. */ static errr Term_pict_xaw(int x, int y, int n, const byte *ap, const char *cp, const byte *tap, const char *tcp, const byte *eap, const char *ecp) { term_data *td = (term_data*)(Term->data); /* Draw the pictures */ AngbandOutputPict(td->widget, x, y, n, ap, cp, tap, tcp, eap, ecp); /* Success */ return (0); } #endif /* USE_GRAPHICS */ /* * Raise a term */ static void term_raise(term_data *td) { Widget widget = (Widget)(td->widget); XRaiseWindow(XtDisplay(XtParent(widget)), XtWindow(XtParent(widget))); } /* * Initialize a term_data */ static errr term_data_init(term_data *td, Widget topLevel, int key_buf, String name, ArgList widget_arg, Cardinal widget_arg_no, int i) { Widget parent; term *t = &td->t; int cols = 80; int rows = 24; char buf[80]; cptr str; int val; /* Create the shell widget */ parent = XtCreatePopupShell(name, topLevelShellWidgetClass, topLevel, NULL, 0); /* 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; /* Hack the main window must be at least 80x24 */ if (i == 0) { if (cols < 80) cols = 80; if (rows < 24) rows = 24; } /* Reset the initial size */ widget_arg[0].value = rows; widget_arg[1].value = cols; /* Create the interior widget */ td->widget = (AngbandWidget) XtCreateManagedWidget(name, angbandWidgetClass, parent, widget_arg, widget_arg_no); /* Initialize the term (full size) */ term_init(t, cols, rows, key_buf); /* Use a "soft" cursor */ t->soft_cursor = TRUE; /* Erase with "white space" */ t->attr_blank = TERM_WHITE; t->char_blank = ' '; /* Hooks */ t->xtra_hook = Term_xtra_xaw; t->curs_hook = Term_curs_xaw; t->wipe_hook = Term_wipe_xaw; t->text_hook = Term_text_xaw; /* Save the data */ t->data = td; /* Register the keypress event handler */ XtAddEventHandler((Widget)td->widget, KeyPressMask, False, (XtEventHandler) handle_event, td); /* Redraw callback */ XtAddCallback((Widget)td->widget, XtNredrawCallback, react_redraw, td); /* Realize the widget */ XtRealizeWidget(parent); /* Make it visible */ XtPopup(parent, XtGrabNone); /* Activate (important) */ Term_activate(t); Resize_term(td->widget); return 0; } /* * Initialization function for an X Athena Widget module to Angband * * We should accept "-d" requests in the "argv" array. XXX XXX XXX */ errr init_xaw(int argc, char *argv[]) { int i; Widget topLevel; Display *dpy; cptr dpy_name = ""; #ifdef USE_GRAPHICS char filename[1024]; int pict_wid = 0; int pict_hgt = 0; bool_ force_old_graphics = FALSE; char *TmpData; #endif /* USE_GRAPHICS */ /* Parse args */ for (i = 1; i < argc; i++) { if (prefix(argv[i], "-d")) { dpy_name = &argv[i][2]; continue; } #ifdef USE_GRAPHICS if (prefix(argv[i], "-s")) { smoothRescaling = FALSE; continue; } if (prefix(argv[i], "-o")) { force_old_graphics = TRUE; continue; } #endif /* USE_GRAPHICS */ if (prefix(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; } plog_fmt("Ignoring option: %s", argv[i]); } /* Attempt to open the local display */ dpy = XOpenDisplay(dpy_name); /* Failure -- assume no X11 available */ if (!dpy) return ( -1); /* Close the local display */ XCloseDisplay(dpy); #ifdef USE_XAW_LANG /* Support locale processing */ XtSetLanguageProc(NULL, NULL, NULL); #endif /* USE_XAW_LANG */ /* Initialize the toolkit */ topLevel = XtAppInitialize(&appcon, "Angband", NULL, 0, &argc, argv, fallback, NULL, 0); XSetIOErrorHandler(x_io_error_handler); /* Initialize the windows */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; term_data_init(td, topLevel, 1024, termNames[i], (i == 0) ? specialArgs : defaultArgs, TERM_FALLBACKS, i); angband_term[i] = Term; } /* Activate the "Angband" window screen */ Term_activate(&data[0].t); /* Raise the "Angband" window */ term_raise(&data[0]); #ifdef USE_GRAPHICS /* Try graphics */ if (arg_graphics) { /* Try the "16x16.bmp" file */ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/16x16.bmp"); /* Use the "16x16.bmp" file if it exists */ if (!force_old_graphics && (0 == fd_close(fd_open(filename, O_RDONLY)))) { /* Use graphics */ use_graphics = TRUE; pict_wid = pict_hgt = 16; ANGBAND_GRAF = "new"; } else { /* Try the "8x8.bmp" file */ path_build(filename, 1024, ANGBAND_DIR_XTRA, "graf/8x8.bmp"); /* Use the "8x8.bmp" file if it exists */ if (0 == fd_close(fd_open(filename, O_RDONLY))) { /* Use graphics */ use_graphics = TRUE; pict_wid = pict_hgt = 8; ANGBAND_GRAF = "old"; } } } /* Load graphics */ if (use_graphics) { /* Hack -- Get the Display */ term_data *td = &data[0]; Widget widget = (Widget)(td->widget); Display *dpy = XtDisplay(widget); XImage *tiles_raw; /* Load the graphical tiles */ tiles_raw = ReadBMP(dpy, filename); /* Initialize the windows */ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; term *t = &td->t; t->pict_hook = Term_pict_xaw; t->higher_pict = TRUE; /* Resize tiles */ td->widget->angband.tiles = ResizeImage(dpy, tiles_raw, pict_wid, pict_hgt, td->widget->angband.fontwidth, td->widget->angband.fontheight); } /* Initialize the transparency temp storage*/ for (i = 0; i < num_term; i++) { term_data *td = &data[i]; int ii, jj; int depth = DefaultDepth(dpy, DefaultScreen(dpy)); Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy)); int total; /* Determine total bytes needed for image */ ii = 1; jj = (depth - 1) >> 2; while (jj >>= 1) ii <<= 1; total = td->widget->angband.fontwidth * td->widget->angband.fontheight * ii; TmpData = (char *)malloc(total); td->widget->angband.TmpImage = XCreateImage(dpy, visual, depth, ZPixmap, 0, TmpData, td->widget->angband.fontwidth, td->widget->angband.fontheight, 8, 0); } /* Free tiles_raw? XXX XXX */ } #endif /* USE_GRAPHICS */ /* Success */ return (0); } #endif /* USE_XAW */