1 /*
2  * Copyright © 2013 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 copyright
7  * notice and this permission notice appear in supporting documentation, and
8  * that the name of the copyright holders not be used in advertising or
9  * publicity pertaining to distribution of the software without specific,
10  * written prior permission.  The copyright holders make no representations
11  * about the suitability of this software for any purpose.  It is provided "as
12  * is" without express or implied warranty.
13  *
14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL THE COPYRIGHT HOLDERS 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 PERFORMANCE
20  * OF THIS SOFTWARE.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <X11/Xlib.h>
30 #include <X11/Xlibint.h>
31 #include <X11/Xutil.h>
32 
33 #include <X11/extensions/extutil.h>
34 #include <X11/extensions/geproto.h>
35 #include <X11/extensions/ge.h>
36 #include <X11/extensions/Xge.h>
37 
38 #include <X11/extensions/Xpresent.h>
39 #include <X11/extensions/presentproto.h>
40 
41 typedef struct _XPresentExtDisplayInfo {
42     struct _XPresentExtDisplayInfo      *next;          /* keep a linked list */
43     Display                             *display;	/* which display this is */
44     XExtCodes                           *codes;	        /* the extension protocol codes */
45     int			                major_version;  /* -1 means we don't know */
46     int			                minor_version;  /* -1 means we don't know */
47 } XPresentExtDisplayInfo;
48 
49 /* replaces XExtensionInfo */
50 typedef struct _XPresentExtInfo {
51     XPresentExtDisplayInfo              *head;          /* start of the list */
52     XPresentExtDisplayInfo              *cur;           /* most recently used */
53     int                                 ndisplays;      /* number of displays */
54 } XPresentExtInfo;
55 
56 extern XPresentExtInfo XPresentExtensionInfo;
57 extern char XPresentExtensionName[];
58 
59 XPresentExtDisplayInfo *
60 XPresentFindDisplay (Display *dpy);
61 
62 #define XPresentHasExtension(i) ((i) && ((i)->codes))
63 
64 #define XPresentCheckExtension(dpy,i,val)               \
65     if (!XPresentHasExtension(i)) { return val; }
66 
67 #define XPresentSimpleCheckExtension(dpy,i)     \
68     if (!XPresentHasExtension(i)) { return; }
69 
70 XPresentExtInfo XPresentExtensionInfo;
71 char XPresentExtensionName[] = PRESENT_NAME;
72 
73 static int
74 XPresentCloseDisplay (Display *dpy, XExtCodes *codes);
75 
76 static Bool
XPresentCopyCookie(Display * dpy,XGenericEventCookie * in,XGenericEventCookie * out)77 XPresentCopyCookie(Display              *dpy,
78                    XGenericEventCookie  *in,
79                    XGenericEventCookie  *out)
80 {
81     int                         ret = True;
82     XPresentExtDisplayInfo        *info = XPresentFindDisplay(dpy);
83 
84     if (in->extension != info->codes->major_opcode)
85     {
86         printf("XFixesCopyCookie: wrong extension opcode %d\n",
87                 in->extension);
88         return False;
89     }
90 
91     *out = *in;
92     out->data = NULL;
93     out->cookie = 0;
94 
95     switch(in->evtype) {
96     case PresentConfigureNotify:
97     case PresentCompleteNotify:
98 #if PRESENT_FUTURE_VERSION
99     case PresentRedirectNotify:
100 #endif
101         break;
102     default:
103         printf("XPresentCopyCookie: unknown evtype %d\n", in->evtype);
104         ret = False;
105     }
106 
107     if (!ret)
108         printf("XPresentCopyCookie: Failed to copy evtype %d", in->evtype);
109     return ret;
110 }
111 
112 static Bool
XPresentWireToCookie(Display * dpy,XGenericEventCookie * cookie,xEvent * wire_event)113 XPresentWireToCookie(Display	                *dpy,
114                      XGenericEventCookie        *cookie,
115                      xEvent	                *wire_event)
116 {
117     XPresentExtDisplayInfo      *info = XPresentFindDisplay(dpy);
118     xGenericEvent               *ge = (xGenericEvent*)wire_event;
119 
120     if (ge->extension != info->codes->major_opcode)
121     {
122         printf("XInputWireToCookie: wrong extension opcode %d\n",
123                 ge->extension);
124         return False;
125     }
126 
127     cookie->type = ge->type & 0x7f;
128     cookie->serial = _XSetLastRequestRead(dpy, (xGenericReply *) ge);
129     cookie->send_event = ((ge->type & 0x80) != 0);
130     cookie->display = dpy;
131     cookie->extension = ge->extension;
132     cookie->evtype = ge->evtype;
133 
134     switch(ge->evtype) {
135     case PresentConfigureNotify:  {
136         xPresentConfigureNotify *proto = (xPresentConfigureNotify *) ge;
137         XPresentConfigureNotifyEvent *ce = malloc (sizeof (XPresentConfigureNotifyEvent));
138         cookie->data = ce;
139 
140         ce->type = cookie->type;
141         ce->serial = cookie->serial;
142         ce->send_event = cookie->send_event;
143         ce->display = cookie->display;
144         ce->extension = cookie->extension;
145         ce->evtype = cookie->evtype;
146 
147         ce->eid = proto->eid;
148         ce->window = proto->window;
149         ce->x = proto->x;
150         ce->y = proto->y;
151         ce->width = proto->width;
152         ce->height = proto->height;
153         ce->off_x = proto->off_x;
154         ce->off_y = proto->off_y;
155         ce->pixmap_width = proto->pixmap_width;
156         ce->pixmap_height = proto->pixmap_height;
157         ce->pixmap_flags = proto->pixmap_flags;
158 
159         break;
160     }
161     case PresentCompleteNotify: {
162         xPresentCompleteNotify *proto = (xPresentCompleteNotify *) ge;
163         XPresentCompleteNotifyEvent *ce = malloc (sizeof (XPresentCompleteNotifyEvent));
164         cookie->data = ce;
165 
166         ce->type = cookie->type;
167         ce->serial = cookie->serial;
168         ce->send_event = cookie->send_event;
169         ce->display = cookie->display;
170         ce->extension = cookie->extension;
171         ce->evtype = cookie->evtype;
172 
173         ce->eid = proto->eid;
174         ce->window = proto->window;
175         ce->serial_number = proto->serial;
176         ce->ust = proto->ust;
177         ce->msc = proto->msc;
178         ce->kind = proto->kind;
179         ce->mode = proto->mode;
180 
181         break;
182     }
183     case PresentIdleNotify: {
184         xPresentIdleNotify *proto = (xPresentIdleNotify *) ge;
185         XPresentIdleNotifyEvent *ce = malloc (sizeof (XPresentIdleNotifyEvent));
186         cookie->data = ce;
187 
188         ce->type = cookie->type;
189         ce->serial = cookie->serial;
190         ce->send_event = cookie->send_event;
191         ce->display = cookie->display;
192         ce->extension = cookie->extension;
193         ce->evtype = cookie->evtype;
194 
195         ce->eid = proto->eid;
196         ce->window = proto->window;
197         ce->serial_number = proto->serial;
198         ce->pixmap = proto->pixmap;
199         ce->idle_fence = proto->idle_fence;
200 
201         break;
202     }
203 #if PRESENT_FUTURE_VERSION
204     case PresentRedirectNotify: {
205         xPresentRedirectNotify *proto = (xPresentRedirectNotify *) ge;
206         xPresentNotify *xNotify = (xPresentNotify *) (proto + 1);
207         int nnotifies = (((proto->length + 8) - (sizeof (xPresentRedirectNotify) >> 2))) >> 1;
208         XPresentRedirectNotifyEvent *re = malloc (sizeof (XPresentRedirectNotifyEvent) + nnotifies * sizeof (XPresentNotify));
209         XPresentNotify *XNotify = (XPresentNotify *) (re + 1);
210         int i;
211         cookie->data = re;
212 
213         re->type = cookie->type;
214         re->serial = cookie->serial;
215         re->send_event = cookie->send_event;
216         re->display = cookie->display;
217         re->extension = cookie->extension;
218         re->evtype = cookie->evtype;
219 
220         re->eid = proto->eid;
221         re->event_window = proto->event_window;
222 
223         re->window = proto->window;
224         re->pixmap = proto->pixmap;
225         re->serial_number = proto->serial;
226 
227         re->valid_region = proto->valid_region;
228         re->update_region = proto->update_region;
229 
230         re->valid_rect = *(XRectangle *) &(proto->valid_rect);
231         re->update_rect = *(XRectangle *) &(proto->update_rect);
232 
233         re->x_off = proto->x_off;
234         re->y_off = proto->y_off;
235         re->target_crtc = proto->target_crtc;
236 
237         re->wait_fence = proto->wait_fence;
238         re->idle_fence = proto->idle_fence;
239 
240         re->options = proto->options;
241 
242         re->target_msc = proto->target_msc;
243         re->divisor = proto->divisor;
244         re->remainder = proto->remainder;
245 
246         re->nnotifies = nnotifies;
247         re->notifies = XNotify;
248         for (i = 0; i < nnotifies; i++) {
249             XNotify[i].window = xNotify[i].window;
250             XNotify[i].serial = xNotify[i].serial;
251         }
252         break;
253     }
254 #endif
255     default:
256         printf("XPresentWireToCookie: Unknown generic event. type %d\n", ge->evtype);
257 
258     }
259     return False;
260 }
261 
262 /*
263  * XPresentExtAddDisplay - add a display to this extension. (Replaces
264  * XextAddDisplay)
265  */
266 static XPresentExtDisplayInfo *
XPresentExtAddDisplay(XPresentExtInfo * extinfo,Display * dpy,char * ext_name)267 XPresentExtAddDisplay (XPresentExtInfo *extinfo,
268                       Display        *dpy,
269                       char           *ext_name)
270 {
271     XPresentExtDisplayInfo    *info;
272 
273     info = (XPresentExtDisplayInfo *) Xmalloc (sizeof (XPresentExtDisplayInfo));
274     if (!info) return NULL;
275     info->display = dpy;
276 
277     info->codes = XInitExtension (dpy, ext_name);
278 
279     /*
280      * if the server has the extension, then we can initialize the
281      * appropriate function vectors
282      */
283     if (info->codes) {
284 	xPresentQueryVersionReply	rep;
285 	xPresentQueryVersionReq	        *req;
286 
287         XESetCloseDisplay (dpy, info->codes->extension, XPresentCloseDisplay);
288 
289         XESetWireToEventCookie(dpy, info->codes->major_opcode, XPresentWireToCookie);
290         XESetCopyEventCookie(dpy, info->codes->major_opcode, XPresentCopyCookie);
291 
292 	/*
293 	 * Get the version info
294 	 */
295 	LockDisplay (dpy);
296 	GetReq (PresentQueryVersion, req);
297 	req->reqType = info->codes->major_opcode;
298 	req->presentReqType = X_PresentQueryVersion;
299 	req->majorVersion = PRESENT_MAJOR;
300 	req->minorVersion = PRESENT_MINOR;
301 	if (!_XReply (dpy, (xReply *) &rep, 0, xTrue))
302 	{
303 	    UnlockDisplay (dpy);
304 	    SyncHandle ();
305 	    Xfree(info);
306 	    return NULL;
307 	}
308 	info->major_version = rep.majorVersion;
309 	info->minor_version = rep.minorVersion;
310 	UnlockDisplay (dpy);
311 	SyncHandle ();
312     } else {
313 	/* The server doesn't have this extension.
314 	 * Use a private Xlib-internal extension to hang the close_display
315 	 * hook on so that the "cache" (extinfo->cur) is properly cleaned.
316 	 * (XBUG 7955)
317 	 */
318 	XExtCodes *codes = XAddExtension(dpy);
319 	if (!codes) {
320 	    XFree(info);
321 	    return NULL;
322 	}
323         XESetCloseDisplay (dpy, codes->extension, XPresentCloseDisplay);
324     }
325 
326     /*
327      * now, chain it onto the list
328      */
329     _XLockMutex(_Xglobal_lock);
330     info->next = extinfo->head;
331     extinfo->head = info;
332     extinfo->cur = info;
333     extinfo->ndisplays++;
334     _XUnlockMutex(_Xglobal_lock);
335     return info;
336 }
337 
338 
339 /*
340  * XPresentExtRemoveDisplay - remove the indicated display from the
341  * extension object. (Replaces XextRemoveDisplay.)
342  */
343 static int
XPresentExtRemoveDisplay(XPresentExtInfo * extinfo,Display * dpy)344 XPresentExtRemoveDisplay (XPresentExtInfo *extinfo, Display *dpy)
345 {
346     XPresentExtDisplayInfo *info, *prev;
347 
348     /*
349      * locate this display and its back link so that it can be removed
350      */
351     _XLockMutex(_Xglobal_lock);
352     prev = NULL;
353     for (info = extinfo->head; info; info = info->next) {
354 	if (info->display == dpy) break;
355 	prev = info;
356     }
357     if (!info) {
358 	_XUnlockMutex(_Xglobal_lock);
359 	return 0;		/* hmm, actually an error */
360     }
361 
362     /*
363      * remove the display from the list; handles going to zero
364      */
365     if (prev)
366 	prev->next = info->next;
367     else
368 	extinfo->head = info->next;
369 
370     extinfo->ndisplays--;
371     if (info == extinfo->cur) extinfo->cur = NULL;  /* flush cache */
372     _XUnlockMutex(_Xglobal_lock);
373 
374     Xfree ((char *) info);
375     return 1;
376 }
377 
378 /*
379  * XPresentExtFindDisplay - look for a display in this extension; keeps a
380  * cache of the most-recently used for efficiency. (Replaces
381  * XextFindDisplay.)
382  */
383 static XPresentExtDisplayInfo *
XPresentExtFindDisplay(XPresentExtInfo * extinfo,Display * dpy)384 XPresentExtFindDisplay (XPresentExtInfo *extinfo,
385                         Display         *dpy)
386 {
387     XPresentExtDisplayInfo *info;
388 
389     /*
390      * see if this was the most recently accessed display
391      */
392     if ((info = extinfo->cur) && info->display == dpy)
393 	return info;
394 
395     /*
396      * look for display in list
397      */
398     _XLockMutex(_Xglobal_lock);
399     for (info = extinfo->head; info; info = info->next) {
400 	if (info->display == dpy) {
401 	    extinfo->cur = info;     /* cache most recently used */
402 	    _XUnlockMutex(_Xglobal_lock);
403 	    return info;
404 	}
405     }
406     _XUnlockMutex(_Xglobal_lock);
407 
408     return NULL;
409 }
410 
411 XPresentExtDisplayInfo *
XPresentFindDisplay(Display * dpy)412 XPresentFindDisplay (Display *dpy)
413 {
414     XPresentExtDisplayInfo *info;
415 
416     info = XPresentExtFindDisplay (&XPresentExtensionInfo, dpy);
417     if (!info)
418 	info = XPresentExtAddDisplay (&XPresentExtensionInfo, dpy,
419 				    XPresentExtensionName);
420     return info;
421 }
422 
423 static int
XPresentCloseDisplay(Display * dpy,XExtCodes * codes)424 XPresentCloseDisplay (Display *dpy, XExtCodes *codes)
425 {
426     return XPresentExtRemoveDisplay (&XPresentExtensionInfo, dpy);
427 }
428 
429 Bool
XPresentQueryExtension(Display * dpy,int * major_opcode_return,int * event_base_return,int * error_base_return)430 XPresentQueryExtension (Display *dpy,
431                         int *major_opcode_return,
432 			int *event_base_return,
433 			int *error_base_return)
434 {
435     XPresentExtDisplayInfo *info = XPresentFindDisplay (dpy);
436 
437     if (XPresentHasExtension(info))
438     {
439         if (major_opcode_return)
440             *major_opcode_return = info->codes->major_opcode;
441         if (event_base_return)
442             *event_base_return = info->codes->first_event;
443         if (error_base_return)
444             *error_base_return = info->codes->first_error;
445 	return True;
446     }
447     else
448 	return False;
449 }
450 
451 Status
XPresentQueryVersion(Display * dpy,int * major_version_return,int * minor_version_return)452 XPresentQueryVersion (Display *dpy,
453 		    int	    *major_version_return,
454 		    int	    *minor_version_return)
455 {
456     XPresentExtDisplayInfo	*info = XPresentFindDisplay (dpy);
457 
458     XPresentCheckExtension (dpy, info, 0);
459 
460     *major_version_return = info->major_version;
461     *minor_version_return = info->minor_version;
462     return 1;
463 }
464 
465 int
XPresentVersion(void)466 XPresentVersion (void)
467 {
468     return PRESENT_VERSION;
469 }
470 
471 void
XPresentPixmap(Display * dpy,Window window,Pixmap pixmap,uint32_t serial,XserverRegion valid,XserverRegion update,int x_off,int y_off,RRCrtc target_crtc,XSyncFence wait_fence,XSyncFence idle_fence,uint32_t options,uint64_t target_msc,uint64_t divisor,uint64_t remainder,XPresentNotify * notifies,int nnotifies)472 XPresentPixmap(Display *dpy,
473                Window window,
474                Pixmap pixmap,
475                uint32_t serial,
476                XserverRegion valid,
477                XserverRegion update,
478                int x_off,
479                int y_off,
480                RRCrtc target_crtc,
481                XSyncFence wait_fence,
482                XSyncFence idle_fence,
483                uint32_t options,
484                uint64_t target_msc,
485                uint64_t divisor,
486                uint64_t remainder,
487                XPresentNotify *notifies,
488                int nnotifies)
489 {
490     XPresentExtDisplayInfo	*info = XPresentFindDisplay (dpy);
491     xPresentPixmapReq           *req;
492     long                        len = ((long) nnotifies) << 1;
493 
494     XPresentSimpleCheckExtension (dpy, info);
495 
496     LockDisplay (dpy);
497     GetReq(PresentPixmap, req);
498     req->reqType = info->codes->major_opcode;
499     req->presentReqType = X_PresentPixmap;
500     req->window = window;
501     req->pixmap = pixmap;
502     req->serial = serial;
503     req->valid = valid;
504     req->update = update;
505     req->x_off = x_off;
506     req->y_off = y_off;
507     req->target_crtc = target_crtc;
508     req->wait_fence = wait_fence;
509     req->idle_fence = idle_fence;
510     req->options = options;
511     req->target_msc = target_msc;
512     req->divisor = divisor;
513     req->remainder = remainder;
514     SetReqLen(req, len, len);
515     Data32(dpy, (CARD32 *) notifies, len);
516     UnlockDisplay (dpy);
517     SyncHandle();
518 }
519 
520 void
XPresentNotifyMSC(Display * dpy,Window window,uint32_t serial,uint64_t target_msc,uint64_t divisor,uint64_t remainder)521 XPresentNotifyMSC(Display *dpy,
522                   Window window,
523                   uint32_t serial,
524                   uint64_t target_msc,
525                   uint64_t divisor,
526                   uint64_t remainder)
527 {
528     XPresentExtDisplayInfo	*info = XPresentFindDisplay (dpy);
529     xPresentNotifyMSCReq        *req;
530 
531     XPresentSimpleCheckExtension (dpy, info);
532 
533     LockDisplay (dpy);
534     GetReq(PresentNotifyMSC, req);
535     req->reqType = info->codes->major_opcode;
536     req->presentReqType = X_PresentNotifyMSC;
537     req->window = window;
538     req->serial = serial;
539     req->target_msc = target_msc;
540     req->divisor = divisor;
541     req->remainder = remainder;
542     UnlockDisplay (dpy);
543     SyncHandle();
544 }
545 
546 XID
XPresentSelectInput(Display * dpy,Window window,unsigned event_mask)547 XPresentSelectInput(Display *dpy,
548                     Window window,
549                     unsigned event_mask)
550 {
551     XPresentExtDisplayInfo	*info = XPresentFindDisplay (dpy);
552     XID                         eid;
553     xPresentSelectInputReq      *req;
554 
555     XPresentCheckExtension (dpy, info, 0);
556     LockDisplay (dpy);
557     GetReq(PresentSelectInput, req);
558     req->reqType = info->codes->major_opcode;
559     req->presentReqType = X_PresentSelectInput;
560     req->eid = eid = XAllocID(dpy);
561     req->window = window;
562     req->eventMask = event_mask;
563     UnlockDisplay (dpy);
564     SyncHandle();
565     return eid;
566 }
567 
568 void
XPresentFreeInput(Display * dpy,Window window,XID event_id)569 XPresentFreeInput(Display *dpy,
570                   Window window,
571                   XID event_id)
572 {
573     XPresentExtDisplayInfo	*info = XPresentFindDisplay (dpy);
574     xPresentSelectInputReq      *req;
575 
576     XPresentSimpleCheckExtension (dpy, info);
577     LockDisplay (dpy);
578     GetReq(PresentSelectInput, req);
579     req->reqType = info->codes->major_opcode;
580     req->presentReqType = X_PresentSelectInput;
581     req->eid = event_id;
582     req->window = window;
583     req->eventMask = 0;
584     UnlockDisplay (dpy);
585     SyncHandle();
586 }
587 
588 uint32_t
XPresentQueryCapabilities(Display * dpy,XID target)589 XPresentQueryCapabilities(Display *dpy,
590                           XID target)
591 {
592     XPresentExtDisplayInfo	        *info = XPresentFindDisplay (dpy);
593     xPresentQueryCapabilitiesReq        *req;
594     xPresentQueryCapabilitiesReply      rep;
595 
596     XPresentCheckExtension (dpy, info, 0);
597     LockDisplay (dpy);
598     GetReq(PresentQueryCapabilities, req);
599     req->reqType = info->codes->major_opcode;
600     req->presentReqType = X_PresentQueryCapabilities;
601     req->target = target;
602 
603     if (!_XReply (dpy, (xReply *) &rep, 0, xFalse)) {
604 	UnlockDisplay (dpy);
605 	SyncHandle ();
606         return 0;
607     }
608 
609     UnlockDisplay (dpy);
610     SyncHandle();
611     return rep.capabilities;
612 }
613 
614