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