compalloc.c revision 05b261ec
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    /*
86     * Only one Manual update is allowed
87     */
88    if (cw && update == CompositeRedirectManual)
89	for (ccw = cw->clients; ccw; ccw = ccw->next)
90	    if (ccw->update == CompositeRedirectManual)
91		return BadAccess;
92
93    /*
94     * Allocate per-client per-window structure
95     * The client *could* allocate multiple, but while supported,
96     * it is not expected to be common
97     */
98    ccw = xalloc (sizeof (CompClientWindowRec));
99    if (!ccw)
100	return BadAlloc;
101    ccw->id = FakeClientID (pClient->index);
102    ccw->update = update;
103    /*
104     * Now make sure there's a per-window structure to hang this from
105     */
106    if (!cw)
107    {
108	cw = xalloc (sizeof (CompWindowRec));
109	if (!cw)
110	{
111	    xfree (ccw);
112	    return BadAlloc;
113	}
114	cw->damage = DamageCreate (compReportDamage,
115				   compDestroyDamage,
116				   DamageReportNonEmpty,
117				   FALSE,
118				   pWin->drawable.pScreen,
119				   pWin);
120	if (!cw->damage)
121	{
122	    xfree (ccw);
123	    xfree (cw);
124	    return BadAlloc;
125	}
126	if (wasMapped)
127	{
128	    DisableMapUnmapEvents (pWin);
129	    UnmapWindow (pWin, FALSE);
130	    EnableMapUnmapEvents (pWin);
131	}
132
133	REGION_NULL (pScreen, &cw->borderClip);
134	cw->update = CompositeRedirectAutomatic;
135	cw->clients = 0;
136	cw->oldx = COMP_ORIGIN_INVALID;
137	cw->oldy = COMP_ORIGIN_INVALID;
138	cw->damageRegistered = FALSE;
139	cw->damaged = FALSE;
140	pWin->devPrivates[CompWindowPrivateIndex].ptr = cw;
141    }
142    ccw->next = cw->clients;
143    cw->clients = ccw;
144    if (!AddResource (ccw->id, CompositeClientWindowType, pWin))
145	return BadAlloc;
146    if (ccw->update == CompositeRedirectManual)
147    {
148	if (cw->damageRegistered)
149	{
150	    DamageUnregister (&pWin->drawable, cw->damage);
151	    cw->damageRegistered = FALSE;
152	}
153	cw->update = CompositeRedirectManual;
154    }
155
156    if (!compCheckRedirect (pWin))
157    {
158	FreeResource (ccw->id, RT_NONE);
159	return BadAlloc;
160    }
161    if (wasMapped && !pWin->mapped)
162    {
163	Bool	overrideRedirect = pWin->overrideRedirect;
164	pWin->overrideRedirect = TRUE;
165	DisableMapUnmapEvents (pWin);
166	MapWindow (pWin, pClient);
167	EnableMapUnmapEvents (pWin);
168	pWin->overrideRedirect = overrideRedirect;
169    }
170
171    return Success;
172}
173
174/*
175 * Free one of the per-client per-window resources, clearing
176 * redirect and the per-window pointer as appropriate
177 */
178void
179compFreeClientWindow (WindowPtr pWin, XID id)
180{
181    CompWindowPtr	cw = GetCompWindow (pWin);
182    CompClientWindowPtr	ccw, *prev;
183    Bool		wasMapped = pWin->mapped;
184
185    if (!cw)
186	return;
187    for (prev = &cw->clients; (ccw = *prev); prev = &ccw->next)
188    {
189	if (ccw->id == id)
190	{
191	    *prev = ccw->next;
192	    if (ccw->update == CompositeRedirectManual)
193		cw->update = CompositeRedirectAutomatic;
194	    xfree (ccw);
195	    break;
196	}
197    }
198    if (!cw->clients)
199    {
200	if (wasMapped)
201	{
202	    DisableMapUnmapEvents (pWin);
203	    UnmapWindow (pWin, FALSE);
204	    EnableMapUnmapEvents (pWin);
205	}
206
207	if (pWin->redirectDraw != RedirectDrawNone)
208	    compFreePixmap (pWin);
209
210	if (cw->damage)
211	    DamageDestroy (cw->damage);
212
213	REGION_UNINIT (pScreen, &cw->borderClip);
214
215	pWin->devPrivates[CompWindowPrivateIndex].ptr = 0;
216	xfree (cw);
217    }
218    else if (cw->update == CompositeRedirectAutomatic &&
219	     !cw->damageRegistered && pWin->redirectDraw != RedirectDrawNone)
220    {
221	DamageRegister (&pWin->drawable, cw->damage);
222	cw->damageRegistered = TRUE;
223	pWin->redirectDraw = RedirectDrawAutomatic;
224	DamageDamageRegion (&pWin->drawable, &pWin->borderSize);
225    }
226    if (wasMapped && !pWin->mapped)
227    {
228	Bool	overrideRedirect = pWin->overrideRedirect;
229	pWin->overrideRedirect = TRUE;
230	DisableMapUnmapEvents (pWin);
231	MapWindow (pWin, clients[CLIENT_ID(id)]);
232	EnableMapUnmapEvents (pWin);
233	pWin->overrideRedirect = overrideRedirect;
234    }
235}
236
237/*
238 * This is easy, just free the appropriate resource.
239 */
240
241int
242compUnredirectWindow (ClientPtr pClient, WindowPtr pWin, int update)
243{
244    CompWindowPtr	cw = GetCompWindow (pWin);
245    CompClientWindowPtr	ccw;
246
247    if (!cw)
248	return BadValue;
249
250    for (ccw = cw->clients; ccw; ccw = ccw->next)
251	if (ccw->update == update && CLIENT_ID(ccw->id) == pClient->index)
252	{
253	    FreeResource (ccw->id, RT_NONE);
254	    return Success;
255	}
256    return BadValue;
257}
258
259/*
260 * Redirect all subwindows for one client
261 */
262
263int
264compRedirectSubwindows (ClientPtr pClient, WindowPtr pWin, int update)
265{
266    CompSubwindowsPtr	csw = GetCompSubwindows (pWin);
267    CompClientWindowPtr	ccw;
268    WindowPtr		pChild;
269
270    /*
271     * Only one Manual update is allowed
272     */
273    if (csw && update == CompositeRedirectManual)
274	for (ccw = csw->clients; ccw; ccw = ccw->next)
275	    if (ccw->update == CompositeRedirectManual)
276		return BadAccess;
277    /*
278     * Allocate per-client per-window structure
279     * The client *could* allocate multiple, but while supported,
280     * it is not expected to be common
281     */
282    ccw = xalloc (sizeof (CompClientWindowRec));
283    if (!ccw)
284	return BadAlloc;
285    ccw->id = FakeClientID (pClient->index);
286    ccw->update = update;
287    /*
288     * Now make sure there's a per-window structure to hang this from
289     */
290    if (!csw)
291    {
292	csw = xalloc (sizeof (CompSubwindowsRec));
293	if (!csw)
294	{
295	    xfree (ccw);
296	    return BadAlloc;
297	}
298	csw->update = CompositeRedirectAutomatic;
299	csw->clients = 0;
300	pWin->devPrivates[CompSubwindowsPrivateIndex].ptr = csw;
301    }
302    /*
303     * Redirect all existing windows
304     */
305    for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib)
306    {
307	int ret = compRedirectWindow (pClient, pChild, update);
308	if (ret != Success)
309	{
310	    for (pChild = pChild->nextSib; pChild; pChild = pChild->nextSib)
311		(void) compUnredirectWindow (pClient, pChild, update);
312	    if (!csw->clients)
313	    {
314		xfree (csw);
315		pWin->devPrivates[CompSubwindowsPrivateIndex].ptr = 0;
316	    }
317	    xfree (ccw);
318	    return ret;
319	}
320    }
321    /*
322     * Hook into subwindows list
323     */
324    ccw->next = csw->clients;
325    csw->clients = ccw;
326    if (!AddResource (ccw->id, CompositeClientSubwindowsType, pWin))
327	return BadAlloc;
328    if (ccw->update == CompositeRedirectManual)
329    {
330	csw->update = CompositeRedirectManual;
331	/*
332	 * tell damage extension that damage events for this client are
333	 * critical output
334	 */
335	DamageExtSetCritical (pClient, TRUE);
336    }
337    return Success;
338}
339
340/*
341 * Free one of the per-client per-subwindows resources,
342 * which frees one redirect per subwindow
343 */
344void
345compFreeClientSubwindows (WindowPtr pWin, XID id)
346{
347    CompSubwindowsPtr	csw = GetCompSubwindows (pWin);
348    CompClientWindowPtr	ccw, *prev;
349    WindowPtr		pChild;
350
351    if (!csw)
352	return;
353    for (prev = &csw->clients; (ccw = *prev); prev = &ccw->next)
354    {
355	if (ccw->id == id)
356	{
357	    ClientPtr	pClient = clients[CLIENT_ID(id)];
358
359	    *prev = ccw->next;
360	    if (ccw->update == CompositeRedirectManual)
361	    {
362		/*
363		 * tell damage extension that damage events for this client are
364		 * critical output
365		 */
366		DamageExtSetCritical (pClient, FALSE);
367		csw->update = CompositeRedirectAutomatic;
368		if (pWin->mapped)
369		    (*pWin->drawable.pScreen->ClearToBackground)(pWin, 0, 0, 0, 0, TRUE);
370	    }
371
372	    /*
373	     * Unredirect all existing subwindows
374	     */
375	    for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib)
376		(void) compUnredirectWindow (pClient, pChild, ccw->update);
377
378	    xfree (ccw);
379	    break;
380	}
381    }
382
383    /*
384     * Check if all of the per-client records are gone
385     */
386    if (!csw->clients)
387    {
388	pWin->devPrivates[CompSubwindowsPrivateIndex].ptr = 0;
389	xfree (csw);
390    }
391}
392
393/*
394 * This is easy, just free the appropriate resource.
395 */
396
397int
398compUnredirectSubwindows (ClientPtr pClient, WindowPtr pWin, int update)
399{
400    CompSubwindowsPtr	csw = GetCompSubwindows (pWin);
401    CompClientWindowPtr	ccw;
402
403    if (!csw)
404	return BadValue;
405    for (ccw = csw->clients; ccw; ccw = ccw->next)
406	if (ccw->update == update && CLIENT_ID(ccw->id) == pClient->index)
407	{
408	    FreeResource (ccw->id, RT_NONE);
409	    return Success;
410	}
411    return BadValue;
412}
413
414/*
415 * Add redirection information for one subwindow (during reparent)
416 */
417
418int
419compRedirectOneSubwindow (WindowPtr pParent, WindowPtr pWin)
420{
421    CompSubwindowsPtr	csw = GetCompSubwindows (pParent);
422    CompClientWindowPtr	ccw;
423
424    if (!csw)
425	return Success;
426    for (ccw = csw->clients; ccw; ccw = ccw->next)
427    {
428	int ret = compRedirectWindow (clients[CLIENT_ID(ccw->id)],
429				      pWin, ccw->update);
430	if (ret != Success)
431	    return ret;
432    }
433    return Success;
434}
435
436/*
437 * Remove redirection information for one subwindow (during reparent)
438 */
439
440int
441compUnredirectOneSubwindow (WindowPtr pParent, WindowPtr pWin)
442{
443    CompSubwindowsPtr	csw = GetCompSubwindows (pParent);
444    CompClientWindowPtr	ccw;
445
446    if (!csw)
447	return Success;
448    for (ccw = csw->clients; ccw; ccw = ccw->next)
449    {
450	int ret = compUnredirectWindow (clients[CLIENT_ID(ccw->id)],
451					pWin, ccw->update);
452	if (ret != Success)
453	    return ret;
454    }
455    return Success;
456}
457
458static PixmapPtr
459compNewPixmap (WindowPtr pWin, int x, int y, int w, int h)
460{
461    ScreenPtr	    pScreen = pWin->drawable.pScreen;
462    WindowPtr	    pParent = pWin->parent;
463    PixmapPtr	    pPixmap;
464
465    pPixmap = (*pScreen->CreatePixmap) (pScreen, w, h, pWin->drawable.depth);
466
467    if (!pPixmap)
468	return 0;
469
470    pPixmap->screen_x = x;
471    pPixmap->screen_y = y;
472
473    if (pParent->drawable.depth == pWin->drawable.depth)
474    {
475	GCPtr	pGC = GetScratchGC (pWin->drawable.depth, pScreen);
476
477	/*
478	 * Copy bits from the parent into the new pixmap so that it will
479	 * have "reasonable" contents in case for background None areas.
480	 */
481	if (pGC)
482	{
483	    XID val = IncludeInferiors;
484
485	    ValidateGC(&pPixmap->drawable, pGC);
486	    dixChangeGC (serverClient, pGC, GCSubwindowMode, &val, NULL);
487	    (*pGC->ops->CopyArea) (&pParent->drawable,
488				   &pPixmap->drawable,
489				   pGC,
490				   x - pParent->drawable.x,
491				   y - pParent->drawable.y,
492				   w, h, 0, 0);
493	    FreeScratchGC (pGC);
494	}
495    }
496    else
497    {
498	PictFormatPtr	pSrcFormat = compWindowFormat (pParent);
499	PictFormatPtr	pDstFormat = compWindowFormat (pWin);
500	XID		inferiors = IncludeInferiors;
501	int		error;
502
503	PicturePtr	pSrcPicture = CreatePicture (None,
504						     &pParent->drawable,
505						     pSrcFormat,
506						     CPSubwindowMode,
507						     &inferiors,
508						     serverClient, &error);
509
510	PicturePtr	pDstPicture = CreatePicture (None,
511						     &pPixmap->drawable,
512						     pDstFormat,
513						     0, 0,
514						     serverClient, &error);
515
516	if (pSrcPicture && pDstPicture)
517	{
518	    CompositePicture (PictOpSrc,
519			      pSrcPicture,
520			      NULL,
521			      pDstPicture,
522			      x - pParent->drawable.x,
523			      y - pParent->drawable.y,
524			      0, 0, 0, 0, w, h);
525	}
526	if (pSrcPicture)
527	    FreePicture (pSrcPicture, 0);
528	if (pDstPicture)
529	    FreePicture (pDstPicture, 0);
530    }
531    return pPixmap;
532}
533
534Bool
535compAllocPixmap (WindowPtr pWin)
536{
537    int		    bw = (int) pWin->borderWidth;
538    int		    x = pWin->drawable.x - bw;
539    int		    y = pWin->drawable.y - bw;
540    int		    w = pWin->drawable.width + (bw << 1);
541    int		    h = pWin->drawable.height + (bw << 1);
542    PixmapPtr	    pPixmap = compNewPixmap (pWin, x, y, w, h);
543    CompWindowPtr   cw = GetCompWindow (pWin);
544
545    if (!pPixmap)
546	return FALSE;
547    if (cw->update == CompositeRedirectAutomatic)
548	pWin->redirectDraw = RedirectDrawAutomatic;
549    else
550	pWin->redirectDraw = RedirectDrawManual;
551
552    compSetPixmap (pWin, pPixmap);
553    cw->oldx = COMP_ORIGIN_INVALID;
554    cw->oldy = COMP_ORIGIN_INVALID;
555    cw->damageRegistered = FALSE;
556    if (cw->update == CompositeRedirectAutomatic)
557    {
558	DamageRegister (&pWin->drawable, cw->damage);
559	cw->damageRegistered = TRUE;
560    }
561    return TRUE;
562}
563
564void
565compFreePixmap (WindowPtr pWin)
566{
567    ScreenPtr	    pScreen = pWin->drawable.pScreen;
568    PixmapPtr	    pRedirectPixmap, pParentPixmap;
569    CompWindowPtr   cw = GetCompWindow (pWin);
570
571    if (cw->damageRegistered)
572    {
573	DamageUnregister (&pWin->drawable, cw->damage);
574	cw->damageRegistered = FALSE;
575	DamageEmpty (cw->damage);
576    }
577    /*
578     * Move the parent-constrained border clip region back into
579     * the window so that ValidateTree will handle the unmap
580     * case correctly.  Unmap adds the window borderClip to the
581     * parent exposed area; regions beyond the parent cause crashes
582     */
583    REGION_COPY (pScreen, &pWin->borderClip, &cw->borderClip);
584    pRedirectPixmap = (*pScreen->GetWindowPixmap) (pWin);
585    pParentPixmap = (*pScreen->GetWindowPixmap) (pWin->parent);
586    pWin->redirectDraw = RedirectDrawNone;
587    compSetPixmap (pWin, pParentPixmap);
588    (*pScreen->DestroyPixmap) (pRedirectPixmap);
589}
590
591/*
592 * Make sure the pixmap is the right size and offset.  Allocate a new
593 * pixmap to change size, adjust origin to change offset, leaving the
594 * old pixmap in cw->pOldPixmap so bits can be recovered
595 */
596Bool
597compReallocPixmap (WindowPtr pWin, int draw_x, int draw_y,
598		   unsigned int w, unsigned int h, int bw)
599{
600    ScreenPtr	    pScreen = pWin->drawable.pScreen;
601    PixmapPtr	    pOld = (*pScreen->GetWindowPixmap) (pWin);
602    PixmapPtr	    pNew;
603    CompWindowPtr   cw = GetCompWindow (pWin);
604    int		    pix_x, pix_y;
605    int		    pix_w, pix_h;
606
607    assert (cw && pWin->redirectDraw != RedirectDrawNone);
608    cw->oldx = pOld->screen_x;
609    cw->oldy = pOld->screen_y;
610    pix_x = draw_x - bw;
611    pix_y = draw_y - bw;
612    pix_w = w + (bw << 1);
613    pix_h = h + (bw << 1);
614    if (pix_w != pOld->drawable.width || pix_h != pOld->drawable.height)
615    {
616	pNew = compNewPixmap (pWin, pix_x, pix_y, pix_w, pix_h);
617	if (!pNew)
618	    return FALSE;
619	cw->pOldPixmap = pOld;
620	compSetPixmap (pWin, pNew);
621    }
622    else
623    {
624	pNew = pOld;
625	cw->pOldPixmap = 0;
626    }
627    pNew->screen_x = pix_x;
628    pNew->screen_y = pix_y;
629    return TRUE;
630}
631