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