1/*
2 * Copyright © 2008 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Soft-
6 * ware"), to deal in the Software without restriction, including without
7 * limitation the rights to use, copy, modify, merge, publish, distribute,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, provided that the above copyright
10 * notice(s) and this permission notice appear in all copies of the Soft-
11 * ware and that both the above copyright notice(s) and this permission
12 * notice appear in supporting documentation.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16 * ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY
17 * RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN
18 * THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSE-
19 * QUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFOR-
22 * MANCE OF THIS SOFTWARE.
23 *
24 * Except as contained in this notice, the name of a copyright holder shall
25 * not be used in advertising or otherwise to promote the sale, use or
26 * other dealings in this Software without prior written authorization of
27 * the copyright holder.
28 *
29 * Authors:
30 *   Kristian Høgsberg (krh@redhat.com)
31 */
32
33
34#include <stdio.h>
35#include <X11/Xlibint.h>
36#include <X11/extensions/Xext.h>
37#include <X11/extensions/extutil.h>
38#include <X11/extensions/dri2proto.h>
39
40#include "dri2.h"
41
42/* Allow the build to work with an older versions of dri2proto.h and
43 * dri2tokens.h.
44 */
45#if DRI2_MINOR < 1
46#undef DRI2_MINOR
47#define DRI2_MINOR 1
48#define X_DRI2GetBuffersWithFormat 7
49#endif
50
51
52static char dri2ExtensionName[] = DRI2_NAME;
53static XExtensionInfo *dri2Info;
54static XEXT_GENERATE_CLOSE_DISPLAY (DRI2CloseDisplay, dri2Info)
55
56static Bool
57DRI2WireToEvent(Display *dpy, XEvent *event, xEvent *wire);
58static Status
59DRI2EventToWire(Display *dpy, XEvent *event, xEvent *wire);
60static int
61DRI2Error(Display *display, xError *err, XExtCodes *codes, int *ret_code);
62
63static /* const */ XExtensionHooks dri2ExtensionHooks = {
64  NULL,                   /* create_gc */
65  NULL,                   /* copy_gc */
66  NULL,                   /* flush_gc */
67  NULL,                   /* free_gc */
68  NULL,                   /* create_font */
69  NULL,                   /* free_font */
70  DRI2CloseDisplay,       /* close_display */
71  DRI2WireToEvent,        /* wire_to_event */
72  DRI2EventToWire,        /* event_to_wire */
73  DRI2Error,              /* error */
74  NULL,                   /* error_string */
75};
76
77static XEXT_GENERATE_FIND_DISPLAY (DRI2FindDisplay,
78                                   dri2Info,
79                                   dri2ExtensionName,
80                                   &dri2ExtensionHooks,
81                                   0, NULL)
82
83static Bool
84DRI2WireToEvent(Display *dpy, XEvent *event, xEvent *wire)
85{
86   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
87
88   XextCheckExtension(dpy, info, dri2ExtensionName, False);
89
90   switch ((wire->u.u.type & 0x7f) - info->codes->first_event) {
91#ifdef X_DRI2SwapBuffers
92   case DRI2_BufferSwapComplete:
93      /* Ignore swap events if we're not looking for them */
94	   printf("BufferSwapComplete\n");
95      return False;
96#endif
97#ifdef DRI2_InvalidateBuffers
98   case DRI2_InvalidateBuffers:
99	   printf("InvalidateBuffers\n");
100      return False;
101#endif
102   default:
103      /* client doesn't support server event */
104      break;
105   }
106
107   return False;
108}
109
110/* We don't actually support this.  It doesn't make sense for clients to
111 * send each other DRI2 events.
112 */
113static Status
114DRI2EventToWire(Display *dpy, XEvent *event, xEvent *wire)
115{
116   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
117
118   XextCheckExtension(dpy, info, dri2ExtensionName, False);
119
120   switch (event->type) {
121   default:
122      /* client doesn't support server event */
123      break;
124   }
125
126   return Success;
127}
128
129static int
130DRI2Error(Display *display, xError *err, XExtCodes *codes, int *ret_code)
131{
132    if (err->majorCode == codes->major_opcode &&
133	err->errorCode == BadDrawable &&
134	err->minorCode == X_DRI2CopyRegion)
135	return True;
136
137    /* If the X drawable was destroyed before the GLX drawable, the
138     * DRI2 drawble will be gone by the time we call
139     * DRI2DestroyDrawable.  So just ignore BadDrawable here. */
140    if (err->majorCode == codes->major_opcode &&
141	err->errorCode == BadDrawable &&
142	err->minorCode == X_DRI2DestroyDrawable)
143	return True;
144
145    /* If the server is non-local DRI2Connect will raise BadRequest.
146     * Swallow this so that DRI2Connect can signal this in its return code */
147    if (err->majorCode == codes->major_opcode &&
148        err->minorCode == X_DRI2Connect &&
149        err->errorCode == BadRequest) {
150	*ret_code = False;
151	return True;
152    }
153
154    return False;
155}
156
157Bool
158DRI2QueryExtension(Display * dpy, int *eventBase, int *errorBase)
159{
160   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
161
162   if (XextHasExtension(info)) {
163      *eventBase = info->codes->first_event;
164      *errorBase = info->codes->first_error;
165      return True;
166   }
167
168   return False;
169}
170
171Bool
172DRI2QueryVersion(Display * dpy, int *major, int *minor)
173{
174   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
175   xDRI2QueryVersionReply rep;
176   xDRI2QueryVersionReq *req;
177   int i, nevents;
178
179   XextCheckExtension(dpy, info, dri2ExtensionName, False);
180
181   LockDisplay(dpy);
182   GetReq(DRI2QueryVersion, req);
183   req->reqType = info->codes->major_opcode;
184   req->dri2ReqType = X_DRI2QueryVersion;
185   req->majorVersion = DRI2_MAJOR;
186   req->minorVersion = DRI2_MINOR;
187   if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) {
188      UnlockDisplay(dpy);
189      SyncHandle();
190      return False;
191   }
192   *major = rep.majorVersion;
193   *minor = rep.minorVersion;
194   UnlockDisplay(dpy);
195   SyncHandle();
196
197   switch (rep.minorVersion) {
198   case 1:
199	   nevents = 0;
200	   break;
201   case 2:
202	   nevents = 1;
203	   break;
204   case 3:
205   default:
206	   nevents = 2;
207	   break;
208   }
209
210   for (i = 0; i < nevents; i++) {
211       XESetWireToEvent (dpy, info->codes->first_event + i, DRI2WireToEvent);
212       XESetEventToWire (dpy, info->codes->first_event + i, DRI2EventToWire);
213   }
214
215   return True;
216}
217
218Bool
219DRI2Connect(Display * dpy, XID window, char **driverName, char **deviceName)
220{
221   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
222   xDRI2ConnectReply rep;
223   xDRI2ConnectReq *req;
224
225   XextCheckExtension(dpy, info, dri2ExtensionName, False);
226
227   LockDisplay(dpy);
228   GetReq(DRI2Connect, req);
229   req->reqType = info->codes->major_opcode;
230   req->dri2ReqType = X_DRI2Connect;
231   req->window = window;
232   req->driverType = DRI2DriverDRI;
233   if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) {
234      UnlockDisplay(dpy);
235      SyncHandle();
236      return False;
237   }
238
239   if (rep.driverNameLength == 0 && rep.deviceNameLength == 0) {
240      UnlockDisplay(dpy);
241      SyncHandle();
242      return False;
243   }
244
245   *driverName = Xmalloc(rep.driverNameLength + 1);
246   if (*driverName == NULL) {
247      _XEatData(dpy,
248                ((rep.driverNameLength + 3) & ~3) +
249                ((rep.deviceNameLength + 3) & ~3));
250      UnlockDisplay(dpy);
251      SyncHandle();
252      return False;
253   }
254   _XReadPad(dpy, *driverName, rep.driverNameLength);
255   (*driverName)[rep.driverNameLength] = '\0';
256
257   *deviceName = Xmalloc(rep.deviceNameLength + 1);
258   if (*deviceName == NULL) {
259      Xfree(*driverName);
260      _XEatData(dpy, ((rep.deviceNameLength + 3) & ~3));
261      UnlockDisplay(dpy);
262      SyncHandle();
263      return False;
264   }
265   _XReadPad(dpy, *deviceName, rep.deviceNameLength);
266   (*deviceName)[rep.deviceNameLength] = '\0';
267
268   UnlockDisplay(dpy);
269   SyncHandle();
270
271   return True;
272}
273
274Bool
275DRI2Authenticate(Display * dpy, XID window, unsigned int magic)
276{
277   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
278   xDRI2AuthenticateReq *req;
279   xDRI2AuthenticateReply rep;
280
281   XextCheckExtension(dpy, info, dri2ExtensionName, False);
282
283   LockDisplay(dpy);
284   GetReq(DRI2Authenticate, req);
285   req->reqType = info->codes->major_opcode;
286   req->dri2ReqType = X_DRI2Authenticate;
287   req->window = window;
288   req->magic = magic;
289
290   if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) {
291      UnlockDisplay(dpy);
292      SyncHandle();
293      return False;
294   }
295
296   UnlockDisplay(dpy);
297   SyncHandle();
298
299   return rep.authenticated;
300}
301
302void
303DRI2CreateDrawable(Display * dpy, XID drawable)
304{
305   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
306   xDRI2CreateDrawableReq *req;
307
308   XextSimpleCheckExtension(dpy, info, dri2ExtensionName);
309
310   LockDisplay(dpy);
311   GetReq(DRI2CreateDrawable, req);
312   req->reqType = info->codes->major_opcode;
313   req->dri2ReqType = X_DRI2CreateDrawable;
314   req->drawable = drawable;
315   UnlockDisplay(dpy);
316   SyncHandle();
317}
318
319void
320DRI2DestroyDrawable(Display * dpy, XID drawable)
321{
322   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
323   xDRI2DestroyDrawableReq *req;
324
325   XextSimpleCheckExtension(dpy, info, dri2ExtensionName);
326
327   XSync(dpy, False);
328
329   LockDisplay(dpy);
330   GetReq(DRI2DestroyDrawable, req);
331   req->reqType = info->codes->major_opcode;
332   req->dri2ReqType = X_DRI2DestroyDrawable;
333   req->drawable = drawable;
334   UnlockDisplay(dpy);
335   SyncHandle();
336}
337
338DRI2Buffer *
339DRI2GetBuffers(Display * dpy, XID drawable,
340               int *width, int *height,
341               unsigned int *attachments, int count, int *outCount)
342{
343   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
344   xDRI2GetBuffersReply rep;
345   xDRI2GetBuffersReq *req;
346   DRI2Buffer *buffers;
347   xDRI2Buffer repBuffer;
348   uint32_t *p;
349   int i;
350
351   XextCheckExtension(dpy, info, dri2ExtensionName, False);
352
353   LockDisplay(dpy);
354   GetReqExtra(DRI2GetBuffers, count * 4, req);
355   req->reqType = info->codes->major_opcode;
356   req->dri2ReqType = X_DRI2GetBuffers;
357   req->drawable = drawable;
358   req->count = count;
359   p = (uint32_t *) & req[1];
360   for (i = 0; i < count; i++)
361      p[i] = attachments[i];
362
363   if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) {
364      UnlockDisplay(dpy);
365      SyncHandle();
366      return NULL;
367   }
368
369   *width = rep.width;
370   *height = rep.height;
371   *outCount = rep.count;
372
373   buffers = Xmalloc(rep.count * sizeof buffers[0]);
374   if (buffers == NULL) {
375      _XEatData(dpy, rep.count * sizeof repBuffer);
376      UnlockDisplay(dpy);
377      SyncHandle();
378      return NULL;
379   }
380
381   for (i = 0; i < rep.count; i++) {
382      _XReadPad(dpy, (char *) &repBuffer, sizeof repBuffer);
383      buffers[i].attachment = repBuffer.attachment;
384      buffers[i].name = repBuffer.name;
385      buffers[i].pitch = repBuffer.pitch;
386      buffers[i].cpp = repBuffer.cpp;
387      buffers[i].flags = repBuffer.flags;
388   }
389
390   UnlockDisplay(dpy);
391   SyncHandle();
392
393   return buffers;
394}
395
396
397DRI2Buffer *
398DRI2GetBuffersWithFormat(Display * dpy, XID drawable,
399                         int *width, int *height,
400                         unsigned int *attachments, int count, int *outCount)
401{
402   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
403   xDRI2GetBuffersReply rep;
404   xDRI2GetBuffersReq *req;
405   DRI2Buffer *buffers;
406   xDRI2Buffer repBuffer;
407   uint32_t *p;
408   int i;
409
410   XextCheckExtension(dpy, info, dri2ExtensionName, False);
411
412   LockDisplay(dpy);
413   GetReqExtra(DRI2GetBuffers, count * (4 * 2), req);
414   req->reqType = info->codes->major_opcode;
415   req->dri2ReqType = X_DRI2GetBuffersWithFormat;
416   req->drawable = drawable;
417   req->count = count;
418   p = (uint32_t *) & req[1];
419   for (i = 0; i < (count * 2); i++)
420      p[i] = attachments[i];
421
422   if (!_XReply(dpy, (xReply *) & rep, 0, xFalse)) {
423      UnlockDisplay(dpy);
424      SyncHandle();
425      return NULL;
426   }
427
428   *width = rep.width;
429   *height = rep.height;
430   *outCount = rep.count;
431
432   buffers = Xmalloc(rep.count * sizeof buffers[0]);
433   if (buffers == NULL) {
434      _XEatData(dpy, rep.count * sizeof repBuffer);
435      UnlockDisplay(dpy);
436      SyncHandle();
437      return NULL;
438   }
439
440   for (i = 0; i < rep.count; i++) {
441      _XReadPad(dpy, (char *) &repBuffer, sizeof repBuffer);
442      buffers[i].attachment = repBuffer.attachment;
443      buffers[i].name = repBuffer.name;
444      buffers[i].pitch = repBuffer.pitch;
445      buffers[i].cpp = repBuffer.cpp;
446      buffers[i].flags = repBuffer.flags;
447   }
448
449   UnlockDisplay(dpy);
450   SyncHandle();
451
452   return buffers;
453}
454
455
456void
457DRI2CopyRegion(Display * dpy, XID drawable, XserverRegion region,
458               uint32_t dest, uint32_t src)
459{
460   XExtDisplayInfo *info = DRI2FindDisplay(dpy);
461   xDRI2CopyRegionReq *req;
462   xDRI2CopyRegionReply rep;
463
464   XextSimpleCheckExtension(dpy, info, dri2ExtensionName);
465
466   LockDisplay(dpy);
467   GetReq(DRI2CopyRegion, req);
468   req->reqType = info->codes->major_opcode;
469   req->dri2ReqType = X_DRI2CopyRegion;
470   req->drawable = drawable;
471   req->region = region;
472   req->dest = dest;
473   req->src = src;
474
475   (void) _XReply(dpy, (xReply *) & rep, 0, xFalse);
476
477   UnlockDisplay(dpy);
478   SyncHandle();
479}
480
481#ifdef X_DRI2SwapBuffers
482static void
483load_swap_req(xDRI2SwapBuffersReq *req, uint64_t target, uint64_t divisor,
484	     uint64_t remainder)
485{
486    req->target_msc_hi = target >> 32;
487    req->target_msc_lo = target & 0xffffffff;
488    req->divisor_hi = divisor >> 32;
489    req->divisor_lo = divisor & 0xffffffff;
490    req->remainder_hi = remainder >> 32;
491    req->remainder_lo = remainder & 0xffffffff;
492}
493
494static uint64_t
495vals_to_card64(uint32_t lo, uint32_t hi)
496{
497    return (uint64_t)hi << 32 | lo;
498}
499
500uint64_t DRI2SwapBuffers(Display *dpy, XID drawable,
501			 uint64_t target_msc, uint64_t divisor, uint64_t remainder)
502{
503    XExtDisplayInfo *info = DRI2FindDisplay(dpy);
504    xDRI2SwapBuffersReq *req;
505    xDRI2SwapBuffersReply rep;
506    uint64_t count;
507
508    XextCheckExtension (dpy, info, dri2ExtensionName, 0);
509
510    LockDisplay(dpy);
511    GetReq(DRI2SwapBuffers, req);
512    req->reqType = info->codes->major_opcode;
513    req->dri2ReqType = X_DRI2SwapBuffers;
514    req->drawable = drawable;
515    load_swap_req(req, target_msc, divisor, remainder);
516
517    (void) _XReply(dpy, (xReply *)&rep, 0, xFalse);
518
519    count = vals_to_card64(rep.swap_lo, rep.swap_hi);
520
521    UnlockDisplay(dpy);
522    SyncHandle();
523
524    return count;
525}
526#endif
527
528#ifdef X_DRI2GetMSC
529Bool DRI2GetMSC(Display *dpy, XID drawable, uint64_t *ust, uint64_t *msc,
530		uint64_t *sbc)
531{
532    XExtDisplayInfo *info = DRI2FindDisplay(dpy);
533    xDRI2GetMSCReq *req;
534    xDRI2MSCReply rep;
535
536    XextCheckExtension (dpy, info, dri2ExtensionName, False);
537
538    LockDisplay(dpy);
539    GetReq(DRI2GetMSC, req);
540    req->reqType = info->codes->major_opcode;
541    req->dri2ReqType = X_DRI2GetMSC;
542    req->drawable = drawable;
543
544    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
545	UnlockDisplay(dpy);
546	SyncHandle();
547	return False;
548    }
549
550    *ust = vals_to_card64(rep.ust_lo, rep.ust_hi);
551    *msc = vals_to_card64(rep.msc_lo, rep.msc_hi);
552    *sbc = vals_to_card64(rep.sbc_lo, rep.sbc_hi);
553
554    UnlockDisplay(dpy);
555    SyncHandle();
556
557    return True;
558}
559#endif
560
561#ifdef X_DRI2WaitMSC
562static void
563load_msc_req(xDRI2WaitMSCReq *req, uint64_t target, uint64_t divisor,
564	     uint64_t remainder)
565{
566    req->target_msc_hi = target >> 32;
567    req->target_msc_lo = target & 0xffffffff;
568    req->divisor_hi = divisor >> 32;
569    req->divisor_lo = divisor & 0xffffffff;
570    req->remainder_hi = remainder >> 32;
571    req->remainder_lo = remainder & 0xffffffff;
572}
573
574Bool DRI2WaitMSC(Display *dpy, XID drawable, uint64_t target_msc, uint64_t divisor,
575		 uint64_t remainder, uint64_t *ust, uint64_t *msc, uint64_t *sbc)
576{
577    XExtDisplayInfo *info = DRI2FindDisplay(dpy);
578    xDRI2WaitMSCReq *req;
579    xDRI2MSCReply rep;
580
581    XextCheckExtension (dpy, info, dri2ExtensionName, False);
582
583    LockDisplay(dpy);
584    GetReq(DRI2WaitMSC, req);
585    req->reqType = info->codes->major_opcode;
586    req->dri2ReqType = X_DRI2WaitMSC;
587    req->drawable = drawable;
588    load_msc_req(req, target_msc, divisor, remainder);
589
590    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
591	UnlockDisplay(dpy);
592	SyncHandle();
593	return False;
594    }
595
596    *ust = ((uint64_t)rep.ust_hi << 32) | (uint64_t)rep.ust_lo;
597    *msc = ((uint64_t)rep.msc_hi << 32) | (uint64_t)rep.msc_lo;
598    *sbc = ((uint64_t)rep.sbc_hi << 32) | (uint64_t)rep.sbc_lo;
599
600    UnlockDisplay(dpy);
601    SyncHandle();
602
603    return True;
604}
605#endif
606
607#ifdef X_DRI2WaitSBC
608static void
609load_sbc_req(xDRI2WaitSBCReq *req, uint64_t target)
610{
611    req->target_sbc_hi = target >> 32;
612    req->target_sbc_lo = target & 0xffffffff;
613}
614
615Bool DRI2WaitSBC(Display *dpy, XID drawable, uint64_t target_sbc, uint64_t *ust,
616		 uint64_t *msc, uint64_t *sbc)
617{
618    XExtDisplayInfo *info = DRI2FindDisplay(dpy);
619    xDRI2WaitSBCReq *req;
620    xDRI2MSCReply rep;
621
622    XextCheckExtension (dpy, info, dri2ExtensionName, False);
623
624    LockDisplay(dpy);
625    GetReq(DRI2WaitSBC, req);
626    req->reqType = info->codes->major_opcode;
627    req->dri2ReqType = X_DRI2WaitSBC;
628    req->drawable = drawable;
629    load_sbc_req(req, target_sbc);
630
631    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
632	UnlockDisplay(dpy);
633	SyncHandle();
634	return False;
635    }
636
637    *ust = ((uint64_t)rep.ust_hi << 32) | rep.ust_lo;
638    *msc = ((uint64_t)rep.msc_hi << 32) | rep.msc_lo;
639    *sbc = ((uint64_t)rep.sbc_hi << 32) | rep.sbc_lo;
640
641    UnlockDisplay(dpy);
642    SyncHandle();
643
644    return True;
645}
646#endif
647
648#ifdef X_DRI2SwapInterval
649void DRI2SwapInterval(Display *dpy, XID drawable, int interval)
650{
651    XExtDisplayInfo *info = DRI2FindDisplay(dpy);
652    xDRI2SwapIntervalReq *req;
653
654    XextSimpleCheckExtension (dpy, info, dri2ExtensionName);
655
656    LockDisplay(dpy);
657    GetReq(DRI2SwapInterval, req);
658    req->reqType = info->codes->major_opcode;
659    req->dri2ReqType = X_DRI2SwapInterval;
660    req->drawable = drawable;
661    req->interval = interval;
662    UnlockDisplay(dpy);
663    SyncHandle();
664}
665#endif
666