1 /*
2  *  Copyright (C) 1995, 1996  Karl-Johan Johnsson.
3  */
4 
5 /*
6 
7 Copyright (c) 1989  X Consortium
8 
9 Permission is hereby granted, free of charge, to any person obtaining
10 a copy of this software and associated documentation files (the
11 "Software"), to deal in the Software without restriction, including
12 without limitation the rights to use, copy, modify, merge, publish,
13 distribute, sublicense, and/or sell copies of the Software, and to
14 permit persons to whom the Software is furnished to do so, subject to
15 the following conditions:
16 
17 The above copyright notice and this permission notice shall be included
18 in all copies or substantial portions of the Software.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
24 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 OTHER DEALINGS IN THE SOFTWARE.
27 
28 Except as contained in this notice, the name of the X Consortium shall
29 not be used in advertising or otherwise to promote the sale, use or
30 other dealings in this Software without prior written authorization
31 from the X Consortium.
32 
33 */
34 
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <X11/IntrinsicP.h>
40 #include <X11/StringDefs.h>
41 #include <X11/ShellP.h>
42 #include <X11/Xatom.h>
43 #include <X11/Xutil.h>
44 
45 #include "Compat.h"
46 #include "Util.h"
47 #include "UtilI.h"
48 
49 /*
50  * This function is modified from the source to XTextWidth,
51  * which is why I included the X Consortium copyright notice.
52  */
MyXWidthToChars(XFontStruct * font,const char * str,int len,int width)53 int MyXWidthToChars(XFontStruct *font, const char *str, int len, int width)
54 {
55     XCharStruct	*def;
56     int		singlerow = (font->max_byte1 == 0);
57     int		n;
58 
59     if (!str || width <= 0)
60 	return 0;
61 
62     if (singlerow)
63 	CI_GET_DEFAULT_INFO_1D(font, def);
64     else
65 	CI_GET_DEFAULT_INFO_2D(font, def);
66 
67     for (n = 0 ; n < len ; n++) {
68 	XCharStruct	*cs;
69 	int		ch = (unsigned char)str[n];
70 
71 	if (singlerow)
72 	    CI_GET_CHAR_INFO_1D(font, ch, def, cs);
73 	else
74 	    /*
75 	     * essentially the macro CI_GET_ROWZERO_CHAR_INFO_2D
76 	     */
77 	    if (font->min_byte1 == 0 &&
78 		ch >= font->min_char_or_byte2 && ch <= font->max_char_or_byte2)
79 		if (!font->per_char)
80 		    cs = &font->min_bounds;
81 		else {
82 		    cs = font->per_char + ch - font->min_char_or_byte2;
83 		    if (CI_NONEXISTCHAR(cs))
84 			cs = def;
85 		}
86 	    else
87 		cs = def;
88 
89 	if (cs)
90 	    width -= cs->width;
91 	if (width < 0)
92 	    break;
93     }
94 
95     return n;
96 }
97 
MyXWidthToWChars(XFontStruct * font,const XChar2b * str,int len,int width)98 int MyXWidthToWChars(XFontStruct *font, const XChar2b *str, int len, int width)
99 {
100     XCharStruct	*def;
101     int		singlerow = (font->max_byte1 == 0);
102     int		n;
103 
104     if (!font || width < 0)
105 	return 0;
106 
107     if (singlerow)
108 	CI_GET_DEFAULT_INFO_1D(font, def);
109     else
110 	CI_GET_DEFAULT_INFO_2D(font, def);
111 
112     for (n = 0 ; n < len ; n++) {
113 	XCharStruct	*cs;
114 
115 	if (singlerow) {
116 	    int	ch = (str[n].byte1 << 8) | str[n].byte2;
117 
118 	    CI_GET_CHAR_INFO_1D(font, ch, def, cs);
119 	} else {
120 	    int	row = str[n].byte1;
121 	    int	col = str[n].byte2;
122 
123 	    CI_GET_CHAR_INFO_2D(font, row, col, def, cs);
124 	}
125 
126 	if (cs)
127 	    width -= cs->width;
128 	if (width < 0)
129 	    break;
130     }
131 
132     return n;
133 }
134 
135 /*************************************************************************/
136 
137 typedef struct  {
138     XtCallbackProc	proc;
139     XtPointer		client_data;
140 } CALLBACK_ENTRY;
141 
WM_DELETE_WINDOW_handler(Widget w,XtPointer client_data,XEvent * event,Boolean * cont)142 static void WM_DELETE_WINDOW_handler(Widget     w,
143 				     XtPointer  client_data,
144 				     XEvent    *event,
145 				     Boolean   *cont)
146 {
147     Display		*disp = XtDisplay(w);
148     CALLBACK_ENTRY	*entry = (CALLBACK_ENTRY *)client_data;
149 
150     if (event->type != ClientMessage || !entry->proc ||
151 	event->xclient.message_type != intern_atom(disp, "WM_PROTOCOLS") ||
152 	event->xclient.data.l[0] != intern_atom(disp, "WM_DELETE_WINDOW"))
153 	return;
154 
155     entry->proc(w, entry->client_data, (XtPointer)event);
156     *cont = False;
157 }
158 
destroy_callback(Widget w,XtPointer client_data,XtPointer call_data)159 static void destroy_callback(Widget w,
160 			     XtPointer client_data,
161 			     XtPointer call_data)
162 {
163     CALLBACK_ENTRY	*entry = (CALLBACK_ENTRY *)client_data;
164 
165     entry->proc        = NULL;
166     entry->client_data = NULL;
167     XtFree((char *)entry);
168 }
169 
add_WM_DELETE_WINDOW_callback(Widget w,XtCallbackProc proc,XtPointer client_data)170 void add_WM_DELETE_WINDOW_callback(Widget w,
171 				   XtCallbackProc proc,
172 				   XtPointer client_data)
173 {
174     Display		*disp = XtDisplay(w);
175     Window		win = XtWindow(w);
176     Atom		wm_delete_window;
177     CALLBACK_ENTRY	*entry;
178 
179     wm_delete_window = intern_atom(disp, "WM_DELETE_WINDOW");
180     XSetWMProtocols(disp, win, &wm_delete_window, 1);
181 
182     entry = (CALLBACK_ENTRY *)XtMalloc(sizeof *entry);
183     entry->proc        = proc;
184     entry->client_data = client_data;
185 
186     XtAddEventHandler(w, 0, True, WM_DELETE_WINDOW_handler, (XtPointer)entry);
187     XtAddCallback(w, XtNdestroyCallback, destroy_callback, (XtPointer)entry);
188 }
189 
190 /*************************************************************************/
191 
get_event_xy(XEvent * event,int * x,int * y)192 int get_event_xy(XEvent *event, int *x, int *y)
193 {
194     if (!event)
195 	return False;
196 
197     switch (event->type) {
198     case ButtonPress:
199     case ButtonRelease:
200         *x = event->xbutton.x;
201         *y = event->xbutton.y;
202         return True;
203     case KeyPress:
204     case KeyRelease:
205         *x = event->xkey.x;
206         *y = event->xkey.y;
207         return True;
208     case EnterNotify:
209     case LeaveNotify:
210         *x = event->xcrossing.x;
211         *y = event->xcrossing.y;
212         return True;
213     case MotionNotify:
214         *x = event->xmotion.x;
215         *y = event->xmotion.y;
216         return True;
217     }
218 
219     return False;
220 }
221 
get_event_time(XEvent * event)222 Time get_event_time(XEvent *event)
223 {
224     switch (event->type) {
225     case ButtonPress:
226     case ButtonRelease:
227 	return event->xbutton.time;
228     case KeyPress:
229     case KeyRelease:
230 	return event->xkey.time;
231     case EnterNotify:
232     case LeaveNotify:
233 	return event->xcrossing.time;
234     case MotionNotify:
235 	return event->xmotion.time;
236     case PropertyNotify:
237 	return event->xproperty.time;
238     case SelectionClear:
239 	return event->xselectionclear.time;
240     case SelectionNotify:
241 	return event->xselection.time;
242     case SelectionRequest:
243 	return event->xselectionrequest.time;
244     }
245 
246     return CurrentTime;
247 }
248 
249 /*************************************************************************/
250 
my_random(int n)251 static int my_random(int n)
252 {
253     static int	i = 1999;
254 
255     i *= 13;
256     i = i % 2371;
257 
258     return (n * i) / 2371;
259 }
260 
261 /*************************************************************************/
262 
is_popped_up(Widget w)263 int is_popped_up(Widget w)
264 {
265     return (XtIsSubclass(w, shellWidgetClass) &&
266 	    ((ShellWidget)w)->shell.popped_up);
267 }
268 
popup_under_pointer(Widget w,XtGrabKind grab)269 void popup_under_pointer(Widget w, XtGrabKind grab)
270 {
271     Widget		parent = XtParent(w);
272     Arg			pos_args[2];
273     int			x, y, x1, y1;
274     Window		w1, w2;
275     unsigned int	m;
276 
277     if (!XQueryPointer(XtDisplay(parent), XtWindow(parent),
278 		       &w1, &w2, &x, &y, &x1, &y1, &m))
279 	x = y = 0;
280     x -= my_random(w->core.width);
281     y -= my_random(w->core.height);
282     if (x < 0)
283 	x = 0;
284     else {
285 	int	temp = WidthOfScreen(XtScreen(parent)) - w->core.width;
286 
287 	if (temp < 0)
288 	    x = 0;
289 	else if (x > temp)
290 	    x = temp;
291     }
292     if (y < 0)
293 	y = 0;
294     else {
295 	int	temp = HeightOfScreen(XtScreen(parent)) - w->core.height;
296 
297 	if (temp < 0)
298 	    y = 0;
299 	else if (y > temp)
300 	    y = temp;
301     }
302 
303     XtSetArg(pos_args[0], XtNx, x);
304     XtSetArg(pos_args[1], XtNy, y);
305     XtSetValues(w, pos_args, XtNumber(pos_args));
306 
307     XtPopup(w, grab);
308 }
309 
310 /*********************************************************************/
311 
ascii_lower(char * c)312 static void ascii_lower(char *c)
313 {
314     while (*c != '\0') {
315 	if ((unsigned int)(*c - 'A') <= 'Z' - 'A')
316 	    *c += 'a' - 'A';
317 	c++;
318     }
319 }
320 
cvt_string_to_long(Display * disp,XrmValue * args,Cardinal * no_args,XrmValue * from,XrmValue * to,XtPointer * client_data)321 Boolean cvt_string_to_long(Display *disp,
322 			   XrmValue *args, Cardinal *no_args,
323 			   XrmValue *from, XrmValue *to,
324 			   XtPointer *client_data)
325 {
326     static long	l;
327 
328     if (sscanf((char *)from->addr, "%ld", &l) != 1) {
329 	XtStringConversionWarning((char *)from->addr, XtRLong);
330 	return False;
331     }
332 
333     if (!to->addr)
334 	to->addr = (XPointer)&l;
335     else {
336 	if (to->size < sizeof l) {
337 	    to->size = sizeof l;
338 	    return False;
339 	}
340 	*(long *)to->addr = l;
341     }
342     to->size = sizeof l;
343 
344     return True;
345 }
346 
cvt_string_to_justify(Display * disp,XrmValue * args,Cardinal * no_args,XrmValue * from,XrmValue * to,XtPointer * client_data)347 Boolean cvt_string_to_justify(Display *disp,
348 			      XrmValue *args, Cardinal *no_args,
349 			      XrmValue *from, XrmValue *to,
350 			      XtPointer *client_data)
351 {
352     static JustifyType	just;
353     static XrmQuark     left = 0, center, right;
354     XrmQuark		q;
355     char		*s = (char *)from->addr;
356     char		buffer[32];
357 
358     if (!s || strlen(s) > 30)
359 	return False;
360 
361     if (!left) {
362         left   = XrmPermStringToQuark("left");
363         center = XrmPermStringToQuark("center");
364         right  = XrmPermStringToQuark("right");
365     }
366 
367     if (to->addr && to->size < sizeof just) {
368 	to->size = sizeof just;
369 	return False;
370     }
371 
372     strcpy(buffer, s);
373     ascii_lower(buffer);
374     q = XrmStringToQuark(buffer);
375 
376     if (q == left)
377 	just = JustifyTypeLeft;
378     else if (q == center)
379 	just = JustifyTypeCenter;
380     else if (q == right)
381 	just = JustifyTypeRight;
382     else {
383 	XtStringConversionWarning((char *)from->addr, XtRJustify);
384 	return False;
385     }
386 
387     to->size = sizeof just;
388     if (to->addr)
389 	*(JustifyType *)to->addr = just;
390     else
391 	to->addr = (XPointer)&just;
392 
393     return True;
394 }
395 
396 /*********************************************************************/
397 
cvt_std_sel(Widget w,Time t,Atom * sel,Atom * target,Atom * type_ret,XPointer * val_ret,unsigned long * len_ret,int * format_ret)398 Boolean cvt_std_sel(Widget           w,
399 		    Time             t,
400 		    Atom            *sel,
401 		    Atom            *target,
402 		    Atom            *type_ret,
403 		    XPointer        *val_ret,
404 		    unsigned long   *len_ret,
405 		    int             *format_ret)
406 {
407     Display	*disp = XtDisplay(w);
408 
409 #if 1
410     if (*target == intern_atom(disp, "TIMESTAMP")) {
411 	long	l = t;
412 	int	i = t;
413 
414 	if (sizeof l != 4 && sizeof i != 4)
415 	    return False;
416         *val_ret = (XPointer)XtMalloc(4);
417 	if (sizeof l == 4)
418 	    memcpy(*val_ret, &l, 4);
419 	else
420 	    memcpy(*val_ret, &i, 4);
421         *type_ret   = XA_INTEGER;
422         *len_ret    = 1;
423         *format_ret = 32;
424         return True;
425     }
426 #endif
427 
428     if (*target == intern_atom(disp, "USER")) {
429         char	*name = getenv("USER");
430 
431         if (!name)
432 	    return False;
433         *val_ret    = (XtPointer)XtNewString(name);
434         *type_ret   = XA_STRING;
435         *len_ret    = strlen(name);
436         *format_ret = 8;
437         return True;
438     }
439 
440     if (*target == intern_atom(disp, "CLASS")) {
441         char	*class;
442         int	len;
443 
444         while (w && !XtIsApplicationShell(w))
445             w = XtParent(w);
446 
447 	if (!w)
448 	    return False;
449 
450 	class = ((ApplicationShellWidget)w)->application.class;
451 	len = strlen(w->core.name);
452         *len_ret = len + strlen(class) + 2;
453         *val_ret = (XPointer)XtMalloc(*len_ret);
454         strcpy((char *)*val_ret, w->core.name);
455         strcpy((char *)*val_ret + len + 1, class);
456         *type_ret = XA_STRING;
457         *format_ret = 8;
458         return True;
459     }
460 
461     if (*target == intern_atom(disp, "NAME")) {
462 	while (w && !XtIsWMShell(w))
463 	    w = XtParent(w);
464 
465         while (w && !XtIsWMShell(w))
466 	    w = XtParent(w);
467 
468         if (!w)
469 	    return False;
470 
471         *val_ret = (XPointer)XtNewString(((WMShellWidget)w)->wm.title);
472         *len_ret = strlen((char *)*val_ret);
473         *type_ret = XA_STRING;
474         *format_ret = 8;
475         return True;
476     }
477 
478     if (*target == intern_atom(disp, "CLIENT_WINDOW")) {
479 	Widget	p;
480 
481         while ((p = XtParent(w)))
482 	    w = p;
483 
484         *val_ret  = (XPointer)XtMalloc(sizeof(Window));
485         *(Window *)*val_ret = w->core.window;
486         *type_ret   = XA_WINDOW;
487         *len_ret    = 1;
488         *format_ret = 32;
489         return True;
490     }
491 
492     if (*target == intern_atom(disp, "TARGETS")) {
493         Atom	*std_targets;
494         int	n = 0;
495 
496 	std_targets = (Atom *)XtMalloc(16 * sizeof *std_targets);
497 
498 #if 1
499         std_targets[n++] = intern_atom(disp, "TIMESTAMP");
500 #endif
501         std_targets[n++] = intern_atom(disp, "USER");
502         std_targets[n++] = intern_atom(disp, "CLASS");
503         std_targets[n++] = intern_atom(disp, "NAME");
504         std_targets[n++] = intern_atom(disp, "CLIENT_WINDOW");
505 
506         *val_ret    = (XPointer)std_targets;
507         *type_ret   = XA_ATOM;
508         *len_ret    = n;
509         *format_ret = 32;
510         return True;
511     }
512 
513     return False;
514 }
515 
516 /*********************************************************************/
517 
518 typedef struct StippleCache StippleCache;
519 
520 struct StippleCache {
521     StippleCache	*next;
522     Screen		*screen;
523     Pixmap		pixmap;
524     unsigned int	ref_cnt;
525 };
526 
527 static StippleCache *stipple_cache = NULL;
528 
create_stipple(Screen * screen)529 Pixmap create_stipple(Screen *screen)
530 {
531     static char		bits[] = {0x01, 0x02};
532     StippleCache	*loop;
533 
534     for (loop = stipple_cache ; loop ; loop = loop->next)
535 	if (loop->screen == screen) {
536 	    loop->ref_cnt++;
537 	    return loop->pixmap;
538 	}
539 
540     loop = (StippleCache *)XtMalloc(sizeof *loop);
541     loop->next = stipple_cache;
542     stipple_cache = loop;
543     loop->screen  = screen;
544     loop->ref_cnt = 1;
545     loop->pixmap =
546 	XCreatePixmapFromBitmapData(DisplayOfScreen(screen),
547 				    RootWindowOfScreen(screen),
548 				    bits, 2, 2, 0, 1, 1);
549 
550     return loop->pixmap;
551 }
552 
release_stipple(Screen * screen,Pixmap pixmap)553 void release_stipple(Screen *screen, Pixmap pixmap)
554 {
555     StippleCache	**loop, *tmp;
556 
557     if (!stipple_cache)
558 	return;
559 
560     for (loop = &stipple_cache ; *loop ; loop = &(*loop)->next)
561 	if ((*loop)->screen == screen)
562 	    break;
563 
564     tmp = *loop;
565 
566     if (!tmp || --tmp->ref_cnt != 0)
567 	return;
568 
569     *loop = tmp->next;
570     XtFree((char *)tmp);
571 }
572 
573 /*********************************************************************/
574 
get_visual(Widget w)575 Visual *get_visual(Widget w)
576 {
577     while (w && !XtIsShell(w))
578 	w = XtParent(w);
579 
580     if (!w)
581 	return NULL;
582 
583     return ((ShellWidget)w)->shell.visual;
584 }
585 
black_and_white(Screen * screen,Visual * visual,Pixel * black,Pixel * white)586 void black_and_white(Screen *screen, Visual *visual,
587 		     Pixel *black, Pixel *white)
588 {
589     Display	*disp = DisplayOfScreen(screen);
590     XVisualInfo	tmp, *info;
591     int		n;
592 
593     *black = BlackPixelOfScreen(screen);
594     *white = WhitePixelOfScreen(screen);
595     if (!visual)
596 	return;
597 
598     tmp.visualid = XVisualIDFromVisual(visual);
599     info = XGetVisualInfo(disp, VisualIDMask, &tmp, &n);
600     if (!info)
601 	return;
602 
603     switch (info->class) {
604     case StaticGray:
605 	*black = 0;
606 	*white = info->colormap_size - 1; /* Is this right? */
607 	break;
608     case GrayScale:
609 	/* ?? */
610 	break;
611     case StaticColor:
612 	*black = 0;
613 	*white = info->red_mask | info->green_mask | info->blue_mask;
614 	break;
615     case PseudoColor:
616 	/* ?? */
617 	break;
618     case TrueColor:
619 	*black = 0;
620 	*white = info->red_mask | info->green_mask | info->blue_mask;
621     case DirectColor:
622 	/* ?? */
623 	break;
624     }
625 
626     XFree((char *)info);
627 }
628 
get_black(Widget w)629 Pixel get_black(Widget w)
630 {
631     Screen	*screen = XtScreen(w);
632     Visual	*visual = get_visual(w);
633     Pixel	black, white;
634 
635     black_and_white(screen, visual, &black, &white);
636     return black;
637 }
638 
639 /*********************************************************************/
640 
641 typedef struct AtomCache {
642     struct AtomCache	*next;
643     Display		*disp;
644     XrmQuark		name;
645     Atom		atom;
646 } AtomCache;
647 
648 /*
649  *  This shouldn't grow to deep...
650  */
651 static AtomCache	*atom_cache = NULL;
652 
intern_atom(Display * disp,char * name_str)653 Atom intern_atom(Display *disp, char *name_str)
654 {
655     XrmQuark	name = XrmStringToQuark(name_str);
656     AtomCache	*loop = atom_cache;
657 
658     for (loop = atom_cache ; loop ; loop = loop->next)
659 	if (loop->name == name && loop->disp == disp)
660 	    return loop->atom;
661 
662     loop = (AtomCache *)XtMalloc(sizeof *loop);
663     loop->next = atom_cache;
664     loop->disp = disp;
665     loop->name = name;
666     loop->atom = XInternAtom(disp, name_str, False);
667 
668     atom_cache = loop;
669 
670     return loop->atom;
671 }
672