1 /* xkeycaps, Copyright (c) 1991, 1992, 1993, 1996, 1997
2  *  Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or
10  * implied warranty.
11  */
12 
13 #include <X11/cursorfont.h>
14 #include <X11/Intrinsic.h>
15 #include "KbdWidget.h"
16 #include "KeyWidgetP.h"
17 #include <stdio.h>
18 
19 #undef MAX
20 #undef MIN
21 #define MAX(a,b) ((a)>(b)?(a):(b))
22 #define MIN(a,b) ((a)<(b)?(a):(b))
23 
24 static void KeyRealize P((Widget, XtValueMask *, XSetWindowAttributes *));
25 static void KeyExpose P((Widget, XEvent *, Region));
26 static void KeyInitialize P((Widget, Widget, ArgList, Cardinal*));
27 
28 static void KeyHighlightProc P((KeyWidget));
29 static void KeyDehighlightProc P((KeyWidget));
30 
31 #define XtNgutterWidth  "gutterWidth"
32 #define XtCGutterWidth  "GutterWidth"
33 #define XtNkeycapColor  "keycapColor"
34 #define XtNkeycodeColor "keycodeColor"
35 #define XtNkeycapFont   "keycapFont"
36 #define XtNkeycodeFont  "keycodeFont"
37 #define XtNcursorFont   "cursorFont"
38 
39 #ifndef CURSORFONT
40 #define CURSORFONT "cursor"
41 #endif
42 
43 static XtResource key_resources [] = {
44   { XtNhighlight, XtCBackground, XtRPixel, sizeof (String),
45       XtOffset (KeyWidget, key.highlight_pixel), XtRString,
46       XtDefaultBackground },
47   { XtNgutterWidth, XtCGutterWidth, XtRInt, sizeof (int),
48       XtOffset (KeyWidget, key.gutter_width), XtRString, "3" },
49   { XtNkeycapColor, XtCForeground, XtRPixel, sizeof (String),
50       XtOffset (KeyWidget, key.keycap_pixel), XtRString,
51       XtDefaultForeground },
52   { XtNkeycodeColor, XtCForeground, XtRPixel, sizeof (String),
53       XtOffset (KeyWidget, key.keycode_pixel), XtRString,
54       XtDefaultForeground },
55   { XtNkeycapFont, XtCFont, XtRFontStruct, sizeof (String),
56       XtOffset (KeyWidget, key.keycap_font),
57       XtRString, "*-helvetica-bold-r-*-*-*-100-*-*-*-*-*-*" },
58   { XtNkeycodeFont, XtCFont, XtRFontStruct, sizeof (String),
59       XtOffset (KeyWidget, key.keycode_font),
60       XtRString, "*-courier-medium-r-*-*-*-100-*-*-*-*-*-*" },
61   { XtNcursorFont, XtCFont, XtRFontStruct, sizeof (String),
62       XtOffset (KeyWidget, key.cursor_font),
63       XtRString, CURSORFONT }
64 };
65 
66 KeyClassRec keyClassRec = {
67     { /*
68        * 	core fields
69        */
70     /* superclass		*/	&widgetClassRec,
71     /* class_name		*/	"Key",
72     /* widget_size		*/	sizeof (KeyRec),
73     /* class_initialize		*/	NULL,
74     /* class_part_initialize	*/	NULL,
75     /* class_inited		*/	FALSE,
76     /* initialize		*/	KeyInitialize,
77     /* initialize_hook		*/	NULL,
78     /* realize			*/	KeyRealize,
79     /* actions			*/	NULL,
80     /* num_actions		*/	0,
81     /* resources		*/	key_resources,
82     /* resource_count		*/	XtNumber (key_resources),
83     /* xrm_class		*/	NULLQUARK,
84     /* compress_motion		*/	TRUE,
85     /* compress_exposure	*/	TRUE,
86     /* compress_enterleave	*/	TRUE,
87     /* visible_interest		*/	FALSE,
88     /* destroy			*/	NULL,
89     /* resize			*/	XtInheritResize,
90     /* expose			*/	KeyExpose,
91     /* set_values		*/	NULL,
92     /* set_values_hook		*/	NULL,
93     /* set_values_almost	*/	XtInheritSetValuesAlmost,
94     /* get_values_hook		*/	NULL,
95     /* accept_focus		*/	XtInheritAcceptFocus,
96     /* version			*/	XtVersion,
97     /* callback_private		*/	NULL,
98     /* tm_table			*/	NULL,
99     /* query_geometry		*/	XtInheritQueryGeometry,
100     /* display_accelerator	*/	XtInheritDisplayAccelerator,
101     /* extension		*/	NULL
102     },
103     { /*
104        * 	key_class fields
105        */
106     /* highlight_key		*/	KeyHighlightProc,
107     /* dehighlight_key		*/	KeyDehighlightProc
108     }
109 };
110 
111 WidgetClass keyWidgetClass = (WidgetClass) &keyClassRec;
112 
113 static void
114 #ifdef __STDC__
KeyInitialize(Widget w1,Widget w2,ArgList av,Cardinal * ac)115 KeyInitialize (Widget w1, Widget w2, ArgList av, Cardinal *ac)
116 #else /* ! __STDC__ */
117 KeyInitialize (w1, w2, av, ac)
118      Widget w1, w2;
119      ArgList av;
120      Cardinal ac;
121 #endif /* ! __STDC__ */
122 {
123   KeyWidget new = (KeyWidget) w2;
124 
125   if (new->core.width <= 0) new->core.width = 3;
126   if (new->core.height <= 0) new->core.height = 3;
127   /* Initialize the non-resource slots */
128   new->key.x = 0;
129   new->key.y = 0;
130   new->key.highlighted_p = 0;
131   new->key.key_highlighted = 0;
132   new->key.mouse_highlighted = 0;
133   new->key.modifier_bits = 0;
134   new->key.auto_repeat_p = 0;
135   new->key.width = 0;
136   new->key.height = 0;
137   new->key.keysym_1 = 0;
138   new->key.keysym_2 = 0;
139   new->key.keysym_3 = 0;
140   new->key.keycode = 0;
141   new->key.default_mods = 0;
142   memset (new->key.default_keysyms, 0, sizeof (new->key.default_keysyms));
143 }
144 
145 
146 static void
147 #ifdef __STDC__
KeyRealize(Widget gw,XtValueMask * valuemaskp,XSetWindowAttributes * attr)148 KeyRealize (Widget gw, XtValueMask *valuemaskp, XSetWindowAttributes *attr)
149 #else /* ! __STDC__ */
150 KeyRealize (gw, valuemaskp, attr)
151      Widget gw;
152      XtValueMask *valuemaskp;
153      XSetWindowAttributes *attr;
154 #endif /* ! __STDC__ */
155 {
156   char *b, buf [255];
157   const char *k1, *k2, *k3;
158   XGCValues gcvalues;
159   KeyWidget w = (KeyWidget) gw;
160   XtCreateWindow ((Widget) w, InputOutput, (Visual *)CopyFromParent,
161 		  *valuemaskp, attr);
162   gcvalues.foreground = w->key.keycap_pixel;
163   gcvalues.font = w->key.keycap_font->fid;
164   w->key.keycap_gc  = XtGetGC (gw, (unsigned) GCFont|GCForeground, &gcvalues);
165   gcvalues.font = w->key.cursor_font->fid;
166   w->key.cursor_gc = XtGetGC (gw, (unsigned) GCFont|GCForeground, &gcvalues);
167   gcvalues.foreground = w->key.keycode_pixel;
168   gcvalues.font = w->key.keycode_font->fid;
169   w->key.keycode_gc = XtGetGC (gw, (unsigned) GCFont|GCForeground, &gcvalues);
170 
171   k1 = w->key.keysym_1;
172   k2 = w->key.keysym_2;
173   k3 = w->key.keysym_3;
174   if (k1 && !*k1) k1 = 0;
175   if (k2 && !*k2) k2 = 0;
176   if (k3 && !*k3) k3 = 0;
177   b = buf;
178   b[0] = 0;
179   if (k1)
180     {
181       int i = strlen (k1);
182       strncpy (buf, k1, i+1);
183       /* This is the kludge that makes "Caps " "Lock" concat as "CapsLock" */
184       if (i > 1 && (buf [i-1] == ' ' || buf [i-1] == '-'))
185 	buf [i-1] = 0, i--;
186       else if (i > 0 && (k2 || k3))
187 	buf [i] = ' ', buf [i+1] = 0, i++;
188       b = buf+i;
189     }
190   if (k2)
191     {
192       int i = strlen (k2);
193       strncpy (b, k2, i+1);
194       b += i;
195       if (k3) *b++ = ' ', *b++ = 0;
196     }
197   if (k3)
198     strcpy (b, k3);
199 
200   b = buf;
201   if (b[0] == ' ' && b[1] == 0) b = "space";
202   w->key.key_name = XtNewString (b);
203 }
204 
205 
206 static void draw_key P((KeyWidget));
207 
208 static void
209 #ifdef __STDC__
KeyExpose(Widget gw,XEvent * e,Region r)210 KeyExpose (Widget gw, XEvent *e, Region r)
211 #else /* ! __STDC__ */
212 KeyExpose (gw, e, r)
213      Widget gw;
214      XEvent *e;
215      Region r;
216 #endif /* ! __STDC__ */
217 {
218   draw_key ((KeyWidget) gw);
219 }
220 
221 
222 static int
223 #ifdef __STDC__
string_width(unsigned char * string,XFontStruct * font)224 string_width (unsigned char *string, XFontStruct *font)
225 #else /* ! __STDC__ */
226 string_width (string, font)
227      unsigned char *string;
228      XFontStruct *font;
229 #endif /* ! __STDC__ */
230 {
231   int size = 0;
232   if (!font) return 0;
233   for (; *string; string++)
234     if (font->per_char)
235       size += font->per_char [(*string) - font->min_char_or_byte2].width;
236     else
237       size += font->max_bounds.width;
238   return size;
239 }
240 
241 
242 static void
243 #ifdef __STDC__
draw_key(KeyWidget w)244 draw_key (KeyWidget w)
245 #else /* ! __STDC__ */
246 draw_key (w)
247      KeyWidget w;
248 #endif /* ! __STDC__ */
249 {
250   Display *dpy = XtDisplay (w);
251   Window window = XtWindow (w);
252   XFontStruct *keycap_font  = w->key.keycap_font;
253   XFontStruct *keycode_font = w->key.keycode_font;
254   XFontStruct *cursor_font  = w->key.cursor_font;
255   GC keycap_gc  = w->key.keycap_gc;
256   GC keycode_gc = w->key.keycode_gc;
257   GC cursor_gc  = w->key.cursor_gc;
258   const char *k1 = w->key.keysym_1;
259   const char *k2 = w->key.keysym_2;
260   const char *k3 = w->key.keysym_3;
261   unsigned char *uk1, *uk2, *uk3;
262   XFontStruct *k1_font = cursor_font;
263   XFontStruct *k2_font = cursor_font;
264   XFontStruct *k3_font = cursor_font;
265   GC k1_gc = cursor_gc;
266   GC k2_gc = cursor_gc;
267   GC k3_gc = cursor_gc;
268   int inner_margin = 2;
269   int x, y;
270 
271   char left[2], right[2], up[2], down[2];
272   left[1] = right[1] = up[1] = down[1] = 0;
273   left[0] = XC_sb_left_arrow; right[0] = XC_sb_right_arrow;
274   up[0] = XC_sb_up_arrow; down[0] = XC_sb_down_arrow;
275 
276   if (k1 && !*k1) k1 = 0;
277   if (k2 && !*k2) k2 = 0;
278   if (k3 && !*k3) k3 = 0;
279 
280   if      (string_equal (k1, "leftArrow"))  k1 = left;
281   else if (string_equal (k1, "rightArrow")) k1 = right;
282   else if (string_equal (k1, "upArrow"))    k1 = up;
283   else if (string_equal (k1, "downArrow"))  k1 = down;
284   else k1_font = keycap_font, k1_gc = keycap_gc;
285 
286   if      (string_equal (k2, "leftArrow"))  k2 = left;
287   else if (string_equal (k2, "rightArrow")) k2 = right;
288   else if (string_equal (k2, "upArrow"))    k2 = up;
289   else if (string_equal (k2, "downArrow"))  k2 = down;
290   else k2_font = keycap_font, k2_gc = keycap_gc;
291 
292   if      (string_equal (k3, "leftArrow"))  k3 = left;
293   else if (string_equal (k3, "rightArrow")) k3 = right;
294   else if (string_equal (k3, "upArrow"))    k3 = up;
295   else if (string_equal (k3, "downArrow"))  k3 = down;
296   else k3_font = keycap_font, k3_gc = keycap_gc;
297 
298   uk1 = (unsigned char *) k1;
299   uk2 = (unsigned char *) k2;
300   uk3 = (unsigned char *) k3;
301 
302 #define PERCHAR(font,c) \
303   (font->per_char \
304    ? &font->per_char[(c) - font->min_char_or_byte2] \
305    : &font->max_bounds)
306 
307 #define STRHEIGHT(font, var) \
308   (font == cursor_font && var != NULL ? PERCHAR (font, var[0])->ascent : font->ascent)
309 
310 #define MAXSTRHEIGHT(font, font2, var) \
311   MAX (font->ascent, \
312        (font2 == cursor_font \
313 	? PERCHAR (font2, var[0])->ascent \
314 	: font2->ascent))
315 
316   XClearWindow (dpy, window);
317   x = y = 0;
318   if (k1)
319     {
320       x = inner_margin - PERCHAR (k1_font, uk1[0])->lbearing;
321       y = inner_margin + STRHEIGHT (k1_font, uk1);
322       XDrawString (dpy, window, k1_gc, x, y, k1, strlen (k1));
323     }
324   if (k2)
325     {
326       x = inner_margin - PERCHAR (k2_font, uk2[0])->lbearing;
327       y = inner_margin
328 	+ (k1 ? MAXSTRHEIGHT (keycap_font, k2_font, uk1)
329 	   : keycap_font->ascent)
330 	  + STRHEIGHT (k2_font, uk2);
331 /*      if (y + STRHEIGHT (k2_font, uk2) < w->core.height) */
332 	XDrawString (dpy, window, k2_gc, x, y, k2, strlen (k2));
333     }
334   if (k3)
335     {
336       int i = strlen (k3) - 1;
337       x = w->core.width - (w->core.border_width * 2
338 			   + string_width (uk3, k3_font)
339 			   + (PERCHAR (k3_font, uk3[i])->width -
340 			      PERCHAR (k3_font, uk3[i])->rbearing));
341       y = inner_margin + STRHEIGHT (k3_font, uk3);
342       if (k1 == 0 || x >= string_width (uk1, k1_font))
343 	XDrawString (dpy, window, k3_gc, x, y, k3, strlen (k3));
344     }
345 
346   if (w->key.keycode)
347     {
348       unsigned char buf [100];
349       sprintf ((char *) buf, "%02X", w->key.keycode);
350       x = w->core.width - (w->core.border_width * 2
351 			   + string_width (buf, keycode_font));
352       y = w->core.height - (w->core.border_width * 2 + keycode_font->descent);
353       if ((x >= inner_margin) &&
354 	  (y - keycode_font->ascent >= inner_margin) &&
355 	  (y > ((STRHEIGHT (k1_font, uk1) * 3) / 2)
356 	   ? (k2 == 0 || x >= string_width (uk2, k2_font))
357 	   : (k1 == 0 || x >= string_width (uk1, k1_font))))
358 	XDrawString (dpy, window, keycode_gc, x, y,
359 		     (char *) buf, strlen ((char *) buf));
360     }
361 #undef PERCHAR
362 #undef STRHEIGHT
363 #undef MAXSTRHEIGHT
364 }
365 
366 
367 
368 void
369 #ifdef __STDC__
KeyHighlight(KeyWidget keyw)370 KeyHighlight (KeyWidget keyw)
371 #else /* ! __STDC__ */
372 KeyHighlight (keyw)
373      KeyWidget keyw;
374 #endif /* ! __STDC__ */
375 {
376   if (keyw->key.highlighted_p) return;
377   ((KeyWidgetClass) keyw->core.widget_class)->key_class.highlight_key (keyw);
378   keyw->key.highlighted_p = 1;
379 }
380 
381 void
382 #ifdef __STDC__
KeyDehighlight(KeyWidget keyw)383 KeyDehighlight (KeyWidget keyw)
384 #else /* ! __STDC__ */
385 KeyDehighlight (keyw)
386      KeyWidget keyw;
387 #endif /* ! __STDC__ */
388 {
389   if (! keyw->key.highlighted_p) return;
390   ((KeyWidgetClass) keyw->core.widget_class)->key_class.dehighlight_key (keyw);
391   keyw->key.highlighted_p = 0;
392 }
393 
394 
395 #include <X11/bitmaps/gray>
396 
397 /* This is a little sleazy: we're caching a pixmap in global space instead
398    of caching it per-display, which means if someone were to include this
399    widget-set in an application that used multiple displays (not bloody
400    likely!) this would have to be fixed.  But the alternative is adding
401    a new slot and resource to each and every key, which isn't worth it.
402  */
403 static Pixmap highlight_pixmap = 0;
404 
405 static Pixmap
406 #ifdef __STDC__
make_highlight_pixmap(KeyWidget w)407 make_highlight_pixmap (KeyWidget w)
408 #else /* ! __STDC__ */
409 make_highlight_pixmap (w)
410      KeyWidget w;
411 #endif /* ! __STDC__ */
412 {
413   return XCreatePixmapFromBitmapData
414     (XtDisplay (w), XtWindow (w), gray_bits, gray_width, gray_height,
415      w->key.keycap_pixel, w->core.background_pixel,
416      DefaultDepthOfScreen (DefaultScreenOfDisplay (XtDisplay (w))));
417 }
418 
419 
420 #ifdef LOSE_LIKE_Xt	/* Doing this the Xt way is just too damn slow... */
421 
422 static void
423 #ifdef __STDC__
KeyHighlightProc(KeyWidget w)424 KeyHighlightProc (KeyWidget w)
425 #else /* ! __STDC__ */
426 KeyHighlightProc (w)
427      KeyWidget w;
428 #endif /* ! __STDC__ */
429 {
430   Arg av [2];
431   int ac = 0;
432   w->key.background_pixel = w->core.background_pixel;
433   if (w->key.background_pixel == w->key.highlight_pixel)
434     {
435       if (! highlight_pixmap) highlight_pixmap = make_hilight_pixmap (w);
436       XtSetArg (av[ac], XtNbackgroundPixmap, highlight_pixmap);
437     }
438   else
439     XtSetArg (av[ac], XtNbackground, w->key.highlight_pixel);
440   ac++;
441   XtSetValues (w, av, ac);
442 }
443 
444 
445 static void
446 #ifdef __STDC__
KeyDehighlightProc(KeyWidget keyw)447 KeyDehighlightProc (KeyWidget keyw)
448 #else /* ! __STDC__ */
449 KeyDehighlightProc (keyw)
450      KeyWidget keyw;
451 #endif /* ! __STDC__ */
452 {
453   Arg av [2];
454   int ac = 0;
455   if (highlight_pixmap)
456     XtSetArg (av[ac], XtNbackgroundPixmap, XtUnspecifiedPixmap);
457   else
458     XtSetArg (av[ac], XtNbackground, keyw->key.background_pixel);
459   ac++;
460   XtSetValues (keyw, av, ac);
461 }
462 
463 
464 #else /* !LOSE_LIKE_Xt */
465 
466 static void
467 #ifdef __STDC__
KeyHighlightProc(KeyWidget w)468 KeyHighlightProc (KeyWidget w)
469 #else /* ! __STDC__ */
470 KeyHighlightProc (w)
471      KeyWidget w;
472 #endif /* ! __STDC__ */
473 {
474   w->key.background_pixel = w->core.background_pixel;
475   if (w->key.background_pixel == w->key.highlight_pixel)
476     {
477       if (! highlight_pixmap) highlight_pixmap = make_highlight_pixmap (w);
478       XSetWindowBackgroundPixmap (XtDisplay (w), XtWindow (w), highlight_pixmap);
479     }
480   else
481     XSetWindowBackground (XtDisplay (w), XtWindow (w), w->key.highlight_pixel);
482   draw_key (w);
483 }
484 
485 
486 static void
487 #ifdef __STDC__
KeyDehighlightProc(KeyWidget w)488 KeyDehighlightProc (KeyWidget w)
489 #else /* ! __STDC__ */
490 KeyDehighlightProc (w)
491      KeyWidget w;
492 #endif /* ! __STDC__ */
493 {
494   XSetWindowBackground (XtDisplay (w), XtWindow (w), w->key.background_pixel);
495   draw_key (w);
496 }
497 
498 #endif /* !LOSE_LIKE_Xt */
499