OSDN Git Service

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