1 /*
2  * Motif
3  *
4  * Copyright (c) 1987-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22 */
23 /*
24  * Motif Release 1.2.3
25 */
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29 
30 
31 #ifdef REV_INFO
32 #ifndef lint
33 static char rcsid[] = "$XConsortium: WmColormap.c /main/5 1996/10/30 11:14:44 drk $"
34 #endif
35 #endif
36 
37 /*
38  * Included Files:
39  */
40 
41 #include "WmGlobal.h"
42 
43 /*
44  * include extern functions
45  */
46 
47 #include "WmColormap.h"
48 #include "WmKeyFocus.h"
49 
50 static Bool ProcessEvents(Display *dpy, XEvent *Event, char *c_pCD);
51 
52 
53 /* Global variables */
54 	static unsigned long firstRequest, lastRequest;
55 
56 
57 /*************************************<->*************************************
58  *
59  *  InitWorkspaceColormap ()
60  *
61  *
62  *  Description:
63  *  -----------
64  *  This function sets up the default workspace colormap and prepares for
65  *  workspace colormap processing.
66  *
67  *
68  *  Inputs:
69  *  -------
70  *  pSD = ptr to screen data
71  *
72  *  Outputs:
73  *  -------
74  *  wmGD = (workspaceColormap)
75  *
76  *************************************<->***********************************/
77 
InitWorkspaceColormap(WmScreenData * pSD)78 void InitWorkspaceColormap (WmScreenData *pSD)
79 {
80     /*
81      * Setup the default (workspace) colormap:
82      * !!! this should be made more general to get the colormap for the !!!
83      * !!! workspace (root) and then track colormap changes             !!!
84      */
85 
86     pSD->workspaceColormap = DefaultColormap (DISPLAY, pSD->screen);
87 
88 } /* END OF FUNCTION InitWorkspaceColormap */
89 
90 
91 
92 /*************************************<->*************************************
93  *
94  *  InitColormapFocus (pSD)
95  *
96  *
97  *  Description:
98  *  -----------
99  *  This function prepares for managing the colormap focus and sets the
100  *  initial colormap focus (if the focus policy is "keyboard" - i.e. the
101  *  colormap focus tracks the keyboard focus) the initial colormap
102  *  installation is done in InitKeyboardFocus.
103  *
104  *  Inputs:
105  *  -------
106  *  pSD = pointer to screen data
107  *
108  *  Outputs:
109  *  -------
110  *  *pSD = (colormapFocus)
111  *
112  *************************************<->***********************************/
113 
InitColormapFocus(WmScreenData * pSD)114 void InitColormapFocus (WmScreenData *pSD)
115 {
116     ClientData *pCD;
117     Boolean sameScreen;
118 
119 
120     /*
121      * Set up the initial colormap focus.  If the colormapFocusPolicy is
122      * "keyboard" or it is "pointer" and the keyboard input focus policy
123      * is "pointer" then set up the initial colormap focus when the
124      * initial keyboard input focus is set up.
125      */
126 
127     pSD->colormapFocus = NULL;
128 
129     if (wmGD.colormapFocusPolicy == CMAP_FOCUS_POINTER)
130     {
131 	if (wmGD.keyboardFocusPolicy != KEYBOARD_FOCUS_POINTER)
132 	{
133 	    if ((pCD = GetClientUnderPointer (&sameScreen)) != NULL)
134 	    {
135 	        SetColormapFocus (pSD, pCD);
136 	    }
137 	    else
138 	    {
139 	        WmInstallColormap (pSD, pSD->workspaceColormap);
140 	    }
141 	}
142     }
143     else
144     {
145 	WmInstallColormap (pSD, pSD->workspaceColormap);
146     }
147 
148 } /* END OF FUNCTION InitColormapFocus */
149 
150 
151 
152 #ifndef OLD_COLORMAP
153 /*************************************<->*************************************
154  *
155  *  ForceColormapFocus (pSD, pCD)
156  *
157  *
158  *  Description:
159  *  -----------
160  * ForceColormapFocus is the working part of the original SetColormapFocus.
161  * This function is used to unconditionally set the colormap focus to a
162  * particular client window or to clear the colormap focus (set focus to
163  * the root window).
164  *
165  * The reason is to permit focus to be dtrced.  We need to do this because
166  * we can already have colormap focus, but still need to set the colormaps.
167  * Examples of when this occurs are:
168  *
169  *	* after the window manager itself has forced a colormap,
170  *	  as happens when it draws transients in the overlay planes.
171  *	* when WM_COLORMAP_WINDOWS changes.
172  *	* when a ColormapNotify (new) event is received.
173  *
174  *
175  *  Inputs:
176  *  ------
177  *  pSD = pointer to Screen Data
178  *  pCD = pointer to client data (clientColormap ...)
179  *
180  *************************************<->***********************************/
181 
ForceColormapFocus(WmScreenData * pSD,ClientData * pCD)182 void ForceColormapFocus (WmScreenData *pSD, ClientData *pCD)
183 {
184     if (pCD && ((pCD->clientState == NORMAL_STATE) ||
185 		(pCD->clientState == MAXIMIZED_STATE)))
186     {
187 	pSD->colormapFocus = pCD;
188 #ifndef OLD_COLORMAP /* colormaps */
189 	ProcessColormapList (pSD, pCD);
190 #else /* OSF original */
191 	WmInstallColormap (pSD, pCD->clientColormap);
192 #endif
193     }
194     else
195     {
196 	/*
197 	 * The default colormap is installed for minimized windows that have
198 	 * the colormap focus.
199 	 * !!! should colormaps be installed for icons with client      !!!
200 	 * !!! icon windows?  should the client colormap be installed ? !!!
201 	 */
202 
203 	pSD->colormapFocus = NULL;
204 	WmInstallColormap (pSD, pSD->workspaceColormap);
205     }
206 
207 } /* END OF FUNCTION ForceColormapFocus */
208 #endif
209 
210 
211 
212 /*************************************<->*************************************
213  *
214  *  SetColormapFocus (pSD, pCD)
215  *
216  *
217  *  Description:
218  *  -----------
219  *  This function is used to set the colormap focus to a particular client
220  *  window or to clear the colormap focus (set focus to the root window).
221  *
222  *
223  *  Inputs:
224  *  ------
225  *  pSD = pointer to Screen Data
226  *  pCD = pointer to client data (clientColormap ...)
227  *
228  *************************************<->***********************************/
229 
SetColormapFocus(WmScreenData * pSD,ClientData * pCD)230 void SetColormapFocus (WmScreenData *pSD, ClientData *pCD)
231 {
232     if (pCD == pSD->colormapFocus)
233     {
234 	/*
235 	 * The focus is already set to the right place.
236 	 */
237 
238 	return;
239     }
240 #ifndef OLD_COLORMAP
241     ForceColormapFocus (pSD, pCD);
242 #else /* OSF original */
243 
244     if (pCD && ((pCD->clientState == NORMAL_STATE) ||
245 		(pCD->clientState == MAXIMIZED_STATE)))
246     {
247 	pSD->colormapFocus = pCD;
248 #ifndef OLD_COLORMAP /* colormaps */
249 	ProcessColormapList (pSD, pCD);
250 #else /* OSF original */
251 	WmInstallColormap (pSD, pCD->clientColormap);
252 #endif
253     }
254     else
255     {
256 	/*
257 	 * The default colormap is installed for minimized windows that have
258 	 * the colormap focus.
259 	 * !!! should colormaps be installed for icons with client      !!!
260 	 * !!! icon windows?  should the client colormap be installed ? !!!
261 	 */
262 
263 	pSD->colormapFocus = NULL;
264 	WmInstallColormap (pSD, pSD->workspaceColormap);
265     }
266 #endif
267 
268 } /* END OF FUNCTION SetColormapFocus */
269 
270 
271 
272 /*************************************<->*************************************
273  *
274  *  WmInstallColormap (pSD, colormap)
275  *
276  *
277  *  Description:
278  *  -----------
279  *  This function installs colormaps for the window manager.  It trys to be
280  *  intelligent and avoid unnecessary installations.  It assumes that no
281  *  other program is installing colormaps.
282  *
283  *
284  *  Inputs:
285  *  ------
286  *  pSD = ptr to screen data
287  *  colormap = the id for the colormap to be installed
288  *
289  *************************************<->***********************************/
290 
WmInstallColormap(WmScreenData * pSD,Colormap colormap)291 void WmInstallColormap (WmScreenData *pSD, Colormap colormap)
292 {
293     /*
294      * !!! this could be generalized to work better for systems that !!!
295      * !!! support multiple installed colormaps                      !!!
296      */
297 
298     if (colormap != pSD->lastInstalledColormap)
299     {
300 	XInstallColormap (DISPLAY, colormap);
301 	pSD->lastInstalledColormap = colormap;
302     }
303 
304 } /* END OF FUNCTION WmInstallColormap */
305 
306 
307 
308 /*************************************<->*************************************
309  *
310  *  ResetColormapData (pCD, pWindows, count)
311  *
312  *
313  *  Description:
314  *  -----------
315  *  This function is used to release old colormap data (contexts, malloc'ed
316  *  space).
317  *
318  *
319  *  Inputs:
320  *  ------
321  *  pCD = pointer to client data (cmapWindows ...)
322  *
323  *  pWindows = new list of colormap windows
324  *
325  *  count = number of windows in new colormap windows list
326  *
327  *************************************<->***********************************/
328 
ResetColormapData(ClientData * pCD,Window * pWindows,int count)329 void ResetColormapData (ClientData *pCD, Window *pWindows, int count)
330 {
331     int i;
332 
333 
334     if (pCD->clientCmapCount)
335     {
336 	if (count == 0)
337 	{
338 	    /* reset the client colormap to the toplevel window colormap */
339 	    for (i = 0; i < pCD->clientCmapCount; i++)
340 	    {
341 		if (pCD->cmapWindows[i] == pCD->client)
342 		{
343 		    pCD->clientColormap = pCD->clientCmapList[i];
344 		    break;
345 		}
346 	    }
347 	}
348 
349 	/*
350 	 * Free up old contexts.
351 	 */
352 
353 	for (i = 0; i < pCD->clientCmapCount; i++)
354 	{
355 	    if (pCD->cmapWindows[i] != pCD->client)
356 	    {
357 #ifndef	IBM_169380
358 		RemoveColormapWindowReference(pCD, pCD->cmapWindows[i]);
359 #else
360 		XDeleteContext (DISPLAY, pCD->cmapWindows[i],
361 		    wmGD.windowContextType);
362 #endif
363 	    }
364 	}
365 
366 	/*
367 	 * Free up old colormap data.
368 	 */
369 
370 	XtFree ((char *)(pCD->cmapWindows));
371 	XtFree ((char *)(pCD->clientCmapList));
372 	pCD->clientCmapCount = 0;
373 #ifndef OLD_COLORMAP /* colormap */
374 	XtFree ((char  *)(pCD->clientCmapFlags));
375 	pCD->clientCmapFlags = 0;		/* DEBUG: */
376 	pCD->clientCmapFlagsInitialized = 0;
377 #endif
378     }
379 
380     if (count)
381     {
382 	/*
383 	 * Set new contexts.
384 	 */
385 
386 	for (i = 0; i < count; i++)
387 	{
388 	    if (pWindows[i] != pCD->client)
389 	    {
390 #ifndef	IBM_169380
391 		AddColormapWindowReference(pCD, pWindows[i]);
392 #else
393 		XSaveContext (DISPLAY, pWindows[i], wmGD.windowContextType,
394 		    (caddr_t)pCD);
395 #endif
396 	    }
397 	}
398     }
399 
400 } /* END OF FUNCTION ResetColormapData */
401 
402 #ifndef IBM_169380
403 /*************************************<->*************************************
404  *
405  *  AddColormapWindowReference (pCD, window)
406  *
407  *  Description:
408  *  -----------
409  *  This function is used to update (or create, if necessary) the structure
410  *  that keeps track of all references to a Window from a toplevel window
411  *  WM_COLORMAP_DATA property.
412  *
413  *************************************<->***********************************/
414 
AddColormapWindowReference(ClientData * pCD,Window window)415 void AddColormapWindowReference (ClientData *pCD, Window window)
416 {
417     ClientData          **cmap_window_data;
418     Boolean             context_exists;
419     int                 i;
420     ClientData          **new_cmap_window_data;
421 
422     context_exists = (!XFindContext (DISPLAY, window,
423                         wmGD.cmapWindowContextType,
424                         (XPointer *) &cmap_window_data));
425     if (context_exists)
426     {
427         for (i = 0; cmap_window_data[i] != NULL; i++)
428         {
429             if (cmap_window_data[i] == pCD)
430             {
431                 /* Reference already exists - return */
432                 return;
433             }
434         }
435         new_cmap_window_data = (ClientData **)
436                                 XtMalloc((i + 2 ) * sizeof(ClientData *));
437         memcpy((void *)new_cmap_window_data,(void *)cmap_window_data,
438                         (i + 1) * sizeof(ClientData *));
439         XtFree((char *) cmap_window_data);
440         XDeleteContext(DISPLAY, window, wmGD.cmapWindowContextType);
441     }
442     else
443     {
444         i = 0;
445         new_cmap_window_data = (ClientData **)
446                                 XtMalloc(2 * sizeof(ClientData *));
447     }
448     new_cmap_window_data[i] = pCD;
449     new_cmap_window_data[i + 1] = NULL;
450     XSaveContext (DISPLAY, window, wmGD.cmapWindowContextType,
451                         (caddr_t)new_cmap_window_data);
452 }
453 
454 /*************************************<->*************************************
455  *
456  *  RemoveColormapWindowReference (pCD, window)
457  *
458  *  Description:
459  *  -----------
460  *  This function is used to update (or delete, if necessary) the structure
461  *  that keeps track of all references to a Window from a toplevel window
462  *  WM_COLORMAP_DATA property.
463  *
464  *************************************<->***********************************/
465 
RemoveColormapWindowReference(ClientData * pCD,Window window)466 void RemoveColormapWindowReference (ClientData *pCD, Window window)
467 {
468     ClientData  **cmap_window_data;
469     Boolean     context_exists;
470     int         i;
471     int         reference_idx = -1;
472     ClientData  **new_cmap_window_data;
473 
474     context_exists = (!XFindContext (DISPLAY, window,
475                         wmGD.cmapWindowContextType,
476                         (XPointer *) &cmap_window_data));
477     if (context_exists)
478     {
479         for (i = 0; cmap_window_data[i] != NULL; i++)
480         {
481             if (cmap_window_data[i] == pCD)
482                 reference_idx = i;
483         }
484         if (reference_idx < 0)
485             return;
486 
487         if (i > 1)
488         {
489         int     j,idx;
490 
491             new_cmap_window_data = (ClientData **)
492                                         XtMalloc(i * sizeof(ClientData *));
493             idx = 0;
494             for (j = 0; cmap_window_data[j] != NULL; j++)
495             {
496                 if (j != reference_idx)
497                 {
498                     new_cmap_window_data[idx] = cmap_window_data[j];
499                     idx++;
500                 }
501             }
502             new_cmap_window_data[idx] = NULL;
503         }
504         XtFree((char *) cmap_window_data);
505         XDeleteContext(DISPLAY, window, wmGD.cmapWindowContextType);
506         if (i > 1)
507         {
508             XSaveContext (DISPLAY, window,
509                         wmGD.cmapWindowContextType,
510                         (caddr_t)new_cmap_window_data);
511         }
512     }
513 }
514 #endif	/* IBM_169380 */
515 
516 /*******************************************************************************
517  **
518  ** The rest of this module contains the SGI-added colormap handling.
519  **
520  ** mwm 1.1.3 didn't even try to deal with multiple colormaps, except to rotate
521  ** them.  We need to see that all of the colormaps from WM_COLORMAP_WINDOWS
522  ** are installed when a window gets colormap focus.
523  **
524  ** The general idea is to keep track of which colormaps bounce which other
525  ** ones, so we only flash the first time (usually not even then).
526  **
527  ** The conflict record of a window is cleared whenever:
528  **	* WM_COLORMAP_WINDOWS property changes
529  **	* ColormapNotify for a new colormap happens
530  **	* windows are rotated (prev_cmap, next_cmap)
531  ** This is because with a changed colormap list, we need to recalculate
532  ** which ones get bounced out during a full colormap installation.
533  **
534  ** We don't just lift the twm code because, after carefully looking over
535  ** the twm code, it appears to have some problems of its own.  In
536  ** particular, it assumes that if a given colormap displaces another one
537  ** once, it will always do so.  This isn't necessarily so for a multiple
538  ** hardware colormaps machine.
539  **
540  ** We still need to add code to keep track of which color maps are really
541  ** installed at any one time.  The current code is ready for this, but it has
542  ** not yet been done.  Then we could do two things:
543  **
544  **	* refrain from installing a colormap if it is already installed
545  **
546  **	* have a way to restore all installed colormaps after the window
547  **	  manager overwrites with it's own.
548  **
549  ******************************************************************************/
550 
551 
552 void
ProcessColormapList(WmScreenData * pSD,ClientData * pCD)553 ProcessColormapList (WmScreenData *pSD, ClientData *pCD)
554 
555 {
556 	register int i;
557 	XEvent event;
558 
559 
560     /*
561      * If there is no client, return.  This can happen when the root gets focus.
562      */
563 	if (!pCD) return;
564 
565     /*
566      * If the window does not have colormap focus, return.  We only install
567      * colormaps for windows with focus.  We'll get another chance when the
568      * window does get focus.
569      */
570 	if (pCD != pSD->colormapFocus) return;
571 
572     /*
573      * If window is iconified, return.
574      */
575 	if (   (pCD->clientState != NORMAL_STATE)
576 	    && (pCD->clientState != MAXIMIZED_STATE)
577 	   ) return;
578 
579     /*
580      * If the list doesn't exist, or has just a single item, no conflicts
581      * exist -- just go ahead and install the indicated colormap.
582      */
583 	if (pCD->clientCmapCount == 0) {
584 		WmInstallColormap (pSD, pCD->clientColormap);
585 		return;
586 	}
587 	if (pCD->clientCmapCount == 1) {
588 		WmInstallColormap (pSD, pCD->clientCmapList[0]);
589 		return;
590 	}
591 
592     /*
593      * If the list has already been initialized, we just need to do installs.
594      * Separate out these loops for performance, and because it isn't nice
595      * to grab the server unnecessarily.
596      *
597      * This code should also check for already-installed, once we put in that
598      * capability.
599      */
600 	if (pCD->clientCmapFlagsInitialized) {
601 
602 	    /* Do the part between the index and zero */
603 		for (i=pCD->clientCmapIndex; --i>=0; ) {
604 			if (pCD->clientCmapFlags[i] == ColormapInstalled) {
605 				WmInstallColormap (pSD, pCD->clientCmapList[i]);
606 			   }
607 		};
608 
609 	    /* Do the part from the end of the list to the index */
610 		for (i=pCD->clientCmapCount; --i>= pCD->clientCmapIndex; ) {
611 			if (pCD->clientCmapFlags[i] == ColormapInstalled) {
612 				WmInstallColormap (pSD, pCD->clientCmapList[i]);
613 			}
614 		}
615 
616 	    /**/
617 		return;
618 	}
619 
620     /*
621      * If we get this far, the list has not yet been initialized.
622      *
623      * Stabilize the input queue -- the issue is that we need to know
624      * which colormap notify install and uninstall events are ours.
625      */
626 	XGrabServer (DISPLAY);	/* Ensure no one else's events for awhile */
627 	XSync (DISPLAY, FALSE);	/* Let pending events settle */
628 	firstRequest = NextRequest (DISPLAY); /* First one that can be ours */
629 
630     /*
631      * Install the colormaps from last to first -- first is the "highest
632      * priority".  "First" is pCD->clientCmapIndex.
633      *
634      * If the list has not been proocessed before, we need to unconditionally
635      * install each colormap.  Colormap flashing is possible this once.
636      *
637      * If the list has already been processed once, all conflict checking
638      * was done then.  All we need to do this time is to install the colormaps
639      * we know we need.
640      */
641 
642 	/* Do the part between the index and zero */
643 	for (i=pCD->clientCmapIndex; --i>=0; ) {
644 		WmInstallColormap (pSD, pCD->clientCmapList[i]);
645 		pCD->clientCmapFlags[i] = ColormapInstalled;
646 	};
647 
648 	/* Do the part from the end of the list to the index */
649 	for (i=pCD->clientCmapCount; --i>= pCD->clientCmapIndex; ) {
650 		WmInstallColormap (pSD, pCD->clientCmapList[i]);
651 		pCD->clientCmapFlags[i] = ColormapInstalled;
652 	}
653 
654     /*
655      * Stabilize the input queue again -- the issue is that we need to know
656      * which colormap notify install and uninstall events we caused.
657      */
658 	XSync (DISPLAY, FALSE);			/* Let pending events settle */
659 	lastRequest = NextRequest (DISPLAY);	/* Last one that can be ours */
660 	XUngrabServer (DISPLAY);		/* Let others use it again */
661 
662     /* Process the install & uninstall events */
663 	XCheckIfEvent (DISPLAY, (XEvent *) &event, ProcessEvents, (char *)pCD);
664 
665     /* Set that the list has been processed once */
666 	pCD->clientCmapFlagsInitialized = True;
667 }
668 
669 
670 /*
671  * Look over the queue for install and uninstall events on colormap/window
672  * combinations we care about.  We don't actually disturb the queue, so
673  * events can be delivered in undisturbed order to the normal event handling
674  * routines.
675  *
676  * For each appropriate install/uninstall ColormapNotify event that is queued:
677  *	   *) if uninstall event
678  *		*) Set the conflict flag for this colormap window
679  *	   else if install event
680  *		*) Clear the conflict flag for this colormap window
681  */
682 static Bool
ProcessEvents(Display * dpy,XEvent * Event,char * c_pCD)683 ProcessEvents(Display *dpy, XEvent *Event, char *c_pCD)
684 {
685 	int i;
686 	XColormapEvent *pEvent = (XColormapEvent *) Event;
687 	ClientData *pCD = (ClientData *) c_pCD;
688 
689 	if (   (pEvent->type == ColormapNotify)
690 	    && (pEvent->serial >= firstRequest)
691 	    && (pEvent->serial <  lastRequest)
692 	    && (pEvent->colormap != None)
693 	    && (!pEvent->new)
694 	   ) {
695 		switch (pEvent->state) {
696 		case ColormapInstalled:
697 			for (i=0; i<pCD->clientCmapCount; i++) {
698 				if (  (pCD->clientCmapList[i]==pEvent->colormap)
699 				    &&(pCD->cmapWindows[i]==pEvent->window)
700 				   ) {
701 					pCD->clientCmapFlags[i]
702 						= ColormapInstalled;
703 					break;
704 				}
705 			}
706 			break;
707 		case ColormapUninstalled:
708 			for (i=0; i<pCD->clientCmapCount; i++) {
709 				if (  (pCD->clientCmapList[i]==pEvent->colormap)
710 				    &&(pCD->cmapWindows[i]==pEvent->window)
711 				   ) {
712 					pCD->clientCmapFlags[i]
713 						= ColormapUninstalled;
714 					break;
715 				}
716 			}
717 			break;
718 		default:		/* Should never get here */
719 			break;
720 		}
721 	}
722 
723     /*
724      * Always return false:
725      *	* so that we get to search the entire queue -- it isn't very long
726      *	* so all events remain on the queue to be handled normally elsewhere
727      */
728 	return False;	/* Always, so no events are lost from the queue */
729 }
730