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