1 /* $Id: props.c,v 1.2 2007/09/12 20:05:58 jwrdegoede Exp $ */
2 /*****************************************************************************/
3 /**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
4 /**                          Salt Lake City, Utah                           **/
5 /**  Portions Copyright 1989 by the Massachusetts Institute of Technology   **/
6 /**                        Cambridge, Massachusetts                         **/
7 /**                                                                         **/
8 /**                           All Rights Reserved                           **/
9 /**                                                                         **/
10 /**    Permission to use, copy, modify, and distribute this software and    **/
11 /**    its documentation  for  any  purpose  and  without  fee is hereby    **/
12 /**    granted, provided that the above copyright notice appear  in  all    **/
13 /**    copies and that both  that  copyright  notice  and  this  permis-    **/
14 /**    sion  notice appear in supporting  documentation,  and  that  the    **/
15 /**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
16 /**    in publicity pertaining to distribution of the  software  without    **/
17 /**    specific, written prior permission.                                  **/
18 /**                                                                         **/
19 /**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
20 /**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
21 /**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
22 /**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
23 /**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
24 /**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
25 /**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
26 /**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
27 /*****************************************************************************/
28 /****************************************************************************
29  * This module is based on Twm, but has been siginificantly modified
30  * by Rob Nation
31  ****************************************************************************/
32 /***********************************************************************
33  * The rest of it is all my fault -- MLM
34  * mwm - "LessTif Window Manager"
35  ***********************************************************************/
36 
37 
38 #include <LTconfig.h>
39 
40 #include <stdio.h>
41 
42 #include <Xm/Xm.h>
43 #include <Xm/MwmUtil.h>
44 #include "mwm.h"
45 
46 
47 Atom XA_MIT_PRIORITY_COLORS;
48 Atom XA_WM_CHANGE_STATE;
49 Atom XA_WM_STATE;
50 Atom XA_WM_COLORMAP_WINDOWS;
51 Atom XA_WM_PROTOCOLS;
52 Atom XA_WM_TAKE_FOCUS;
53 Atom XA_WM_DELETE_WINDOW;
54 Atom XA_WM_SAVE_YOURSELF;
55 Atom XA_WM_DESKTOP;
56 Atom XA_MWM_HINTS;
57 Atom XA_MWM_MESSAGES;
58 Atom XA_MWM_MENU;
59 Atom XA_MWM_INFO;
60 
61 /***********************************************************************
62  *
63  * <Properties description>
64  *
65  ***********************************************************************/
66 
67 /*
68  * Intern some commonly used atoms
69  */
70 void
PROP_Initialize(void)71 PROP_Initialize(void)
72 {
73     XA_MIT_PRIORITY_COLORS = XInternAtom(dpy, "_MIT_PRIORITY_COLORS", False);
74     XA_WM_CHANGE_STATE = XInternAtom(dpy, "WM_CHANGE_STATE", False);
75     XA_WM_STATE = XInternAtom(dpy, "WM_STATE", False);
76     XA_WM_COLORMAP_WINDOWS = XInternAtom(dpy, "WM_COLORMAP_WINDOWS", False);
77     XA_WM_PROTOCOLS = XInternAtom(dpy, "WM_PROTOCOLS", False);
78     XA_WM_TAKE_FOCUS = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
79     XA_WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
80     XA_WM_SAVE_YOURSELF = XInternAtom(dpy, "WM_SAVE_YOURSELF", False);
81     XA_WM_DESKTOP = XInternAtom(dpy, "WM_DESKTOP", False);
82     XA_MWM_HINTS = XInternAtom(dpy, _XA_MWM_HINTS, False);
83     XA_MWM_MESSAGES = XInternAtom(dpy, _XA_MWM_MESSAGES, False);
84     XA_MWM_MENU = XInternAtom(dpy, _XA_MWM_MENU, False);
85     XA_MWM_INFO = XInternAtom(dpy, _XA_MWM_INFO, False);
86 }
87 
88 /*
89  * set the behavior property on the root window.  This will be used for the
90  * f.set_behavior function
91  */
92 void
PROP_SetBehavior(ScreenInfo * scr,Boolean custom)93 PROP_SetBehavior(ScreenInfo *scr, Boolean custom)
94 {
95     long info[PROP_MWM_INFO_ELEMENTS];
96 
97     /* set the MWM_INFO property on the Root, notice that we
98        use an array of longs here and not the PropMotifWmInfo struct,
99        this is because this struct looks like this in lesstif:
100 
101        typedef struct {
102            CARD32 flags;
103            CARD32 wmWindow;
104        } PropMotifWmInfo;
105 
106        But when setting 32 bit properties XChangeProperty expects an array of
107        longs, which it will convert to 32 bit values if need. Thus passing an
108        actual PropMotifWmInfo struct will mess things up on archs where longs
109        are 64 bit. */
110 
111     if (custom)
112 	info[0] = MWM_INFO_STARTUP_CUSTOM;   /* set flags "member" */
113     else
114 	info[0] = MWM_INFO_STARTUP_STANDARD; /* set flags "member" */
115 
116     info[1] = scr->root_win; /* set wmWindow "member" */
117 
118     XChangeProperty(dpy, scr->root_win, XA_MWM_INFO, XA_MWM_INFO,
119 		    32, PropModeReplace,
120 		    (unsigned char *)&info, PROP_MWM_INFO_ELEMENTS);
121 }
122 
123 /*
124  * clear the behavior property on the root window.
125  */
126 void
PROP_ClearBehavior(ScreenInfo * scr)127 PROP_ClearBehavior(ScreenInfo *scr)
128 {
129     /* set the MWM_INFO property on the Root */
130     XChangeProperty(dpy, scr->root_win, XA_MWM_INFO, XA_MWM_INFO,
131 		    32, PropModeReplace,
132 		    NULL, 0);
133 }
134 
135 /*
136  * get the current state of the behavior property.  This also will be used
137  * by f.set_behavior
138  */
139 int
PROP_GetBehavior(ScreenInfo * scr)140 PROP_GetBehavior(ScreenInfo *scr)
141 {
142     int actual_format, ret;
143     Atom actual_type;
144     unsigned long nitems, bytesafter;
145     /* We use a long pointer here and not a PropMotifWmInfo pointer,
146        this is because this type looks like this in lesstif:
147 
148        typedef struct {
149            CARD32 flags;
150            CARD32 wmWindow;
151        } PropMotifWmInfo;
152 
153        But when getting 32 bit properties XGetWindowProperty returns an array
154        of longs. Thus interpreting the returned data as PropMotifWmInfo will
155        mess things up on archs where longs are 64 bit. */
156     unsigned long *info;
157 
158     if (XGetWindowProperty(dpy, scr->root_win, XA_MWM_INFO, 0L,
159 			   PROP_MOTIF_WM_INFO_ELEMENTS, False,
160 			   XA_MWM_INFO, &actual_type, &actual_format,
161 			   &nitems, &bytesafter,
162 			   (unsigned char **)&info) == Success)
163     {
164 	if (nitems > 0 && info)
165 	    ret = info[0]; /* Return flags "member" */
166 	else
167 	    ret = 0;
168 	XFree((char *)info);
169 	return ret;
170     }
171 
172     return 0;
173 }
174 
175 /*
176  * Make sure property priority colors is empty
177  */
178 void
PROP_SetPriorityColors(ScreenInfo * scr)179 PROP_SetPriorityColors(ScreenInfo *scr)
180 {
181     XChangeProperty(dpy, scr->root_win, XA_MIT_PRIORITY_COLORS,
182 		    XA_CARDINAL, 32, PropModeReplace, NULL, 0);
183 }
184 
185 /*
186  * check the desktop.
187  */
188 Boolean
PROP_CheckDesktop(ScreenInfo * scr)189 PROP_CheckDesktop(ScreenInfo *scr)
190 {
191     Atom atype;
192     int aformat;
193     unsigned long nitems, bytes_remain;
194     unsigned char *prop;
195     Boolean restart = False;
196 
197     /*
198      * Set the current desktop number to zero
199      * Multiple desks are available even in non-virtual
200      * compilations
201      */
202     scr->current_desk = 0;
203     if ((XGetWindowProperty(dpy, scr->root_win, XA_WM_DESKTOP, 0L, 1L, True,
204 			    XA_WM_DESKTOP, &atype, &aformat, &nitems,
205 			    &bytes_remain, &prop)) == Success)
206     {
207 	if (prop != NULL)
208 	{
209 	    restart = True;
210 	    scr->current_desk = *(unsigned long *)prop;
211 	}
212     }
213 
214     return restart;
215 }
216 
217 /*
218  * This is used to tell applications which windows on the screen are
219  * top level appication windows, and which windows are the icon windows
220  * that go with them.
221  */
222 void
PROP_SetState(MwmWindow * tmp_win,int state)223 PROP_SetState(MwmWindow *tmp_win, int state)
224 {
225     unsigned long data[2];	/* "suggested" by ICCCM version 1 */
226 
227     data[0] = (unsigned long)state;
228     data[1] = (unsigned long)tmp_win->icon_w;
229 /*  data[2] = (unsigned long) tmp_win->icon_pixmap_w; */
230 
231     XChangeProperty(dpy, tmp_win->w, XA_WM_STATE, XA_WM_STATE, 32,
232 		    PropModeReplace, (unsigned char *)data, 2);
233 }
234 
235 /*
236  * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
237  * client messages will have the following form:
238  *
239  *     event type       ClientMessage
240  *     message type     XA_WM_PROTOCOLS
241  *     window           tmp->w
242  *     format           32
243  *     data[0]          message atom
244  *     data[1]          time stamp
245  */
246 void
PROP_SendClientMessage(Window w,Atom a,Time timestamp)247 PROP_SendClientMessage(Window w, Atom a, Time timestamp)
248 {
249     XClientMessageEvent ev;
250 
251     ev.type = ClientMessage;
252     ev.window = w;
253     ev.message_type = XA_WM_PROTOCOLS;
254     ev.format = 32;
255     ev.data.l[0] = a;
256     ev.data.l[1] = timestamp;
257     XSendEvent(dpy, w, False, 0L, (XEvent *)&ev);
258 }
259 
260 /*
261  * verify that a client is expecting to receive a ClientMessage from mwm
262  */
263 Boolean
PROP_VerifyMwmMessage(MwmWindow * win,Atom message)264 PROP_VerifyMwmMessage(MwmWindow *win, Atom message)
265 {
266     unsigned int i;
267 
268     for (i = 0; i < win->num_messages; i++)
269     {
270 	if (win->mwm_messages[i] == message)
271 	    return True;
272     }
273 
274     return False;
275 }
276 
277 /*
278  * send a mwm specific message
279  */
280 void
PROP_SendMwmMessage(Window w,Atom a,Time timestamp)281 PROP_SendMwmMessage(Window w, Atom a, Time timestamp)
282 {
283     XClientMessageEvent ev;
284 
285     ev.type = ClientMessage;
286     ev.window = w;
287     ev.message_type = XA_MWM_MESSAGES;
288     ev.format = 32;
289     ev.data.l[0] = a;
290     ev.data.l[1] = timestamp;
291     XSendEvent(dpy, w, False, 0L, (XEvent *)&ev);
292 }
293 
294 /*
295  * Reads the property MOTIF_WM_HINTS
296  */
297 void
PROP_GetMwmHints(MwmWindow * win)298 PROP_GetMwmHints(MwmWindow *win)
299 {
300     int actual_format;
301     Atom actual_type;
302     unsigned long nitems, bytesafter;
303 
304     if (XGetWindowProperty(dpy, win->w, XA_MWM_HINTS, 0L,
305 			   PROP_MOTIF_WM_HINTS_ELEMENTS, False, XA_MWM_HINTS,
306 			   &actual_type, &actual_format, &nitems, &bytesafter,
307 			   (unsigned char **)&win->mwm_hints) == Success)
308     {
309 
310 	if (nitems >= PROP_MOTIF_WM_HINTS_ELEMENTS)
311 	{
312 	    /* Fixup X*Property long == 32 bits confusion if needed */
313 	    if (sizeof(long) != 4)
314 	    {
315 	        long *prop_hints = (long *)win->mwm_hints;
316 	        win->mwm_hints = XtMalloc(sizeof(MwmHints));
317 	        win->mwm_hints->flags       = prop_hints[0];
318 	        win->mwm_hints->functions   = prop_hints[1];
319 	        win->mwm_hints->decorations = prop_hints[2];
320 	        win->mwm_hints->input_mode  = prop_hints[3];
321 	        win->mwm_hints->status      = prop_hints[4];
322 	        XFree(prop_hints);
323 	    }
324 	    return;
325         }
326 
327 	XFree((char *)win->mwm_hints);
328     }
329 
330     win->mwm_hints = NULL;
331 }
332 
333 /*
334  * Reads the property MOTIF_WM_MENU
335  */
336 void
PROP_GetMwmMenu(MwmWindow * win)337 PROP_GetMwmMenu(MwmWindow *win)
338 {
339     int actual_format;
340     Atom actual_type;
341     unsigned long nitems, bytesafter;
342 
343     if (XGetWindowProperty(dpy, win->w, XA_MWM_MENU,
344 			   0L, 1024L, False,
345 			   XA_MWM_MENU, &actual_type, &actual_format,
346 			   &nitems, &bytesafter,
347 			   (unsigned char **)&win->mwm_menu) == Success)
348     {
349 
350 	if (bytesafter)
351 	    fprintf(stderr, "Maximum size of MWM_MENU exceeded\n");
352 
353 	return;
354     }
355 
356     win->mwm_menu = NULL;
357 }
358 
359 /*
360  * Reads the property MOTIF_WM_MESSAGES
361  */
362 void
PROP_GetMwmMessages(MwmWindow * win)363 PROP_GetMwmMessages(MwmWindow *win)
364 {
365     int actual_format;
366     Atom actual_type;
367     unsigned long bytesafter;
368 
369     if (XGetWindowProperty(dpy, win->w, XA_MWM_MESSAGES, 0L, 1024L, False,
370 			   AnyPropertyType, &actual_type, &actual_format,
371 			   &win->num_messages, &bytesafter,
372 			   (unsigned char **)&win->mwm_messages) == Success)
373     {
374 
375 	if (bytesafter)
376 	    fprintf(stderr, "Maximum size of MWM_MESSAGES exceeded: %ld.\n", bytesafter);
377 
378 	return;
379     }
380 
381     win->mwm_messages = NULL;
382 }
383 
384 /*
385  * gets application supplied size info
386  */
387 void
PROP_GetWindowSizeHints(MwmWindow * win)388 PROP_GetWindowSizeHints(MwmWindow *win)
389 {
390     long supplied = 0;
391 
392     if (!XGetWMNormalHints(dpy, win->w, &win->hints, &supplied))
393 	win->hints.flags = 0;
394 
395     /* Beat up our copy of the hints, so that all important field are
396      * filled in! */
397     if (win->hints.flags & PResizeInc)
398     {
399 	if (win->hints.width_inc == 0)
400 	    win->hints.width_inc = 1;
401 	if (win->hints.height_inc == 0)
402 	    win->hints.height_inc = 1;
403     }
404     else
405     {
406 	win->hints.width_inc = 1;
407 	win->hints.height_inc = 1;
408     }
409 
410     /*
411      * ICCCM says that PMinSize is the default if no PBaseSize is given,
412      * and vice-versa.
413      */
414 
415     if (!(win->hints.flags & PBaseSize))
416     {
417 	if (win->hints.flags & PMinSize)
418 	{
419 	    win->hints.base_width = win->hints.min_width;
420 	    win->hints.base_height = win->hints.min_height;
421 	}
422 	else
423 	{
424 	    win->hints.base_width = 0;
425 	    win->hints.base_height = 0;
426 	}
427     }
428     if (!(win->hints.flags & PMinSize))
429     {
430 	win->hints.min_width = win->hints.base_width;
431 	win->hints.min_height = win->hints.base_height;
432     }
433     if (!(win->hints.flags & PMaxSize))
434     {
435 	win->hints.max_width = MAX_WINDOW_WIDTH;
436 	win->hints.max_height = MAX_WINDOW_HEIGHT;
437     }
438     if (win->hints.max_width < win->hints.min_width)
439 	win->hints.max_width = MAX_WINDOW_WIDTH;
440     if (win->hints.max_height < win->hints.min_height)
441 	win->hints.max_height = MAX_WINDOW_HEIGHT;
442 
443     /* Zero width/height windows are bad news! */
444     if (win->hints.min_height <= 0)
445 	win->hints.min_height = 1;
446     if (win->hints.min_width <= 0)
447 	win->hints.min_width = 1;
448 
449     if (!(win->hints.flags & PWinGravity))
450     {
451 	win->hints.win_gravity = NorthWestGravity;
452 	win->hints.flags |= PWinGravity;
453     }
454 }
455 
456 /*
457  * finds out which protocols the window supports
458  */
459 void
PROP_GetWmProtocols(MwmWindow * win)460 PROP_GetWmProtocols(MwmWindow *win)
461 {
462     unsigned long flags = 0L;
463     Atom *protocols = NULL, *ap;
464     int i, n;
465     Atom atype;
466     int aformat;
467     unsigned long bytes_remain, nitems;
468 
469     if (win == NULL)
470 	return;
471 
472     /*
473      * First, try the Xlib function to read the protocols.
474      * This is what Twm uses.
475      */
476     if (XGetWMProtocols(dpy, win->w, &protocols, &n))
477     {
478 	win->flags &= ~(WM_TAKES_FOCUS | WM_DELS_WINDOW | WM_SAVE_SELF | MWM_MESSAGES);
479 	for (i = 0, ap = protocols; i < n; i++, ap++)
480 	{
481 	    if (*ap == (Atom)XA_WM_TAKE_FOCUS)
482 		flags |= WM_TAKES_FOCUS;
483 	    if (*ap == (Atom)XA_WM_DELETE_WINDOW)
484 		flags |= WM_DELS_WINDOW;
485 	    if (*ap == (Atom)XA_WM_SAVE_YOURSELF)
486 		flags |= WM_SAVE_SELF;
487 	    if (*ap == (Atom)XA_MWM_MESSAGES)
488 		flags |= MWM_MESSAGES;
489 	}
490 	if (protocols)
491 	    XFree((char *)protocols);
492     }
493     /*
494      * Next, read it the hard way. mosaic from Coreldraw needs to
495      * be read in this way.
496      */
497     else if ((XGetWindowProperty(dpy, win->w, XA_WM_PROTOCOLS, 0L, 10L, False,
498 				 XA_WM_PROTOCOLS, &atype, &aformat, &nitems,
499 				 &bytes_remain,
500 				 (unsigned char **)&protocols)) == Success)
501     {
502 	win->flags &= ~(WM_TAKES_FOCUS | WM_DELS_WINDOW | WM_SAVE_SELF | MWM_MESSAGES);
503 	for (i = 0, ap = protocols; (unsigned long)i < nitems; i++, ap++)
504 	{
505 	    if (*ap == (Atom)XA_WM_TAKE_FOCUS)
506 		flags |= WM_TAKES_FOCUS;
507 	    if (*ap == (Atom)XA_WM_DELETE_WINDOW)
508 		flags |= WM_DELS_WINDOW;
509 	    if (*ap == (Atom)XA_WM_SAVE_YOURSELF)
510 		flags |= WM_SAVE_SELF;
511 	    if (*ap == (Atom)XA_MWM_MESSAGES)
512 		flags |= MWM_MESSAGES;
513 	}
514 	if (protocols)
515 	    XFree((char *)protocols);
516     }
517     win->flags |= flags;
518 }
519 
520 /*
521  * Gets the WM_COLORMAP_WINDOWS property from the window
522  * This property typically doesn't exist, but a few applications
523  * use it. These seem to occur mostly on SGI machines.
524  */
525 void
PROP_GetWmColormapWindows(MwmWindow * tmp)526 PROP_GetWmColormapWindows(MwmWindow *tmp)
527 {
528     if (tmp->cmap_windows != (Window *)NULL)
529 	XFree((void *)tmp->cmap_windows);
530 
531     if (!XGetWMColormapWindows(dpy, tmp->w, &tmp->cmap_windows,
532 			       &tmp->number_cmap_windows))
533     {
534 	tmp->number_cmap_windows = 0;
535 	tmp->cmap_windows = NULL;
536     }
537 }
538 
539 /*
540  * get the icon name
541  */
542 void
PROP_GetWmIconName(MwmWindow * tmp)543 PROP_GetWmIconName(MwmWindow *tmp)
544 {
545     Atom actual_type;
546     int actual_format;
547     unsigned long nitems, bytesafter;
548     unsigned char *prop;
549 
550     if (XGetWindowProperty(dpy, tmp->w, XA_WM_ICON_NAME, 0L, 200L, False,
551 		       XA_STRING, &actual_type, &actual_format, &nitems,
552 		       &bytesafter, &prop)
553 	== Success && prop != NULL)
554     {
555 	tmp->icon_label = (char *)prop; /* FIXME */
556     }
557 }
558