1/* 2 * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. 3 * 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining 7 * a copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation on the rights to use, copy, modify, merge, 10 * publish, distribute, sublicense, and/or sell copies of the Software, 11 * and to permit persons to whom the Software is furnished to do so, 12 * subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial 16 * portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS 22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 * SOFTWARE. 26 */ 27 28/* 29 * Author: 30 * Rickard E. (Rik) Faith <faith@redhat.com> 31 * Kevin E. Martin <kem@redhat.com> 32 * 33 */ 34 35/** \file 36 * This file provides the only interface to the X server extension support 37 * in programs/Xserver/Xext. Those programs should only include dmxext.h 38 */ 39 40#ifdef HAVE_DMX_CONFIG_H 41#include <dmx-config.h> 42#endif 43 44#include <stdlib.h> 45 46#include "dmx.h" 47#include "dmxinit.h" 48#include "dmxextension.h" 49#include "dmxwindow.h" 50#include "dmxcb.h" 51#include "dmxcursor.h" 52#include "dmxpixmap.h" 53#include "dmxgc.h" 54#include "dmxfont.h" 55#include "dmxcmap.h" 56#include "dmxpict.h" 57#include "dmxinput.h" 58#include "dmxsync.h" 59#include "dmxscrinit.h" 60#include "input/dmxinputinit.h" 61 62#include "windowstr.h" 63#include "inputstr.h" /* For DeviceIntRec */ 64#include <X11/extensions/dmxproto.h> /* For DMX_BAD_* */ 65#include "cursorstr.h" 66 67/* The default font is declared in dix/globals.c, but is not included in 68 * _any_ header files. */ 69extern FontPtr defaultFont; 70 71/** This routine provides information to the DMX protocol extension 72 * about a particular screen. */ 73Bool dmxGetScreenAttributes(int physical, DMXScreenAttributesPtr attr) 74{ 75 DMXScreenInfo *dmxScreen; 76 77 if (physical < 0 || physical >= dmxNumScreens) return FALSE; 78 79 dmxScreen = &dmxScreens[physical]; 80 attr->displayName = dmxScreen->name; 81#ifdef PANORAMIX 82 attr->logicalScreen = noPanoramiXExtension ? dmxScreen->index : 0; 83#else 84 attr->logicalScreen = dmxScreen->index; 85#endif 86 87 attr->screenWindowWidth = dmxScreen->scrnWidth; 88 attr->screenWindowHeight = dmxScreen->scrnHeight; 89 attr->screenWindowXoffset = dmxScreen->scrnX; 90 attr->screenWindowYoffset = dmxScreen->scrnY; 91 92 attr->rootWindowWidth = dmxScreen->rootWidth; 93 attr->rootWindowHeight = dmxScreen->rootHeight; 94 attr->rootWindowXoffset = dmxScreen->rootX; 95 attr->rootWindowYoffset = dmxScreen->rootY; 96 97 attr->rootWindowXorigin = dmxScreen->rootXOrigin; 98 attr->rootWindowYorigin = dmxScreen->rootYOrigin; 99 100 return TRUE; 101} 102 103/** This routine provides information to the DMX protocol extension 104 * about a particular window. */ 105Bool dmxGetWindowAttributes(WindowPtr pWindow, DMXWindowAttributesPtr attr) 106{ 107 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWindow); 108 109 attr->screen = pWindow->drawable.pScreen->myNum; 110 attr->window = pWinPriv->window; 111 112 attr->pos.x = pWindow->drawable.x; 113 attr->pos.y = pWindow->drawable.y; 114 attr->pos.width = pWindow->drawable.width; 115 attr->pos.height = pWindow->drawable.height; 116 117 if (!pWinPriv->window || pWinPriv->offscreen) { 118 attr->vis.x = 0; 119 attr->vis.y = 0; 120 attr->vis.height = 0; 121 attr->vis.width = 0; 122 return pWinPriv->window ? TRUE : FALSE; 123 } 124 125 /* Compute display-relative coordinates */ 126 attr->vis.x = pWindow->drawable.x; 127 attr->vis.y = pWindow->drawable.y; 128 attr->vis.width = pWindow->drawable.width; 129 attr->vis.height = pWindow->drawable.height; 130 131 if (attr->pos.x < 0) { 132 attr->vis.x -= attr->pos.x; 133 attr->vis.width = attr->pos.x + attr->pos.width - attr->vis.x; 134 } 135 if (attr->pos.x + attr->pos.width > pWindow->drawable.pScreen->width) { 136 if (attr->pos.x < 0) 137 attr->vis.width = pWindow->drawable.pScreen->width; 138 else 139 attr->vis.width = pWindow->drawable.pScreen->width - attr->pos.x; 140 } 141 if (attr->pos.y < 0) { 142 attr->vis.y -= attr->pos.y; 143 attr->vis.height = attr->pos.y + attr->pos.height - attr->vis.y; 144 } 145 if (attr->pos.y + attr->pos.height > pWindow->drawable.pScreen->height) { 146 if (attr->pos.y < 0) 147 attr->vis.height = pWindow->drawable.pScreen->height; 148 else 149 attr->vis.height = pWindow->drawable.pScreen->height - attr->pos.y; 150 } 151 152 /* Convert to window-relative coordinates */ 153 attr->vis.x -= attr->pos.x; 154 attr->vis.y -= attr->pos.y; 155 156 return TRUE; 157} 158 159void dmxGetDesktopAttributes(DMXDesktopAttributesPtr attr) 160{ 161 attr->width = dmxGlobalWidth; 162 attr->height = dmxGlobalHeight; 163 attr->shiftX = 0; /* NOTE: The upper left hand corner of */ 164 attr->shiftY = 0; /* the desktop is always <0,0>. */ 165} 166 167/** Return the total number of devices, not just #dmxNumInputs. The 168 * number returned should be the same as that returned by 169 * XListInputDevices. */ 170int dmxGetInputCount(void) 171{ 172 int i, total; 173 174 for (total = i = 0; i < dmxNumInputs; i++) total += dmxInputs[i].numDevs; 175 return total; 176} 177 178/** Return information about the device with id = \a deviceId. This 179 * information is primarily for the #ProcDMXGetInputAttributes() 180 * function, which does not have access to the appropriate data 181 * structure. */ 182int dmxGetInputAttributes(int deviceId, DMXInputAttributesPtr attr) 183{ 184 int i, j; 185 DMXInputInfo *dmxInput; 186 187 if (deviceId < 0) return -1; 188 for (i = 0; i < dmxNumInputs; i++) { 189 dmxInput = &dmxInputs[i]; 190 for (j = 0; j < dmxInput->numDevs; j++) { 191 DMXLocalInputInfoPtr dmxLocal = dmxInput->devs[j]; 192 if (deviceId != dmxLocal->pDevice->id) continue; 193 attr->isCore = !!dmxLocal->isCore; 194 attr->sendsCore = !!dmxLocal->sendsCore; 195 attr->detached = !!dmxInput->detached; 196 attr->physicalScreen = -1; 197 attr->physicalId = -1; 198 attr->name = NULL; 199 switch (dmxLocal->extType) { 200 case DMX_LOCAL_TYPE_LOCAL: 201 attr->inputType = 0; 202 break; 203 case DMX_LOCAL_TYPE_CONSOLE: 204 attr->inputType = 1; 205 attr->name = dmxInput->name; 206 attr->physicalId = dmxLocal->deviceId; 207 break; 208 case DMX_LOCAL_TYPE_BACKEND: 209 case DMX_LOCAL_TYPE_COMMON: 210 attr->inputType = 2; 211 attr->physicalScreen = dmxInput->scrnIdx; 212 attr->name = dmxInput->name; 213 attr->physicalId = dmxLocal->deviceId; 214 break; 215 } 216 return 0; /* Success */ 217 } 218 } 219 return -1; /* Failure */ 220} 221 222/** Reinitialized the cursor boundaries. */ 223static void dmxAdjustCursorBoundaries(void) 224{ 225 int i; 226 227 dmxReInitOrigins(); 228 dmxInitOverlap(); 229 dmxComputeWidthHeight(DMX_NO_RECOMPUTE_BOUNDING_BOX); 230 dmxConnectionBlockCallback(); 231 for (i = 0; i < dmxNumInputs; i++) { 232 DMXInputInfo *dmxInput = &dmxInputs[i]; 233 if (!dmxInput->detached) dmxInputReInit(dmxInput); 234 } 235 236 dmxCheckCursor(); 237 238 for (i = 0; i < dmxNumInputs; i++) { 239 DMXInputInfo *dmxInput = &dmxInputs[i]; 240 if (!dmxInput->detached) dmxInputLateReInit(dmxInput); 241 } 242} 243 244/** Add an input with the specified attributes. If the input is added, 245 * the physical id is returned in \a deviceId. */ 246int dmxAddInput(DMXInputAttributesPtr attr, int *id) 247{ 248 int retcode = BadValue; 249 250 if (attr->inputType == 1) /* console */ 251 retcode = dmxInputAttachConsole(attr->name, attr->sendsCore, id); 252 else if (attr->inputType == 2) /* backend */ 253 retcode = dmxInputAttachBackend(attr->physicalScreen, 254 attr->sendsCore,id); 255 256 if (retcode == Success) { 257 /* Adjust the cursor boundaries */ 258 dmxAdjustCursorBoundaries(); 259 260 /* Force completion of the changes */ 261 dmxSync(NULL, TRUE); 262 } 263 264 return retcode; 265} 266 267/** Remove the input with physical id \a id. */ 268int dmxRemoveInput(int id) 269{ 270 return dmxInputDetachId(id); 271} 272 273/** Return the value of #dmxNumScreens -- the total number of backend 274 * screens in use (these are logical screens and may be larger than the 275 * number of backend displays). */ 276unsigned long dmxGetNumScreens(void) 277{ 278 return dmxNumScreens; 279} 280 281/** Make sure that #dmxCreateAndRealizeWindow has been called for \a 282 * pWindow. */ 283void dmxForceWindowCreation(WindowPtr pWindow) 284{ 285 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWindow); 286 if (!pWinPriv->window) dmxCreateAndRealizeWindow(pWindow, TRUE); 287} 288 289/** Flush pending syncs for all screens. */ 290void dmxFlushPendingSyncs(void) 291{ 292 dmxSync(NULL, TRUE); 293} 294 295/** Update DMX's screen resources to match those of the newly moved 296 * and/or resized "root" window. */ 297void dmxUpdateScreenResources(ScreenPtr pScreen, int x, int y, int w, int h) 298{ 299 DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; 300 WindowPtr pRoot = pScreen->root; 301 WindowPtr pChild; 302 Bool anyMarked = FALSE; 303 304 /* Handle special case where width and/or height are zero */ 305 if (w == 0 || h == 0) { 306 w = 1; 307 h = 1; 308 } 309 310 /* Change screen size */ 311 pScreen->width = w; 312 pScreen->height = h; 313 314 /* Reset the root window's drawable's size */ 315 pRoot->drawable.width = w; 316 pRoot->drawable.height = h; 317 318 /* Set the root window's new winSize and borderSize */ 319 pRoot->winSize.extents.x1 = 0; 320 pRoot->winSize.extents.y1 = 0; 321 pRoot->winSize.extents.x2 = w; 322 pRoot->winSize.extents.y2 = h; 323 324 pRoot->borderSize.extents.x1 = 0; 325 pRoot->borderSize.extents.y1 = 0; 326 pRoot->borderSize.extents.x2 = w; 327 pRoot->borderSize.extents.y2 = h; 328 329 /* Recompute this screen's mmWidth & mmHeight */ 330 pScreen->mmWidth = 331 (w * 254 + dmxScreen->beXDPI * 5) / (dmxScreen->beXDPI * 10); 332 pScreen->mmHeight = 333 (h * 254 + dmxScreen->beYDPI * 5) / (dmxScreen->beYDPI * 10); 334 335 /* Recompute this screen's window's clip rects as follows: */ 336 /* 1. Mark all of root's children's windows */ 337 for (pChild = pRoot->firstChild; pChild; pChild = pChild->nextSib) 338 anyMarked |= pScreen->MarkOverlappedWindows(pChild, pChild, 339 (WindowPtr *)NULL); 340 341 /* 2. Set the root window's borderClip */ 342 pRoot->borderClip.extents.x1 = 0; 343 pRoot->borderClip.extents.y1 = 0; 344 pRoot->borderClip.extents.x2 = w; 345 pRoot->borderClip.extents.y2 = h; 346 347 /* 3. Set the root window's clipList */ 348 if (anyMarked) { 349 /* If any windows have been marked, set the root window's 350 * clipList to be broken since it will be recalculated in 351 * ValidateTree() 352 */ 353 RegionBreak(&pRoot->clipList); 354 } else { 355 /* Otherwise, we just set it directly since there are no 356 * windows visible on this screen 357 */ 358 pRoot->clipList.extents.x1 = 0; 359 pRoot->clipList.extents.y1 = 0; 360 pRoot->clipList.extents.x2 = w; 361 pRoot->clipList.extents.y2 = h; 362 } 363 364 /* 4. Revalidate all clip rects and generate expose events */ 365 if (anyMarked) { 366 pScreen->ValidateTree(pRoot, NULL, VTBroken); 367 pScreen->HandleExposures(pRoot); 368 if (pScreen->PostValidateTree) 369 pScreen->PostValidateTree(pRoot, NULL, VTBroken); 370 } 371} 372 373#ifdef PANORAMIX 374#include "panoramiXsrv.h" 375 376/** Change the "screen" window attributes by resizing the actual window 377 * on the back-end display (if necessary). */ 378static void dmxConfigureScreenWindow(int idx, 379 int x, int y, int w, int h) 380{ 381 DMXScreenInfo *dmxScreen = &dmxScreens[idx]; 382 ScreenPtr pScreen = screenInfo.screens[idx]; 383 384 /* Resize "screen" window */ 385 if (dmxScreen->scrnX != x || 386 dmxScreen->scrnY != y || 387 dmxScreen->scrnWidth != w || 388 dmxScreen->scrnHeight != h) { 389 dmxResizeScreenWindow(pScreen, x, y, w, h); 390 } 391 392 /* Change "screen" window values */ 393 dmxScreen->scrnX = x; 394 dmxScreen->scrnY = y; 395 dmxScreen->scrnWidth = w; 396 dmxScreen->scrnHeight = h; 397} 398 399/** Change the "root" window position and size by resizing the actual 400 * window on the back-end display (if necessary) and updating all of 401 * DMX's resources by calling #dmxUpdateScreenResources. */ 402static void dmxConfigureRootWindow(int idx, int x, int y, int w, int h) 403{ 404 DMXScreenInfo *dmxScreen = &dmxScreens[idx]; 405 WindowPtr pRoot = screenInfo.screens[idx]->root; 406 407 /* NOTE: Either this function or the ones that it calls must handle 408 * the case where w == 0 || h == 0. Currently, the functions that 409 * this one calls handle that case. */ 410 411 /* 1. Resize "root" window */ 412 if (dmxScreen->rootX != x || 413 dmxScreen->rootY != y || 414 dmxScreen->rootWidth != w || 415 dmxScreen->rootHeight != h) { 416 dmxResizeRootWindow(pRoot, x, y, w, h); 417 } 418 419 /* 2. Update all of the screen's resources associated with this root 420 * window */ 421 if (dmxScreen->rootWidth != w || 422 dmxScreen->rootHeight != h) { 423 dmxUpdateScreenResources(screenInfo.screens[idx], x, y, w, h); 424 } 425 426 /* Change "root" window values */ 427 dmxScreen->rootX = x; 428 dmxScreen->rootY = y; 429 dmxScreen->rootWidth = w; 430 dmxScreen->rootHeight = h; 431} 432 433/** Change the "root" window's origin by updating DMX's internal data 434 * structures (dix and Xinerama) to use the new origin and adjust the 435 * positions of windows that overlap this "root" window. */ 436static void dmxSetRootWindowOrigin(int idx, int x, int y) 437{ 438 DMXScreenInfo *dmxScreen = &dmxScreens[idx]; 439 ScreenPtr pScreen = screenInfo.screens[idx]; 440 WindowPtr pRoot = pScreen->root; 441 WindowPtr pChild; 442 int xoff; 443 int yoff; 444 445 /* Change "root" window's origin */ 446 dmxScreen->rootXOrigin = x; 447 dmxScreen->rootYOrigin = y; 448 449 /* Compute offsets here in case <x,y> has been changed above */ 450 xoff = x - pScreen->x; 451 yoff = y - pScreen->y; 452 453 /* Adjust the root window's position */ 454 pScreen->x = dmxScreen->rootXOrigin; 455 pScreen->y = dmxScreen->rootYOrigin; 456 457 /* Recalculate the Xinerama regions and data structs */ 458 XineramaReinitData(pScreen); 459 460 /* Adjust each of the root window's children */ 461 if (!idx) ReinitializeRootWindow(screenInfo.screens[0]->root, xoff, yoff); 462 pChild = pRoot->firstChild; 463 while (pChild) { 464 /* Adjust child window's position */ 465 pScreen->MoveWindow(pChild, 466 pChild->origin.x - wBorderWidth(pChild) - xoff, 467 pChild->origin.y - wBorderWidth(pChild) - yoff, 468 pChild->nextSib, 469 VTMove); 470 471 /* Note that the call to MoveWindow will eventually call 472 * dmxPositionWindow which will automatically create a 473 * window if it is now exposed on screen (for lazy window 474 * creation optimization) and it will properly set the 475 * offscreen flag. 476 */ 477 478 pChild = pChild->nextSib; 479 } 480} 481 482/** Configure the attributes of each "screen" and "root" window. */ 483int dmxConfigureScreenWindows(int nscreens, 484 CARD32 *screens, 485 DMXScreenAttributesPtr attribs, 486 int *errorScreen) 487{ 488 int i; 489 490 for (i = 0; i < nscreens; i++) { 491 DMXScreenAttributesPtr attr = &attribs[i]; 492 int idx = screens[i]; 493 DMXScreenInfo *dmxScreen = &dmxScreens[idx]; 494 495 if (errorScreen) *errorScreen = i; 496 497 if (!dmxScreen->beDisplay) return DMX_BAD_VALUE; 498 499 /* Check for illegal values */ 500 if (idx < 0 || idx >= dmxNumScreens) return BadValue; 501 502 /* The "screen" and "root" windows must have valid sizes */ 503 if (attr->screenWindowWidth <= 0 || attr->screenWindowHeight <= 0 || 504 attr->rootWindowWidth < 0 || attr->rootWindowHeight < 0) 505 return DMX_BAD_VALUE; 506 507 /* The "screen" window must fit entirely within the BE display */ 508 if (attr->screenWindowXoffset < 0 || 509 attr->screenWindowYoffset < 0 || 510 attr->screenWindowXoffset 511 + attr->screenWindowWidth > (unsigned)dmxScreen->beWidth || 512 attr->screenWindowYoffset 513 + attr->screenWindowHeight > (unsigned)dmxScreen->beHeight) 514 return DMX_BAD_VALUE; 515 516 /* The "root" window must fit entirely within the "screen" window */ 517 if (attr->rootWindowXoffset < 0 || 518 attr->rootWindowYoffset < 0 || 519 attr->rootWindowXoffset 520 + attr->rootWindowWidth > attr->screenWindowWidth || 521 attr->rootWindowYoffset 522 + attr->rootWindowHeight > attr->screenWindowHeight) 523 return DMX_BAD_VALUE; 524 525 /* The "root" window must not expose unaddressable coordinates */ 526 if (attr->rootWindowXorigin < 0 || 527 attr->rootWindowYorigin < 0 || 528 attr->rootWindowXorigin + attr->rootWindowWidth > 32767 || 529 attr->rootWindowYorigin + attr->rootWindowHeight > 32767) 530 return DMX_BAD_VALUE; 531 532 /* The "root" window must fit within the global bounding box */ 533 if (attr->rootWindowXorigin 534 + attr->rootWindowWidth > (unsigned)dmxGlobalWidth || 535 attr->rootWindowYorigin 536 + attr->rootWindowHeight > (unsigned)dmxGlobalHeight) 537 return DMX_BAD_VALUE; 538 539 /* FIXME: Handle the rest of the illegal value checking */ 540 } 541 542 /* No illegal values found */ 543 if (errorScreen) *errorScreen = 0; 544 545 for (i = 0; i < nscreens; i++) { 546 DMXScreenAttributesPtr attr = &attribs[i]; 547 int idx = screens[i]; 548 DMXScreenInfo *dmxScreen = &dmxScreens[idx]; 549 550 dmxLog(dmxInfo, "Changing screen #%d attributes " 551 "from %dx%d+%d+%d %dx%d+%d+%d +%d+%d " 552 "to %dx%d+%d+%d %dx%d+%d+%d +%d+%d\n", 553 idx, 554 dmxScreen->scrnWidth, dmxScreen->scrnHeight, 555 dmxScreen->scrnX, dmxScreen->scrnY, 556 dmxScreen->rootWidth, dmxScreen->rootHeight, 557 dmxScreen->rootX, dmxScreen->rootY, 558 dmxScreen->rootXOrigin, dmxScreen->rootYOrigin, 559 attr->screenWindowWidth, attr->screenWindowHeight, 560 attr->screenWindowXoffset, attr->screenWindowYoffset, 561 attr->rootWindowWidth, attr->rootWindowHeight, 562 attr->rootWindowXoffset, attr->rootWindowYoffset, 563 attr->rootWindowXorigin, attr->rootWindowYorigin); 564 565 /* Configure "screen" window */ 566 dmxConfigureScreenWindow(idx, 567 attr->screenWindowXoffset, 568 attr->screenWindowYoffset, 569 attr->screenWindowWidth, 570 attr->screenWindowHeight); 571 572 /* Configure "root" window */ 573 dmxConfigureRootWindow(idx, 574 attr->rootWindowXoffset, 575 attr->rootWindowYoffset, 576 attr->rootWindowWidth, 577 attr->rootWindowHeight); 578 579 580 /* Set "root" window's origin */ 581 dmxSetRootWindowOrigin(idx, 582 attr->rootWindowXorigin, 583 attr->rootWindowYorigin); 584 } 585 586 /* Adjust the cursor boundaries */ 587 dmxAdjustCursorBoundaries(); 588 589 /* Force completion of the changes */ 590 dmxSync(NULL, TRUE); 591 592 return Success; 593} 594 595/** Configure the attributes of the global desktop. */ 596int dmxConfigureDesktop(DMXDesktopAttributesPtr attribs) 597{ 598 if (attribs->width <= 0 || attribs->width >= 32767 || 599 attribs->height <= 0 || attribs->height >= 32767) 600 return DMX_BAD_VALUE; 601 602 /* If the desktop is shrinking, adjust the "root" windows on each 603 * "screen" window to only show the visible desktop. Also, handle 604 * the special case where the desktop shrinks such that the it no 605 * longer overlaps an portion of a "screen" window. */ 606 if (attribs->width < dmxGlobalWidth || attribs->height < dmxGlobalHeight) { 607 int i; 608 for (i = 0; i < dmxNumScreens; i++) { 609 DMXScreenInfo *dmxScreen = &dmxScreens[i]; 610 if (dmxScreen->rootXOrigin 611 + dmxScreen->rootWidth > attribs->width || 612 dmxScreen->rootYOrigin 613 + dmxScreen->rootHeight > attribs->height) { 614 int w, h; 615 if ((w = attribs->width - dmxScreen->rootXOrigin) < 0) w = 0; 616 if ((h = attribs->height - dmxScreen->rootYOrigin) < 0) h = 0; 617 if (w > dmxScreen->scrnWidth) w = dmxScreen->scrnWidth; 618 if (h > dmxScreen->scrnHeight) h = dmxScreen->scrnHeight; 619 if (w > dmxScreen->rootWidth) w = dmxScreen->rootWidth; 620 if (h > dmxScreen->rootHeight) h = dmxScreen->rootHeight; 621 dmxConfigureRootWindow(i, 622 dmxScreen->rootX, 623 dmxScreen->rootY, 624 w, h); 625 } 626 } 627 } 628 629 /* Set the global width/height */ 630 dmxSetWidthHeight(attribs->width, attribs->height); 631 632 /* Handle shift[XY] changes */ 633 if (attribs->shiftX || attribs->shiftY) { 634 int i; 635 for (i = 0; i < dmxNumScreens; i++) { 636 ScreenPtr pScreen = screenInfo.screens[i]; 637 WindowPtr pChild = pScreen->root->firstChild; 638 while (pChild) { 639 /* Adjust child window's position */ 640 pScreen->MoveWindow(pChild, 641 pChild->origin.x - wBorderWidth(pChild) 642 - attribs->shiftX, 643 pChild->origin.y - wBorderWidth(pChild) 644 - attribs->shiftY, 645 pChild->nextSib, 646 VTMove); 647 648 /* Note that the call to MoveWindow will eventually call 649 * dmxPositionWindow which will automatically create a 650 * window if it is now exposed on screen (for lazy 651 * window creation optimization) and it will properly 652 * set the offscreen flag. 653 */ 654 655 pChild = pChild->nextSib; 656 } 657 } 658 } 659 660 /* Update connection block, Xinerama, etc. -- these appears to 661 * already be handled in dmxConnectionBlockCallback(), which is 662 * called from dmxAdjustCursorBoundaries() [below]. */ 663 664 /* Adjust the cursor boundaries */ 665 dmxAdjustCursorBoundaries(); 666 667 /* Force completion of the changes */ 668 dmxSync(NULL, TRUE); 669 670 return Success; 671} 672#endif 673 674/** Create the scratch GCs per depth. */ 675static void dmxBECreateScratchGCs(int scrnNum) 676{ 677 ScreenPtr pScreen = screenInfo.screens[scrnNum]; 678 GCPtr *ppGC = pScreen->GCperDepth; 679 int i; 680 681 for (i = 0; i <= pScreen->numDepths; i++) 682 dmxBECreateGC(pScreen, ppGC[i]); 683} 684 685#ifdef PANORAMIX 686static Bool FoundPixImage; 687 688/** Search the Xinerama XRT_PIXMAP resources for the pixmap that needs 689 * to have its image restored. When it is found, see if there is 690 * another screen with the same image. If so, copy the pixmap image 691 * from the existing screen to the newly created pixmap. */ 692static void dmxBERestorePixmapImage(pointer value, XID id, RESTYPE type, 693 pointer p) 694{ 695 if ((type & TypeMask) == (XRT_PIXMAP & TypeMask)) { 696 PixmapPtr pDst = (PixmapPtr)p; 697 int idx = pDst->drawable.pScreen->myNum; 698 PanoramiXRes *pXinPix = (PanoramiXRes *)value; 699 PixmapPtr pPix; 700 int i; 701 702 dixLookupResourceByType((pointer*) &pPix, pXinPix->info[idx].id, 703 RT_PIXMAP, NullClient, DixUnknownAccess); 704 if (pPix != pDst) return; /* Not a match.... Next! */ 705 706 for (i = 0; i < PanoramiXNumScreens; i++) { 707 PixmapPtr pSrc; 708 dmxPixPrivPtr pSrcPriv = NULL; 709 710 if (i == idx) continue; /* Self replication is bad */ 711 712 dixLookupResourceByType((pointer*) &pSrc, pXinPix->info[i].id, 713 RT_PIXMAP, NullClient, DixUnknownAccess); 714 pSrcPriv = DMX_GET_PIXMAP_PRIV(pSrc); 715 if (pSrcPriv->pixmap) { 716 DMXScreenInfo *dmxSrcScreen = &dmxScreens[i]; 717 DMXScreenInfo *dmxDstScreen = &dmxScreens[idx]; 718 dmxPixPrivPtr pDstPriv = DMX_GET_PIXMAP_PRIV(pDst); 719 XImage *img; 720 int j; 721 XlibGC gc = NULL; 722 723 /* This should never happen, but just in case.... */ 724 if (pSrc->drawable.width != pDst->drawable.width || 725 pSrc->drawable.height != pDst->drawable.height) 726 return; 727 728 /* Copy from src pixmap to dst pixmap */ 729 img = XGetImage(dmxSrcScreen->beDisplay, 730 pSrcPriv->pixmap, 731 0, 0, 732 pSrc->drawable.width, pSrc->drawable.height, 733 -1, 734 ZPixmap); 735 736 for (j = 0; j < dmxDstScreen->beNumPixmapFormats; j++) { 737 if (dmxDstScreen->bePixmapFormats[j].depth == img->depth) { 738 unsigned long m; 739 XGCValues v; 740 741 m = GCFunction | GCPlaneMask | GCClipMask; 742 v.function = GXcopy; 743 v.plane_mask = AllPlanes; 744 v.clip_mask = None; 745 746 gc = XCreateGC(dmxDstScreen->beDisplay, 747 dmxDstScreen->scrnDefDrawables[j], 748 m, &v); 749 break; 750 } 751 } 752 753 if (gc) { 754 XPutImage(dmxDstScreen->beDisplay, 755 pDstPriv->pixmap, 756 gc, img, 0, 0, 0, 0, 757 pDst->drawable.width, pDst->drawable.height); 758 XFreeGC(dmxDstScreen->beDisplay, gc); 759 FoundPixImage = True; 760 } else { 761 dmxLog(dmxWarning, "Could not create GC\n"); 762 } 763 764 XDestroyImage(img); 765 return; 766 } 767 } 768 } 769} 770#endif 771 772/** Restore the pixmap image either from another screen or from an image 773 * that was saved when the screen was previously detached. */ 774static void dmxBERestorePixmap(PixmapPtr pPixmap) 775{ 776#ifdef PANORAMIX 777 int i; 778 779 /* If Xinerama is not active, there's nothing we can do (see comment 780 * in #else below for more info). */ 781 if (noPanoramiXExtension) { 782 dmxLog(dmxWarning, "Cannot restore pixmap image\n"); 783 return; 784 } 785 786 FoundPixImage = False; 787 for (i = currentMaxClients; --i >= 0; ) 788 if (clients[i]) 789 FindAllClientResources(clients[i], dmxBERestorePixmapImage, 790 (pointer)pPixmap); 791 792 /* No corresponding pixmap image was found on other screens, so we 793 * need to copy it from the saved image when the screen was detached 794 * (if available). */ 795 if (!FoundPixImage) { 796 dmxPixPrivPtr pPixPriv = DMX_GET_PIXMAP_PRIV(pPixmap); 797 798 if (pPixPriv->detachedImage) { 799 ScreenPtr pScreen = pPixmap->drawable.pScreen; 800 DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; 801 XlibGC gc = NULL; 802 803 for (i = 0; i < dmxScreen->beNumPixmapFormats; i++) { 804 if (dmxScreen->bePixmapFormats[i].depth == 805 pPixPriv->detachedImage->depth) { 806 unsigned long m; 807 XGCValues v; 808 809 m = GCFunction | GCPlaneMask | GCClipMask; 810 v.function = GXcopy; 811 v.plane_mask = AllPlanes; 812 v.clip_mask = None; 813 814 gc = XCreateGC(dmxScreen->beDisplay, 815 dmxScreen->scrnDefDrawables[i], 816 m, &v); 817 break; 818 } 819 } 820 821 if (gc) { 822 XPutImage(dmxScreen->beDisplay, 823 pPixPriv->pixmap, 824 gc, 825 pPixPriv->detachedImage, 826 0, 0, 0, 0, 827 pPixmap->drawable.width, pPixmap->drawable.height); 828 XFreeGC(dmxScreen->beDisplay, gc); 829 } else { 830 dmxLog(dmxWarning, "Cannot restore pixmap image\n"); 831 } 832 833 XDestroyImage(pPixPriv->detachedImage); 834 pPixPriv->detachedImage = NULL; 835 } else { 836 dmxLog(dmxWarning, "Cannot restore pixmap image\n"); 837 } 838 } 839#else 840 /* If Xinerama is not enabled, then there is no other copy of the 841 * pixmap image that we can restore. Saving all pixmap data is not 842 * a feasible option since there is no mechanism for updating pixmap 843 * data when a screen is detached, which means that the data that 844 * was previously saved would most likely be out of date. */ 845 dmxLog(dmxWarning, "Cannot restore pixmap image\n"); 846 return; 847#endif 848} 849 850/** Create resources on the back-end server. This function is called 851 * from #dmxAttachScreen() via the dix layer's FindAllResources 852 * function. It walks all resources, compares them to the screen 853 * number passed in as \a n and calls the appropriate DMX function to 854 * create the associated resource on the back-end server. */ 855static void dmxBECreateResources(pointer value, XID id, RESTYPE type, 856 pointer n) 857{ 858 int scrnNum = (uintptr_t)n; 859 ScreenPtr pScreen = screenInfo.screens[scrnNum]; 860 861 if ((type & TypeMask) == (RT_WINDOW & TypeMask)) { 862 /* Window resources are created below in dmxBECreateWindowTree */ 863 } else if ((type & TypeMask) == (RT_PIXMAP & TypeMask)) { 864 PixmapPtr pPix = value; 865 if (pPix->drawable.pScreen->myNum == scrnNum) { 866 dmxBECreatePixmap(pPix); 867 dmxBERestorePixmap(pPix); 868 } 869 } else if ((type & TypeMask) == (RT_GC & TypeMask)) { 870 GCPtr pGC = value; 871 if (pGC->pScreen->myNum == scrnNum) { 872 /* Create the GC on the back-end server */ 873 dmxBECreateGC(pScreen, pGC); 874 /* Create any pixmaps associated with this GC */ 875 if (!pGC->tileIsPixel) { 876 dmxBECreatePixmap(pGC->tile.pixmap); 877 dmxBERestorePixmap(pGC->tile.pixmap); 878 } 879 if (pGC->stipple != pScreen->PixmapPerDepth[0]) { 880 dmxBECreatePixmap(pGC->stipple); 881 dmxBERestorePixmap(pGC->stipple); 882 } 883 if (pGC->font != defaultFont) { 884 (void)dmxBELoadFont(pScreen, pGC->font); 885 } 886 /* Update the GC on the back-end server */ 887 dmxChangeGC(pGC, -1L); 888 } 889 } else if ((type & TypeMask) == (RT_FONT & TypeMask)) { 890 (void)dmxBELoadFont(pScreen, (FontPtr)value); 891 } else if ((type & TypeMask) == (RT_CURSOR & TypeMask)) { 892 dmxBECreateCursor(pScreen, (CursorPtr)value); 893 } else if ((type & TypeMask) == (RT_COLORMAP & TypeMask)) { 894 ColormapPtr pCmap = value; 895 if (pCmap->pScreen->myNum == scrnNum) 896 (void)dmxBECreateColormap((ColormapPtr)value); 897#if 0 898 /* TODO: Recreate Picture and GlyphSet resources */ 899 } else if ((type & TypeMask) == (PictureType & TypeMask)) { 900 /* Picture resources are created when windows are created */ 901 } else if ((type & TypeMask) == (GlyphSetType & TypeMask)) { 902 dmxBEFreeGlyphSet(pScreen, (GlyphSetPtr)value); 903#endif 904 } else { 905 /* Other resource types??? */ 906 } 907} 908 909/** Create window hierachy on back-end server. The window tree is 910 * created in a special order (bottom most subwindow first) so that the 911 * #dmxCreateNonRootWindow() function does not need to recursively call 912 * itself to create each window's parents. This is required so that we 913 * have the opportunity to create each window's border and background 914 * pixmaps (where appropriate) before the window is created. */ 915static void dmxBECreateWindowTree(int idx) 916{ 917 DMXScreenInfo *dmxScreen = &dmxScreens[idx]; 918 WindowPtr pRoot = screenInfo.screens[idx]->root; 919 dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pRoot); 920 WindowPtr pWin; 921 922 /* Create the pixmaps associated with the root window */ 923 if (!pRoot->borderIsPixel) { 924 dmxBECreatePixmap(pRoot->border.pixmap); 925 dmxBERestorePixmap(pRoot->border.pixmap); 926 } 927 if (pRoot->backgroundState == BackgroundPixmap) { 928 dmxBECreatePixmap(pRoot->background.pixmap); 929 dmxBERestorePixmap(pRoot->background.pixmap); 930 } 931 932 /* Create root window first */ 933 dmxScreen->rootWin = pWinPriv->window = dmxCreateRootWindow(pRoot); 934 XMapWindow(dmxScreen->beDisplay, dmxScreen->rootWin); 935 936 pWin = pRoot->lastChild; 937 while (pWin) { 938 pWinPriv = DMX_GET_WINDOW_PRIV(pWin); 939 940 /* Create the pixmaps regardless of whether or not the 941 * window is created or not due to lazy window creation. 942 */ 943 if (!pWin->borderIsPixel) { 944 dmxBECreatePixmap(pWin->border.pixmap); 945 dmxBERestorePixmap(pWin->border.pixmap); 946 } 947 if (pWin->backgroundState == BackgroundPixmap) { 948 dmxBECreatePixmap(pWin->background.pixmap); 949 dmxBERestorePixmap(pWin->background.pixmap); 950 } 951 952 /* Reset the window attributes */ 953 dmxGetDefaultWindowAttributes(pWin, 954 &pWinPriv->cmap, 955 &pWinPriv->visual); 956 957 /* Create the window */ 958 if (pWinPriv->mapped && !pWinPriv->offscreen) 959 dmxCreateAndRealizeWindow(pWin, TRUE); 960 961 /* Next, create the bottom-most child */ 962 if (pWin->lastChild) { 963 pWin = pWin->lastChild; 964 continue; 965 } 966 967 /* If the window has no children, move on to the next higher window */ 968 while (!pWin->prevSib && (pWin != pRoot)) 969 pWin = pWin->parent; 970 971 if (pWin->prevSib) { 972 pWin = pWin->prevSib; 973 continue; 974 } 975 976 /* When we reach the root window, we are finished */ 977 if (pWin == pRoot) 978 break; 979 } 980} 981 982/* Refresh screen by generating exposure events for all windows */ 983static void dmxForceExposures(int idx) 984{ 985 ScreenPtr pScreen = screenInfo.screens[idx]; 986 WindowPtr pRoot = pScreen->root; 987 Bool anyMarked = FALSE; 988 WindowPtr pChild; 989 990 for (pChild = pRoot->firstChild; pChild; pChild = pChild->nextSib) 991 anyMarked |= pScreen->MarkOverlappedWindows(pChild, pChild, 992 (WindowPtr *)NULL); 993 if (anyMarked) { 994 /* If any windows have been marked, set the root window's 995 * clipList to be broken since it will be recalculated in 996 * ValidateTree() 997 */ 998 RegionBreak(&pRoot->clipList); 999 pScreen->ValidateTree(pRoot, NULL, VTBroken); 1000 pScreen->HandleExposures(pRoot); 1001 if (pScreen->PostValidateTree) 1002 pScreen->PostValidateTree(pRoot, NULL, VTBroken); 1003 } 1004} 1005 1006/** Compare the new and old screens to see if they are compatible. */ 1007static Bool dmxCompareScreens(DMXScreenInfo *new, DMXScreenInfo *old) 1008{ 1009 int i; 1010 1011 if (new->beWidth != old->beWidth) return FALSE; 1012 if (new->beHeight != old->beHeight) return FALSE; 1013 if (new->beDepth != old->beDepth) return FALSE; 1014 if (new->beBPP != old->beBPP) return FALSE; 1015 1016 if (new->beNumDepths != old->beNumDepths) return FALSE; 1017 for (i = 0; i < old->beNumDepths; i++) 1018 if (new->beDepths[i] != old->beDepths[i]) return FALSE; 1019 1020 if (new->beNumPixmapFormats != old->beNumPixmapFormats) return FALSE; 1021 for (i = 0; i < old->beNumPixmapFormats; i++) { 1022 if (new->bePixmapFormats[i].depth != 1023 old->bePixmapFormats[i].depth) return FALSE; 1024 if (new->bePixmapFormats[i].bits_per_pixel != 1025 old->bePixmapFormats[i].bits_per_pixel) return FALSE; 1026 if (new->bePixmapFormats[i].scanline_pad != 1027 old->bePixmapFormats[i].scanline_pad) return FALSE; 1028 } 1029 1030 if (new->beNumVisuals != old->beNumVisuals) return FALSE; 1031 for (i = 0; i < old->beNumVisuals; i++) { 1032 if (new->beVisuals[i].visualid != 1033 old->beVisuals[i].visualid) return FALSE; 1034 if (new->beVisuals[i].screen != 1035 old->beVisuals[i].screen) return FALSE; 1036 if (new->beVisuals[i].depth != 1037 old->beVisuals[i].depth) return FALSE; 1038 if (new->beVisuals[i].class != 1039 old->beVisuals[i].class) return FALSE; 1040 if (new->beVisuals[i].red_mask != 1041 old->beVisuals[i].red_mask) return FALSE; 1042 if (new->beVisuals[i].green_mask != 1043 old->beVisuals[i].green_mask) return FALSE; 1044 if (new->beVisuals[i].blue_mask != 1045 old->beVisuals[i].blue_mask) return FALSE; 1046 if (new->beVisuals[i].colormap_size != 1047 old->beVisuals[i].colormap_size) return FALSE; 1048 if (new->beVisuals[i].bits_per_rgb != 1049 old->beVisuals[i].bits_per_rgb) return FALSE; 1050 } 1051 1052 if (new->beDefVisualIndex != old->beDefVisualIndex) return FALSE; 1053 1054 return TRUE; 1055} 1056 1057/** Restore Render's picture */ 1058static void dmxBERestoreRenderPict(pointer value, XID id, pointer n) 1059{ 1060 PicturePtr pPicture = value; /* The picture */ 1061 DrawablePtr pDraw = pPicture->pDrawable; /* The picture's drawable */ 1062 int scrnNum = (uintptr_t)n; 1063 1064 if (pDraw->pScreen->myNum != scrnNum) { 1065 /* Picture not on the screen we are restoring*/ 1066 return; 1067 } 1068 1069 if (pDraw->type == DRAWABLE_PIXMAP) { 1070 PixmapPtr pPixmap = (PixmapPtr)pDraw; 1071 1072 /* Create and restore the pixmap drawable */ 1073 dmxBECreatePixmap(pPixmap); 1074 dmxBERestorePixmap(pPixmap); 1075 } 1076 1077 dmxBECreatePicture(pPicture); 1078} 1079 1080/** Restore Render's glyphs */ 1081static void dmxBERestoreRenderGlyph(pointer value, XID id, pointer n) 1082{ 1083 GlyphSetPtr glyphSet = value; 1084 int scrnNum = (uintptr_t)n; 1085 dmxGlyphPrivPtr glyphPriv = DMX_GET_GLYPH_PRIV(glyphSet); 1086 DMXScreenInfo *dmxScreen = &dmxScreens[scrnNum]; 1087 GlyphRefPtr table; 1088 char *images; 1089 Glyph *gids; 1090 XGlyphInfo *glyphs; 1091 char *pos; 1092 int beret; 1093 int len_images = 0; 1094 int i; 1095 int ctr; 1096 1097 if (glyphPriv->glyphSets[scrnNum]) { 1098 /* Only restore glyphs on the screen we are attaching */ 1099 return; 1100 } 1101 1102 /* First we must create the glyph set on the backend. */ 1103 if ((beret = dmxBECreateGlyphSet(scrnNum, glyphSet)) != Success) { 1104 dmxLog(dmxWarning, 1105 "\tdmxBERestoreRenderGlyph failed to create glyphset!\n"); 1106 return; 1107 } 1108 1109 /* Now for the complex part, restore the glyph data */ 1110 table = glyphSet->hash.table; 1111 1112 /* We need to know how much memory to allocate for this part */ 1113 for (i = 0; i < glyphSet->hash.hashSet->size; i++) { 1114 GlyphRefPtr gr = &table[i]; 1115 GlyphPtr gl = gr->glyph; 1116 1117 if (!gl || gl == DeletedGlyph) continue; 1118 len_images += gl->size - sizeof(gl->info); 1119 } 1120 1121 /* Now allocate the memory we need */ 1122 images = calloc(len_images, sizeof(char)); 1123 gids = malloc(glyphSet->hash.tableEntries*sizeof(Glyph)); 1124 glyphs = malloc(glyphSet->hash.tableEntries*sizeof(XGlyphInfo)); 1125 1126 pos = images; 1127 ctr = 0; 1128 1129 /* Fill the allocated memory with the proper data */ 1130 for (i = 0; i < glyphSet->hash.hashSet->size; i++) { 1131 GlyphRefPtr gr = &table[i]; 1132 GlyphPtr gl = gr->glyph; 1133 1134 if (!gl || gl == DeletedGlyph) continue; 1135 1136 /* First lets put the data into gids */ 1137 gids[ctr] = gr->signature; 1138 1139 /* Next do the glyphs data structures */ 1140 glyphs[ctr].width = gl->info.width; 1141 glyphs[ctr].height = gl->info.height; 1142 glyphs[ctr].x = gl->info.x; 1143 glyphs[ctr].y = gl->info.y; 1144 glyphs[ctr].xOff = gl->info.xOff; 1145 glyphs[ctr].yOff = gl->info.yOff; 1146 1147 /* Copy the images from the DIX's data into the buffer */ 1148 memcpy(pos, gl+1, gl->size - sizeof(gl->info)); 1149 pos += gl->size - sizeof(gl->info); 1150 ctr++; 1151 } 1152 1153 /* Now restore the glyph data */ 1154 XRenderAddGlyphs(dmxScreen->beDisplay, glyphPriv->glyphSets[scrnNum], 1155 gids,glyphs, glyphSet->hash.tableEntries, images, 1156 len_images); 1157 1158 /* Clean up */ 1159 free(images); 1160 free(gids); 1161 free(glyphs); 1162} 1163 1164/** Reattach previously detached back-end screen. */ 1165int dmxAttachScreen(int idx, DMXScreenAttributesPtr attr) 1166{ 1167 ScreenPtr pScreen; 1168 DMXScreenInfo *dmxScreen; 1169 CARD32 scrnNum = idx; 1170 DMXScreenInfo oldDMXScreen; 1171 int i; 1172 1173 /* Return failure if dynamic addition/removal of screens is disabled */ 1174 if (!dmxAddRemoveScreens) { 1175 dmxLog(dmxWarning, 1176 "Attempting to add a screen, but the AddRemoveScreen\n"); 1177 dmxLog(dmxWarning, 1178 "extension has not been enabled. To enable this extension\n"); 1179 dmxLog(dmxWarning, 1180 "add the \"-addremovescreens\" option either to the command\n"); 1181 dmxLog(dmxWarning, 1182 "line or in the configuration file.\n"); 1183 return 1; 1184 } 1185 1186 /* Cannot add a screen that does not exist */ 1187 if (idx < 0 || idx >= dmxNumScreens) return 1; 1188 pScreen = screenInfo.screens[idx]; 1189 dmxScreen = &dmxScreens[idx]; 1190 1191 /* Cannot attach to a screen that is already opened */ 1192 if (dmxScreen->beDisplay) { 1193 dmxLog(dmxWarning, 1194 "Attempting to add screen #%d but a screen already exists\n", 1195 idx); 1196 return 1; 1197 } 1198 1199 dmxLogOutput(dmxScreen, "Attaching screen #%d\n", idx); 1200 1201 /* Save old info */ 1202 oldDMXScreen = *dmxScreen; 1203 1204 /* Copy the name to the new screen */ 1205 dmxScreen->name = strdup(attr->displayName); 1206 1207 /* Open display and get all of the screen info */ 1208 if (!dmxOpenDisplay(dmxScreen)) { 1209 dmxLog(dmxWarning, 1210 "dmxOpenDisplay: Unable to open display %s\n", 1211 dmxScreen->name); 1212 1213 /* Restore the old screen */ 1214 *dmxScreen = oldDMXScreen; 1215 return 1; 1216 } 1217 1218 dmxSetErrorHandler(dmxScreen); 1219 dmxCheckForWM(dmxScreen); 1220 dmxGetScreenAttribs(dmxScreen); 1221 1222 if (!dmxGetVisualInfo(dmxScreen)) { 1223 dmxLog(dmxWarning, "dmxGetVisualInfo: No matching visuals found\n"); 1224 XFree(dmxScreen->beVisuals); 1225 XCloseDisplay(dmxScreen->beDisplay); 1226 1227 /* Restore the old screen */ 1228 *dmxScreen = oldDMXScreen; 1229 return 1; 1230 } 1231 1232 dmxGetColormaps(dmxScreen); 1233 dmxGetPixmapFormats(dmxScreen); 1234 1235 /* Verify that the screen to be added has the same info as the 1236 * previously added screen. */ 1237 if (!dmxCompareScreens(dmxScreen, &oldDMXScreen)) { 1238 dmxLog(dmxWarning, 1239 "New screen data (%s) does not match previously\n", 1240 dmxScreen->name); 1241 dmxLog(dmxWarning, 1242 "attached screen data (%s)\n", 1243 oldDMXScreen.name); 1244 dmxLog(dmxWarning, 1245 "All data must match in order to attach to screen #%d\n", 1246 idx); 1247 XFree(dmxScreen->beVisuals); 1248 XFree(dmxScreen->beDepths); 1249 XFree(dmxScreen->bePixmapFormats); 1250 XCloseDisplay(dmxScreen->beDisplay); 1251 1252 /* Restore the old screen */ 1253 *dmxScreen = oldDMXScreen; 1254 return 1; 1255 } 1256 1257 /* Initialize the BE screen resources */ 1258 dmxBEScreenInit(idx, screenInfo.screens[idx]); 1259 1260 /* TODO: Handle GLX visual initialization. GLXProxy needs to be 1261 * updated to handle dynamic addition/removal of screens. */ 1262 1263 /* Create default stipple */ 1264 dmxBECreatePixmap(pScreen->PixmapPerDepth[0]); 1265 dmxBERestorePixmap(pScreen->PixmapPerDepth[0]); 1266 1267 /* Create the scratch GCs */ 1268 dmxBECreateScratchGCs(idx); 1269 1270 /* Create the default font */ 1271 (void)dmxBELoadFont(pScreen, defaultFont); 1272 1273 /* Create all resources that don't depend on windows */ 1274 for (i = currentMaxClients; --i >= 0; ) 1275 if (clients[i]) 1276 FindAllClientResources(clients[i], dmxBECreateResources, 1277 (pointer)(uintptr_t)idx); 1278 1279 /* Create window hierarchy (top down) */ 1280 dmxBECreateWindowTree(idx); 1281 1282 /* Restore the picture state for RENDER */ 1283 for (i = currentMaxClients; --i >= 0; ) 1284 if (clients[i]) 1285 FindClientResourcesByType(clients[i],PictureType, 1286 dmxBERestoreRenderPict, 1287 (pointer)(uintptr_t)idx); 1288 1289 /* Restore the glyph state for RENDER */ 1290 for (i = currentMaxClients; --i >= 0; ) 1291 if (clients[i]) 1292 FindClientResourcesByType(clients[i],GlyphSetType, 1293 dmxBERestoreRenderGlyph, 1294 (pointer)(uintptr_t)idx); 1295 1296 /* Refresh screen by generating exposure events for all windows */ 1297 dmxForceExposures(idx); 1298 1299 dmxSync(&dmxScreens[idx], TRUE); 1300 1301 /* We used these to compare the old and new screens. They are no 1302 * longer needed since we have a newly attached screen, so we can 1303 * now free the old screen's resources. */ 1304 XFree(oldDMXScreen.beVisuals); 1305 XFree(oldDMXScreen.beDepths); 1306 XFree(oldDMXScreen.bePixmapFormats); 1307 /* TODO: should oldDMXScreen.name be freed?? */ 1308 1309#ifdef PANORAMIX 1310 if (!noPanoramiXExtension) 1311 return dmxConfigureScreenWindows(1, &scrnNum, attr, NULL); 1312 else 1313#endif 1314 return 0; /* Success */ 1315} 1316 1317/* 1318 * Resources that may have state on the BE server and need to be freed: 1319 * 1320 * RT_NONE 1321 * RT_WINDOW 1322 * RT_PIXMAP 1323 * RT_GC 1324 * RT_FONT 1325 * RT_CURSOR 1326 * RT_COLORMAP 1327 * RT_CMAPENTRY 1328 * RT_OTHERCLIENT 1329 * RT_PASSIVEGRAB 1330 * XRT_WINDOW 1331 * XRT_PIXMAP 1332 * XRT_GC 1333 * XRT_COLORMAP 1334 * XRT_PICTURE 1335 * PictureType 1336 * PictFormatType 1337 * GlyphSetType 1338 * ClientType 1339 * EventType 1340 * RT_INPUTCLIENT 1341 * XETrapType 1342 * RTCounter 1343 * RTAwait 1344 * RTAlarmClient 1345 * RT_XKBCLIENT 1346 * RTContext 1347 * TagResType 1348 * StalledResType 1349 * SecurityAuthorizationResType 1350 * RTEventClient 1351 * __glXContextRes 1352 * __glXClientRes 1353 * __glXPixmapRes 1354 * __glXWindowRes 1355 * __glXPbufferRes 1356 */ 1357 1358#ifdef PANORAMIX 1359/** Search the Xinerama XRT_PIXMAP resources for the pixmap that needs 1360 * to have its image saved. */ 1361static void dmxBEFindPixmapImage(pointer value, XID id, RESTYPE type, 1362 pointer p) 1363{ 1364 if ((type & TypeMask) == (XRT_PIXMAP & TypeMask)) { 1365 PixmapPtr pDst = (PixmapPtr)p; 1366 int idx = pDst->drawable.pScreen->myNum; 1367 PanoramiXRes *pXinPix = (PanoramiXRes *)value; 1368 PixmapPtr pPix; 1369 int i; 1370 1371 dixLookupResourceByType((pointer*) &pPix, pXinPix->info[idx].id, 1372 RT_PIXMAP, NullClient, DixUnknownAccess); 1373 if (pPix != pDst) return; /* Not a match.... Next! */ 1374 1375 for (i = 0; i < PanoramiXNumScreens; i++) { 1376 PixmapPtr pSrc; 1377 dmxPixPrivPtr pSrcPriv = NULL; 1378 1379 if (i == idx) continue; /* Self replication is bad */ 1380 1381 dixLookupResourceByType((pointer*) &pSrc, pXinPix->info[i].id, 1382 RT_PIXMAP, NullClient, DixUnknownAccess); 1383 pSrcPriv = DMX_GET_PIXMAP_PRIV(pSrc); 1384 if (pSrcPriv->pixmap) { 1385 FoundPixImage = True; 1386 return; 1387 } 1388 } 1389 } 1390} 1391#endif 1392 1393/** Save the pixmap image only when there is not another screen with 1394 * that pixmap from which the image can be read when the screen is 1395 * reattached. To do this, we first try to find a pixmap on another 1396 * screen corresponding to the one we are trying to save. If we find 1397 * one, then we do not need to save the image data since during 1398 * reattachment, the image data can be read from that other pixmap. 1399 * However, if we do not find one, then we need to save the image data. 1400 * The common case for these are for the default stipple and root 1401 * tile. */ 1402static void dmxBESavePixmap(PixmapPtr pPixmap) 1403{ 1404#ifdef PANORAMIX 1405 int i; 1406 1407 /* If Xinerama is not active, there's nothing we can do (see comment 1408 * in #else below for more info). */ 1409 if (noPanoramiXExtension) return; 1410 1411 FoundPixImage = False; 1412 for (i = currentMaxClients; --i >= 0; ) 1413 if (clients[i]) 1414 FindAllClientResources(clients[i], dmxBEFindPixmapImage, 1415 (pointer)pPixmap); 1416 1417 /* Save the image only if there is no other screens that have a 1418 * pixmap that corresponds to the one we are trying to save. */ 1419 if (!FoundPixImage) { 1420 dmxPixPrivPtr pPixPriv = DMX_GET_PIXMAP_PRIV(pPixmap); 1421 1422 if (!pPixPriv->detachedImage) { 1423 ScreenPtr pScreen = pPixmap->drawable.pScreen; 1424 DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; 1425 1426 pPixPriv->detachedImage = XGetImage(dmxScreen->beDisplay, 1427 pPixPriv->pixmap, 1428 0, 0, 1429 pPixmap->drawable.width, 1430 pPixmap->drawable.height, 1431 -1, 1432 ZPixmap); 1433 if (!pPixPriv->detachedImage) 1434 dmxLog(dmxWarning, "Cannot save pixmap image\n"); 1435 } 1436 } 1437#else 1438 /* NOTE: The only time there is a pixmap on another screen that 1439 * corresponds to the one we are trying to save is when Xinerama is 1440 * active. Otherwise, the pixmap image data is only stored on a 1441 * single screen, which means that once it is detached, that data is 1442 * lost. We could save the data here, but then that would require 1443 * us to implement the ability for Xdmx to keep the pixmap up to 1444 * date while the screen is detached, which is beyond the scope of 1445 * the current project. */ 1446 return; 1447#endif 1448} 1449 1450/** Destroy resources on the back-end server. This function is called 1451 * from #dmxDetachScreen() via the dix layer's FindAllResources 1452 * function. It walks all resources, compares them to the screen 1453 * number passed in as \a n and calls the appropriate DMX function to 1454 * free the associated resource on the back-end server. */ 1455static void dmxBEDestroyResources(pointer value, XID id, RESTYPE type, 1456 pointer n) 1457{ 1458 int scrnNum = (uintptr_t)n; 1459 ScreenPtr pScreen = screenInfo.screens[scrnNum]; 1460 1461 if ((type & TypeMask) == (RT_WINDOW & TypeMask)) { 1462 /* Window resources are destroyed below in dmxBEDestroyWindowTree */ 1463 } else if ((type & TypeMask) == (RT_PIXMAP & TypeMask)) { 1464 PixmapPtr pPix = value; 1465 if (pPix->drawable.pScreen->myNum == scrnNum) { 1466 dmxBESavePixmap(pPix); 1467 dmxBEFreePixmap(pPix); 1468 } 1469 } else if ((type & TypeMask) == (RT_GC & TypeMask)) { 1470 GCPtr pGC = value; 1471 if (pGC->pScreen->myNum == scrnNum) 1472 dmxBEFreeGC(pGC); 1473 } else if ((type & TypeMask) == (RT_FONT & TypeMask)) { 1474 dmxBEFreeFont(pScreen, (FontPtr)value); 1475 } else if ((type & TypeMask) == (RT_CURSOR & TypeMask)) { 1476 dmxBEFreeCursor(pScreen, (CursorPtr)value); 1477 } else if ((type & TypeMask) == (RT_COLORMAP & TypeMask)) { 1478 ColormapPtr pCmap = value; 1479 if (pCmap->pScreen->myNum == scrnNum) 1480 dmxBEFreeColormap((ColormapPtr)value); 1481 } else if ((type & TypeMask) == (PictureType & TypeMask)) { 1482 PicturePtr pPict = value; 1483 if (pPict->pDrawable->pScreen->myNum == scrnNum) { 1484 /* Free the pixmaps on the backend if needed */ 1485 if (pPict->pDrawable->type == DRAWABLE_PIXMAP) { 1486 PixmapPtr pPixmap = (PixmapPtr)(pPict->pDrawable); 1487 dmxBESavePixmap(pPixmap); 1488 dmxBEFreePixmap(pPixmap); 1489 } 1490 dmxBEFreePicture((PicturePtr)value); 1491 } 1492 } else if ((type & TypeMask) == (GlyphSetType & TypeMask)) { 1493 dmxBEFreeGlyphSet(pScreen, (GlyphSetPtr)value); 1494 } else { 1495 /* Other resource types??? */ 1496 } 1497} 1498 1499/** Destroy the scratch GCs that are created per depth. */ 1500static void dmxBEDestroyScratchGCs(int scrnNum) 1501{ 1502 ScreenPtr pScreen = screenInfo.screens[scrnNum]; 1503 GCPtr *ppGC = pScreen->GCperDepth; 1504 int i; 1505 1506 for (i = 0; i <= pScreen->numDepths; i++) 1507 dmxBEFreeGC(ppGC[i]); 1508} 1509 1510/** Destroy window hierachy on back-end server. To ensure that all 1511 * XDestroyWindow() calls succeed, they must be performed in a bottom 1512 * up order so that windows are not destroyed before their children. 1513 * XDestroyWindow(), which is called from #dmxBEDestroyWindow(), will 1514 * destroy a window as well as all of it's children. */ 1515static void dmxBEDestroyWindowTree(int idx) 1516{ 1517 WindowPtr pWin = screenInfo.screens[idx]->root; 1518 WindowPtr pChild = pWin; 1519 1520 while (1) { 1521 if (pChild->firstChild) { 1522 pChild = pChild->firstChild; 1523 continue; 1524 } 1525 1526 /* Destroy the window */ 1527 dmxBEDestroyWindow(pChild); 1528 1529 /* Make sure we destroy the window's border and background 1530 * pixmaps if they exist */ 1531 if (!pChild->borderIsPixel) { 1532 dmxBESavePixmap(pChild->border.pixmap); 1533 dmxBEFreePixmap(pChild->border.pixmap); 1534 } 1535 if (pChild->backgroundState == BackgroundPixmap) { 1536 dmxBESavePixmap(pChild->background.pixmap); 1537 dmxBEFreePixmap(pChild->background.pixmap); 1538 } 1539 1540 while (!pChild->nextSib && (pChild != pWin)) { 1541 pChild = pChild->parent; 1542 dmxBEDestroyWindow(pChild); 1543 if (!pChild->borderIsPixel) { 1544 dmxBESavePixmap(pChild->border.pixmap); 1545 dmxBEFreePixmap(pChild->border.pixmap); 1546 } 1547 if (pChild->backgroundState == BackgroundPixmap) { 1548 dmxBESavePixmap(pChild->background.pixmap); 1549 dmxBEFreePixmap(pChild->background.pixmap); 1550 } 1551 } 1552 1553 if (pChild == pWin) 1554 break; 1555 1556 pChild = pChild->nextSib; 1557 } 1558} 1559 1560/** Detach back-end screen. */ 1561int dmxDetachScreen(int idx) 1562{ 1563 DMXScreenInfo *dmxScreen = &dmxScreens[idx]; 1564 int i; 1565 1566 /* Return failure if dynamic addition/removal of screens is disabled */ 1567 if (!dmxAddRemoveScreens) { 1568 dmxLog(dmxWarning, 1569 "Attempting to remove a screen, but the AddRemoveScreen\n"); 1570 dmxLog(dmxWarning, 1571 "extension has not been enabled. To enable this extension\n"); 1572 dmxLog(dmxWarning, 1573 "add the \"-addremovescreens\" option either to the command\n"); 1574 dmxLog(dmxWarning, 1575 "line or in the configuration file.\n"); 1576 return 1; 1577 } 1578 1579 /* Cannot remove a screen that does not exist */ 1580 if (idx < 0 || idx >= dmxNumScreens) return 1; 1581 1582 /* Cannot detach from a screen that is not opened */ 1583 if (!dmxScreen->beDisplay) { 1584 dmxLog(dmxWarning, 1585 "Attempting to remove screen #%d but it has not been opened\n", 1586 idx); 1587 return 1; 1588 } 1589 1590 dmxLogOutput(dmxScreen, "Detaching screen #%d\n", idx); 1591 1592 /* Detach input */ 1593 dmxInputDetachAll(dmxScreen); 1594 1595 /* Save all relevant state (TODO) */ 1596 1597 /* Free all non-window resources related to this screen */ 1598 for (i = currentMaxClients; --i >= 0; ) 1599 if (clients[i]) 1600 FindAllClientResources(clients[i], dmxBEDestroyResources, 1601 (pointer)(uintptr_t)idx); 1602 1603 /* Free scratch GCs */ 1604 dmxBEDestroyScratchGCs(idx); 1605 1606 /* Free window resources related to this screen */ 1607 dmxBEDestroyWindowTree(idx); 1608 1609 /* Free default stipple */ 1610 dmxBESavePixmap(screenInfo.screens[idx]->PixmapPerDepth[0]); 1611 dmxBEFreePixmap(screenInfo.screens[idx]->PixmapPerDepth[0]); 1612 1613 /* Free the remaining screen resources and close the screen */ 1614 dmxBECloseScreen(screenInfo.screens[idx]); 1615 1616 /* Adjust the cursor boundaries (paints detached console window) */ 1617 dmxAdjustCursorBoundaries(); 1618 1619 return 0; /* Success */ 1620} 1621