1/*
2 * Copyright © 2002 Keith Packard
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 Keith Packard not be used in
9 * advertising or publicity pertaining to distribution of the software without
10 * specific, written prior permission.  Keith Packard 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 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL KEITH PACKARD 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 "damageextint.h"
28#include "protocol-versions.h"
29
30static unsigned char	DamageReqCode;
31static int		DamageEventBase;
32static RESTYPE		DamageExtType;
33static RESTYPE		DamageExtWinType;
34
35static DevPrivateKeyRec DamageClientPrivateKeyRec;
36#define DamageClientPrivateKey (&DamageClientPrivateKeyRec)
37
38static void
39DamageExtNotify (DamageExtPtr pDamageExt, BoxPtr pBoxes, int nBoxes)
40{
41    ClientPtr		pClient = pDamageExt->pClient;
42    DamageClientPtr	pDamageClient = GetDamageClient (pClient);
43    DrawablePtr		pDrawable = pDamageExt->pDrawable;
44    xDamageNotifyEvent	ev;
45    int			i;
46
47    UpdateCurrentTimeIf ();
48    ev.type = DamageEventBase + XDamageNotify;
49    ev.level = pDamageExt->level;
50    ev.drawable = pDamageExt->drawable;
51    ev.damage = pDamageExt->id;
52    ev.timestamp = currentTime.milliseconds;
53    ev.geometry.x = pDrawable->x;
54    ev.geometry.y = pDrawable->y;
55    ev.geometry.width = pDrawable->width;
56    ev.geometry.height = pDrawable->height;
57    if (pBoxes)
58    {
59	for (i = 0; i < nBoxes; i++)
60	{
61	    ev.level = pDamageExt->level;
62	    if (i < nBoxes - 1)
63		ev.level |= DamageNotifyMore;
64	    ev.area.x = pBoxes[i].x1;
65	    ev.area.y = pBoxes[i].y1;
66	    ev.area.width = pBoxes[i].x2 - pBoxes[i].x1;
67	    ev.area.height = pBoxes[i].y2 - pBoxes[i].y1;
68	    WriteEventsToClient (pClient, 1, (xEvent *) &ev);
69	}
70    }
71    else
72    {
73	ev.area.x = 0;
74	ev.area.y = 0;
75	ev.area.width = pDrawable->width;
76	ev.area.height = pDrawable->height;
77	WriteEventsToClient (pClient, 1, (xEvent *) &ev);
78    }
79    /* Composite extension marks clients with manual Subwindows as critical */
80    if (pDamageClient->critical > 0)
81    {
82	SetCriticalOutputPending ();
83	pClient->smart_priority = SMART_MAX_PRIORITY;
84    }
85}
86
87static void
88DamageExtReport (DamagePtr pDamage, RegionPtr pRegion, void *closure)
89{
90    DamageExtPtr    pDamageExt = closure;
91
92    switch (pDamageExt->level) {
93    case DamageReportRawRegion:
94    case DamageReportDeltaRegion:
95	DamageExtNotify (pDamageExt, RegionRects(pRegion), RegionNumRects(pRegion));
96	break;
97    case DamageReportBoundingBox:
98	DamageExtNotify (pDamageExt, RegionExtents(pRegion), 1);
99	break;
100    case DamageReportNonEmpty:
101	DamageExtNotify (pDamageExt, NullBox, 0);
102	break;
103    case DamageReportNone:
104	break;
105    }
106}
107
108static void
109DamageExtDestroy (DamagePtr pDamage, void *closure)
110{
111    DamageExtPtr    pDamageExt = closure;
112
113    pDamageExt->pDamage = 0;
114    if (pDamageExt->id)
115	FreeResource (pDamageExt->id, RT_NONE);
116}
117
118void
119DamageExtSetCritical (ClientPtr pClient, Bool critical)
120{
121    DamageClientPtr pDamageClient = GetDamageClient (pClient);
122
123    if (pDamageClient)
124	pDamageClient->critical += critical ? 1 : -1;
125}
126
127static int
128ProcDamageQueryVersion(ClientPtr client)
129{
130    DamageClientPtr pDamageClient = GetDamageClient (client);
131    xDamageQueryVersionReply rep;
132    register int n;
133    REQUEST(xDamageQueryVersionReq);
134
135    REQUEST_SIZE_MATCH(xDamageQueryVersionReq);
136    rep.type = X_Reply;
137    rep.length = 0;
138    rep.sequenceNumber = client->sequence;
139    if (stuff->majorVersion < SERVER_DAMAGE_MAJOR_VERSION) {
140	rep.majorVersion = stuff->majorVersion;
141	rep.minorVersion = stuff->minorVersion;
142    } else {
143	rep.majorVersion = SERVER_DAMAGE_MAJOR_VERSION;
144	if (stuff->majorVersion == SERVER_DAMAGE_MAJOR_VERSION &&
145	    stuff->minorVersion < SERVER_DAMAGE_MINOR_VERSION)
146	    rep.minorVersion = stuff->minorVersion;
147	else
148	    rep.minorVersion = SERVER_DAMAGE_MINOR_VERSION;
149    }
150    pDamageClient->major_version = rep.majorVersion;
151    pDamageClient->minor_version = rep.minorVersion;
152    if (client->swapped) {
153    	swaps(&rep.sequenceNumber, n);
154    	swapl(&rep.length, n);
155	swapl(&rep.majorVersion, n);
156	swapl(&rep.minorVersion, n);
157    }
158    WriteToClient(client, sizeof(xDamageQueryVersionReply), (char *)&rep);
159    return Success;
160}
161
162static int
163ProcDamageCreate (ClientPtr client)
164{
165    DrawablePtr		pDrawable;
166    DamageExtPtr	pDamageExt;
167    DamageReportLevel	level;
168    RegionPtr		pRegion;
169    int			rc;
170
171    REQUEST(xDamageCreateReq);
172
173    REQUEST_SIZE_MATCH(xDamageCreateReq);
174    LEGAL_NEW_RESOURCE(stuff->damage, client);
175    rc = dixLookupDrawable(&pDrawable, stuff->drawable, client, 0,
176			   DixGetAttrAccess|DixReadAccess);
177    if (rc != Success)
178	return rc;
179
180    switch (stuff->level) {
181    case XDamageReportRawRectangles:
182	level = DamageReportRawRegion;
183	break;
184    case XDamageReportDeltaRectangles:
185	level = DamageReportDeltaRegion;
186	break;
187    case XDamageReportBoundingBox:
188	level = DamageReportBoundingBox;
189	break;
190    case XDamageReportNonEmpty:
191	level = DamageReportNonEmpty;
192	break;
193    default:
194	client->errorValue = stuff->level;
195	return BadValue;
196    }
197
198    pDamageExt = malloc(sizeof (DamageExtRec));
199    if (!pDamageExt)
200	return BadAlloc;
201    pDamageExt->id = stuff->damage;
202    pDamageExt->drawable = stuff->drawable;
203    pDamageExt->pDrawable = pDrawable;
204    pDamageExt->level = level;
205    pDamageExt->pClient = client;
206    pDamageExt->pDamage = DamageCreate (DamageExtReport,
207					DamageExtDestroy,
208					level,
209					FALSE,
210					pDrawable->pScreen,
211					pDamageExt);
212    if (!pDamageExt->pDamage)
213    {
214	free(pDamageExt);
215	return BadAlloc;
216    }
217    if (!AddResource (stuff->damage, DamageExtType, (pointer) pDamageExt))
218	return BadAlloc;
219
220    DamageSetReportAfterOp (pDamageExt->pDamage, TRUE);
221    DamageRegister (pDamageExt->pDrawable, pDamageExt->pDamage);
222
223    if (pDrawable->type == DRAWABLE_WINDOW)
224    {
225	pRegion = &((WindowPtr) pDrawable)->borderClip;
226	DamageDamageRegion(pDrawable, pRegion);
227    }
228
229    return Success;
230}
231
232static int
233ProcDamageDestroy (ClientPtr client)
234{
235    REQUEST(xDamageDestroyReq);
236    DamageExtPtr    pDamageExt;
237
238    REQUEST_SIZE_MATCH(xDamageDestroyReq);
239    VERIFY_DAMAGEEXT(pDamageExt, stuff->damage, client, DixWriteAccess);
240    FreeResource (stuff->damage, RT_NONE);
241    return Success;
242}
243
244static int
245ProcDamageSubtract (ClientPtr client)
246{
247    REQUEST(xDamageSubtractReq);
248    DamageExtPtr    pDamageExt;
249    RegionPtr	    pRepair;
250    RegionPtr	    pParts;
251
252    REQUEST_SIZE_MATCH(xDamageSubtractReq);
253    VERIFY_DAMAGEEXT(pDamageExt, stuff->damage, client, DixWriteAccess);
254    VERIFY_REGION_OR_NONE(pRepair, stuff->repair, client, DixWriteAccess);
255    VERIFY_REGION_OR_NONE(pParts, stuff->parts, client, DixWriteAccess);
256
257    if (pDamageExt->level != DamageReportRawRegion)
258    {
259	DamagePtr   pDamage = pDamageExt->pDamage;
260	if (pRepair)
261	{
262	    if (pParts)
263		RegionIntersect(pParts, DamageRegion (pDamage), pRepair);
264	    if (DamageSubtract (pDamage, pRepair))
265		DamageExtReport (pDamage, DamageRegion (pDamage), (void *) pDamageExt);
266	}
267	else
268	{
269	    if (pParts)
270		RegionCopy(pParts, DamageRegion (pDamage));
271	    DamageEmpty (pDamage);
272	}
273    }
274    return Success;
275}
276
277static int
278ProcDamageAdd (ClientPtr client)
279{
280    REQUEST(xDamageAddReq);
281    DrawablePtr	    pDrawable;
282    RegionPtr	    pRegion;
283    int		    rc;
284
285    REQUEST_SIZE_MATCH(xDamageAddReq);
286    VERIFY_REGION(pRegion, stuff->region, client, DixWriteAccess);
287    rc = dixLookupDrawable(&pDrawable, stuff->drawable, client, 0,
288			   DixWriteAccess);
289    if (rc != Success)
290	return rc;
291
292    /* The region is relative to the drawable origin, so translate it out to
293     * screen coordinates like damage expects.
294     */
295    RegionTranslate(pRegion, pDrawable->x, pDrawable->y);
296    DamageDamageRegion(pDrawable, pRegion);
297    RegionTranslate(pRegion, -pDrawable->x, -pDrawable->y);
298
299    return Success;
300}
301
302/* Major version controls available requests */
303static const int version_requests[] = {
304    X_DamageQueryVersion,	/* before client sends QueryVersion */
305    X_DamageAdd,		/* Version 1 */
306};
307
308#define NUM_VERSION_REQUESTS	(sizeof (version_requests) / sizeof (version_requests[0]))
309
310static int (*ProcDamageVector[XDamageNumberRequests])(ClientPtr) = {
311/*************** Version 1 ******************/
312    ProcDamageQueryVersion,
313    ProcDamageCreate,
314    ProcDamageDestroy,
315    ProcDamageSubtract,
316/*************** Version 1.1 ****************/
317    ProcDamageAdd,
318};
319
320
321static int
322ProcDamageDispatch (ClientPtr client)
323{
324    REQUEST(xDamageReq);
325    DamageClientPtr pDamageClient = GetDamageClient (client);
326
327    if (pDamageClient->major_version >= NUM_VERSION_REQUESTS)
328	return BadRequest;
329    if (stuff->damageReqType > version_requests[pDamageClient->major_version])
330	return BadRequest;
331    return (*ProcDamageVector[stuff->damageReqType]) (client);
332}
333
334static int
335SProcDamageQueryVersion(ClientPtr client)
336{
337    register int n;
338    REQUEST(xDamageQueryVersionReq);
339
340    swaps(&stuff->length, n);
341    REQUEST_SIZE_MATCH(xDamageQueryVersionReq);
342    swapl(&stuff->majorVersion, n);
343    swapl(&stuff->minorVersion, n);
344    return (*ProcDamageVector[stuff->damageReqType]) (client);
345}
346
347static int
348SProcDamageCreate (ClientPtr client)
349{
350    register int n;
351    REQUEST(xDamageCreateReq);
352
353    swaps (&stuff->length, n);
354    REQUEST_SIZE_MATCH(xDamageCreateReq);
355    swapl (&stuff->damage, n);
356    swapl (&stuff->drawable, n);
357    return (*ProcDamageVector[stuff->damageReqType]) (client);
358}
359
360static int
361SProcDamageDestroy (ClientPtr client)
362{
363    register int n;
364    REQUEST(xDamageDestroyReq);
365
366    swaps (&stuff->length, n);
367    REQUEST_SIZE_MATCH(xDamageDestroyReq);
368    swapl (&stuff->damage, n);
369    return (*ProcDamageVector[stuff->damageReqType]) (client);
370}
371
372static int
373SProcDamageSubtract (ClientPtr client)
374{
375    register int n;
376    REQUEST(xDamageSubtractReq);
377
378    swaps (&stuff->length, n);
379    REQUEST_SIZE_MATCH(xDamageSubtractReq);
380    swapl (&stuff->damage, n);
381    swapl (&stuff->repair, n);
382    swapl (&stuff->parts, n);
383    return (*ProcDamageVector[stuff->damageReqType]) (client);
384}
385
386static int
387SProcDamageAdd (ClientPtr client)
388{
389    register int n;
390    REQUEST(xDamageAddReq);
391
392    swaps (&stuff->length, n);
393    REQUEST_SIZE_MATCH(xDamageSubtractReq);
394    swapl (&stuff->drawable, n);
395    swapl (&stuff->region, n);
396    return (*ProcDamageVector[stuff->damageReqType]) (client);
397}
398
399static int (*SProcDamageVector[XDamageNumberRequests])(ClientPtr) = {
400/*************** Version 1 ******************/
401    SProcDamageQueryVersion,
402    SProcDamageCreate,
403    SProcDamageDestroy,
404    SProcDamageSubtract,
405/*************** Version 1.1 ****************/
406    SProcDamageAdd,
407};
408
409static int
410SProcDamageDispatch (ClientPtr client)
411{
412    REQUEST(xDamageReq);
413    if (stuff->damageReqType >= XDamageNumberRequests)
414	return BadRequest;
415    return (*SProcDamageVector[stuff->damageReqType]) (client);
416}
417
418static void
419DamageClientCallback (CallbackListPtr	*list,
420		      pointer		closure,
421		      pointer		data)
422{
423    NewClientInfoRec	*clientinfo = (NewClientInfoRec *) data;
424    ClientPtr		pClient = clientinfo->client;
425    DamageClientPtr	pDamageClient = GetDamageClient (pClient);
426
427    pDamageClient->critical = 0;
428    pDamageClient->major_version = 0;
429    pDamageClient->minor_version = 0;
430}
431
432/*ARGSUSED*/
433static void
434DamageResetProc (ExtensionEntry *extEntry)
435{
436    DeleteCallback (&ClientStateCallback, DamageClientCallback, 0);
437}
438
439static int
440FreeDamageExt (pointer value, XID did)
441{
442    DamageExtPtr    pDamageExt = (DamageExtPtr) value;
443
444    /*
445     * Get rid of the resource table entry hanging from the window id
446     */
447    pDamageExt->id = 0;
448    if (WindowDrawable(pDamageExt->pDrawable->type))
449	FreeResourceByType (pDamageExt->pDrawable->id, DamageExtWinType, TRUE);
450    if (pDamageExt->pDamage)
451    {
452	DamageUnregister (pDamageExt->pDrawable, pDamageExt->pDamage);
453	DamageDestroy (pDamageExt->pDamage);
454    }
455    free(pDamageExt);
456    return Success;
457}
458
459static int
460FreeDamageExtWin (pointer value, XID wid)
461{
462    DamageExtPtr    pDamageExt = (DamageExtPtr) value;
463
464    if (pDamageExt->id)
465	FreeResource (pDamageExt->id, RT_NONE);
466    return Success;
467}
468
469static void
470SDamageNotifyEvent (xDamageNotifyEvent *from,
471		    xDamageNotifyEvent *to)
472{
473    to->type = from->type;
474    cpswaps (from->sequenceNumber, to->sequenceNumber);
475    cpswapl (from->drawable, to->drawable);
476    cpswapl (from->damage, to->damage);
477    cpswaps (from->area.x, to->area.x);
478    cpswaps (from->area.y, to->area.y);
479    cpswaps (from->area.width, to->area.width);
480    cpswaps (from->area.height, to->area.height);
481    cpswaps (from->geometry.x, to->geometry.x);
482    cpswaps (from->geometry.y, to->geometry.y);
483    cpswaps (from->geometry.width, to->geometry.width);
484    cpswaps (from->geometry.height, to->geometry.height);
485}
486
487void
488DamageExtensionInit(void)
489{
490    ExtensionEntry *extEntry;
491    int		    s;
492
493    for (s = 0; s < screenInfo.numScreens; s++)
494	DamageSetup (screenInfo.screens[s]);
495
496    DamageExtType = CreateNewResourceType (FreeDamageExt, "DamageExt");
497    if (!DamageExtType)
498	return;
499
500    DamageExtWinType = CreateNewResourceType (FreeDamageExtWin, "DamageExtWin");
501    if (!DamageExtWinType)
502	return;
503
504    if (!dixRegisterPrivateKey(&DamageClientPrivateKeyRec, PRIVATE_CLIENT, sizeof (DamageClientRec)))
505	return;
506
507    if (!AddCallback (&ClientStateCallback, DamageClientCallback, 0))
508	return;
509
510    if ((extEntry = AddExtension(DAMAGE_NAME, XDamageNumberEvents,
511				 XDamageNumberErrors,
512				 ProcDamageDispatch, SProcDamageDispatch,
513				 DamageResetProc, StandardMinorOpcode)) != 0)
514    {
515	DamageReqCode = (unsigned char)extEntry->base;
516	DamageEventBase = extEntry->eventBase;
517	EventSwapVector[DamageEventBase + XDamageNotify] =
518			(EventSwapPtr) SDamageNotifyEvent;
519	SetResourceTypeErrorValue(DamageExtType, extEntry->errorBase + BadDamage);
520    }
521}
522