OSDN Git Service

Merge pull request #1503 from Hourier/feature/Remove-Type-Aliases-Alpha38
[hengbandforosx/hengbandosx.git] / src / main-xaw.cpp
1 /* File: main-xaw.c */
2
3 /* Purpose: Support for X Athena Widget based Angband */
4 /* Most code written by Torbj\vn Lindgren (tl@cd.chalmers.se) */
5
6 #ifdef USE_XAW
7
8 #include "game-option/runtime-arguments.h"
9 #include "system/angband.h"
10 #include "util/int-char-converter.h"
11
12 #ifndef __MAKEDEPEND__
13 #include <X11/CoreP.h>
14 #include <X11/Intrinsic.h>
15 #include <X11/IntrinsicP.h>
16 #include <X11/Shell.h>
17 #include <X11/ShellP.h>
18 #include <X11/StringDefs.h>
19 #include <X11/Xaw/Simple.h>
20 #include <X11/Xaw/SimpleP.h>
21 #include <X11/Xaw/XawInit.h>
22 #include <X11/Xlib.h>
23 #include <X11/Xutil.h>
24 #include <X11/keysym.h>
25 #include <X11/keysymdef.h>
26 #endif /* __MAKEDEPEND__ */
27
28 /*****************************************************
29  *
30  * Resource description
31  *
32  *****************************************************/
33
34 /* Resources:
35
36 Name                Class              RepType         Default Value
37 ----                -----              -------         -------------
38 background          Background         Pixel           XtDefaultBackground
39 border              BorderColor        Pixel           XtDefaultForeground
40 borderWidth         BorderWidth        Dimension       1
41 cursor              Cursor             Cursor          None
42 cursorName          Cursor             String          nullptr
43 destroyCallback     Callback           Pointer         nullptr
44 height              Height             Dimension       0
45 insensitiveBorder   Insensitive        Pixmap          Gray
46 mappedWhenManaged   MappedWhenManaged  Boolean         True
47 pointerColor        Foreground         Pixel           XtDefaultForeground
48 pointerColorBackground Background      Pixel           XtDefaultBackground
49 sensitive           Sensitive          Boolean         True
50 width               Width              Dimension       0
51 x                   Position           Position        0
52 y                   Position           Position        0
53
54  */
55
56 /*
57
58 My own X Resources look like this (on a 1152x900 screen):
59
60 angband*angband*font:                   12x24
61 angband*angband*geometry:               +0+-20
62 angband*recall*font:                    7x13
63 angband*recall*geometry:                80x10+0+586
64 angband*choice*font:                    7x13
65 angband*choice*geometry:                -0-0
66
67 It's also possible to change the colors using X Resources, the
68 standard colors would look like:
69
70 angband*color0:                         #000000
71 angband*color1:                         #ffffff
72 angband*color2:                         #a6a6a6
73 angband*color3:                         #ff6302
74 angband*color4:                         #ca0808
75 angband*color5:                         #008e18
76 angband*color6:                         #0000e3
77 angband*color7:                         #814007
78 angband*color8:                         #6b6b6b
79 angband*color9:                         #d6d6d6
80 angband*color10:                        #5100c2
81 angband*color11:                        #fdf105
82 angband*color12:                        #ff9259
83 angband*color13:                        #26cf17
84 angband*color14:                        #02b2f2
85 angband*color15:                        #b28b48
86
87 And the newer colors look like:
88
89 angband*color0:                         #000000
90 angband*color1:                         #ffffff
91 angband*color2:                         #d7d7d7
92 angband*color3:                         #ff9200
93 angband*color4:                         #ff0000
94 angband*color5:                         #00cd00
95 angband*color6:                         #0000fe
96 angband*color7:                         #c86400
97 angband*color8:                         #a3a3a3
98 angband*color9:                         #ebebeb
99 angband*color10:                        #a500ff
100 angband*color11:                        #fffd00
101 angband*color12:                        #ff00bc
102 angband*color13:                        #00ff00
103 angband*color14:                        #00c8ff
104 angband*color15:                        #ffcc80
105
106 Some older monochrome monitors have problem with white text on black
107 background. The new code can handle the reverse situation if the user
108 wants/needs this.
109
110 The following X Resources gives black text on white background using
111 Angband/Xaw. The other colors (2-15) isn't changed, since they're not
112 used on a monochrome monitor.
113
114 angband*color0: #ffffff
115 angband*color1: #000000
116
117  */
118
119 /* New resource names */
120 #define XtNstartRows "startRows"
121 #define XtNstartColumns "startColumns"
122 #define XtNminRows "minRows"
123 #define XtNminColumns "minColumns"
124 #define XtNmaxRows "maxRows"
125 #define XtNmaxColumns "maxColumns"
126 #define XtNinternalBorder "internalBorder"
127 #define XtNcolor0 "color0"
128 #define XtNcolor1 "color1"
129 #define XtNcolor2 "color2"
130 #define XtNcolor3 "color3"
131 #define XtNcolor4 "color4"
132 #define XtNcolor5 "color5"
133 #define XtNcolor6 "color6"
134 #define XtNcolor7 "color7"
135 #define XtNcolor8 "color8"
136 #define XtNcolor9 "color9"
137 #define XtNcolor10 "color10"
138 #define XtNcolor11 "color11"
139 #define XtNcolor12 "color12"
140 #define XtNcolor13 "color13"
141 #define XtNcolor14 "color14"
142 #define XtNcolor15 "color15"
143 #define XtNredrawCallback "redrawCallback"
144
145 /* External definitions */
146 #define COLOR_XOR 16
147 #define NUM_COLORS 16
148
149 /* C Widget type definition */
150
151 typedef struct AngbandRec *AngbandWidget;
152
153 /* C Widget class type definition */
154
155 typedef struct AngbandClassRec *AngbandWidgetClass;
156
157 /*
158  * New fields for the Angband widget record
159  */
160
161 struct AngbandPart {
162     /* Settable resources */
163     int start_rows;
164     int start_columns;
165     int min_rows;
166     int min_columns;
167     int max_rows;
168     int max_columns;
169     int internal_border;
170     String font;
171     Pixel color[NUM_COLORS];
172     XtCallbackList redraw_callbacks;
173
174     /* Private state */
175     XFontStruct *fnt;
176     Dimension fontheight;
177     Dimension fontwidth;
178     Dimension fontascent;
179     GC gc[NUM_COLORS + 1]; /* Includes a special 'xor' color */
180 };
181
182 /*
183  * Full instance record declaration
184  */
185 struct AngbandRec {
186     CorePart core;
187     SimplePart simple;
188     AngbandPart angband;
189 };
190
191 /*
192  * New fields for the Angband widget class record
193  */
194 struct AngbandClassPart {
195     int dummy;
196 };
197
198 /*
199  * Full class record declaration
200  */
201 struct AngbandClassRec {
202     CoreClassPart core_class;
203     SimpleClassPart simple_class;
204     AngbandClassPart angband_class;
205 };
206
207 /* Angband widget, Created by Torbj\vn Lindgren (tl@cd.chalmers.se) */
208
209 /*
210  * Note that it isn't as self-contained as it really should be,
211  * originally everything was output to a Pixmap which was later copied
212  * to the screen when necessary. I had to abandon that idea since
213  * Pixmaps creates big performance problems for some really old X
214  * terminals (such as 3/50's running Xkernel).
215  */
216
217 /*
218  * The default colors used is based on the ones used in main-mac.c.
219  * The main difference is that they are gamma corrected for a gamma of
220  * 1.6.  MacOS do gamma correction afterwards, but X uses raw
221  * colors. The Gamma of most color screens are about 1.5 - 1.7.
222  * Color 12 was later changed a bit so that it didn't look as similar
223  * to color 3/4.
224  */
225
226 #define offset(field) XtOffsetOf(AngbandRec, angband.field)
227
228 /*
229  * Fallback resources for Angband widget
230  */
231 static XtResource resources[] = { { XtNstartRows, XtCValue, XtRInt, sizeof(int), offset(start_rows), XtRImmediate, (XtPointer)24 },
232     { XtNstartColumns, XtCValue, XtRInt, sizeof(int), offset(start_columns), XtRImmediate, (XtPointer)80 },
233     { XtNminRows, XtCValue, XtRInt, sizeof(int), offset(min_rows), XtRImmediate, (XtPointer)1 },
234     { XtNminColumns, XtCValue, XtRInt, sizeof(int), offset(min_columns), XtRImmediate, (XtPointer)1 },
235     { XtNmaxRows, XtCValue, XtRInt, sizeof(int), offset(max_rows), XtRImmediate, (XtPointer)24 },
236     { XtNmaxColumns, XtCValue, XtRInt, sizeof(int), offset(max_columns), XtRImmediate, (XtPointer)80 },
237     { XtNinternalBorder, XtCValue, XtRInt, sizeof(int), offset(internal_border), XtRImmediate, (XtPointer)2 },
238     { XtNfont, XtCFont, XtRString, sizeof(char *), offset(font), XtRString, "9x15" },
239     { XtNcolor0, XtCColor, XtRPixel, sizeof(Pixel), offset(color[0]), XtRString, "black" },
240     { XtNcolor1, XtCColor, XtRPixel, sizeof(Pixel), offset(color[1]), XtRString, "white" },
241     { XtNcolor2, XtCColor, XtRPixel, sizeof(Pixel), offset(color[2]), XtRString, "#d7d7d7" },
242     { XtNcolor3, XtCColor, XtRPixel, sizeof(Pixel), offset(color[3]), XtRString, "#ff9200" },
243     { XtNcolor4, XtCColor, XtRPixel, sizeof(Pixel), offset(color[4]), XtRString, "#ff0000" },
244     { XtNcolor5, XtCColor, XtRPixel, sizeof(Pixel), offset(color[5]), XtRString, "#00cd00" },
245     { XtNcolor6, XtCColor, XtRPixel, sizeof(Pixel), offset(color[6]), XtRString, "#0000fe" },
246     { XtNcolor7, XtCColor, XtRPixel, sizeof(Pixel), offset(color[7]), XtRString, "#c86400" },
247     { XtNcolor8, XtCColor, XtRPixel, sizeof(Pixel), offset(color[8]), XtRString, "#a3a3a3" },
248     { XtNcolor9, XtCColor, XtRPixel, sizeof(Pixel), offset(color[9]), XtRString, "#ebebeb" },
249     { XtNcolor10, XtCColor, XtRPixel, sizeof(Pixel), offset(color[10]), XtRString, "#a500ff" },
250     { XtNcolor11, XtCColor, XtRPixel, sizeof(Pixel), offset(color[11]), XtRString, "#fffd00" },
251     { XtNcolor12, XtCColor, XtRPixel, sizeof(Pixel), offset(color[12]), XtRString, "#ff00bc" },
252     { XtNcolor13, XtCColor, XtRPixel, sizeof(Pixel), offset(color[13]), XtRString, "#00ff00" },
253     { XtNcolor14, XtCColor, XtRPixel, sizeof(Pixel), offset(color[14]), XtRString, "#00c8ff" },
254     { XtNcolor15, XtCColor, XtRPixel, sizeof(Pixel), offset(color[15]), XtRString, "#ffcc80" },
255
256     { XtNredrawCallback, XtCCallback, XtRCallback, sizeof(XtPointer), offset(redraw_callbacks), XtRCallback, (XtPointer)nullptr } };
257
258 #undef offset
259
260 /* Forward declarations for Widget functions */
261 static void Initialize(AngbandWidget request, AngbandWidget new);
262 static void Redisplay(AngbandWidget w, XEvent *event, Region region);
263 static Boolean SetValues(AngbandWidget current, AngbandWidget request, AngbandWidget new, ArgList args, Cardinal *num_args);
264 static void Destroy(AngbandWidget widget);
265
266 /* Forward declaration for internal functions */
267 static void calculateSizeHints(AngbandWidget new);
268 static XFontStruct *getFont(AngbandWidget widget, String font, Boolean fallback);
269
270 /* Class record constanst */
271 AngbandClassRec angbandClassRec = { {
272 /* Core class fields initialization */
273 #define superclass (&simpleClassRec)
274                                         /* superclass           */ (WidgetClass)superclass,
275                                         /* class_name           */ "Angband",
276                                         /* widget_size          */ sizeof(AngbandRec),
277                                         /* class_initialize     */ nullptr,
278                                         /* class_part_initialize*/ nullptr,
279                                         /* class_inited         */ false,
280                                         /* initialize           */ (XtInitProc)Initialize,
281                                         /* initialize_hook      */ nullptr,
282                                         /* realize              */ XtInheritRealize,
283                                         /* actions              */ nullptr,
284                                         /* num_actions          */ 0,
285                                         /* resources            */ resources,
286                                         /* num_resources        */ XtNumber(resources),
287                                         /* xrm_class            */ nullptrQUARK,
288                                         /* compress_motion      */ true,
289                                         /* compress_exposure    */ XtExposeCompressMultiple,
290                                         /* compress_enterleave  */ true,
291                                         /* visible_interest     */ false,
292                                         /* destroy              */ (XtWidgetProc)Destroy,
293                                         /* resize               */ nullptr,
294                                         /* expose               */ (XtExposeProc)Redisplay,
295                                         /* set_values           */ (XtSetValuesFunc)SetValues,
296                                         /* set_values_hook      */ nullptr,
297                                         /* set_values_almost    */ XtInheritSetValuesAlmost,
298                                         /* get_values_hook      */ nullptr,
299                                         /* accept_focus         */ nullptr,
300                                         /* version              */ XtVersion,
301                                         /* callback_private     */ nullptr,
302                                         /* tm_table             */ nullptr,
303                                         /* query_geometry       */ nullptr,
304                                         /* display_accelerator  */ XtInheritDisplayAccelerator,
305                                         /* extension            */ nullptr },
306     /* Simple class fields initialization */
307     { /* change_sensitive     */ XtInheritChangeSensitive },
308     /* Angband class fields initialization */
309     { /* nothing              */ 0 } };
310
311 /* Class record pointer */
312 WidgetClass angbandWidgetClass = (WidgetClass)&angbandClassRec;
313
314 /*
315  * Public procedures
316  */
317 static void AngbandOutputText(AngbandWidget widget, int x, int y, String txt, int len, int color)
318 {
319     /* Do nothing if the string is null */
320     if (!txt || !*txt)
321         return;
322
323     /* Check the lenght, and fix it if it's below zero */
324     if (len < 0)
325         len = strlen(txt);
326
327     /* Figure out where to place the text */
328     y = y * widget->angband.fontheight + widget->angband.fontascent + widget->angband.internal_border;
329     x = x * widget->angband.fontwidth + widget->angband.internal_border;
330
331     /* Place the string */
332     XDrawImageString(XtDisplay(widget), XtWindow(widget), widget->angband.gc[color], x, y, txt, len);
333 }
334
335 static void AngbandClearArea(AngbandWidget widget, int x, int y, int w, int h, int color)
336 {
337     /* Figure out which area to clear */
338     y = y * widget->angband.fontheight + widget->angband.internal_border;
339     x = x * widget->angband.fontwidth + widget->angband.internal_border;
340
341     /* Clear the area */
342     XFillRectangle(XtDisplay(widget), XtWindow(widget), widget->angband.gc[color], x, y, widget->angband.fontwidth * w, widget->angband.fontheight * h);
343 }
344
345 /*
346  * Private procedures
347  */
348
349 /*
350  * Procedure Initialize() is called during the widget creation
351  * process.  Initialize() load fonts and calculates window geometry.
352  * The request parameter is filled in by parents to this widget.  The
353  * new parameter is the request parameter plus data filled in by this
354  * widget. All changes should be done to the new parameter.
355  */
356 static void Initialize(AngbandWidget request, AngbandWidget new)
357 {
358     XGCValues gcv;
359     int depth = DefaultDepthOfScreen(XtScreen((Widget) new));
360     TopLevelShellWidget parent = (TopLevelShellWidget)XtParent((Widget) new);
361     int n;
362
363     /* Fix the background color */
364     new->core.background_pixel = new->angband.color[0];
365
366     /* Get some information about the font */
367     new->angband.fnt = getFont(new, new->angband.font, TRUE);
368     new->angband.fontheight = new->angband.fnt->ascent + new->angband.fnt->descent;
369     new->angband.fontwidth = new->angband.fnt->max_bounds.width;
370     new->angband.fontascent = new->angband.fnt->ascent;
371
372     /* Create and initialize the graphics contexts */ /* GXset? */
373     gcv.font = new->angband.fnt->fid;
374     gcv.graphics_exposures = FALSE;
375     gcv.background = new->angband.color[0];
376     for (n = 0; n < NUM_COLORS; n++) {
377         if (depth == 1 && n >= 1)
378             gcv.foreground = new->angband.color[1];
379         else
380             gcv.foreground = new->angband.color[n];
381         new->angband.gc[n] = XtGetGC((Widget) new, GCFont | GCForeground | GCBackground | GCGraphicsExposures, &gcv);
382     }
383
384     /* Create a special GC for highlighting */
385     gcv.foreground = BlackPixelOfScreen(XtScreen((Widget) new)) ^ WhitePixelOfScreen(XtScreen((Widget) new));
386     gcv.function = GXxor;
387     new->angband.gc[NUM_COLORS] = XtGetGC((Widget) new, GCFunction | GCGraphicsExposures | GCForeground, &gcv);
388
389     /* Calculate window geometry */
390     new->core.height = new->angband.start_rows *new->angband.fontheight + 2 * new->angband.internal_border;
391     new->core.width = new->angband.start_columns *new->angband.fontwidth + 2 * new->angband.internal_border;
392
393     /* We need to be able to resize the Widget if the user want's to
394     change font on the fly! */
395     parent->shell.allow_shell_resize = TRUE;
396
397     /* Calculates all the size hints */
398     calculateSizeHints(new);
399 }
400
401 /*
402  * Procedure Destroy() is called during the destruction of the widget.
403  * Destroy() releases and frees GCs, frees the pixmaps and frees the
404  * fonts.
405  */
406 static void Destroy(AngbandWidget widget)
407 {
408     int n;
409
410     /* Free all GC's */
411     for (n = 0; n < NUM_COLORS + 1; n++)
412         XtReleaseGC((Widget)widget, widget->angband.gc[n]);
413
414     /* Free the font */
415     XFreeFont(XtDisplay((Widget)widget), widget->angband.fnt);
416 }
417
418 /*
419  * Procedure Redisplay() is called as the result of an Expose event.
420  * Use the redraw callback to do a full redraw
421  */
422 static void Redisplay(AngbandWidget widget, XEvent *event, Region region)
423 {
424     if (XtHasCallbacks((Widget)widget, XtNredrawCallback) == XtCallbackHasSome)
425         XtCallCallbacks((Widget)widget, XtNredrawCallback, nullptr);
426 }
427
428 /*
429  * Font, colors and internal_border can be changed on the fly.
430  * The entire widget is redrawn if any of those parameters change (all
431  * can potentially have effects that spans the whole widget).
432  */
433 static Boolean SetValues(AngbandWidget current, AngbandWidget request, AngbandWidget new, ArgList args, Cardinal *num_args)
434 {
435     int depth = DefaultDepthOfScreen(XtScreen((Widget) new));
436     Boolean font_changed = FALSE;
437     Boolean border_changed = FALSE;
438     Boolean color_changed = FALSE;
439     XGCValues gcv;
440     int height, width;
441     int n;
442
443     /* Changed font? */
444     if (current->angband.font != new->angband.font) {
445         /* Check if the font exists */
446         new->angband.fnt = getFont(new, new->angband.font, FALSE);
447
448         /* The font didn't exist */
449         if (new->angband.fnt == nullptr) {
450             new->angband.fnt = current->angband.fnt;
451             new->angband.font = current->angband.font;
452             XtWarning("Couldn't find the request font!");
453         } else {
454             font_changed = TRUE;
455             /* Free the old font */
456             XFreeFont(XtDisplay((Widget) new), current->angband.fnt);
457             /* Update font information */
458             new->angband.fontheight = new->angband.fnt->ascent + new->angband.fnt->descent;
459             new->angband.fontwidth = new->angband.fnt->max_bounds.width;
460             new->angband.fontascent = new->angband.fnt->ascent;
461         }
462     }
463
464     /* Check all colors, if one or more has changed the redo all GC's */
465     for (n = 0; n < NUM_COLORS; n++)
466         if (current->angband.color[n] != new->angband.color[n])
467             color_changed = TRUE;
468
469     /* Change all GC's if color or font has changed */
470     if (color_changed || font_changed) {
471         gcv.font = new->angband.fnt->fid;
472         gcv.graphics_exposures = FALSE;
473         gcv.background = new->angband.color[0];
474
475         /* Do all GC's */
476         for (n = 0; n < NUM_COLORS; n++) {
477             if (depth == 1 && n >= 1)
478                 gcv.foreground = new->angband.color[1];
479             else
480                 gcv.foreground = new->angband.color[n];
481             /* Release the old GC */
482             XtReleaseGC((Widget)current, current->angband.gc[n]);
483             /* Get the new GC */
484             new->angband.gc[n] = XtGetGC((Widget) new, GCFont | GCForeground | GCBackground | GCGraphicsExposures, &gcv);
485         }
486
487         /* Replace the old XOR/highlighting GC */
488         gcv.foreground = BlackPixelOfScreen(XtScreen((Widget) new)) ^ WhitePixelOfScreen(XtScreen((Widget) new));
489         gcv.function = GXxor;
490         XtReleaseGC((Widget)current, current->angband.gc[NUM_COLORS]);
491         new->angband.gc[NUM_COLORS] = XtGetGC((Widget) new, GCFunction | GCGraphicsExposures | GCForeground, &gcv);
492         /* Fix the background color */
493         new->core.background_pixel = new->angband.color[0];
494     }
495
496     /* Check if internal border width has changed, used later */
497     if (current->angband.internal_border != new->angband.internal_border)
498         border_changed = TRUE;
499
500     /* If the font or the internal border has changed, all geometry
501     has to be recalculated */
502     if (font_changed || border_changed) {
503         /* Change window size */
504         height = (current->core.height - 2 * current->angband.internal_border) / current->angband.fontheight * new->angband.fontheight
505             + 2 * current->angband.internal_border;
506         width = (current->core.width - 2 * current->angband.internal_border) / current->angband.fontwidth * new->angband.fontwidth
507             + 2 * new->angband.internal_border;
508
509         /* Get the new width */
510         if (XtMakeResizeRequest((Widget) new, width, height, nullptr, nullptr) == XtGeometryNo) {
511             /* Not allowed */
512             XtWarning("Size change denied!");
513         } else {
514             /* Recalculate size hints */
515             calculateSizeHints(new);
516         }
517     }
518
519     /* Tell it to redraw the widget if anything has changed */
520     return (font_changed || color_changed || border_changed);
521 }
522
523 /*
524  * Calculate size hints
525  */
526 static void calculateSizeHints(AngbandWidget new)
527 {
528     TopLevelShellWidget parent = (TopLevelShellWidget)XtParent((Widget) new);
529
530     /* Calculate minimum size */
531     parent->wm.size_hints.min_height = new->angband.min_rows *new->angband.fontheight + 2 * new->angband.internal_border;
532     parent->wm.size_hints.min_width = new->angband.min_columns *new->angband.fontwidth + 2 * new->angband.internal_border;
533     parent->wm.size_hints.flags |= PMinSize;
534
535     /* Calculate maximum size */
536     parent->wm.size_hints.max_height = new->angband.max_rows *new->angband.fontheight + 2 * new->angband.internal_border;
537     parent->wm.size_hints.max_width = new->angband.max_columns *new->angband.fontwidth + 2 * new->angband.internal_border;
538     parent->wm.size_hints.flags |= PMaxSize;
539
540     /* Calculate increment size */
541     parent->wm.size_hints.height_inc = new->angband.fontheight;
542     parent->wm.size_hints.width_inc = new->angband.fontwidth;
543     parent->wm.size_hints.flags |= PResizeInc;
544
545     /* Calculate base size */
546     parent->wm.base_height = 2 * new->angband.internal_border;
547     parent->wm.base_width = 2 * new->angband.internal_border;
548     parent->wm.size_hints.flags |= PBaseSize;
549 }
550
551 /*
552  * Load a font
553  */
554 static XFontStruct *getFont(AngbandWidget widget, String font, Boolean fallback)
555 {
556     Display *dpy = XtDisplay((Widget)widget);
557     char buf[256];
558     XFontStruct *fnt = nullptr;
559
560     if (!(fnt = XLoadQueryFont(dpy, font)) && fallback) {
561         sprintf(buf, "Can't find the font \"%s\", trying fixed\n", font);
562         XtWarning(buf);
563         if (!(fnt = XLoadQueryFont(dpy, "fixed")))
564             XtError("Can't fint the font \"fixed\"!, bailing out\n");
565     }
566
567     return fnt;
568 }
569
570 /*** The non-widget code ****/
571
572 #ifndef IsModifierKey
573
574 /*
575  * Keysym macros, used on Keysyms to test for classes of symbols
576  * These were stolen from one of the X11 header files
577  */
578
579 #define IsKeypadKey(keysym) (((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal))
580
581 #define IsCursorKey(keysym) (((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select))
582
583 #define IsPFKey(keysym) (((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4))
584
585 #define IsFunctionKey(keysym) (((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35))
586
587 #define IsMiscFunctionKey(keysym) (((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space))
588
589 #define IsModifierKey(keysym) (((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R))
590
591 #endif
592
593 /*
594  * Checks if the keysym is a special key or a normal key
595  * Assume that XK_MISCELLANY keysyms are special
596  */
597 #define IsSpecialKey(keysym) ((unsigned)(keysym) >= 0xFF00)
598
599 /*
600  * Maximum number of windows XXX XXX XXX XXX
601  */
602 #define MAX_TERM_DATA 8
603
604 /*
605  * Number of fallback resources per window
606  */
607 #define TERM_FALLBACKS 6
608
609 /*
610  * Number of windows with special fallback resources
611  */
612 #define SPECIAL_FALLBACKS 3
613
614 /*
615  * A structure for each "term"
616  */
617 struct term_data {
618     term t;
619
620     AngbandWidget widget;
621 };
622
623 /*
624  * An array of term_data's
625  */
626 static term_data data[MAX_TERM_DATA];
627
628 char *termNames[MAX_TERM_DATA] = { "angband", "mirror", "recall", "choice", "term-4", "term-5", "term-6", "term-7" };
629
630 Arg specialArgs[TERM_FALLBACKS][SPECIAL_FALLBACKS] = {
631     /* The main screen */
632     { { XtNstartRows, 24 }, { XtNstartColumns, 80 }, { XtNminRows, 24 }, { XtNminColumns, 80 }, { XtNmaxRows, 24 }, { XtNmaxColumns, 80 } },
633
634     /* The "mirror" window */
635     { { XtNstartRows, 24 }, { XtNstartColumns, 80 }, { XtNminRows, 1 }, { XtNminColumns, 1 }, { XtNmaxRows, 24 }, { XtNmaxColumns, 80 } },
636
637     /* The "recall" window */
638     { { XtNstartRows, 8 }, { XtNstartColumns, 80 }, { XtNminRows, 1 }, { XtNminColumns, 1 }, { XtNmaxRows, 24 }, { XtNmaxColumns, 80 } }
639 };
640
641 Arg defaultArgs[TERM_FALLBACKS]
642     = { { XtNstartRows, 24 }, { XtNstartColumns, 80 }, { XtNminRows, 1 }, { XtNminColumns, 1 }, { XtNmaxRows, 24 }, { XtNmaxColumns, 80 } };
643
644 /*
645  * The application context
646  */
647 XtAppContext appcon;
648
649 /*
650  * User changable information about widgets
651  */
652 static String fallback[] = { "Angband.angband.iconName:            Angband", "Angband.angband.title:               Angband",
653     "Angband.mirror.iconName:             Mirror", "Angband.mirror.title:                Mirror", "Angband.recall.iconName:             Recall",
654     "Angband.recall.title:                Recall", "Angband.choice.iconName:             Choice", "Angband.choice.title:                Choice",
655     "Angband.term-4.iconName:         Term 4", "Angband.term-4.title:                 Term 4", "Angband.term-5.iconName:              Term 5",
656     "Angband.term-5.title:                    Term 5", "Angband.term-6.iconName:              Term 6", "Angband.term-6.title:                 Term 6",
657     "Angband.term-7.iconName:         Term 7", "Angband.term-7.title:                 Term 7", nullptr };
658
659 /*
660  * Do a redraw
661  */
662 static void react_redraw(Widget widget, XtPointer client_data, XtPointer call_data)
663 {
664     term_data *old_td = (term_data *)(Term->data);
665     term_data *td = (term_data *)client_data;
666
667     /* Activate the proper Term */
668     Term_activate(&td->t);
669
670     /* Request a redraw */
671     Term_redraw();
672
673     /* Activate the old Term */
674     Term_activate(&old_td->t);
675 }
676
677 /*
678  * Process a keypress event
679  */
680 static void react_keypress(XKeyEvent *ev)
681 {
682     int i, n, mc, ms, mo, mx;
683
684     KeySym ks;
685
686     char buf[128];
687     char msg[128];
688
689     /* Check for "normal" keypresses */
690     n = XLookupString(ev, buf, 125, &ks, nullptr);
691
692     /* Terminate */
693     buf[n] = '\0';
694
695     /* Extract four "modifier flags" */
696     mc = (ev->state & ControlMask) ? TRUE : FALSE;
697     ms = (ev->state & ShiftMask) ? TRUE : FALSE;
698     mo = (ev->state & Mod1Mask) ? TRUE : FALSE;
699     mx = (ev->state & Mod2Mask) ? TRUE : FALSE;
700
701     /* Hack -- Ignore "modifier keys" */
702     if (IsModifierKey(ks))
703         return;
704
705     /* Normal keys with no modifiers */
706     if (n && !mo && !mx && !IsSpecialKey(ks)) {
707         /* Enqueue the normal key(s) */
708         for (i = 0; buf[i]; i++)
709             Term_keypress(buf[i]);
710
711         /* All done */
712         return;
713     }
714
715     /* Handle a few standard keys */
716     switch (ks) {
717     case XK_Escape:
718         Term_keypress(ESCAPE);
719         return;
720
721     case XK_Return:
722         Term_keypress('\r');
723         return;
724
725     case XK_Tab:
726         Term_keypress('\t');
727         return;
728
729     case XK_Delete:
730     case XK_BackSpace:
731         Term_keypress('\010');
732         return;
733     }
734
735     /* Hack -- Use the KeySym */
736     if (ks) {
737         sprintf(msg, "%c%s%s%s%s_%lX%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", (unsigned long)(ks), 13);
738     }
739
740     /* Hack -- Use the Keycode */
741     else {
742         sprintf(msg, "%c%s%s%s%sK_%X%c", 31, mc ? "N" : "", ms ? "S" : "", mo ? "O" : "", mx ? "M" : "", ev->keycode, 13);
743     }
744
745     /* Enqueue the "fake" string */
746     for (i = 0; msg[i]; i++)
747         Term_keypress(msg[i]);
748
749     /* Hack -- dump an "extra" string */
750     if (n) {
751         /* Start the "extra" string */
752         Term_keypress(28);
753
754         /* Enqueue the "real" string */
755         for (i = 0; buf[i]; i++)
756             Term_keypress(buf[i]);
757
758         /* End the "extra" string */
759         Term_keypress(28);
760     }
761 }
762
763 static void handle_event(Widget widget, XtPointer client_data, XEvent *event, Boolean *continue_to_dispatch)
764 {
765     term_data *old_td = (term_data *)(Term->data);
766     term_data *td = (term_data *)client_data;
767
768     /* Continue to process the event by default */
769     *continue_to_dispatch = TRUE;
770
771     /* Activate the Term */
772     Term_activate(&td->t);
773
774     switch (event->type) {
775     case KeyPress:
776         react_keypress(&(event->xkey));
777         *continue_to_dispatch = FALSE; /* We took care of the event */
778         break;
779
780     default:
781         break; /* Huh? Shouldn't happen! */
782     }
783
784     /* Activate the old term */
785     Term_activate(&old_td->t);
786
787     return;
788 }
789
790 /*
791  * Process an event (or just check for one)
792  */
793 errr CheckEvent(bool wait)
794 {
795     XEvent event;
796
797     /* No events ready, and told to just check */
798     if (!wait && !XtAppPending(appcon))
799         return 1;
800
801     /* Process */
802     while (1) {
803         XtAppNextEvent(appcon, &event);
804         XtDispatchEvent(&event);
805         if (!XtAppPending(appcon))
806             break;
807     }
808
809     return (0);
810 }
811
812 /*
813  * Handle a "special request"
814  */
815 static errr Term_xtra_xaw(int n, int v)
816 {
817     int i;
818
819     /* Handle a subset of the legal requests */
820     switch (n) {
821     /* Make a noise */
822     case TERM_XTRA_NOISE:
823         XBell(XtDisplay((Widget)data[0].widget), 100);
824         return (0);
825
826     /* Flush the output */
827     case TERM_XTRA_FRESH:
828         XFlush(XtDisplay((Widget)data[0].widget));
829         /* Nonblock event-check so the flushed events can be showed */
830         CheckEvent(FALSE);
831         return (0);
832
833     /* Process random events */
834     case TERM_XTRA_BORED:
835         return (CheckEvent(0));
836
837     /* Process events */
838     case TERM_XTRA_EVENT:
839         return (CheckEvent(v));
840
841     /* Flush events */
842     case TERM_XTRA_FLUSH:
843         while (!CheckEvent(FALSE))
844             ;
845         return (0);
846
847     /* Delay */
848     case TERM_XTRA_DELAY:
849         usleep(1000 * v);
850         return (0);
851
852     /* Clear the window */
853     case TERM_XTRA_CLEAR:
854         for (i = 0; i < MAX_TERM_DATA; i++) {
855             if (Term == &data[i].t)
856                 XClearWindow(XtDisplay((Widget)data[i].widget), XtWindow((Widget)data[i].widget));
857         }
858         return (0);
859     }
860
861     /* Unknown */
862     return (1);
863 }
864
865 /*
866  * Erase a number of characters
867  */
868 static errr Term_wipe_xaw(int x, int y, int n)
869 {
870     term_data *td = (term_data *)(Term->data);
871
872     /* Erase using color 0 */
873     AngbandClearArea(td->widget, x, y, n, 1, 0);
874
875     /* Success */
876     return (0);
877 }
878
879 /*
880  * Draw the cursor (XXX by hiliting)
881  */
882 static errr Term_curs_xaw(int x, int y)
883 {
884     term_data *td = (term_data *)(Term->data);
885
886     /* Hilite the cursor character */
887     AngbandClearArea(td->widget, x, y, 1, 1, COLOR_XOR);
888
889     /* Success */
890     return (0);
891 }
892
893 /*
894  * Draw a number of characters
895  */
896 static errr Term_text_xaw(int x, int y, int n, byte a, concptr s)
897 {
898     term_data *td = (term_data *)(Term->data);
899
900     /* Draw the text */
901     AngbandOutputText(td->widget, x, y, (String)s, n, (a & 0x0F));
902
903     /* Success */
904     return (0);
905 }
906
907 /*
908  * Raise a term
909  */
910 static void term_raise(term_data win)
911 {
912     Widget widget = (Widget)win.widget;
913
914     XRaiseWindow(XtDisplay(XtParent(widget)), XtWindow(XtParent(widget)));
915 }
916
917 /*
918  * Initialize a term_data
919  */
920 static errr term_data_init(term_data *td, Widget topLevel, int key_buf, String name, ArgList widget_arg, Cardinal widget_arg_no)
921 {
922     Widget parent;
923     term *t = &td->t;
924
925     /* Create the shell widget */
926     parent = XtCreatePopupShell(name, topLevelShellWidgetClass, topLevel, nullptr, 0);
927
928     /* Create the interior widget */
929     td->widget = (AngbandWidget)XtCreateManagedWidget(name, angbandWidgetClass, parent, widget_arg, widget_arg_no);
930
931     /* Initialize the term (full size) */
932     term_init(t, 80, 24, key_buf);
933
934     /* Use a "soft" cursor */
935     t->soft_cursor = TRUE;
936
937     /* Erase with "white space" */
938     t->attr_blank = TERM_WHITE;
939     t->char_blank = ' ';
940
941     /* Hooks */
942     t->xtra_hook = Term_xtra_xaw;
943     t->curs_hook = Term_curs_xaw;
944     t->wipe_hook = Term_wipe_xaw;
945     t->text_hook = Term_text_xaw;
946
947     /* Save the data */
948     t->data = td;
949
950     /* Register the keypress event handler */
951     XtAddEventHandler((Widget)td->widget, KeyPressMask, False, (XtEventHandler)handle_event, td);
952
953     /* Redraw callback */
954     XtAddCallback((Widget)td->widget, XtNredrawCallback, react_redraw, td);
955
956     /* Realize the widget */
957     XtRealizeWidget(parent);
958
959     /* Make it visible */
960     XtPopup(parent, XtGrabNone);
961
962     /* Activate (important) */
963     Term_activate(t);
964
965     return 0;
966 }
967
968 /*
969  * Initialization function for an X Athena Widget module to Angband
970  *
971  * Mega-Hack -- we are not given the actual "argc" and "argv" from
972  * the main program, so we fake one.  Thus, we are unable to parse
973  * any "display" requests for external devices.  This is okay, since
974  * we need to verify the display anyway, to work with "main.c".
975  */
976 errr init_xaw(void)
977 {
978     int argc, i;
979     char *argv[2];
980     Widget topLevel;
981     Display *dpy;
982
983     /* One fake argument */
984     argc = 1;
985
986     /* Save the program name */
987     argv[0] = argv0;
988
989     /* Terminate */
990     argv[1] = nullptr;
991
992     /* Attempt to open the local display */
993     dpy = XOpenDisplay("");
994
995     /* Failure -- assume no X11 available */
996     if (!dpy)
997         return (-1);
998
999     /* Close the local display */
1000     XCloseDisplay(dpy);
1001
1002 #ifdef USE_XAW_LANG
1003     /* Support locale processing */
1004     XtSetLanguageProc(nullptr, nullptr, nullptr);
1005 #endif
1006
1007     /* Initialize the toolkit */
1008     topLevel = XtAppInitialize(&appcon, "Angband", nullptr, 0, &argc, argv, fallback, nullptr, 0);
1009
1010     /* Initialize the windows */
1011     for (i = 0; i < MAX_TERM_DATA; i++) {
1012         term_data_init(&data[i], topLevel, 1024, termNames[i], i < SPECIAL_FALLBACKS ? specialArgs[i] : defaultArgs, TERM_FALLBACKS);
1013         angband_term[i] = Term;
1014     }
1015
1016     /* Activate the "Angband" window screen */
1017     Term_activate(&data[0].t);
1018
1019     /* Raise the "Angband" window */
1020     term_raise(data[0]);
1021
1022     /* Success */
1023     return (0);
1024 }
1025
1026 #endif