compalloc.c revision 4642e01f
1/*
2 * Copyright © 2006 Sun Microsystems
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 Sun Microsystems not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  Sun Microsystems 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 * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL SUN MICROSYSTEMS 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 * Copyright © 2003 Keith Packard
23 *
24 * Permission to use, copy, modify, distribute, and sell this software and its
25 * documentation for any purpose is hereby granted without fee, provided that
26 * the above copyright notice appear in all copies and that both that
27 * copyright notice and this permission notice appear in supporting
28 * documentation, and that the name of Keith Packard not be used in
29 * advertising or publicity pertaining to distribution of the software without
30 * specific, written prior permission.  Keith Packard makes no
31 * representations about the suitability of this software for any purpose.  It
32 * is provided "as is" without express or implied warranty.
33 *
34 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
35 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
36 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
37 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
38 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
39 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
40 * PERFORMANCE OF THIS SOFTWARE.
41 */
42
43#ifdef HAVE_DIX_CONFIG_H
44#include <dix-config.h>
45#endif
46
47#include "compint.h"
48
49static void
50compReportDamage (DamagePtr pDamage, RegionPtr pRegion, void *closure)
51{
52    WindowPtr	    pWin = (WindowPtr) closure;
53    ScreenPtr	    pScreen = pWin->drawable.pScreen;
54    CompScreenPtr   cs = GetCompScreen (pScreen);
55    CompWindowPtr   cw = GetCompWindow (pWin);
56
57    cs->damaged = TRUE;
58    cw->damaged = TRUE;
59}
60
61static void
62compDestroyDamage (DamagePtr pDamage, void *closure)
63{
64    WindowPtr	    pWin = (WindowPtr) closure;
65    CompWindowPtr   cw = GetCompWindow (pWin);
66
67    cw->damage = 0;
68}
69
70/*
71 * Redirect one window for one client
72 */
73int
74compRedirectWindow (ClientPtr pClient, WindowPtr pWin, int update)
75{
76    CompWindowPtr	cw = GetCompWindow (pWin);
77    CompClientWindowPtr	ccw;
78    Bool		wasMapped = pWin->mapped;
79    CompScreenPtr       cs = GetCompScreen(pWin->drawable.pScreen);
80
81    if (pWin == cs->pOverlayWin) {
82	return Success;
83    }
84
85    if (!pWin->parent)
86	return BadMatch;
87
88    /*
89     * Only one Manual update is allowed
90     */
91    if (cw && update == CompositeRedirectManual)
92	for (ccw = cw->clients; ccw; ccw = ccw->next)
93	    if (ccw->update == CompositeRedirectManual)
94		return BadAccess;
95
96    /*
97     * Allocate per-client per-window structure
98     * The client *could* allocate multiple, but while supported,
99     * it is not expected to be common
100     */
101    ccw = xalloc (sizeof (CompClientWindowRec));
102    if (!ccw)
103	return BadAlloc;
104    ccw->id = FakeClientID (pClient->index);
105    ccw->update = update;
106    /*
107     * Now make sure there's a per-window structure to hang this from
108     */
109    if (!cw)
110    {
111	cw = xalloc (sizeof (CompWindowRec));
112	if (!cw)
113	{
114	    xfree (ccw);
115	    return BadAlloc;
116	}
117	cw->damage = DamageCreate (compReportDamage,
118				   compDestroyDamage,
119				   DamageReportNonEmpty,
120				   FALSE,
121				   pWin->drawable.pScreen,
122				   pWin);
123	if (!cw->damage)
124	{
125	    xfree (ccw);
126	    xfree (cw);
127	    return BadAlloc;
128	}
129	if (wasMapped)
130	{
131	    DisableMapUnmapEvents (pWin);
132	    UnmapWindow (pWin, FALSE);
133	    EnableMapUnmapEvents (pWin);
134	}
135
136	REGION_NULL (pScreen, &cw->borderClip);
137	cw->update = CompositeRedirectAutomatic;
138	cw->clients = 0;
139	cw->oldx = COMP_ORIGIN_INVALID;
140	cw->oldy = COMP_ORIGIN_INVALID;
141	cw->damageRegistered = FALSE;
142	cw->damaged = FALSE;
143	dixSetPrivate(&pWin->devPrivates, CompWindowPrivateKey, cw);
144    }
145    ccw->next = cw->clients;
146    cw->clients = ccw;
147    if (!AddResource (ccw->id, CompositeClientWindowType, pWin))
148	return BadAlloc;
149    if (ccw->update == CompositeRedirectManual)
150    {
151	/* If the window was CompositeRedirectAutomatic, then
152	 * unmap the window so that the parent clip list will
153	 * be correctly recomputed.
154	 */
155	if (pWin->mapped)
156	{
157	    DisableMapUnmapEvents (pWin);
158	    UnmapWindow (pWin, FALSE);
159	    EnableMapUnmapEvents (pWin);
160	}
161	if (cw->damageRegistered)
162	{
163	    DamageUnregister (&pWin->drawable, cw->damage);
164	    cw->damageRegistered = FALSE;
165	}
166	cw->update = CompositeRedirectManual;
167    }
168
169    if (!compCheckRedirect (pWin))
170    {
171	FreeResource (ccw->id, RT_NONE);
172	return BadAlloc;
173    }
174    if (wasMapped && !pWin->mapped)
175    {
176	Bool	overrideRedirect = pWin->overrideRedirect;
177	pWin->overrideRedirect = TRUE;
178	DisableMapUnmapEvents (pWin);
179	MapWindow (pWin, pClient);
180	EnableMapUnmapEvents (pWin);
181	pWin->overrideRedirect = overrideRedirect;
182    }
183
184    return Success;
185}
186
187/*
188 * Free one of the per-client per-window resources, clearing
189 * redirect and the per-window pointer as appropriate
190 */
191void
192compFreeClientWindow (WindowPtr pWin, XID id)
193{
194    CompWindowPtr	cw = GetCompWindow (pWin);
195    CompClientWindowPtr	ccw, *prev;
196    Bool		wasMapped = pWin->mapped;
197
198    if (!cw)
199	return;
200    for (prev = &cw->clients; (ccw = *prev); prev = &ccw->next)
201    {
202	if (ccw->id == id)
203	{
204	    *prev = ccw->next;
205	    if (ccw->update == CompositeRedirectManual)
206		cw->update = CompositeRedirectAutomatic;
207	    xfree (ccw);
208	    break;
209	}
210    }
211    if (!cw->clients)
212    {
213	if (wasMapped)
214	{
215	    DisableMapUnmapEvents (pWin);
216	    UnmapWindow (pWin, FALSE);
217	    EnableMapUnmapEvents (pWin);
218	}
219
220	if (pWin->redirectDraw != RedirectDrawNone)
221	    compFreePixmap (pWin);
222
223	if (cw->damage)
224	    DamageDestroy (cw->damage);
225
226	REGION_UNINIT (pScreen, &cw->borderClip);
227
228	dixSetPrivate(&pWin->devPrivates, CompWindowPrivateKey, NULL);
229	xfree (cw);
230    }
231    else if (cw->update == CompositeRedirectAutomatic &&
232	     !cw->damageRegistered && pWin->redirectDraw != RedirectDrawNone)
233    {
234	DamageRegister (&pWin->drawable, cw->damage);
235	cw->damageRegistered = TRUE;
236	pWin->redirectDraw = RedirectDrawAutomatic;
237	DamageRegionAppend(&pWin->drawable, &pWin->borderSize);
238    }
239    if (wasMapped && !pWin->mapped)
240    {
241	Bool	overrideRedirect = pWin->overrideRedirect;
242	pWin->overrideRedirect = TRUE;
243	DisableMapUnmapEvents (pWin);
244	MapWindow (pWin, clients[CLIENT_ID(id)]);
245	EnableMapUnmapEvents (pWin);
246	pWin->overrideRedirect = overrideRedirect;
247    }
248}
249
250/*
251 * This is easy, just free the appropriate resource.
252 */
253
254int
255compUnredirectWindow (ClientPtr pClient, WindowPtr pWin, int update)
256{
257    CompWindowPtr	cw = GetCompWindow (pWin);
258    CompClientWindowPtr	ccw;
259
260    if (!cw)
261	return BadValue;
262
263    for (ccw = cw->clients; ccw; ccw = ccw->next)
264	if (ccw->update == update && CLIENT_ID(ccw->id) == pClient->index)
265	{
266	    FreeResource (ccw->id, RT_NONE);
267	    return Success;
268	}
269    return BadValue;
270}
271
272/*
273 * Redirect all subwindows for one client
274 */
275
276int
277compRedirectSubwindows (ClientPtr pClient, WindowPtr pWin, int update)
278{
279    CompSubwindowsPtr	csw = GetCompSubwindows (pWin);
280    CompClientWindowPtr	ccw;
281    WindowPtr		pChild;
282
283    /*
284     * Only one Manual update is allowed
285     */
286    if (csw && update == CompositeRedirectManual)
287	for (ccw = csw->clients; ccw; ccw = ccw->next)
288	    if (ccw->update == CompositeRedirectManual)
289		return BadAccess;
290    /*
291     * Allocate per-client per-window structure
292     * The client *could* allocate multiple, but while supported,
293     * it is not expected to be common
294     */
295    ccw = xalloc (sizeof (CompClientWindowRec));
296    if (!ccw)
297	return BadAlloc;
298    ccw->id = FakeClientID (pClient->index);
299    ccw->update = update;
300    /*
301     * Now make sure there's a per-window structure to hang this from
302     */
303    if (!csw)
304    {
305	csw = xalloc (sizeof (CompSubwindowsRec));
306	if (!csw)
307	{
308	    xfree (ccw);
309	    return BadAlloc;
310	}
311	csw->update = CompositeRedirectAutomatic;
312	csw->clients = 0;
313	dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, csw);
314    }
315    /*
316     * Redirect all existing windows
317     */
318    for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib)
319    {
320	int ret = compRedirectWindow (pClient, pChild, update);
321	if (ret != Success)
322	{
323	    for (pChild = pChild->nextSib; pChild; pChild = pChild->nextSib)
324		(void) compUnredirectWindow (pClient, pChild, update);
325	    if (!csw->clients)
326	    {
327		xfree (csw);
328		dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, 0);
329	    }
330	    xfree (ccw);
331	    return ret;
332	}
333    }
334    /*
335     * Hook into subwindows list
336     */
337    ccw->next = csw->clients;
338    csw->clients = ccw;
339    if (!AddResource (ccw->id, CompositeClientSubwindowsType, pWin))
340	return BadAlloc;
341    if (ccw->update == CompositeRedirectManual)
342    {
343	csw->update = CompositeRedirectManual;
344	/*
345	 * tell damage extension that damage events for this client are
346	 * critical output
347	 */
348	DamageExtSetCritical (pClient, TRUE);
349    }
350    return Success;
351}
352
353/*
354 * Free one of the per-client per-subwindows resources,
355 * which frees one redirect per subwindow
356 */
357void
358compFreeClientSubwindows (WindowPtr pWin, XID id)
359{
360    CompSubwindowsPtr	csw = GetCompSubwindows (pWin);
361    CompClientWindowPtr	ccw, *prev;
362    WindowPtr		pChild;
363
364    if (!csw)
365	return;
366    for (prev = &csw->clients; (ccw = *prev); prev = &ccw->next)
367    {
368	if (ccw->id == id)
369	{
370	    ClientPtr	pClient = clients[CLIENT_ID(id)];
371
372	    *prev = ccw->next;
373	    if (ccw->update == CompositeRedirectManual)
374	    {
375		/*
376		 * tell damage extension that damage events for this client are
377		 * critical output
378		 */
379		DamageExtSetCritical (pClient, FALSE);
380		csw->update = CompositeRedirectAutomatic;
381		if (pWin->mapped)
382		    (*pWin->drawable.pScreen->ClearToBackground)(pWin, 0, 0, 0, 0, TRUE);
383	    }
384
385	    /*
386	     * Unredirect all existing subwindows
387	     */
388	    for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib)
389		(void) compUnredirectWindow (pClient, pChild, ccw->update);
390
391	    xfree (ccw);
392	    break;
393	}
394    }
395
396    /*
397     * Check if all of the per-client records are gone
398     */
399    if (!csw->clients)
400    {
401	dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, NULL);
402	xfree (csw);
403    }
404}
405
406/*
407 * This is easy, just free the appropriate resource.
408 */
409
410int
411compUnredirectSubwindows (ClientPtr pClient, WindowPtr pWin, int update)
412{
413    CompSubwindowsPtr	csw = GetCompSubwindows (pWin);
414    CompClientWindowPtr	ccw;
415
416    if (!csw)
417	return BadValue;
418    for (ccw = csw->clients; ccw; ccw = ccw->next)
419	if (ccw->update == update && CLIENT_ID(ccw->id) == pClient->index)
420	{
421	    FreeResource (ccw->id, RT_NONE);
422	    return Success;
423	}
424    return BadValue;
425}
426
427/*
428 * Add redirection information for one subwindow (during reparent)
429 */
430
431int
432compRedirectOneSubwindow (WindowPtr pParent, WindowPtr pWin)
433{
434    CompSubwindowsPtr	csw = GetCompSubwindows (pParent);
435    CompClientWindowPtr	ccw;
436
437    if (!csw)
438	return Success;
439    for (ccw = csw->clients; ccw; ccw = ccw->next)
440    {
441	int ret = compRedirectWindow (clients[CLIENT_ID(ccw->id)],
442				      pWin, ccw->update);
443	if (ret != Success)
444	    return ret;
445    }
446    return Success;
447}
448
449/*
450 * Remove redirection information for one subwindow (during reparent)
451 */
452
453int
454compUnredirectOneSubwindow (WindowPtr pParent, WindowPtr pWin)
455{
456    CompSubwindowsPtr	csw = GetCompSubwindows (pParent);
457    CompClientWindowPtr	ccw;
458
459    if (!csw)
460	return Success;
461    for (ccw = csw->clients; ccw; ccw = ccw->next)
462    {
463	int ret = compUnredirectWindow (clients[CLIENT_ID(ccw->id)],
464					pWin, ccw->update);
465	if (ret != Success)
466	    return ret;
467    }
468    return Success;
469}
470
471static PixmapPtr
472compNewPixmap (WindowPtr pWin, int x, int y, int w, int h)
473{
474    ScreenPtr	    pScreen = pWin->drawable.pScreen;
475    WindowPtr	    pParent = pWin->parent;
476    PixmapPtr	    pPixmap;
477
478    pPixmap = (*pScreen->CreatePixmap) (pScreen, w, h, pWin->drawable.depth,
479					CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
480
481    if (!pPixmap)
482	return 0;
483
484    pPixmap->screen_x = x;
485    pPixmap->screen_y = y;
486
487    if (pParent->drawable.depth == pWin->drawable.depth)
488    {
489	GCPtr	pGC = GetScratchGC (pWin->drawable.depth, pScreen);
490
491	/*
492	 * Copy bits from the parent into the new pixmap so that it will
493	 * have "reasonable" contents in case for background None areas.
494	 */
495	if (pGC)
496	{
497	    XID val = IncludeInferiors;
498
499	    ValidateGC(&pPixmap->drawable, pGC);
500	    dixChangeGC (serverClient, pGC, GCSubwindowMode, &val, NULL);
501	    (*pGC->ops->CopyArea) (&pParent->drawable,
502				   &pPixmap->drawable,
503				   pGC,
504				   x - pParent->drawable.x,
505				   y - pParent->drawable.y,
506				   w, h, 0, 0);
507	    FreeScratchGC (pGC);
508	}
509    }
510    else
511    {
512	PictFormatPtr	pSrcFormat = compWindowFormat (pParent);
513	PictFormatPtr	pDstFormat = compWindowFormat (pWin);
514	XID		inferiors = IncludeInferiors;
515	int		error;
516
517	PicturePtr	pSrcPicture = CreatePicture (None,
518						     &pParent->drawable,
519						     pSrcFormat,
520						     CPSubwindowMode,
521						     &inferiors,
522						     serverClient, &error);
523
524	PicturePtr	pDstPicture = CreatePicture (None,
525						     &pPixmap->drawable,
526						     pDstFormat,
527						     0, 0,
528						     serverClient, &error);
529
530	if (pSrcPicture && pDstPicture)
531	{
532	    CompositePicture (PictOpSrc,
533			      pSrcPicture,
534			      NULL,
535			      pDstPicture,
536			      x - pParent->drawable.x,
537			      y - pParent->drawable.y,
538			      0, 0, 0, 0, w, h);
539	}
540	if (pSrcPicture)
541	    FreePicture (pSrcPicture, 0);
542	if (pDstPicture)
543	    FreePicture (pDstPicture, 0);
544    }
545    return pPixmap;
546}
547
548Bool
549compAllocPixmap (WindowPtr pWin)
550{
551    int		    bw = (int) pWin->borderWidth;
552    int		    x = pWin->drawable.x - bw;
553    int		    y = pWin->drawable.y - bw;
554    int		    w = pWin->drawable.width + (bw << 1);
555    int		    h = pWin->drawable.height + (bw << 1);
556    PixmapPtr	    pPixmap = compNewPixmap (pWin, x, y, w, h);
557    CompWindowPtr   cw = GetCompWindow (pWin);
558
559    if (!pPixmap)
560	return FALSE;
561    if (cw->update == CompositeRedirectAutomatic)
562	pWin->redirectDraw = RedirectDrawAutomatic;
563    else
564	pWin->redirectDraw = RedirectDrawManual;
565
566    compSetPixmap (pWin, pPixmap);
567    cw->oldx = COMP_ORIGIN_INVALID;
568    cw->oldy = COMP_ORIGIN_INVALID;
569    cw->damageRegistered = FALSE;
570    if (cw->update == CompositeRedirectAutomatic)
571    {
572	DamageRegister (&pWin->drawable, cw->damage);
573	cw->damageRegistered = TRUE;
574    }
575    return TRUE;
576}
577
578void
579compFreePixmap (WindowPtr pWin)
580{
581    ScreenPtr	    pScreen = pWin->drawable.pScreen;
582    PixmapPtr	    pRedirectPixmap, pParentPixmap;
583    CompWindowPtr   cw = GetCompWindow (pWin);
584
585    if (cw->damageRegistered)
586    {
587	DamageUnregister (&pWin->drawable, cw->damage);
588	cw->damageRegistered = FALSE;
589	DamageEmpty (cw->damage);
590    }
591    /*
592     * Move the parent-constrained border clip region back into
593     * the window so that ValidateTree will handle the unmap
594     * case correctly.  Unmap adds the window borderClip to the
595     * parent exposed area; regions beyond the parent cause crashes
596     */
597    REGION_COPY (pScreen, &pWin->borderClip, &cw->borderClip);
598    pRedirectPixmap = (*pScreen->GetWindowPixmap) (pWin);
599    pParentPixmap = (*pScreen->GetWindowPixmap) (pWin->parent);
600    pWin->redirectDraw = RedirectDrawNone;
601    compSetPixmap (pWin, pParentPixmap);
602    (*pScreen->DestroyPixmap) (pRedirectPixmap);
603}
604
605/*
606 * Make sure the pixmap is the right size and offset.  Allocate a new
607 * pixmap to change size, adjust origin to change offset, leaving the
608 * old pixmap in cw->pOldPixmap so bits can be recovered
609 */
610Bool
611compReallocPixmap (WindowPtr pWin, int draw_x, int draw_y,
612		   unsigned int w, unsigned int h, int bw)
613{
614    ScreenPtr	    pScreen = pWin->drawable.pScreen;
615    PixmapPtr	    pOld = (*pScreen->GetWindowPixmap) (pWin);
616    PixmapPtr	    pNew;
617    CompWindowPtr   cw = GetCompWindow (pWin);
618    int		    pix_x, pix_y;
619    int		    pix_w, pix_h;
620
621    assert (cw && pWin->redirectDraw != RedirectDrawNone);
622    cw->oldx = pOld->screen_x;
623    cw->oldy = pOld->screen_y;
624    pix_x = draw_x - bw;
625    pix_y = draw_y - bw;
626    pix_w = w + (bw << 1);
627    pix_h = h + (bw << 1);
628    if (pix_w != pOld->drawable.width || pix_h != pOld->drawable.height)
629    {
630	pNew = compNewPixmap (pWin, pix_x, pix_y, pix_w, pix_h);
631	if (!pNew)
632	    return FALSE;
633	cw->pOldPixmap = pOld;
634	compSetPixmap (pWin, pNew);
635    }
636    else
637    {
638	pNew = pOld;
639	cw->pOldPixmap = 0;
640    }
641    pNew->screen_x = pix_x;
642    pNew->screen_y = pix_y;
643    return TRUE;
644}
645