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