Imported Upstream version 1.15.1
[deb_xorg-server.git] / Xext / panoramiX.c
CommitLineData
a09e091a
JB
1/*****************************************************************
2Copyright (c) 1991, 1997 Digital Equipment Corporation, Maynard, Massachusetts.
3Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal
5in the Software without restriction, including without limitation the rights
6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software.
8
9The above copyright notice and this permission notice shall be included in
10all copies or substantial portions of the Software.
11
12THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
15DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING,
16BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY,
17WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
18IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
20Except as contained in this notice, the name of Digital Equipment Corporation
21shall not be used in advertising or otherwise to promote the sale, use or other
22dealings in this Software without prior written authorization from Digital
23Equipment Corporation.
24******************************************************************/
25
26#ifdef HAVE_DIX_CONFIG_H
27#include <dix-config.h>
28#endif
29
30#ifdef HAVE_DMX_CONFIG_H
31#include <dmx-config.h>
32#endif
33
34#include <stdio.h>
35#include <X11/X.h>
36#include <X11/Xproto.h>
37#include <X11/Xarch.h>
38#include "misc.h"
39#include "cursor.h"
40#include "cursorstr.h"
41#include "extnsionst.h"
42#include "dixstruct.h"
43#include "gc.h"
44#include "gcstruct.h"
45#include "scrnintstr.h"
46#include "window.h"
47#include "windowstr.h"
48#include "pixmapstr.h"
49#include "panoramiX.h"
50#include <X11/extensions/panoramiXproto.h>
51#include "panoramiXsrv.h"
52#include "globals.h"
53#include "servermd.h"
54#include "resource.h"
55#include "picturestr.h"
56#include "xfixesint.h"
57#include "damageextint.h"
58#ifdef COMPOSITE
59#include "compint.h"
60#endif
61#include "extinit.h"
62#include "protocol-versions.h"
63
64#ifdef GLXPROXY
65extern VisualPtr glxMatchVisual(ScreenPtr pScreen,
66 VisualPtr pVisual, ScreenPtr pMatchScreen);
67#endif
68
69/*
70 * PanoramiX data declarations
71 */
72
73int PanoramiXPixWidth = 0;
74int PanoramiXPixHeight = 0;
75int PanoramiXNumScreens = 0;
76
77_X_EXPORT RegionRec PanoramiXScreenRegion = { {0, 0, 0, 0}, NULL };
78
79static int PanoramiXNumDepths;
80static DepthPtr PanoramiXDepths;
81static int PanoramiXNumVisuals;
82static VisualPtr PanoramiXVisuals;
83
84RESTYPE XRC_DRAWABLE;
85RESTYPE XRT_WINDOW;
86RESTYPE XRT_PIXMAP;
87RESTYPE XRT_GC;
88RESTYPE XRT_COLORMAP;
89
90static Bool VisualsEqual(VisualPtr, ScreenPtr, VisualPtr);
91XineramaVisualsEqualProcPtr XineramaVisualsEqualPtr = &VisualsEqual;
92
93/*
94 * Function prototypes
95 */
96
97static int panoramiXGeneration;
98static int ProcPanoramiXDispatch(ClientPtr client);
99
100static void PanoramiXResetProc(ExtensionEntry *);
101
102/*
103 * External references for functions and data variables
104 */
105
106#include "panoramiXh.h"
107
108int (*SavedProcVector[256]) (ClientPtr client) = {
109NULL,};
110
111static DevPrivateKeyRec PanoramiXGCKeyRec;
112
113#define PanoramiXGCKey (&PanoramiXGCKeyRec)
114static DevPrivateKeyRec PanoramiXScreenKeyRec;
115
116#define PanoramiXScreenKey (&PanoramiXScreenKeyRec)
117
118typedef struct {
119 DDXPointRec clipOrg;
120 DDXPointRec patOrg;
121 GCFuncs *wrapFuncs;
122} PanoramiXGCRec, *PanoramiXGCPtr;
123
124typedef struct {
125 CreateGCProcPtr CreateGC;
126 CloseScreenProcPtr CloseScreen;
127} PanoramiXScreenRec, *PanoramiXScreenPtr;
128
129static void XineramaValidateGC(GCPtr, unsigned long, DrawablePtr);
130static void XineramaChangeGC(GCPtr, unsigned long);
131static void XineramaCopyGC(GCPtr, unsigned long, GCPtr);
132static void XineramaDestroyGC(GCPtr);
133static void XineramaChangeClip(GCPtr, int, pointer, int);
134static void XineramaDestroyClip(GCPtr);
135static void XineramaCopyClip(GCPtr, GCPtr);
136
137static GCFuncs XineramaGCFuncs = {
138 XineramaValidateGC, XineramaChangeGC, XineramaCopyGC, XineramaDestroyGC,
139 XineramaChangeClip, XineramaDestroyClip, XineramaCopyClip
140};
141
142#define Xinerama_GC_FUNC_PROLOGUE(pGC)\
143 PanoramiXGCPtr pGCPriv = (PanoramiXGCPtr) \
144 dixLookupPrivate(&(pGC)->devPrivates, PanoramiXGCKey); \
145 (pGC)->funcs = pGCPriv->wrapFuncs;
146
147#define Xinerama_GC_FUNC_EPILOGUE(pGC)\
148 pGCPriv->wrapFuncs = (pGC)->funcs;\
149 (pGC)->funcs = &XineramaGCFuncs;
150
151static Bool
152XineramaCloseScreen(ScreenPtr pScreen)
153{
154 PanoramiXScreenPtr pScreenPriv = (PanoramiXScreenPtr)
155 dixLookupPrivate(&pScreen->devPrivates, PanoramiXScreenKey);
156
157 pScreen->CloseScreen = pScreenPriv->CloseScreen;
158 pScreen->CreateGC = pScreenPriv->CreateGC;
159
160 if (pScreen->myNum == 0)
161 RegionUninit(&PanoramiXScreenRegion);
162
163 free((pointer) pScreenPriv);
164
165 return (*pScreen->CloseScreen) (pScreen);
166}
167
168static Bool
169XineramaCreateGC(GCPtr pGC)
170{
171 ScreenPtr pScreen = pGC->pScreen;
172 PanoramiXScreenPtr pScreenPriv = (PanoramiXScreenPtr)
173 dixLookupPrivate(&pScreen->devPrivates, PanoramiXScreenKey);
174 Bool ret;
175
176 pScreen->CreateGC = pScreenPriv->CreateGC;
177 if ((ret = (*pScreen->CreateGC) (pGC))) {
178 PanoramiXGCPtr pGCPriv = (PanoramiXGCPtr)
179 dixLookupPrivate(&pGC->devPrivates, PanoramiXGCKey);
180
181 pGCPriv->wrapFuncs = pGC->funcs;
182 pGC->funcs = &XineramaGCFuncs;
183
184 pGCPriv->clipOrg.x = pGC->clipOrg.x;
185 pGCPriv->clipOrg.y = pGC->clipOrg.y;
186 pGCPriv->patOrg.x = pGC->patOrg.x;
187 pGCPriv->patOrg.y = pGC->patOrg.y;
188 }
189 pScreen->CreateGC = XineramaCreateGC;
190
191 return ret;
192}
193
194static void
195XineramaValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDraw)
196{
197 Xinerama_GC_FUNC_PROLOGUE(pGC);
198
199 if ((pDraw->type == DRAWABLE_WINDOW) && !(((WindowPtr) pDraw)->parent)) {
200 /* the root window */
201 int x_off = pGC->pScreen->x;
202 int y_off = pGC->pScreen->y;
203 int new_val;
204
205 new_val = pGCPriv->clipOrg.x - x_off;
206 if (pGC->clipOrg.x != new_val) {
207 pGC->clipOrg.x = new_val;
208 changes |= GCClipXOrigin;
209 }
210 new_val = pGCPriv->clipOrg.y - y_off;
211 if (pGC->clipOrg.y != new_val) {
212 pGC->clipOrg.y = new_val;
213 changes |= GCClipYOrigin;
214 }
215 new_val = pGCPriv->patOrg.x - x_off;
216 if (pGC->patOrg.x != new_val) {
217 pGC->patOrg.x = new_val;
218 changes |= GCTileStipXOrigin;
219 }
220 new_val = pGCPriv->patOrg.y - y_off;
221 if (pGC->patOrg.y != new_val) {
222 pGC->patOrg.y = new_val;
223 changes |= GCTileStipYOrigin;
224 }
225 }
226 else {
227 if (pGC->clipOrg.x != pGCPriv->clipOrg.x) {
228 pGC->clipOrg.x = pGCPriv->clipOrg.x;
229 changes |= GCClipXOrigin;
230 }
231 if (pGC->clipOrg.y != pGCPriv->clipOrg.y) {
232 pGC->clipOrg.y = pGCPriv->clipOrg.y;
233 changes |= GCClipYOrigin;
234 }
235 if (pGC->patOrg.x != pGCPriv->patOrg.x) {
236 pGC->patOrg.x = pGCPriv->patOrg.x;
237 changes |= GCTileStipXOrigin;
238 }
239 if (pGC->patOrg.y != pGCPriv->patOrg.y) {
240 pGC->patOrg.y = pGCPriv->patOrg.y;
241 changes |= GCTileStipYOrigin;
242 }
243 }
244
245 (*pGC->funcs->ValidateGC) (pGC, changes, pDraw);
246 Xinerama_GC_FUNC_EPILOGUE(pGC);
247}
248
249static void
250XineramaDestroyGC(GCPtr pGC)
251{
252 Xinerama_GC_FUNC_PROLOGUE(pGC);
253 (*pGC->funcs->DestroyGC) (pGC);
254 Xinerama_GC_FUNC_EPILOGUE(pGC);
255}
256
257static void
258XineramaChangeGC(GCPtr pGC, unsigned long mask)
259{
260 Xinerama_GC_FUNC_PROLOGUE(pGC);
261
262 if (mask & GCTileStipXOrigin)
263 pGCPriv->patOrg.x = pGC->patOrg.x;
264 if (mask & GCTileStipYOrigin)
265 pGCPriv->patOrg.y = pGC->patOrg.y;
266 if (mask & GCClipXOrigin)
267 pGCPriv->clipOrg.x = pGC->clipOrg.x;
268 if (mask & GCClipYOrigin)
269 pGCPriv->clipOrg.y = pGC->clipOrg.y;
270
271 (*pGC->funcs->ChangeGC) (pGC, mask);
272 Xinerama_GC_FUNC_EPILOGUE(pGC);
273}
274
275static void
276XineramaCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst)
277{
278 PanoramiXGCPtr pSrcPriv = (PanoramiXGCPtr)
279 dixLookupPrivate(&pGCSrc->devPrivates, PanoramiXGCKey);
280
281 Xinerama_GC_FUNC_PROLOGUE(pGCDst);
282
283 if (mask & GCTileStipXOrigin)
284 pGCPriv->patOrg.x = pSrcPriv->patOrg.x;
285 if (mask & GCTileStipYOrigin)
286 pGCPriv->patOrg.y = pSrcPriv->patOrg.y;
287 if (mask & GCClipXOrigin)
288 pGCPriv->clipOrg.x = pSrcPriv->clipOrg.x;
289 if (mask & GCClipYOrigin)
290 pGCPriv->clipOrg.y = pSrcPriv->clipOrg.y;
291
292 (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst);
293 Xinerama_GC_FUNC_EPILOGUE(pGCDst);
294}
295
296static void
297XineramaChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects)
298{
299 Xinerama_GC_FUNC_PROLOGUE(pGC);
300 (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects);
301 Xinerama_GC_FUNC_EPILOGUE(pGC);
302}
303
304static void
305XineramaCopyClip(GCPtr pgcDst, GCPtr pgcSrc)
306{
307 Xinerama_GC_FUNC_PROLOGUE(pgcDst);
308 (*pgcDst->funcs->CopyClip) (pgcDst, pgcSrc);
309 Xinerama_GC_FUNC_EPILOGUE(pgcDst);
310}
311
312static void
313XineramaDestroyClip(GCPtr pGC)
314{
315 Xinerama_GC_FUNC_PROLOGUE(pGC);
316 (*pGC->funcs->DestroyClip) (pGC);
317 Xinerama_GC_FUNC_EPILOGUE(pGC);
318}
319
320int
321XineramaDeleteResource(pointer data, XID id)
322{
323 free(data);
324 return 1;
325}
326
327typedef struct {
328 int screen;
329 int id;
330} PanoramiXSearchData;
331
332static Bool
333XineramaFindIDByScrnum(pointer resource, XID id, pointer privdata)
334{
335 PanoramiXRes *res = (PanoramiXRes *) resource;
336 PanoramiXSearchData *data = (PanoramiXSearchData *) privdata;
337
338 return res->info[data->screen].id == data->id;
339}
340
341PanoramiXRes *
342PanoramiXFindIDByScrnum(RESTYPE type, XID id, int screen)
343{
344 PanoramiXSearchData data;
345 pointer val;
346
347 if (!screen) {
348 dixLookupResourceByType(&val, id, type, serverClient, DixReadAccess);
349 return val;
350 }
351
352 data.screen = screen;
353 data.id = id;
354
355 return LookupClientResourceComplex(clients[CLIENT_ID(id)], type,
356 XineramaFindIDByScrnum, &data);
357}
358
359typedef struct _connect_callback_list {
360 void (*func) (void);
361 struct _connect_callback_list *next;
362} XineramaConnectionCallbackList;
363
364static XineramaConnectionCallbackList *ConnectionCallbackList = NULL;
365
366Bool
367XineramaRegisterConnectionBlockCallback(void (*func) (void))
368{
369 XineramaConnectionCallbackList *newlist;
370
371 if (!(newlist = malloc(sizeof(XineramaConnectionCallbackList))))
372 return FALSE;
373
374 newlist->next = ConnectionCallbackList;
375 newlist->func = func;
376 ConnectionCallbackList = newlist;
377
378 return TRUE;
379}
380
381static void
382XineramaInitData(void)
383{
384 int i, w, h;
385
386 RegionNull(&PanoramiXScreenRegion);
387 FOR_NSCREENS(i) {
388 BoxRec TheBox;
389 RegionRec ScreenRegion;
390
391 ScreenPtr pScreen = screenInfo.screens[i];
392
393 TheBox.x1 = pScreen->x;
394 TheBox.x2 = TheBox.x1 + pScreen->width;
395 TheBox.y1 = pScreen->y;
396 TheBox.y2 = TheBox.y1 + pScreen->height;
397
398 RegionInit(&ScreenRegion, &TheBox, 1);
399 RegionUnion(&PanoramiXScreenRegion, &PanoramiXScreenRegion,
400 &ScreenRegion);
401 RegionUninit(&ScreenRegion);
402 }
403
404 PanoramiXPixWidth = screenInfo.screens[0]->x + screenInfo.screens[0]->width;
405 PanoramiXPixHeight =
406 screenInfo.screens[0]->y + screenInfo.screens[0]->height;
407
408 FOR_NSCREENS_FORWARD_SKIP(i) {
409 ScreenPtr pScreen = screenInfo.screens[i];
410
411 w = pScreen->x + pScreen->width;
412 h = pScreen->y + pScreen->height;
413
414 if (PanoramiXPixWidth < w)
415 PanoramiXPixWidth = w;
416 if (PanoramiXPixHeight < h)
417 PanoramiXPixHeight = h;
418 }
419}
420
421void
422XineramaReinitData(void)
423{
424 RegionUninit(&PanoramiXScreenRegion);
425 XineramaInitData();
426}
427
428/*
429 * PanoramiXExtensionInit():
430 * Called from InitExtensions in main().
431 * Register PanoramiXeen Extension
432 * Initialize global variables.
433 */
434
435void
436PanoramiXExtensionInit(void)
437{
438 int i;
439 Bool success = FALSE;
440 ExtensionEntry *extEntry;
441 ScreenPtr pScreen = screenInfo.screens[0];
442 PanoramiXScreenPtr pScreenPriv;
443
444 if (noPanoramiXExtension)
445 return;
446
447 if (!dixRegisterPrivateKey(&PanoramiXScreenKeyRec, PRIVATE_SCREEN, 0)) {
448 noPanoramiXExtension = TRUE;
449 return;
450 }
451
452 if (!dixRegisterPrivateKey
453 (&PanoramiXGCKeyRec, PRIVATE_GC, sizeof(PanoramiXGCRec))) {
454 noPanoramiXExtension = TRUE;
455 return;
456 }
457
458 PanoramiXNumScreens = screenInfo.numScreens;
459 if (PanoramiXNumScreens == 1) { /* Only 1 screen */
460 noPanoramiXExtension = TRUE;
461 return;
462 }
463
464 while (panoramiXGeneration != serverGeneration) {
465 extEntry = AddExtension(PANORAMIX_PROTOCOL_NAME, 0, 0,
466 ProcPanoramiXDispatch,
467 SProcPanoramiXDispatch, PanoramiXResetProc,
468 StandardMinorOpcode);
469 if (!extEntry)
470 break;
471
472 /*
473 * First make sure all the basic allocations succeed. If not,
474 * run in non-PanoramiXeen mode.
475 */
476
477 FOR_NSCREENS(i) {
478 pScreen = screenInfo.screens[i];
479 pScreenPriv = malloc(sizeof(PanoramiXScreenRec));
480 dixSetPrivate(&pScreen->devPrivates, PanoramiXScreenKey,
481 pScreenPriv);
482 if (!pScreenPriv) {
483 noPanoramiXExtension = TRUE;
484 return;
485 }
486
487 pScreenPriv->CreateGC = pScreen->CreateGC;
488 pScreenPriv->CloseScreen = pScreen->CloseScreen;
489
490 pScreen->CreateGC = XineramaCreateGC;
491 pScreen->CloseScreen = XineramaCloseScreen;
492 }
493
494 XRC_DRAWABLE = CreateNewResourceClass();
495 XRT_WINDOW = CreateNewResourceType(XineramaDeleteResource,
496 "XineramaWindow");
497 if (XRT_WINDOW)
498 XRT_WINDOW |= XRC_DRAWABLE;
499 XRT_PIXMAP = CreateNewResourceType(XineramaDeleteResource,
500 "XineramaPixmap");
501 if (XRT_PIXMAP)
502 XRT_PIXMAP |= XRC_DRAWABLE;
503 XRT_GC = CreateNewResourceType(XineramaDeleteResource, "XineramaGC");
504 XRT_COLORMAP = CreateNewResourceType(XineramaDeleteResource,
505 "XineramaColormap");
506
507 if (XRT_WINDOW && XRT_PIXMAP && XRT_GC && XRT_COLORMAP) {
508 panoramiXGeneration = serverGeneration;
509 success = TRUE;
510 }
511 SetResourceTypeErrorValue(XRT_WINDOW, BadWindow);
512 SetResourceTypeErrorValue(XRT_PIXMAP, BadPixmap);
513 SetResourceTypeErrorValue(XRT_GC, BadGC);
514 SetResourceTypeErrorValue(XRT_COLORMAP, BadColor);
515 }
516
517 if (!success) {
518 noPanoramiXExtension = TRUE;
519 ErrorF(PANORAMIX_PROTOCOL_NAME " extension failed to initialize\n");
520 return;
521 }
522
523 XineramaInitData();
524
525 /*
526 * Put our processes into the ProcVector
527 */
528
529 for (i = 256; i--;)
530 SavedProcVector[i] = ProcVector[i];
531
532 ProcVector[X_CreateWindow] = PanoramiXCreateWindow;
533 ProcVector[X_ChangeWindowAttributes] = PanoramiXChangeWindowAttributes;
534 ProcVector[X_DestroyWindow] = PanoramiXDestroyWindow;
535 ProcVector[X_DestroySubwindows] = PanoramiXDestroySubwindows;
536 ProcVector[X_ChangeSaveSet] = PanoramiXChangeSaveSet;
537 ProcVector[X_ReparentWindow] = PanoramiXReparentWindow;
538 ProcVector[X_MapWindow] = PanoramiXMapWindow;
539 ProcVector[X_MapSubwindows] = PanoramiXMapSubwindows;
540 ProcVector[X_UnmapWindow] = PanoramiXUnmapWindow;
541 ProcVector[X_UnmapSubwindows] = PanoramiXUnmapSubwindows;
542 ProcVector[X_ConfigureWindow] = PanoramiXConfigureWindow;
543 ProcVector[X_CirculateWindow] = PanoramiXCirculateWindow;
544 ProcVector[X_GetGeometry] = PanoramiXGetGeometry;
545 ProcVector[X_TranslateCoords] = PanoramiXTranslateCoords;
546 ProcVector[X_CreatePixmap] = PanoramiXCreatePixmap;
547 ProcVector[X_FreePixmap] = PanoramiXFreePixmap;
548 ProcVector[X_CreateGC] = PanoramiXCreateGC;
549 ProcVector[X_ChangeGC] = PanoramiXChangeGC;
550 ProcVector[X_CopyGC] = PanoramiXCopyGC;
551 ProcVector[X_SetDashes] = PanoramiXSetDashes;
552 ProcVector[X_SetClipRectangles] = PanoramiXSetClipRectangles;
553 ProcVector[X_FreeGC] = PanoramiXFreeGC;
554 ProcVector[X_ClearArea] = PanoramiXClearToBackground;
555 ProcVector[X_CopyArea] = PanoramiXCopyArea;
556 ProcVector[X_CopyPlane] = PanoramiXCopyPlane;
557 ProcVector[X_PolyPoint] = PanoramiXPolyPoint;
558 ProcVector[X_PolyLine] = PanoramiXPolyLine;
559 ProcVector[X_PolySegment] = PanoramiXPolySegment;
560 ProcVector[X_PolyRectangle] = PanoramiXPolyRectangle;
561 ProcVector[X_PolyArc] = PanoramiXPolyArc;
562 ProcVector[X_FillPoly] = PanoramiXFillPoly;
563 ProcVector[X_PolyFillRectangle] = PanoramiXPolyFillRectangle;
564 ProcVector[X_PolyFillArc] = PanoramiXPolyFillArc;
565 ProcVector[X_PutImage] = PanoramiXPutImage;
566 ProcVector[X_GetImage] = PanoramiXGetImage;
567 ProcVector[X_PolyText8] = PanoramiXPolyText8;
568 ProcVector[X_PolyText16] = PanoramiXPolyText16;
569 ProcVector[X_ImageText8] = PanoramiXImageText8;
570 ProcVector[X_ImageText16] = PanoramiXImageText16;
571 ProcVector[X_CreateColormap] = PanoramiXCreateColormap;
572 ProcVector[X_FreeColormap] = PanoramiXFreeColormap;
573 ProcVector[X_CopyColormapAndFree] = PanoramiXCopyColormapAndFree;
574 ProcVector[X_InstallColormap] = PanoramiXInstallColormap;
575 ProcVector[X_UninstallColormap] = PanoramiXUninstallColormap;
576 ProcVector[X_AllocColor] = PanoramiXAllocColor;
577 ProcVector[X_AllocNamedColor] = PanoramiXAllocNamedColor;
578 ProcVector[X_AllocColorCells] = PanoramiXAllocColorCells;
579 ProcVector[X_AllocColorPlanes] = PanoramiXAllocColorPlanes;
580 ProcVector[X_FreeColors] = PanoramiXFreeColors;
581 ProcVector[X_StoreColors] = PanoramiXStoreColors;
582 ProcVector[X_StoreNamedColor] = PanoramiXStoreNamedColor;
583
584 PanoramiXRenderInit();
585 PanoramiXFixesInit();
586 PanoramiXDamageInit();
587#ifdef COMPOSITE
588 PanoramiXCompositeInit();
589#endif
590
591}
592
593Bool
594PanoramiXCreateConnectionBlock(void)
595{
596 int i, j, length;
597 Bool disable_backing_store = FALSE;
598 int old_width, old_height;
599 float width_mult, height_mult;
600 xWindowRoot *root;
601 xVisualType *visual;
602 xDepth *depth;
603 VisualPtr pVisual;
604 ScreenPtr pScreen;
605
606 /*
607 * Do normal CreateConnectionBlock but faking it for only one screen
608 */
609
610 if (!PanoramiXNumDepths) {
611 ErrorF("Xinerama error: No common visuals\n");
612 return FALSE;
613 }
614
615 for (i = 1; i < screenInfo.numScreens; i++) {
616 pScreen = screenInfo.screens[i];
617 if (pScreen->rootDepth != screenInfo.screens[0]->rootDepth) {
618 ErrorF("Xinerama error: Root window depths differ\n");
619 return FALSE;
620 }
621 if (pScreen->backingStoreSupport !=
622 screenInfo.screens[0]->backingStoreSupport)
623 disable_backing_store = TRUE;
624 }
625
626 if (disable_backing_store) {
627 for (i = 0; i < screenInfo.numScreens; i++) {
628 pScreen = screenInfo.screens[i];
629 pScreen->backingStoreSupport = NotUseful;
630 }
631 }
632
633 i = screenInfo.numScreens;
634 screenInfo.numScreens = 1;
635 if (!CreateConnectionBlock()) {
636 screenInfo.numScreens = i;
637 return FALSE;
638 }
639
640 screenInfo.numScreens = i;
641
642 root = (xWindowRoot *) (ConnectionInfo + connBlockScreenStart);
643 length = connBlockScreenStart + sizeof(xWindowRoot);
644
645 /* overwrite the connection block */
646 root->nDepths = PanoramiXNumDepths;
647
648 for (i = 0; i < PanoramiXNumDepths; i++) {
649 depth = (xDepth *) (ConnectionInfo + length);
650 depth->depth = PanoramiXDepths[i].depth;
651 depth->nVisuals = PanoramiXDepths[i].numVids;
652 length += sizeof(xDepth);
653 visual = (xVisualType *) (ConnectionInfo + length);
654
655 for (j = 0; j < depth->nVisuals; j++, visual++) {
656 visual->visualID = PanoramiXDepths[i].vids[j];
657
658 for (pVisual = PanoramiXVisuals;
659 pVisual->vid != visual->visualID; pVisual++);
660
661 visual->class = pVisual->class;
662 visual->bitsPerRGB = pVisual->bitsPerRGBValue;
663 visual->colormapEntries = pVisual->ColormapEntries;
664 visual->redMask = pVisual->redMask;
665 visual->greenMask = pVisual->greenMask;
666 visual->blueMask = pVisual->blueMask;
667 }
668
669 length += (depth->nVisuals * sizeof(xVisualType));
670 }
671
672 connSetupPrefix.length = bytes_to_int32(length);
673
674 for (i = 0; i < PanoramiXNumDepths; i++)
675 free(PanoramiXDepths[i].vids);
676 free(PanoramiXDepths);
677 PanoramiXDepths = NULL;
678
679 /*
680 * OK, change some dimensions so it looks as if it were one big screen
681 */
682
683 old_width = root->pixWidth;
684 old_height = root->pixHeight;
685
686 root->pixWidth = PanoramiXPixWidth;
687 root->pixHeight = PanoramiXPixHeight;
688 width_mult = (1.0 * root->pixWidth) / old_width;
689 height_mult = (1.0 * root->pixHeight) / old_height;
690 root->mmWidth *= width_mult;
691 root->mmHeight *= height_mult;
692
693 while (ConnectionCallbackList) {
694 pointer tmp;
695
696 tmp = (pointer) ConnectionCallbackList;
697 (*ConnectionCallbackList->func) ();
698 ConnectionCallbackList = ConnectionCallbackList->next;
699 free(tmp);
700 }
701
702 return TRUE;
703}
704
705/*
706 * This isn't just memcmp(), bitsPerRGBValue is skipped. markv made that
707 * change way back before xf86 4.0, but the comment for _why_ is a bit
708 * opaque, so I'm not going to question it for now.
709 *
710 * This is probably better done as a screen hook so DBE/EVI/GLX can add
711 * their own tests, and adding privates to VisualRec so they don't have to
712 * do their own back-mapping.
713 */
714static Bool
715VisualsEqual(VisualPtr a, ScreenPtr pScreenB, VisualPtr b)
716{
717 return ((a->class == b->class) &&
718 (a->ColormapEntries == b->ColormapEntries) &&
719 (a->nplanes == b->nplanes) &&
720 (a->redMask == b->redMask) &&
721 (a->greenMask == b->greenMask) &&
722 (a->blueMask == b->blueMask) &&
723 (a->offsetRed == b->offsetRed) &&
724 (a->offsetGreen == b->offsetGreen) &&
725 (a->offsetBlue == b->offsetBlue));
726}
727
728static void
729PanoramiXMaybeAddDepth(DepthPtr pDepth)
730{
731 ScreenPtr pScreen;
732 int j, k;
733 Bool found = FALSE;
734
735 FOR_NSCREENS_FORWARD_SKIP(j) {
736 pScreen = screenInfo.screens[j];
737 for (k = 0; k < pScreen->numDepths; k++) {
738 if (pScreen->allowedDepths[k].depth == pDepth->depth) {
739 found = TRUE;
740 break;
741 }
742 }
743 }
744
745 if (!found)
746 return;
747
748 j = PanoramiXNumDepths;
749 PanoramiXNumDepths++;
750 PanoramiXDepths = realloc(PanoramiXDepths,
751 PanoramiXNumDepths * sizeof(DepthRec));
752 PanoramiXDepths[j].depth = pDepth->depth;
753 PanoramiXDepths[j].numVids = 0;
754 /* XXX suboptimal, should grow these dynamically */
755 if (pDepth->numVids)
756 PanoramiXDepths[j].vids = malloc(sizeof(VisualID) * pDepth->numVids);
757 else
758 PanoramiXDepths[j].vids = NULL;
759}
760
761static void
762PanoramiXMaybeAddVisual(VisualPtr pVisual)
763{
764 ScreenPtr pScreen;
765 int j, k;
766 Bool found = FALSE;
767
768 FOR_NSCREENS_FORWARD_SKIP(j) {
769 pScreen = screenInfo.screens[j];
770 found = FALSE;
771
772 for (k = 0; k < pScreen->numVisuals; k++) {
773 VisualPtr candidate = &pScreen->visuals[k];
774
775 if ((*XineramaVisualsEqualPtr) (pVisual, pScreen, candidate)
776#ifdef GLXPROXY
777 && glxMatchVisual(screenInfo.screens[0], pVisual, pScreen)
778#endif
779 ) {
780 found = TRUE;
781 break;
782 }
783 }
784
785 if (!found)
786 return;
787 }
788
789 /* found a matching visual on all screens, add it to the subset list */
790 j = PanoramiXNumVisuals;
791 PanoramiXNumVisuals++;
792 PanoramiXVisuals = realloc(PanoramiXVisuals,
793 PanoramiXNumVisuals * sizeof(VisualRec));
794
795 memcpy(&PanoramiXVisuals[j], pVisual, sizeof(VisualRec));
796
797 for (k = 0; k < PanoramiXNumDepths; k++) {
798 if (PanoramiXDepths[k].depth == pVisual->nplanes) {
799 PanoramiXDepths[k].vids[PanoramiXDepths[k].numVids] = pVisual->vid;
800 PanoramiXDepths[k].numVids++;
801 break;
802 }
803 }
804}
805
806extern void
807PanoramiXConsolidate(void)
808{
809 int i;
810 PanoramiXRes *root, *defmap, *saver;
811 ScreenPtr pScreen = screenInfo.screens[0];
812 DepthPtr pDepth = pScreen->allowedDepths;
813 VisualPtr pVisual = pScreen->visuals;
814
815 PanoramiXNumDepths = 0;
816 PanoramiXNumVisuals = 0;
817
818 for (i = 0; i < pScreen->numDepths; i++)
819 PanoramiXMaybeAddDepth(pDepth++);
820
821 for (i = 0; i < pScreen->numVisuals; i++)
822 PanoramiXMaybeAddVisual(pVisual++);
823
824 root = malloc(sizeof(PanoramiXRes));
825 root->type = XRT_WINDOW;
826 defmap = malloc(sizeof(PanoramiXRes));
827 defmap->type = XRT_COLORMAP;
828 saver = malloc(sizeof(PanoramiXRes));
829 saver->type = XRT_WINDOW;
830
831 FOR_NSCREENS(i) {
832 ScreenPtr scr = screenInfo.screens[i];
833
834 root->info[i].id = scr->root->drawable.id;
835 root->u.win.class = InputOutput;
836 root->u.win.root = TRUE;
837 saver->info[i].id = scr->screensaver.wid;
838 saver->u.win.class = InputOutput;
839 saver->u.win.root = TRUE;
840 defmap->info[i].id = scr->defColormap;
841 }
842
843 AddResource(root->info[0].id, XRT_WINDOW, root);
844 AddResource(saver->info[0].id, XRT_WINDOW, saver);
845 AddResource(defmap->info[0].id, XRT_COLORMAP, defmap);
846}
847
848VisualID
849PanoramiXTranslateVisualID(int screen, VisualID orig)
850{
851 ScreenPtr pOtherScreen = screenInfo.screens[screen];
852 VisualPtr pVisual = NULL;
853 int i;
854
855 for (i = 0; i < PanoramiXNumVisuals; i++) {
856 if (orig == PanoramiXVisuals[i].vid) {
857 pVisual = &PanoramiXVisuals[i];
858 break;
859 }
860 }
861
862 if (!pVisual)
863 return 0;
864
865 /* if screen is 0, orig is already the correct visual ID */
866 if (screen == 0)
867 return orig;
868
869 /* found the original, now translate it relative to the backend screen */
870 for (i = 0; i < pOtherScreen->numVisuals; i++) {
871 VisualPtr pOtherVisual = &pOtherScreen->visuals[i];
872
873 if ((*XineramaVisualsEqualPtr) (pVisual, pOtherScreen, pOtherVisual))
874 return pOtherVisual->vid;
875 }
876
877 return 0;
878}
879
880/*
881 * PanoramiXResetProc()
882 * Exit, deallocating as needed.
883 */
884
885static void
886PanoramiXResetProc(ExtensionEntry * extEntry)
887{
888 int i;
889
890 PanoramiXRenderReset();
891 PanoramiXFixesReset();
892 PanoramiXDamageReset();
893#ifdef COMPOSITE
894 PanoramiXCompositeReset ();
895#endif
896 screenInfo.numScreens = PanoramiXNumScreens;
897 for (i = 256; i--;)
898 ProcVector[i] = SavedProcVector[i];
899}
900
901int
902ProcPanoramiXQueryVersion(ClientPtr client)
903{
904 /* REQUEST(xPanoramiXQueryVersionReq); */
905 xPanoramiXQueryVersionReply rep = {
906 .type = X_Reply,
907 .sequenceNumber = client->sequence,
908 .length = 0,
909 .majorVersion = SERVER_PANORAMIX_MAJOR_VERSION,
910 .minorVersion = SERVER_PANORAMIX_MINOR_VERSION
911 };
912
913 REQUEST_SIZE_MATCH(xPanoramiXQueryVersionReq);
914 if (client->swapped) {
915 swaps(&rep.sequenceNumber);
916 swapl(&rep.length);
917 swaps(&rep.majorVersion);
918 swaps(&rep.minorVersion);
919 }
920 WriteToClient(client, sizeof(xPanoramiXQueryVersionReply), &rep);
921 return Success;
922}
923
924int
925ProcPanoramiXGetState(ClientPtr client)
926{
927 REQUEST(xPanoramiXGetStateReq);
928 WindowPtr pWin;
929 xPanoramiXGetStateReply rep;
930 int rc;
931
932 REQUEST_SIZE_MATCH(xPanoramiXGetStateReq);
933 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
934 if (rc != Success)
935 return rc;
936
937 rep = (xPanoramiXGetStateReply) {
938 .type = X_Reply,
939 .state = !noPanoramiXExtension,
940 .sequenceNumber = client->sequence,
941 .length = 0,
942 .window = stuff->window
943 };
944 if (client->swapped) {
945 swaps(&rep.sequenceNumber);
946 swapl(&rep.length);
947 swapl(&rep.window);
948 }
949 WriteToClient(client, sizeof(xPanoramiXGetStateReply), &rep);
950 return Success;
951
952}
953
954int
955ProcPanoramiXGetScreenCount(ClientPtr client)
956{
957 REQUEST(xPanoramiXGetScreenCountReq);
958 WindowPtr pWin;
959 xPanoramiXGetScreenCountReply rep;
960 int rc;
961
962 REQUEST_SIZE_MATCH(xPanoramiXGetScreenCountReq);
963 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
964 if (rc != Success)
965 return rc;
966
967 rep = (xPanoramiXGetScreenCountReply) {
968 .type = X_Reply,
969 .ScreenCount = PanoramiXNumScreens,
970 .sequenceNumber = client->sequence,
971 .length = 0,
972 .window = stuff->window
973 };
974 if (client->swapped) {
975 swaps(&rep.sequenceNumber);
976 swapl(&rep.length);
977 swapl(&rep.window);
978 }
979 WriteToClient(client, sizeof(xPanoramiXGetScreenCountReply), &rep);
980 return Success;
981}
982
983int
984ProcPanoramiXGetScreenSize(ClientPtr client)
985{
986 REQUEST(xPanoramiXGetScreenSizeReq);
987 WindowPtr pWin;
988 xPanoramiXGetScreenSizeReply rep;
989 int rc;
990
991 if (stuff->screen >= PanoramiXNumScreens)
992 return BadMatch;
993
994 REQUEST_SIZE_MATCH(xPanoramiXGetScreenSizeReq);
995 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
996 if (rc != Success)
997 return rc;
998
999 rep = (xPanoramiXGetScreenSizeReply) {
1000 .type = X_Reply,
1001 .sequenceNumber = client->sequence,
1002 .length = 0,
1003 /* screen dimensions */
1004 .width = screenInfo.screens[stuff->screen]->width,
1005 .height = screenInfo.screens[stuff->screen]->height,
1006 .window = stuff->window,
1007 .screen = stuff->screen
1008 };
1009 if (client->swapped) {
1010 swaps(&rep.sequenceNumber);
1011 swapl(&rep.length);
1012 swapl(&rep.width);
1013 swapl(&rep.height);
1014 swapl(&rep.window);
1015 swapl(&rep.screen);
1016 }
1017 WriteToClient(client, sizeof(xPanoramiXGetScreenSizeReply), &rep);
1018 return Success;
1019}
1020
1021int
1022ProcXineramaIsActive(ClientPtr client)
1023{
1024 /* REQUEST(xXineramaIsActiveReq); */
1025 xXineramaIsActiveReply rep;
1026
1027 REQUEST_SIZE_MATCH(xXineramaIsActiveReq);
1028
1029 rep = (xXineramaIsActiveReply) {
1030 .type = X_Reply,
1031 .sequenceNumber = client->sequence,
1032 .length = 0,
1033#if 1
1034 /* The following hack fools clients into thinking that Xinerama
1035 * is disabled even though it is not. */
1036 .state = !noPanoramiXExtension && !PanoramiXExtensionDisabledHack
1037#else
1038 .state = !noPanoramiXExtension;
1039#endif
1040 };
1041 if (client->swapped) {
1042 swaps(&rep.sequenceNumber);
1043 swapl(&rep.length);
1044 swapl(&rep.state);
1045 }
1046 WriteToClient(client, sizeof(xXineramaIsActiveReply), &rep);
1047 return Success;
1048}
1049
1050int
1051ProcXineramaQueryScreens(ClientPtr client)
1052{
1053 /* REQUEST(xXineramaQueryScreensReq); */
1054 CARD32 number = (noPanoramiXExtension) ? 0 : PanoramiXNumScreens;
1055 xXineramaQueryScreensReply rep = {
1056 .type = X_Reply,
1057 .sequenceNumber = client->sequence,
1058 .length = bytes_to_int32(number * sz_XineramaScreenInfo),
1059 .number = number
1060 };
1061
1062 REQUEST_SIZE_MATCH(xXineramaQueryScreensReq);
1063
1064 if (client->swapped) {
1065 swaps(&rep.sequenceNumber);
1066 swapl(&rep.length);
1067 swapl(&rep.number);
1068 }
1069 WriteToClient(client, sizeof(xXineramaQueryScreensReply), &rep);
1070
1071 if (!noPanoramiXExtension) {
1072 xXineramaScreenInfo scratch;
1073 int i;
1074
1075 FOR_NSCREENS(i) {
1076 scratch.x_org = screenInfo.screens[i]->x;
1077 scratch.y_org = screenInfo.screens[i]->y;
1078 scratch.width = screenInfo.screens[i]->width;
1079 scratch.height = screenInfo.screens[i]->height;
1080
1081 if (client->swapped) {
1082 swaps(&scratch.x_org);
1083 swaps(&scratch.y_org);
1084 swaps(&scratch.width);
1085 swaps(&scratch.height);
1086 }
1087 WriteToClient(client, sz_XineramaScreenInfo, &scratch);
1088 }
1089 }
1090
1091 return Success;
1092}
1093
1094static int
1095ProcPanoramiXDispatch(ClientPtr client)
1096{
1097 REQUEST(xReq);
1098 switch (stuff->data) {
1099 case X_PanoramiXQueryVersion:
1100 return ProcPanoramiXQueryVersion(client);
1101 case X_PanoramiXGetState:
1102 return ProcPanoramiXGetState(client);
1103 case X_PanoramiXGetScreenCount:
1104 return ProcPanoramiXGetScreenCount(client);
1105 case X_PanoramiXGetScreenSize:
1106 return ProcPanoramiXGetScreenSize(client);
1107 case X_XineramaIsActive:
1108 return ProcXineramaIsActive(client);
1109 case X_XineramaQueryScreens:
1110 return ProcXineramaQueryScreens(client);
1111 }
1112 return BadRequest;
1113}
1114
1115#if X_BYTE_ORDER == X_LITTLE_ENDIAN
1116#define SHIFT_L(v,s) (v) << (s)
1117#define SHIFT_R(v,s) (v) >> (s)
1118#else
1119#define SHIFT_L(v,s) (v) >> (s)
1120#define SHIFT_R(v,s) (v) << (s)
1121#endif
1122
1123static void
1124CopyBits(char *dst, int shiftL, char *src, int bytes)
1125{
1126 /* Just get it to work. Worry about speed later */
1127 int shiftR = 8 - shiftL;
1128
1129 while (bytes--) {
1130 *dst |= SHIFT_L(*src, shiftL);
1131 *(dst + 1) |= SHIFT_R(*src, shiftR);
1132 dst++;
1133 src++;
1134 }
1135}
1136
1137/* Caution. This doesn't support 2 and 4 bpp formats. We expect
1138 1 bpp and planar data to be already cleared when presented
1139 to this function */
1140
1141void
1142XineramaGetImageData(DrawablePtr *pDrawables,
1143 int left,
1144 int top,
1145 int width,
1146 int height,
1147 unsigned int format,
1148 unsigned long planemask,
1149 char *data, int pitch, Bool isRoot)
1150{
1151 RegionRec SrcRegion, ScreenRegion, GrabRegion;
1152 BoxRec SrcBox, *pbox;
1153 int x, y, w, h, i, j, nbox, size, sizeNeeded, ScratchPitch, inOut, depth;
1154 DrawablePtr pDraw = pDrawables[0];
1155 char *ScratchMem = NULL;
1156
1157 size = 0;
1158
1159 /* find box in logical screen space */
1160 SrcBox.x1 = left;
1161 SrcBox.y1 = top;
1162 if (!isRoot) {
1163 SrcBox.x1 += pDraw->x + screenInfo.screens[0]->x;
1164 SrcBox.y1 += pDraw->y + screenInfo.screens[0]->y;
1165 }
1166 SrcBox.x2 = SrcBox.x1 + width;
1167 SrcBox.y2 = SrcBox.y1 + height;
1168
1169 RegionInit(&SrcRegion, &SrcBox, 1);
1170 RegionNull(&GrabRegion);
1171
1172 depth = (format == XYPixmap) ? 1 : pDraw->depth;
1173
1174 FOR_NSCREENS(i) {
1175 BoxRec TheBox;
1176 ScreenPtr pScreen;
1177
1178 pDraw = pDrawables[i];
1179 pScreen = pDraw->pScreen;
1180
1181 TheBox.x1 = pScreen->x;
1182 TheBox.x2 = TheBox.x1 + pScreen->width;
1183 TheBox.y1 = pScreen->y;
1184 TheBox.y2 = TheBox.y1 + pScreen->height;
1185
1186 RegionInit(&ScreenRegion, &TheBox, 1);
1187 inOut = RegionContainsRect(&ScreenRegion, &SrcBox);
1188 if (inOut == rgnPART)
1189 RegionIntersect(&GrabRegion, &SrcRegion, &ScreenRegion);
1190 RegionUninit(&ScreenRegion);
1191
1192 if (inOut == rgnIN) {
1193 (*pScreen->GetImage) (pDraw,
1194 SrcBox.x1 - pDraw->x -
1195 screenInfo.screens[i]->x,
1196 SrcBox.y1 - pDraw->y -
1197 screenInfo.screens[i]->y, width, height,
1198 format, planemask, data);
1199 break;
1200 }
1201 else if (inOut == rgnOUT)
1202 continue;
1203
1204 nbox = RegionNumRects(&GrabRegion);
1205
1206 if (nbox) {
1207 pbox = RegionRects(&GrabRegion);
1208
1209 while (nbox--) {
1210 w = pbox->x2 - pbox->x1;
1211 h = pbox->y2 - pbox->y1;
1212 ScratchPitch = PixmapBytePad(w, depth);
1213 sizeNeeded = ScratchPitch * h;
1214
1215 if (sizeNeeded > size) {
1216 char *tmpdata = ScratchMem;
1217
1218 ScratchMem = realloc(ScratchMem, sizeNeeded);
1219 if (ScratchMem)
1220 size = sizeNeeded;
1221 else {
1222 ScratchMem = tmpdata;
1223 break;
1224 }
1225 }
1226
1227 x = pbox->x1 - pDraw->x - screenInfo.screens[i]->x;
1228 y = pbox->y1 - pDraw->y - screenInfo.screens[i]->y;
1229
1230 (*pScreen->GetImage) (pDraw, x, y, w, h,
1231 format, planemask, ScratchMem);
1232
1233 /* copy the memory over */
1234
1235 if (depth == 1) {
1236 int k, shift, leftover, index, index2;
1237
1238 x = pbox->x1 - SrcBox.x1;
1239 y = pbox->y1 - SrcBox.y1;
1240 shift = x & 7;
1241 x >>= 3;
1242 leftover = w & 7;
1243 w >>= 3;
1244
1245 /* clean up the edge */
1246 if (leftover) {
1247 int mask = (1 << leftover) - 1;
1248
1249 for (j = h, k = w; j--; k += ScratchPitch)
1250 ScratchMem[k] &= mask;
1251 }
1252
1253 for (j = 0, index = (pitch * y) + x, index2 = 0; j < h;
1254 j++, index += pitch, index2 += ScratchPitch) {
1255 if (w) {
1256 if (!shift)
1257 memcpy(data + index, ScratchMem + index2, w);
1258 else
1259 CopyBits(data + index, shift,
1260 ScratchMem + index2, w);
1261 }
1262
1263 if (leftover) {
1264 data[index + w] |=
1265 SHIFT_L(ScratchMem[index2 + w], shift);
1266 if ((shift + leftover) > 8)
1267 data[index + w + 1] |=
1268 SHIFT_R(ScratchMem[index2 + w],
1269 (8 - shift));
1270 }
1271 }
1272 }
1273 else {
1274 j = BitsPerPixel(depth) >> 3;
1275 x = (pbox->x1 - SrcBox.x1) * j;
1276 y = pbox->y1 - SrcBox.y1;
1277 w *= j;
1278
1279 for (j = 0; j < h; j++) {
1280 memcpy(data + (pitch * (y + j)) + x,
1281 ScratchMem + (ScratchPitch * j), w);
1282 }
1283 }
1284 pbox++;
1285 }
1286
1287 RegionSubtract(&SrcRegion, &SrcRegion, &GrabRegion);
1288 if (!RegionNotEmpty(&SrcRegion))
1289 break;
1290 }
1291
1292 }
1293
1294 free(ScratchMem);
1295
1296 RegionUninit(&SrcRegion);
1297 RegionUninit(&GrabRegion);
1298}