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