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