1/*
2 * Copyright © 2004 Eric Anholt
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of Eric Anholt not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  Eric Anholt makes no
11 * representations about the suitability of this software for any purpose.  It
12 * is provided "as is" without express or implied warranty.
13 *
14 * ERIC ANHOLT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL ERIC ANHOLT BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#ifdef HAVE_DIX_CONFIG_H
24#include <dix-config.h>
25#endif
26
27#include <string.h>
28
29#include "gcstruct.h"
30#include "windowstr.h"
31#include "cw.h"
32
33#define CW_DEBUG 1
34
35#if CW_DEBUG
36#define CW_ASSERT(x) do {						\
37    if (!(x)) {								\
38	ErrorF("composite wrapper: assertion failed at %s:%d\n", __FUNC__, \
39	    __LINE__);							\
40    }									\
41} while (0)
42#else
43#define CW_ASSERT(x) do {} while (0)
44#endif
45
46DevPrivateKeyRec cwGCKeyRec;
47DevPrivateKeyRec cwScreenKeyRec;
48DevPrivateKeyRec cwWindowKeyRec;
49DevPrivateKeyRec cwPictureKeyRec;
50
51extern GCOps cwGCOps;
52
53static Bool
54cwCloseScreen (int i, ScreenPtr pScreen);
55
56static void
57cwValidateGC(GCPtr pGC, unsigned long stateChanges, DrawablePtr pDrawable);
58static void
59cwChangeGC(GCPtr pGC, unsigned long mask);
60static void
61cwCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst);
62static void
63cwDestroyGC(GCPtr pGC);
64static void
65cwChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects);
66static void
67cwCopyClip(GCPtr pgcDst, GCPtr pgcSrc);
68static void
69cwDestroyClip(GCPtr pGC);
70
71GCFuncs cwGCFuncs = {
72    cwValidateGC,
73    cwChangeGC,
74    cwCopyGC,
75    cwDestroyGC,
76    cwChangeClip,
77    cwDestroyClip,
78    cwCopyClip,
79};
80
81/* Find the real drawable to draw to, and provide offsets that will translate
82 * window coordinates to backing pixmap coordinates.
83 */
84DrawablePtr
85cwGetBackingDrawable(DrawablePtr pDrawable, int *x_off, int *y_off)
86{
87    PixmapPtr	pPixmap;
88
89    if (pDrawable->type == DRAWABLE_WINDOW &&
90	(pPixmap = getCwPixmap ((WindowPtr) pDrawable)))
91    {
92	*x_off = pDrawable->x - pPixmap->screen_x;
93	*y_off = pDrawable->y - pPixmap->screen_y;
94	return &pPixmap->drawable;
95    } else {
96	*x_off = *y_off = 0;
97	return pDrawable;
98    }
99}
100
101#define FUNC_PROLOGUE(pGC, pPriv) do {					\
102    (pGC)->funcs = (pPriv)->wrapFuncs;					\
103    (pGC)->ops = (pPriv)->wrapOps;					\
104} while (0)
105
106#define FUNC_EPILOGUE(pGC, pPriv) do {					\
107    (pPriv)->wrapFuncs = (pGC)->funcs;					\
108    (pPriv)->wrapOps = (pGC)->ops;					\
109    (pGC)->funcs = &cwGCFuncs;						\
110    (pGC)->ops = &cwGCOps;						\
111} while (0)
112
113
114static Bool
115cwCreateBackingGC(GCPtr pGC, DrawablePtr pDrawable)
116{
117    cwGCRec *pPriv = getCwGC(pGC);
118    int status, x_off, y_off;
119    XID noexpose = xFalse;
120    DrawablePtr pBackingDrawable;
121
122    pBackingDrawable = cwGetBackingDrawable(pDrawable, &x_off, &y_off);
123    pPriv->pBackingGC = CreateGC(pBackingDrawable, GCGraphicsExposures,
124				 &noexpose, &status, (XID)0, serverClient);
125    if (status != Success)
126	return FALSE;
127
128    pPriv->serialNumber = 0;
129    pPriv->stateChanges = GCAllBits;
130
131    return TRUE;
132}
133
134static void
135cwDestroyBackingGC(GCPtr pGC)
136{
137    cwGCPtr pPriv;
138
139    pPriv = (cwGCPtr) getCwGC (pGC);
140
141    if (pPriv->pBackingGC) {
142	FreeGC(pPriv->pBackingGC, (XID)0);
143	pPriv->pBackingGC = NULL;
144    }
145}
146
147static void
148cwValidateGC(GCPtr pGC, unsigned long stateChanges, DrawablePtr pDrawable)
149{
150    GCPtr   	  	pBackingGC;
151    cwGCPtr		pPriv;
152    DrawablePtr		pBackingDrawable;
153    int			x_off, y_off;
154
155    pPriv = (cwGCPtr) getCwGC (pGC);
156
157    FUNC_PROLOGUE(pGC, pPriv);
158
159    /*
160     * Must call ValidateGC to ensure pGC->pCompositeClip is valid
161     */
162    (*pGC->funcs->ValidateGC)(pGC, stateChanges, pDrawable);
163
164    if (!cwDrawableIsRedirWindow(pDrawable)) {
165	cwDestroyBackingGC(pGC);
166	FUNC_EPILOGUE(pGC, pPriv);
167	return;
168    } else {
169	if (!pPriv->pBackingGC && !cwCreateBackingGC(pGC, pDrawable)) {
170	    FUNC_EPILOGUE(pGC, pPriv);
171	    return;
172	}
173    }
174
175    pBackingGC = pPriv->pBackingGC;
176    pBackingDrawable = cwGetBackingDrawable(pDrawable, &x_off, &y_off);
177
178    pPriv->stateChanges |= stateChanges;
179
180    /*
181     * Copy the composite clip into the backing GC if either
182     * the drawable clip list has changed or the client has changed
183     * the client clip data
184     */
185    if (pDrawable->serialNumber != pPriv->serialNumber ||
186	(pPriv->stateChanges & (GCClipXOrigin|GCClipYOrigin|GCClipMask)))
187    {
188	ChangeGCVal vals[2];
189	RegionPtr   pCompositeClip;
190
191	pCompositeClip = RegionCreate(NULL, 0);
192	RegionCopy(pCompositeClip, pGC->pCompositeClip);
193
194	/* Either the drawable has changed, or the clip list in the drawable has
195	 * changed.  Copy the new clip list over and set the new translated
196	 * offset for it.
197	 */
198
199	(*pBackingGC->funcs->ChangeClip) (pBackingGC, CT_REGION,
200					  (pointer) pCompositeClip, 0);
201
202	vals[0].val = x_off - pDrawable->x;
203	vals[1].val = y_off - pDrawable->y;
204	ChangeGC(NullClient, pBackingGC,
205		    (GCClipXOrigin | GCClipYOrigin), vals);
206
207	pPriv->serialNumber = pDrawable->serialNumber;
208	/*
209	 * Mask off any client clip changes to make sure
210	 * the clip list set above remains in effect
211	 */
212	pPriv->stateChanges &= ~(GCClipXOrigin|GCClipYOrigin|GCClipMask);
213    }
214
215    if (pPriv->stateChanges) {
216	CopyGC(pGC, pBackingGC, pPriv->stateChanges);
217	pPriv->stateChanges = 0;
218    }
219
220    if ((pGC->patOrg.x + x_off) != pBackingGC->patOrg.x ||
221	(pGC->patOrg.y + y_off) != pBackingGC->patOrg.y)
222    {
223	ChangeGCVal vals[2];
224	vals[0].val = pGC->patOrg.x + x_off;
225	vals[1].val = pGC->patOrg.y + y_off;
226	ChangeGC(NullClient, pBackingGC,
227		    (GCTileStipXOrigin | GCTileStipYOrigin), vals);
228    }
229
230    ValidateGC(pBackingDrawable, pBackingGC);
231
232    FUNC_EPILOGUE(pGC, pPriv);
233}
234
235static void
236cwChangeGC(GCPtr pGC, unsigned long mask)
237{
238    cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pGC->devPrivates, cwGCKey);
239
240    FUNC_PROLOGUE(pGC, pPriv);
241
242    (*pGC->funcs->ChangeGC) (pGC, mask);
243
244    FUNC_EPILOGUE(pGC, pPriv);
245}
246
247static void
248cwCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst)
249{
250    cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pGCDst->devPrivates, cwGCKey);
251
252    FUNC_PROLOGUE(pGCDst, pPriv);
253
254    (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst);
255
256    FUNC_EPILOGUE(pGCDst, pPriv);
257}
258
259static void
260cwDestroyGC(GCPtr pGC)
261{
262    cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pGC->devPrivates, cwGCKey);
263
264    FUNC_PROLOGUE(pGC, pPriv);
265
266    cwDestroyBackingGC(pGC);
267
268    (*pGC->funcs->DestroyGC) (pGC);
269
270    /* leave it unwrapped */
271}
272
273static void
274cwChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects)
275{
276    cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pGC->devPrivates, cwGCKey);
277
278    FUNC_PROLOGUE(pGC, pPriv);
279
280    (*pGC->funcs->ChangeClip)(pGC, type, pvalue, nrects);
281
282    FUNC_EPILOGUE(pGC, pPriv);
283}
284
285static void
286cwCopyClip(GCPtr pgcDst, GCPtr pgcSrc)
287{
288    cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pgcDst->devPrivates, cwGCKey);
289
290    FUNC_PROLOGUE(pgcDst, pPriv);
291
292    (*pgcDst->funcs->CopyClip)(pgcDst, pgcSrc);
293
294    FUNC_EPILOGUE(pgcDst, pPriv);
295}
296
297static void
298cwDestroyClip(GCPtr pGC)
299{
300    cwGCPtr pPriv = (cwGCPtr)dixLookupPrivate(&pGC->devPrivates, cwGCKey);
301
302    FUNC_PROLOGUE(pGC, pPriv);
303
304    (*pGC->funcs->DestroyClip)(pGC);
305
306    FUNC_EPILOGUE(pGC, pPriv);
307}
308
309/*
310 * Screen wrappers.
311 */
312
313#define SCREEN_PROLOGUE(pScreen, field)				\
314  ((pScreen)->field = getCwScreen(pScreen)->field)
315
316#define SCREEN_EPILOGUE(pScreen, field, wrapper) do {		\
317    getCwScreen(pScreen)->field = (pScreen)->field;		\
318    (pScreen)->field = (wrapper);				\
319} while (0)
320
321static Bool
322cwCreateGC(GCPtr pGC)
323{
324    cwGCPtr	pPriv = getCwGC(pGC);
325    ScreenPtr	pScreen = pGC->pScreen;
326    Bool	ret;
327
328    SCREEN_PROLOGUE(pScreen, CreateGC);
329
330    if ( (ret = (*pScreen->CreateGC)(pGC)) )
331	FUNC_EPILOGUE(pGC, pPriv);
332
333    SCREEN_EPILOGUE(pScreen, CreateGC, cwCreateGC);
334
335    return ret;
336}
337
338static void
339cwGetImage(DrawablePtr pSrc, int x, int y, int w, int h, unsigned int format,
340	   unsigned long planemask, char *pdstLine)
341{
342    ScreenPtr pScreen = pSrc->pScreen;
343    DrawablePtr pBackingDrawable;
344    int src_off_x, src_off_y;
345
346    SCREEN_PROLOGUE(pScreen, GetImage);
347
348    pBackingDrawable = cwGetBackingDrawable(pSrc, &src_off_x, &src_off_y);
349
350    CW_OFFSET_XY_SRC(x, y);
351
352    (*pScreen->GetImage)(pBackingDrawable, x, y, w, h, format, planemask,
353			 pdstLine);
354
355    SCREEN_EPILOGUE(pScreen, GetImage, cwGetImage);
356}
357
358static void
359cwGetSpans(DrawablePtr pSrc, int wMax, DDXPointPtr ppt, int *pwidth,
360	   int nspans, char *pdstStart)
361{
362    ScreenPtr pScreen = pSrc->pScreen;
363    DrawablePtr pBackingDrawable;
364    int i;
365    int src_off_x, src_off_y;
366
367    SCREEN_PROLOGUE(pScreen, GetSpans);
368
369    pBackingDrawable = cwGetBackingDrawable(pSrc, &src_off_x, &src_off_y);
370
371    for (i = 0; i < nspans; i++)
372	CW_OFFSET_XY_SRC(ppt[i].x, ppt[i].y);
373
374    (*pScreen->GetSpans)(pBackingDrawable, wMax, ppt, pwidth, nspans,
375			 pdstStart);
376
377    SCREEN_EPILOGUE(pScreen, GetSpans, cwGetSpans);
378}
379
380
381static void
382cwCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc)
383{
384    ScreenPtr pScreen = pWin->drawable.pScreen;
385
386    SCREEN_PROLOGUE(pScreen, CopyWindow);
387
388    if (!cwDrawableIsRedirWindow((DrawablePtr)pWin)) {
389	(*pScreen->CopyWindow)(pWin, ptOldOrg, prgnSrc);
390    } else {
391	GCPtr	    pGC;
392	BoxPtr	    pExtents;
393	int	    x_off, y_off;
394	int	    dx, dy;
395	PixmapPtr   pBackingPixmap;
396	RegionPtr   pClip;
397	int	    src_x, src_y, dst_x, dst_y, w, h;
398
399	dx = ptOldOrg.x - pWin->drawable.x;
400	dy = ptOldOrg.y - pWin->drawable.y;
401
402	pExtents = RegionExtents(prgnSrc);
403
404	pBackingPixmap = (PixmapPtr) cwGetBackingDrawable((DrawablePtr)pWin,
405							  &x_off, &y_off);
406
407	src_x = pExtents->x1 - pBackingPixmap->screen_x;
408	src_y = pExtents->y1 - pBackingPixmap->screen_y;
409	w = pExtents->x2 - pExtents->x1;
410	h = pExtents->y2 - pExtents->y1;
411	dst_x = src_x - dx;
412	dst_y = src_y - dy;
413
414	/* Translate region (as required by API) */
415	RegionTranslate(prgnSrc, -dx, -dy);
416
417	pGC = GetScratchGC(pBackingPixmap->drawable.depth, pScreen);
418	/*
419	 * Copy region to GC as clip, aligning as dest clip
420	 */
421	pClip = RegionCreate(NULL, 0);
422	RegionIntersect(pClip, &pWin->borderClip, prgnSrc);
423	RegionTranslate(pClip,
424			 -pBackingPixmap->screen_x,
425			 -pBackingPixmap->screen_y);
426
427	(*pGC->funcs->ChangeClip) (pGC, CT_REGION, pClip, 0);
428
429	ValidateGC(&pBackingPixmap->drawable, pGC);
430
431	(*pGC->ops->CopyArea) (&pBackingPixmap->drawable,
432			       &pBackingPixmap->drawable, pGC,
433			       src_x, src_y, w, h, dst_x, dst_y);
434
435	(*pGC->funcs->DestroyClip) (pGC);
436
437	FreeScratchGC(pGC);
438    }
439
440    SCREEN_EPILOGUE(pScreen, CopyWindow, cwCopyWindow);
441}
442
443static PixmapPtr
444cwGetWindowPixmap (WindowPtr pWin)
445{
446    PixmapPtr	pPixmap = getCwPixmap (pWin);
447
448    if (!pPixmap)
449    {
450	ScreenPtr   pScreen = pWin->drawable.pScreen;
451	SCREEN_PROLOGUE(pScreen, GetWindowPixmap);
452	if (pScreen->GetWindowPixmap)
453	    pPixmap = (*pScreen->GetWindowPixmap) (pWin);
454	SCREEN_EPILOGUE(pScreen, GetWindowPixmap, cwGetWindowPixmap);
455    }
456    return pPixmap;
457}
458
459static void
460cwSetWindowPixmap (WindowPtr pWindow, PixmapPtr pPixmap)
461{
462    ScreenPtr	pScreen = pWindow->drawable.pScreen;
463
464    if (pPixmap == (*pScreen->GetScreenPixmap) (pScreen))
465	pPixmap = NULL;
466    setCwPixmap (pWindow, pPixmap);
467}
468
469/* Screen initialization/teardown */
470void
471miInitializeCompositeWrapper(ScreenPtr pScreen)
472{
473    cwScreenPtr pScreenPriv;
474    Bool has_render = GetPictureScreenIfSet(pScreen) != NULL;
475
476    if (!dixRegisterPrivateKey(&cwScreenKeyRec, PRIVATE_SCREEN, 0))
477	return;
478
479    if (!dixRegisterPrivateKey(&cwGCKeyRec, PRIVATE_GC, sizeof(cwGCRec)))
480	return;
481
482    if (!dixRegisterPrivateKey(&cwWindowKeyRec, PRIVATE_WINDOW, 0))
483	return;
484
485    if (!dixRegisterPrivateKey(&cwPictureKeyRec, PRIVATE_PICTURE, 0))
486	return;
487
488    pScreenPriv = malloc(sizeof(cwScreenRec));
489    if (!pScreenPriv)
490	return;
491
492    dixSetPrivate(&pScreen->devPrivates, cwScreenKey, pScreenPriv);
493
494    SCREEN_EPILOGUE(pScreen, CloseScreen, cwCloseScreen);
495    SCREEN_EPILOGUE(pScreen, GetImage, cwGetImage);
496    SCREEN_EPILOGUE(pScreen, GetSpans, cwGetSpans);
497    SCREEN_EPILOGUE(pScreen, CreateGC, cwCreateGC);
498    SCREEN_EPILOGUE(pScreen, CopyWindow, cwCopyWindow);
499
500    SCREEN_EPILOGUE(pScreen, SetWindowPixmap, cwSetWindowPixmap);
501    SCREEN_EPILOGUE(pScreen, GetWindowPixmap, cwGetWindowPixmap);
502
503    if (has_render)
504	cwInitializeRender(pScreen);
505}
506
507static Bool
508cwCloseScreen (int i, ScreenPtr pScreen)
509{
510    cwScreenPtr   pScreenPriv;
511    PictureScreenPtr ps = GetPictureScreenIfSet(pScreen);
512
513    pScreenPriv = (cwScreenPtr)dixLookupPrivate(&pScreen->devPrivates,
514						cwScreenKey);
515    pScreen->CloseScreen = pScreenPriv->CloseScreen;
516    pScreen->GetImage = pScreenPriv->GetImage;
517    pScreen->GetSpans = pScreenPriv->GetSpans;
518    pScreen->CreateGC = pScreenPriv->CreateGC;
519    pScreen->CopyWindow = pScreenPriv->CopyWindow;
520
521    if (ps)
522	cwFiniRender(pScreen);
523
524    free((pointer)pScreenPriv);
525
526    return (*pScreen->CloseScreen)(i, pScreen);
527}
528