1/*
2 * Copyright © 2003 Keith Packard
3 * Copyright © 2007 Eric Anholt
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of Keith Packard not be used in
10 * advertising or publicity pertaining to distribution of the software without
11 * specific, written prior permission.  Keith Packard makes no
12 * representations about the suitability of this software for any purpose.  It
13 * is provided "as is" without express or implied warranty.
14 *
15 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21 * PERFORMANCE OF THIS SOFTWARE.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27#include "xdamageint.h"
28#include <X11/Xfuncproto.h>
29
30XDamageExtInfo XDamageExtensionInfo;
31
32const char XDamageExtensionName[] = DAMAGE_NAME;
33
34static int
35XDamageCloseDisplay (Display *dpy, XExtCodes *codes);
36
37static Bool
38XDamageWireToEvent(Display *dpy, XEvent *event, xEvent *wire);
39
40static Status
41XDamageEventToWire(Display *dpy, XEvent *event, xEvent *wire);
42
43/*
44 * XDamageExtAddDisplay - add a display to this extension. (Replaces
45 * XextAddDisplay)
46 */
47static XDamageExtDisplayInfo *
48XDamageExtAddDisplay (XDamageExtInfo	*extinfo,
49                      Display		*dpy,
50                      const char	*ext_name)
51{
52    XDamageExtDisplayInfo    *info;
53
54    info = Xmalloc (sizeof (XDamageExtDisplayInfo));
55    if (!info) return NULL;
56    info->display = dpy;
57
58    info->codes = XInitExtension (dpy, ext_name);
59
60    /*
61     * if the server has the extension, then we can initialize the
62     * appropriate function vectors
63     */
64    if (info->codes) {
65	xDamageQueryVersionReply	rep;
66	xDamageQueryVersionReq	*req;
67        XESetCloseDisplay (dpy, info->codes->extension,
68                           XDamageCloseDisplay);
69	for (int ev = info->codes->first_event;
70	     ev < info->codes->first_event + XDamageNumberEvents;
71	     ev++)
72	{
73	    XESetWireToEvent (dpy, ev, XDamageWireToEvent);
74	    XESetEventToWire (dpy, ev, XDamageEventToWire);
75	}
76	/*
77	 * Get the version info
78	 */
79	LockDisplay (dpy);
80	GetReq (DamageQueryVersion, req);
81	req->reqType = (CARD8) info->codes->major_opcode;
82	req->damageReqType = X_DamageQueryVersion;
83	req->majorVersion = DAMAGE_MAJOR;
84	req->minorVersion = DAMAGE_MINOR;
85	if (!_XReply (dpy, (xReply *) &rep, 0, xTrue))
86	{
87	    UnlockDisplay (dpy);
88	    SyncHandle ();
89	    Xfree(info);
90	    return NULL;
91	}
92	info->major_version = rep.majorVersion;
93	info->minor_version = rep.minorVersion;
94	UnlockDisplay (dpy);
95	SyncHandle ();
96    } else {
97	/* The server doesn't have this extension.
98	 * Use a private Xlib-internal extension to hang the close_display
99	 * hook on so that the "cache" (extinfo->cur) is properly cleaned.
100	 * (XBUG 7955)
101	 */
102	XExtCodes *codes = XAddExtension(dpy);
103	if (!codes) {
104	    Xfree(info);
105	    return NULL;
106	}
107        XESetCloseDisplay (dpy, codes->extension, XDamageCloseDisplay);
108    }
109
110    /*
111     * now, chain it onto the list
112     */
113    _XLockMutex(_Xglobal_lock);
114    info->next = extinfo->head;
115    extinfo->head = info;
116    extinfo->cur = info;
117    extinfo->ndisplays++;
118    _XUnlockMutex(_Xglobal_lock);
119    return info;
120}
121
122
123/*
124 * XDamageExtRemoveDisplay - remove the indicated display from the
125 * extension object. (Replaces XextRemoveDisplay.)
126 */
127static int
128XDamageExtRemoveDisplay (XDamageExtInfo *extinfo, const Display *dpy)
129{
130    XDamageExtDisplayInfo *info, *prev;
131
132    /*
133     * locate this display and its back link so that it can be removed
134     */
135    _XLockMutex(_Xglobal_lock);
136    prev = NULL;
137    for (info = extinfo->head; info; info = info->next) {
138	if (info->display == dpy) break;
139	prev = info;
140    }
141    if (!info) {
142	_XUnlockMutex(_Xglobal_lock);
143	return 0;		/* hmm, actually an error */
144    }
145
146    /*
147     * remove the display from the list; handles going to zero
148     */
149    if (prev)
150	prev->next = info->next;
151    else
152	extinfo->head = info->next;
153
154    extinfo->ndisplays--;
155    if (info == extinfo->cur) extinfo->cur = NULL;  /* flush cache */
156    _XUnlockMutex(_Xglobal_lock);
157
158    Xfree (info);
159    return 1;
160}
161
162/*
163 * XDamageExtFindDisplay - look for a display in this extension; keeps a
164 * cache of the most-recently used for efficiency. (Replaces
165 * XextFindDisplay.)
166 */
167static XDamageExtDisplayInfo *
168XDamageExtFindDisplay (XDamageExtInfo *extinfo,
169		       const Display  *dpy)
170{
171    XDamageExtDisplayInfo *info;
172
173    /*
174     * see if this was the most recently accessed display
175     */
176    if ((info = extinfo->cur) && info->display == dpy)
177	return info;
178
179    /*
180     * look for display in list
181     */
182    _XLockMutex(_Xglobal_lock);
183    for (info = extinfo->head; info; info = info->next) {
184	if (info->display == dpy) {
185	    extinfo->cur = info;     /* cache most recently used */
186	    _XUnlockMutex(_Xglobal_lock);
187	    return info;
188	}
189    }
190    _XUnlockMutex(_Xglobal_lock);
191
192    return NULL;
193}
194
195XDamageExtDisplayInfo *
196XDamageFindDisplay (Display *dpy)
197{
198    XDamageExtDisplayInfo *info;
199
200    info = XDamageExtFindDisplay (&XDamageExtensionInfo, dpy);
201    if (!info)
202	info = XDamageExtAddDisplay (&XDamageExtensionInfo, dpy,
203				    XDamageExtensionName);
204    return info;
205}
206
207static int
208XDamageCloseDisplay (Display *dpy, _X_UNUSED XExtCodes *codes)
209{
210    return XDamageExtRemoveDisplay (&XDamageExtensionInfo, dpy);
211}
212
213static Bool
214XDamageWireToEvent(Display *dpy, XEvent *event, xEvent *wire)
215{
216    XDamageExtDisplayInfo *info = XDamageFindDisplay(dpy);
217
218    XDamageCheckExtension(dpy, info, False);
219
220    switch ((wire->u.u.type & 0x7F) - info->codes->first_event)
221    {
222    case XDamageNotify: {
223	XDamageNotifyEvent *aevent = (XDamageNotifyEvent *) event;
224	xDamageNotifyEvent *awire = (xDamageNotifyEvent *) wire;
225
226	aevent->type = awire->type & 0x7F;
227	aevent->serial = _XSetLastRequestRead(dpy,
228					      (xGenericReply *) wire);
229	aevent->send_event = (awire->type & 0x80) != 0;
230	aevent->display = dpy;
231	aevent->drawable = awire->drawable;
232	aevent->damage = awire->damage;
233	aevent->level = awire->level & ~DamageNotifyMore;
234	aevent->more = (awire->level & DamageNotifyMore) ? True : False;
235	aevent->timestamp = awire->timestamp;
236	aevent->area.x = awire->area.x;
237	aevent->area.y = awire->area.y;
238	aevent->area.width = awire->area.width;
239	aevent->area.height = awire->area.height;
240	aevent->geometry.x = awire->geometry.x;
241	aevent->geometry.y = awire->geometry.y;
242	aevent->geometry.width = awire->geometry.width;
243	aevent->geometry.height = awire->geometry.height;
244	return True;
245    }
246    }
247    return False;
248}
249
250static Status
251XDamageEventToWire(Display *dpy, XEvent *event, xEvent *wire)
252{
253    XDamageExtDisplayInfo *info = XDamageFindDisplay(dpy);
254
255    XDamageCheckExtension(dpy, info, False);
256
257    switch ((event->type & 0x7F) - info->codes->first_event)
258    {
259    case XDamageNotify: {
260	XDamageNotifyEvent *aevent;
261	xDamageNotifyEvent *awire;
262	awire = (xDamageNotifyEvent *) wire;
263	aevent = (XDamageNotifyEvent *) event;
264	awire->type = (CARD8) aevent->type | (aevent->send_event ? 0x80 : 0);
265	awire->drawable = (CARD32) aevent->drawable;
266	awire->damage = (CARD32) aevent->damage;
267	awire->level = (CARD8) aevent->level | (aevent->more ? DamageNotifyMore : 0);
268	awire->timestamp = (CARD32) aevent->timestamp;
269	awire->area.x = aevent->area.x;
270	awire->area.y = aevent->area.y;
271	awire->area.width = aevent->area.width;
272	awire->area.height = aevent->area.height;
273	awire->geometry.x = aevent->geometry.x;
274	awire->geometry.y = aevent->geometry.y;
275	awire->geometry.width = aevent->geometry.width;
276	awire->geometry.height = aevent->geometry.height;
277	return True;
278    }
279    }
280    return False;
281}
282
283Bool
284XDamageQueryExtension (Display *dpy,
285			int *event_base_return,
286			int *error_base_return)
287{
288    XDamageExtDisplayInfo *info = XDamageFindDisplay (dpy);
289
290    if (XDamageHasExtension(info))
291    {
292	*event_base_return = info->codes->first_event;
293	*error_base_return = info->codes->first_error;
294	return True;
295    }
296    else
297	return False;
298}
299
300Status
301XDamageQueryVersion (Display *dpy,
302		    int	    *major_version_return,
303		    int	    *minor_version_return)
304{
305    XDamageExtDisplayInfo	*info = XDamageFindDisplay (dpy);
306
307    XDamageCheckExtension (dpy, info, 0);
308
309    *major_version_return = info->major_version;
310    *minor_version_return = info->minor_version;
311    return 1;
312}
313
314Damage
315XDamageCreate (Display *dpy, Drawable drawable, int level)
316{
317    XDamageExtDisplayInfo	*info = XDamageFindDisplay (dpy);
318    xDamageCreateReq		*req;
319    Damage			damage;
320
321    XDamageCheckExtension (dpy, info, 0);
322    LockDisplay (dpy);
323    GetReq (DamageCreate, req);
324    req->reqType = (CARD8) info->codes->major_opcode;
325    req->damageReqType = X_DamageCreate;
326    damage = XAllocID (dpy);
327    req->damage = (CARD32) damage;
328    req->drawable = (CARD32) drawable;
329    req->level = (CARD8) level;
330    UnlockDisplay (dpy);
331    SyncHandle ();
332    return damage;
333}
334
335void
336XDamageDestroy (Display *dpy, Damage damage)
337{
338    XDamageExtDisplayInfo	*info = XDamageFindDisplay (dpy);
339    xDamageDestroyReq		*req;
340
341    XDamageSimpleCheckExtension (dpy, info);
342    LockDisplay (dpy);
343    GetReq (DamageDestroy, req);
344    req->reqType = (CARD8) info->codes->major_opcode;
345    req->damageReqType = X_DamageDestroy;
346    req->damage = (CARD32) damage;
347    UnlockDisplay (dpy);
348    SyncHandle ();
349}
350
351void
352XDamageSubtract (Display *dpy, Damage damage,
353		 XserverRegion repair, XserverRegion parts)
354{
355    XDamageExtDisplayInfo	*info = XDamageFindDisplay (dpy);
356    xDamageSubtractReq		*req;
357
358    XDamageSimpleCheckExtension (dpy, info);
359    LockDisplay (dpy);
360    GetReq (DamageSubtract, req);
361    req->reqType = (CARD8) info->codes->major_opcode;
362    req->damageReqType = X_DamageSubtract;
363    req->damage = (CARD32) damage;
364    req->repair = (CARD32) repair;
365    req->parts = (CARD32) parts;
366    UnlockDisplay (dpy);
367    SyncHandle ();
368}
369
370void
371XDamageAdd (Display *dpy, Drawable drawable, XserverRegion region)
372{
373    XDamageExtDisplayInfo	*info = XDamageFindDisplay (dpy);
374    xDamageAddReq		*req;
375
376    XDamageSimpleCheckExtension (dpy, info);
377    LockDisplay (dpy);
378    GetReq (DamageAdd, req);
379    req->reqType = (CARD8) info->codes->major_opcode;
380    req->damageReqType = X_DamageAdd;
381    req->drawable = (CARD32) drawable;
382    req->region = (CARD32) region;
383
384    UnlockDisplay (dpy);
385    SyncHandle ();
386}
387