colormaps.c revision 0bbfda8a
1/*
2 * Colormap handling
3 */
4
5#include "ctwm.h"
6
7#include <stdio.h>
8#include <stdlib.h>
9
10#include "colormaps.h"
11#include "screen.h"
12
13
14/*
15 * From events.c; imported manually since I'm not listing it in events.h
16 * because nowhere but here needs it.
17 */
18extern bool ColortableThrashing;
19
20static Bool UninstallRootColormapQScanner(Display *display, XEvent *ev,
21                char *args);
22
23
24/***********************************************************************
25 *
26 *  Procedure:
27 *      InstallWindowColormaps - install the colormaps for one twm window
28 *
29 *  Inputs:
30 *      type    - type of event that caused the installation
31 *      tmp     - for a subset of event types, the address of the
32 *                window structure, whose colormaps are to be installed.
33 *
34 ***********************************************************************
35 *
36 * Previously in events.c
37 */
38bool
39InstallWindowColormaps(int type, TwmWindow *tmp)
40{
41	if(tmp) {
42		return InstallColormaps(type, &tmp->cmaps);
43	}
44	else {
45		return InstallColormaps(type, NULL);
46	}
47}
48
49
50bool
51InstallColormaps(int type, Colormaps *cmaps)
52{
53	int i, j, n, number_cwins, state;
54	ColormapWindow **cwins, *cwin, **maxcwin = NULL;
55	TwmColormap *cmap;
56	char *row, *scoreboard;
57
58	switch(type) {
59		case EnterNotify:
60		case LeaveNotify:
61		case DestroyNotify:
62		default:
63			/* Save the colormap to be loaded for when force loading of
64			 * root colormap(s) ends.
65			 */
66			Scr->cmapInfo.pushed_cmaps = cmaps;
67			/* Don't load any new colormap if root colormap(s) has been
68			 * force loaded.
69			 */
70			if(Scr->cmapInfo.root_pushes) {
71				return false;
72			}
73			/* Don't reload the current window colormap list.
74			if (Scr->cmapInfo.cmaps == cmaps)
75			    return false;
76			 */
77			if(Scr->cmapInfo.cmaps) {
78				for(i = Scr->cmapInfo.cmaps->number_cwins,
79				                cwins = Scr->cmapInfo.cmaps->cwins; i-- > 0; cwins++) {
80					(*cwins)->colormap->state &= ~CM_INSTALLABLE;
81				}
82			}
83			Scr->cmapInfo.cmaps = cmaps;
84			break;
85
86		case PropertyNotify:
87		case VisibilityNotify:
88		case ColormapNotify:
89			break;
90	}
91
92	number_cwins = Scr->cmapInfo.cmaps->number_cwins;
93	cwins = Scr->cmapInfo.cmaps->cwins;
94	scoreboard = Scr->cmapInfo.cmaps->scoreboard;
95
96	ColortableThrashing = false; /* in case installation aborted */
97
98	state = CM_INSTALLED;
99
100	for(i = n = 0; i < number_cwins; i++) {
101		cwins[i]->colormap->state &= ~CM_INSTALL;
102	}
103	for(i = n = 0; i < number_cwins && n < Scr->cmapInfo.maxCmaps; i++) {
104		cwin = cwins[i];
105		cmap = cwin->colormap;
106		if(cmap->state & CM_INSTALL) {
107			continue;
108		}
109		cmap->state |= CM_INSTALLABLE;
110		cmap->w = cwin->w;
111		if(cwin->visibility != VisibilityFullyObscured) {
112			row = scoreboard + (i * (i - 1) / 2);
113			for(j = 0; j < i; j++)
114				if(row[j] && (cwins[j]->colormap->state & CM_INSTALL)) {
115					break;
116				}
117			if(j != i) {
118				continue;
119			}
120			n++;
121			maxcwin = &cwins[i];
122			state &= (cmap->state & CM_INSTALLED);
123			cmap->state |= CM_INSTALL;
124		}
125	}
126	Scr->cmapInfo.first_req = NextRequest(dpy);
127
128	for(; n > 0 && maxcwin >= &cwins[0]; maxcwin--) {
129		cmap = (*maxcwin)->colormap;
130		if(cmap->state & CM_INSTALL) {
131			cmap->state &= ~CM_INSTALL;
132			if(!(state & CM_INSTALLED)) {
133				cmap->install_req = NextRequest(dpy);
134				/* printf ("XInstallColormap : %x, %x\n", cmap, cmap->c); */
135				XInstallColormap(dpy, cmap->c);
136			}
137			cmap->state |= CM_INSTALLED;
138			n--;
139		}
140	}
141	return true;
142}
143
144
145
146/***********************************************************************
147 *
148 *  Procedures:
149 *      <Uni/I>nstallRootColormap - Force (un)loads root colormap(s)
150 *
151 *         These matching routines provide a mechanism to insure that
152 *         the root colormap(s) is installed during operations like
153 *         rubber banding or menu display that require colors from
154 *         that colormap.  Calls may be nested arbitrarily deeply,
155 *         as long as there is one UninstallRootColormap call per
156 *         InstallRootColormap call.
157 *
158 *         The final UninstallRootColormap will cause the colormap list
159 *         which would otherwise have be loaded to be loaded, unless
160 *         Enter or Leave Notify events are queued, indicating some
161 *         other colormap list would potentially be loaded anyway.
162 ***********************************************************************
163 *
164 * Previously in events.c
165 */
166void
167InstallRootColormap(void)
168{
169	Colormaps *tmp;
170	if(Scr->cmapInfo.root_pushes == 0) {
171		/*
172		 * The saving and restoring of cmapInfo.pushed_window here
173		 * is a slimy way to remember the actual pushed list and
174		 * not that of the root window.
175		 */
176		tmp = Scr->cmapInfo.pushed_cmaps;
177		InstallColormaps(0, &Scr->RootColormaps);
178		Scr->cmapInfo.pushed_cmaps = tmp;
179	}
180	Scr->cmapInfo.root_pushes++;
181}
182
183
184/* ARGSUSED*/
185static Bool
186UninstallRootColormapQScanner(Display *display, XEvent *ev,
187                              char *args)
188{
189	if(!*args) {
190		if(ev->type == EnterNotify) {
191			if(ev->xcrossing.mode != NotifyGrab) {
192				*args = 1;
193			}
194		}
195		else if(ev->type == LeaveNotify) {
196			if(ev->xcrossing.mode == NotifyNormal) {
197				*args = 1;
198			}
199		}
200	}
201
202	return (False);
203}
204
205
206void
207UninstallRootColormap(void)
208{
209	char args;
210	XEvent dummy;
211
212	if(Scr->cmapInfo.root_pushes) {
213		Scr->cmapInfo.root_pushes--;
214	}
215
216	if(!Scr->cmapInfo.root_pushes) {
217		/*
218		 * If we have subsequent Enter or Leave Notify events,
219		 * we can skip the reload of pushed colormaps.
220		 */
221		XSync(dpy, 0);
222		args = 0;
223		XCheckIfEvent(dpy, &dummy, UninstallRootColormapQScanner, &args);
224
225		if(!args) {
226			InstallColormaps(0, Scr->cmapInfo.pushed_cmaps);
227		}
228	}
229}
230
231
232/*
233 * Create a TwmColormap struct and tie it to an [X] Colormap.  Places
234 * that need to mess with colormaps and look up the metainfo we hang off
235 * them need to look this up and find it via the X Context.
236 *
237 * Previously in add_window.c
238 */
239TwmColormap *
240CreateTwmColormap(Colormap c)
241{
242	TwmColormap *cmap;
243	cmap = malloc(sizeof(TwmColormap));
244	if(!cmap || XSaveContext(dpy, c, ColormapContext, (XPointer) cmap)) {
245		if(cmap) {
246			free(cmap);
247		}
248		return (NULL);
249	}
250	cmap->c = c;
251	cmap->state = 0;
252	cmap->install_req = 0;
253	cmap->w = None;
254	cmap->refcnt = 1;
255	return (cmap);
256}
257
258
259/*
260 * Put together a ColormapWindow struct.  This is a thing we hang off a
261 * TwmWindow for some colormap tracking stuff.
262 *
263 * Previously in add_window.c
264 */
265ColormapWindow *
266CreateColormapWindow(Window w, bool creating_parent, bool property_window)
267{
268	ColormapWindow *cwin;
269	TwmColormap *cmap;
270	XWindowAttributes attributes;
271
272	cwin = malloc(sizeof(ColormapWindow));
273	if(cwin) {
274		if(!XGetWindowAttributes(dpy, w, &attributes) ||
275		                XSaveContext(dpy, w, ColormapContext, (XPointer) cwin)) {
276			free(cwin);
277			return (NULL);
278		}
279
280		if(XFindContext(dpy, attributes.colormap,  ColormapContext,
281		                (XPointer *)&cwin->colormap) == XCNOENT) {
282			cwin->colormap = cmap = CreateTwmColormap(attributes.colormap);
283			if(!cmap) {
284				XDeleteContext(dpy, w, ColormapContext);
285				free(cwin);
286				return (NULL);
287			}
288		}
289		else {
290			cwin->colormap->refcnt++;
291		}
292
293		cwin->w = w;
294		/*
295		 * Assume that windows in colormap list are
296		 * obscured if we are creating the parent window.
297		 * Otherwise, we assume they are unobscured.
298		 */
299		cwin->visibility = creating_parent ?
300		                   VisibilityPartiallyObscured : VisibilityUnobscured;
301		cwin->refcnt = 1;
302
303		/*
304		 * If this is a ColormapWindow property window and we
305		 * are not monitoring ColormapNotify or VisibilityNotify
306		 * events, we need to.
307		 */
308		if(property_window &&
309		                (attributes.your_event_mask &
310		                 (ColormapChangeMask | VisibilityChangeMask)) !=
311		                (ColormapChangeMask | VisibilityChangeMask)) {
312			XSelectInput(dpy, w, attributes.your_event_mask |
313			             (ColormapChangeMask | VisibilityChangeMask));
314		}
315	}
316
317	return (cwin);
318}
319
320
321/*
322 * Do something with looking up stuff from WM_COLORMAPS_WINDOWS (relating
323 * to windows with their own colormap) and finding or putting this window
324 * into it.
325 *
326 * XXX Someone should figure it out better than that...
327 *
328 * Previously in add_window.c
329 */
330void
331FetchWmColormapWindows(TwmWindow *tmp)
332{
333	int i, j;
334	Window *cmap_windows = NULL;
335	bool can_free_cmap_windows = false;
336	int number_cmap_windows = 0;
337	ColormapWindow **cwins = NULL;
338	bool previnst;
339
340	number_cmap_windows = 0;
341
342	previnst = (Scr->cmapInfo.cmaps == &tmp->cmaps && tmp->cmaps.number_cwins);
343	if(previnst) {
344		cwins = tmp->cmaps.cwins;
345		for(i = 0; i < tmp->cmaps.number_cwins; i++) {
346			cwins[i]->colormap->state = 0;
347		}
348	}
349
350	if(XGetWMColormapWindows(dpy, tmp->w, &cmap_windows,
351	                         &number_cmap_windows) &&
352	                number_cmap_windows > 0) {
353
354		/*
355		 * check if the top level is in the list, add to front if not
356		 */
357		for(i = 0; i < number_cmap_windows; i++) {
358			if(cmap_windows[i] == tmp->w) {
359				break;
360			}
361		}
362		if(i == number_cmap_windows) {   /* not in list */
363			Window *new_cmap_windows =
364			        calloc((number_cmap_windows + 1), sizeof(Window));
365
366			if(!new_cmap_windows) {
367				fprintf(stderr,
368				        "%s:  unable to allocate %d element colormap window array\n",
369				        ProgramName, number_cmap_windows + 1);
370				goto done;
371			}
372			new_cmap_windows[0] = tmp->w;  /* add to front */
373			for(i = 0; i < number_cmap_windows; i++) {   /* append rest */
374				new_cmap_windows[i + 1] = cmap_windows[i];
375			}
376			XFree(cmap_windows);
377			can_free_cmap_windows = true;  /* do not use XFree any more */
378			cmap_windows = new_cmap_windows;
379			number_cmap_windows++;
380		}
381
382		cwins = calloc(number_cmap_windows, sizeof(ColormapWindow *));
383		if(cwins) {
384			for(i = 0; i < number_cmap_windows; i++) {
385
386				/*
387				 * Copy any existing entries into new list.
388				 */
389				for(j = 0; j < tmp->cmaps.number_cwins; j++) {
390					if(tmp->cmaps.cwins[j]->w == cmap_windows[i]) {
391						cwins[i] = tmp->cmaps.cwins[j];
392						cwins[i]->refcnt++;
393						break;
394					}
395				}
396
397				/*
398				 * If the colormap window is not being pointed by
399				 * some other applications colormap window list,
400				 * create a new entry.
401				 */
402				if(j == tmp->cmaps.number_cwins) {
403					if(XFindContext(dpy, cmap_windows[i], ColormapContext,
404					                (XPointer *)&cwins[i]) == XCNOENT) {
405						if((cwins[i] = CreateColormapWindow(cmap_windows[i],
406						                                    tmp->cmaps.number_cwins == 0,
407						                                    true)) == NULL) {
408							int k;
409							for(k = i + 1; k < number_cmap_windows; k++) {
410								cmap_windows[k - 1] = cmap_windows[k];
411							}
412							i--;
413							number_cmap_windows--;
414						}
415					}
416					else {
417						cwins[i]->refcnt++;
418					}
419				}
420			}
421		}
422	}
423
424	/* No else here, in case we bailed out of clause above.
425	 */
426	if(number_cmap_windows == 0) {
427
428		number_cmap_windows = 1;
429
430		cwins = malloc(sizeof(ColormapWindow *));
431		if(XFindContext(dpy, tmp->w, ColormapContext, (XPointer *)&cwins[0]) ==
432		                XCNOENT)
433			cwins[0] = CreateColormapWindow(tmp->w,
434			                                tmp->cmaps.number_cwins == 0, false);
435		else {
436			cwins[0]->refcnt++;
437		}
438	}
439
440	if(tmp->cmaps.number_cwins) {
441		free_cwins(tmp);
442	}
443
444	tmp->cmaps.cwins = cwins;
445	tmp->cmaps.number_cwins = number_cmap_windows;
446	if(number_cmap_windows > 1)
447		tmp->cmaps.scoreboard =
448		        calloc(1, ColormapsScoreboardLength(&tmp->cmaps));
449
450	if(previnst) {
451		InstallColormaps(PropertyNotify, NULL);
452	}
453
454done:
455	if(cmap_windows) {
456		if(can_free_cmap_windows) {
457			free(cmap_windows);
458		}
459		else {
460			XFree(cmap_windows);
461		}
462	}
463}
464
465
466/*
467 * BumpWindowColormap - rotate our internal copy of WM_COLORMAP_WINDOWS.
468 * This is the backend for f.colormap.
469 *
470 * Previously in functions.c
471 */
472void
473BumpWindowColormap(TwmWindow *tmp, int inc)
474{
475	int i, j;
476	bool previously_installed;
477	ColormapWindow **cwins;
478
479	if(!tmp) {
480		return;
481	}
482
483	if(inc && tmp->cmaps.number_cwins > 0) {
484		cwins = calloc(tmp->cmaps.number_cwins, sizeof(ColormapWindow *));
485		if(cwins) {
486			previously_installed = (Scr->cmapInfo.cmaps == &tmp->cmaps &&
487			                        tmp->cmaps.number_cwins);
488			if(previously_installed) {
489				for(i = tmp->cmaps.number_cwins; i-- > 0;) {
490					tmp->cmaps.cwins[i]->colormap->state = 0;
491				}
492			}
493
494			for(i = 0; i < tmp->cmaps.number_cwins; i++) {
495				j = i - inc;
496				if(j >= tmp->cmaps.number_cwins) {
497					j -= tmp->cmaps.number_cwins;
498				}
499				else if(j < 0) {
500					j += tmp->cmaps.number_cwins;
501				}
502				cwins[j] = tmp->cmaps.cwins[i];
503			}
504
505			free(tmp->cmaps.cwins);
506
507			tmp->cmaps.cwins = cwins;
508
509			if(tmp->cmaps.number_cwins > 1)
510				memset(tmp->cmaps.scoreboard, 0,
511				       ColormapsScoreboardLength(&tmp->cmaps));
512
513			if(previously_installed) {
514				InstallColormaps(PropertyNotify, NULL);
515			}
516		}
517	}
518	else {
519		FetchWmColormapWindows(tmp);
520	}
521	return;
522}
523
524
525/*
526 * Handlers for creating and linkin in, as well as deleting, a StdCmap
527 * for a given XStandardColormap.
528 *
529 * Previously in util.c
530 */
531void
532InsertRGBColormap(Atom a, XStandardColormap *maps, int nmaps,
533                  bool replace)
534{
535	StdCmap *sc = NULL;
536
537	if(replace) {                       /* locate existing entry */
538		for(sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
539			if(sc->atom == a) {
540				break;
541			}
542		}
543	}
544
545	if(!sc) {                           /* no existing, allocate new */
546		sc = calloc(1, sizeof(StdCmap));
547		if(!sc) {
548			fprintf(stderr, "%s:  unable to allocate %lu bytes for StdCmap\n",
549			        ProgramName, (unsigned long) sizeof(StdCmap));
550			return;
551		}
552	}
553
554	if(replace) {                       /* just update contents */
555		if(sc->maps) {
556			XFree(maps);
557		}
558		if(sc == Scr->StdCmapInfo.mru) {
559			Scr->StdCmapInfo.mru = NULL;
560		}
561	}
562	else {                              /* else appending */
563		sc->next = NULL;
564		sc->atom = a;
565		if(Scr->StdCmapInfo.tail) {
566			Scr->StdCmapInfo.tail->next = sc;
567		}
568		else {
569			Scr->StdCmapInfo.head = sc;
570		}
571		Scr->StdCmapInfo.tail = sc;
572	}
573	sc->nmaps = nmaps;
574	sc->maps = maps;
575
576	return;
577}
578
579
580void
581RemoveRGBColormap(Atom a)
582{
583	StdCmap *sc, *prev;
584
585	prev = NULL;
586	for(sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
587		if(sc->atom == a) {
588			break;
589		}
590		prev = sc;
591	}
592	if(sc) {                            /* found one */
593		if(sc->maps) {
594			XFree(sc->maps);
595		}
596		if(prev) {
597			prev->next = sc->next;
598		}
599		if(Scr->StdCmapInfo.head == sc) {
600			Scr->StdCmapInfo.head = sc->next;
601		}
602		if(Scr->StdCmapInfo.tail == sc) {
603			Scr->StdCmapInfo.tail = prev;
604		}
605		if(Scr->StdCmapInfo.mru == sc) {
606			Scr->StdCmapInfo.mru = NULL;
607		}
608	}
609	return;
610}
611
612
613/*
614 * Go through all the properties of the root window and setup
615 * XStandardColormap's and our StdCmap's for any of them that are
616 * actually RGB_COLORMAP types.  We're not actually _checking_ the types,
617 * just letting XGetRGBColormaps() refuse to handle probably most of
618 * them.  Called during startup.
619 *
620 * Previouly in util.c
621 */
622void
623LocateStandardColormaps(void)
624{
625	Atom *atoms;
626	int natoms;
627	int i;
628
629	atoms = XListProperties(dpy, Scr->Root, &natoms);
630	for(i = 0; i < natoms; i++) {
631		XStandardColormap *maps = NULL;
632		int nmaps;
633
634		if(XGetRGBColormaps(dpy, Scr->Root, &maps, &nmaps, atoms[i])) {
635			/* if got one, then append to current list */
636			InsertRGBColormap(atoms[i], maps, nmaps, false);
637		}
638	}
639	if(atoms) {
640		XFree(atoms);
641	}
642	return;
643}
644
645
646/*
647 * Clear out and free TwmWindow.cmaps (struct Colormaps) bits for a window.
648 *
649 * Previously in events.c
650 */
651void
652free_cwins(TwmWindow *tmp)
653{
654	int i;
655	TwmColormap *cmap;
656
657	if(tmp->cmaps.number_cwins) {
658		for(i = 0; i < tmp->cmaps.number_cwins; i++) {
659			if(--tmp->cmaps.cwins[i]->refcnt == 0) {
660				cmap = tmp->cmaps.cwins[i]->colormap;
661				if(--cmap->refcnt == 0) {
662					XDeleteContext(dpy, cmap->c, ColormapContext);
663					free(cmap);
664				}
665				XDeleteContext(dpy, tmp->cmaps.cwins[i]->w, ColormapContext);
666				free(tmp->cmaps.cwins[i]);
667			}
668		}
669		free(tmp->cmaps.cwins);
670		if(tmp->cmaps.number_cwins > 1) {
671			free(tmp->cmaps.scoreboard);
672			tmp->cmaps.scoreboard = NULL;
673		}
674		tmp->cmaps.number_cwins = 0;
675	}
676}
677