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