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