1/*
2 * Mesa 3-D graphics library
3 * Version:  7.8
4 *
5 * Copyright (C) 2009-2010 Chia-I Wu <olv@0xlab.org>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25
26#include <unistd.h>
27#include <fcntl.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <xf86drm.h>
31#include <X11/Xlibint.h>
32#include <X11/extensions/XShm.h>
33
34#include "util/u_memory.h"
35#include "egllog.h"
36
37#include "x11_screen.h"
38#include "dri2.h"
39#include "glxinit.h"
40
41struct x11_screen {
42   Display *dpy;
43   int number;
44
45   /*
46    * This is used to fetch GLX visuals/fbconfigs.  It steals code from GLX.
47    * It might be better to rewrite the part in Xlib or XCB.
48    */
49   __GLXdisplayPrivate *glx_dpy;
50
51   int dri_major, dri_minor;
52   char *dri_driver;
53   char *dri_device;
54   int dri_fd;
55
56   x11_drawable_invalidate_buffers dri_invalidate_buffers;
57   void *dri_user_data;
58
59   XVisualInfo *visuals;
60   int num_visuals;
61
62   /* cached values for x11_drawable_get_depth */
63   Drawable last_drawable;
64   unsigned int last_depth;
65};
66
67
68/**
69 * Create a X11 screen.
70 */
71struct x11_screen *
72x11_screen_create(Display *dpy, int screen)
73{
74   struct x11_screen *xscr;
75
76   if (screen >= ScreenCount(dpy))
77      return NULL;
78
79   xscr = CALLOC_STRUCT(x11_screen);
80   if (xscr) {
81      xscr->dpy = dpy;
82      xscr->number = screen;
83
84      xscr->dri_major = -1;
85      xscr->dri_fd = -1;
86   }
87   return xscr;
88}
89
90/**
91 * Destroy a X11 screen.
92 */
93void
94x11_screen_destroy(struct x11_screen *xscr)
95{
96   if (xscr->dri_fd >= 0)
97      close(xscr->dri_fd);
98   if (xscr->dri_driver)
99      Xfree(xscr->dri_driver);
100   if (xscr->dri_device)
101      Xfree(xscr->dri_device);
102
103#ifdef GLX_DIRECT_RENDERING
104   /* xscr->glx_dpy will be destroyed with the X display */
105   if (xscr->glx_dpy)
106      xscr->glx_dpy->xscr = NULL;
107#endif
108
109   if (xscr->visuals)
110      XFree(xscr->visuals);
111   FREE(xscr);
112}
113
114#ifdef GLX_DIRECT_RENDERING
115
116static boolean
117x11_screen_init_dri2(struct x11_screen *xscr)
118{
119   if (xscr->dri_major < 0) {
120      int eventBase, errorBase;
121
122      if (!DRI2QueryExtension(xscr->dpy, &eventBase, &errorBase) ||
123          !DRI2QueryVersion(xscr->dpy, &xscr->dri_major, &xscr->dri_minor))
124         xscr->dri_major = -1;
125   }
126   return (xscr->dri_major >= 0);
127}
128
129static boolean
130x11_screen_init_glx(struct x11_screen *xscr)
131{
132   if (!xscr->glx_dpy)
133      xscr->glx_dpy = __glXInitialize(xscr->dpy);
134   return (xscr->glx_dpy != NULL);
135}
136
137#endif /* GLX_DIRECT_RENDERING */
138
139/**
140 * Return true if the screen supports the extension.
141 */
142boolean
143x11_screen_support(struct x11_screen *xscr, enum x11_screen_extension ext)
144{
145   boolean supported = FALSE;
146
147   switch (ext) {
148   case X11_SCREEN_EXTENSION_XSHM:
149      supported = XShmQueryExtension(xscr->dpy);
150      break;
151#ifdef GLX_DIRECT_RENDERING
152   case X11_SCREEN_EXTENSION_GLX:
153      supported = x11_screen_init_glx(xscr);
154      break;
155   case X11_SCREEN_EXTENSION_DRI2:
156      supported = x11_screen_init_dri2(xscr);
157      break;
158#endif
159   default:
160      break;
161   }
162
163   return supported;
164}
165
166/**
167 * Return the X visuals.
168 */
169const XVisualInfo *
170x11_screen_get_visuals(struct x11_screen *xscr, int *num_visuals)
171{
172   if (!xscr->visuals) {
173      XVisualInfo vinfo_template;
174      vinfo_template.screen = xscr->number;
175      xscr->visuals = XGetVisualInfo(xscr->dpy, VisualScreenMask,
176            &vinfo_template, &xscr->num_visuals);
177   }
178
179   if (num_visuals)
180      *num_visuals = xscr->num_visuals;
181   return xscr->visuals;
182}
183
184/**
185 * Return the depth of a drawable.
186 *
187 * Unlike other drawable functions, the drawable needs not be a DRI2 drawable.
188 */
189uint
190x11_drawable_get_depth(struct x11_screen *xscr, Drawable drawable)
191{
192   unsigned int depth;
193
194   if (drawable != xscr->last_drawable) {
195      Window root;
196      int x, y;
197      unsigned int w, h, border;
198      Status ok;
199
200      ok = XGetGeometry(xscr->dpy, drawable, &root,
201            &x, &y, &w, &h, &border, &depth);
202      if (!ok)
203         depth = 0;
204
205      xscr->last_drawable = drawable;
206      xscr->last_depth = depth;
207   }
208   else {
209      depth = xscr->last_depth;
210   }
211
212   return depth;
213}
214
215#ifdef GLX_DIRECT_RENDERING
216
217/**
218 * Return the GLX fbconfigs.
219 */
220const __GLcontextModes *
221x11_screen_get_glx_configs(struct x11_screen *xscr)
222{
223   return (x11_screen_init_glx(xscr))
224      ? xscr->glx_dpy->screenConfigs[xscr->number]->configs
225      : NULL;
226}
227
228/**
229 * Probe the screen for the DRI2 driver name.
230 */
231const char *
232x11_screen_probe_dri2(struct x11_screen *xscr, int *major, int *minor)
233{
234   if (!x11_screen_init_dri2(xscr))
235      return NULL;
236
237   /* get the driver name and the device name */
238   if (!xscr->dri_driver) {
239      if (!DRI2Connect(xscr->dpy, RootWindow(xscr->dpy, xscr->number),
240               &xscr->dri_driver, &xscr->dri_device))
241         xscr->dri_driver = xscr->dri_device = NULL;
242   }
243   if (major)
244      *major = xscr->dri_major;
245   if (minor)
246      *minor = xscr->dri_minor;
247
248   return xscr->dri_driver;
249}
250
251/**
252 * Enable DRI2 and returns the file descriptor of the DRM device.  The file
253 * descriptor will be closed automatically when the screen is destoryed.
254 */
255int
256x11_screen_enable_dri2(struct x11_screen *xscr,
257                       x11_drawable_invalidate_buffers invalidate_buffers,
258                       void *user_data)
259{
260   if (xscr->dri_fd < 0) {
261      int fd;
262      drm_magic_t magic;
263
264      /* get the driver name and the device name first */
265      if (!x11_screen_probe_dri2(xscr, NULL, NULL))
266         return -1;
267
268#ifdef O_CLOEXEC
269      fd = open(xscr->dri_device, O_RDWR | O_CLOEXEC);
270      if (fd == -1 && errno == EINVAL)
271#endif
272      {
273         fd = open(xscr->dri_device, O_RDWR);
274         if (fd != -1)
275            fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
276      }
277      if (fd < 0) {
278         _eglLog(_EGL_WARNING, "failed to open %s", xscr->dri_device);
279         return -1;
280      }
281
282      memset(&magic, 0, sizeof(magic));
283      if (drmGetMagic(fd, &magic)) {
284         _eglLog(_EGL_WARNING, "failed to get magic");
285         close(fd);
286         return -1;
287      }
288
289      if (!DRI2Authenticate(xscr->dpy,
290               RootWindow(xscr->dpy, xscr->number), magic)) {
291         _eglLog(_EGL_WARNING, "failed to authenticate magic");
292         close(fd);
293         return -1;
294      }
295
296      if (!x11_screen_init_glx(xscr)) {
297         _eglLog(_EGL_WARNING, "failed to initialize GLX");
298         close(fd);
299         return -1;
300      }
301      if (xscr->glx_dpy->xscr) {
302         _eglLog(_EGL_WARNING,
303               "display is already managed by another x11 screen");
304         close(fd);
305         return -1;
306      }
307
308      xscr->glx_dpy->xscr = xscr;
309      xscr->dri_invalidate_buffers = invalidate_buffers;
310      xscr->dri_user_data = user_data;
311
312      xscr->dri_fd = fd;
313   }
314
315   return xscr->dri_fd;
316}
317
318char *
319x11_screen_get_device_name(struct x11_screen *xscr)
320{
321   return xscr->dri_device;
322}
323
324int
325x11_screen_authenticate(struct x11_screen *xscr, uint32_t id)
326{
327   boolean authenticated;
328
329   authenticated = DRI2Authenticate(xscr->dpy,
330         RootWindow(xscr->dpy, xscr->number), id);
331
332   return authenticated ? 0 : -1;
333}
334
335/**
336 * Create/Destroy the DRI drawable.
337 */
338void
339x11_drawable_enable_dri2(struct x11_screen *xscr,
340                         Drawable drawable, boolean on)
341{
342   if (on)
343      DRI2CreateDrawable(xscr->dpy, drawable);
344   else
345      DRI2DestroyDrawable(xscr->dpy, drawable);
346}
347
348/**
349 * Copy between buffers of the DRI2 drawable.
350 */
351void
352x11_drawable_copy_buffers_region(struct x11_screen *xscr, Drawable drawable,
353                                 int num_rects, const int *rects,
354                                 int src_buf, int dst_buf)
355{
356   XserverRegion region;
357   XRectangle *rectangles = CALLOC(num_rects, sizeof(XRectangle));
358
359   for (int i = 0; i < num_rects; i++) {
360      rectangles[i].x = rects[i * 4 + 0];
361      rectangles[i].y = rects[i * 4 + 1];
362      rectangles[i].width = rects[i * 4 + 2];
363      rectangles[i].height = rects[i * 4 + 3];
364   }
365
366   region = XFixesCreateRegion(xscr->dpy, rectangles, num_rects);
367   DRI2CopyRegion(xscr->dpy, drawable, region, dst_buf, src_buf);
368   XFixesDestroyRegion(xscr->dpy, region);
369   FREE(rectangles);
370}
371
372/**
373 * Get the buffers of the DRI2 drawable.  The returned array should be freed.
374 */
375struct x11_drawable_buffer *
376x11_drawable_get_buffers(struct x11_screen *xscr, Drawable drawable,
377                         int *width, int *height, unsigned int *attachments,
378                         boolean with_format, int num_ins, int *num_outs)
379{
380   DRI2Buffer *dri2bufs;
381
382   if (with_format)
383      dri2bufs = DRI2GetBuffersWithFormat(xscr->dpy, drawable, width, height,
384            attachments, num_ins, num_outs);
385   else
386      dri2bufs = DRI2GetBuffers(xscr->dpy, drawable, width, height,
387            attachments, num_ins, num_outs);
388
389   return (struct x11_drawable_buffer *) dri2bufs;
390}
391
392/**
393 * Create a mode list of the given size.
394 */
395__GLcontextModes *
396x11_context_modes_create(unsigned count)
397{
398   const size_t size = sizeof(__GLcontextModes);
399   __GLcontextModes *base = NULL;
400   __GLcontextModes **next;
401   unsigned i;
402
403   next = &base;
404   for (i = 0; i < count; i++) {
405      *next = (__GLcontextModes *) CALLOC(1, size);
406      if (*next == NULL) {
407         x11_context_modes_destroy(base);
408         base = NULL;
409         break;
410      }
411      next = &((*next)->next);
412   }
413
414   return base;
415}
416
417/**
418 * Destroy a mode list.
419 */
420void
421x11_context_modes_destroy(__GLcontextModes *modes)
422{
423   while (modes != NULL) {
424      __GLcontextModes *next = modes->next;
425      FREE(modes);
426      modes = next;
427   }
428}
429
430/**
431 * Return the number of the modes in the mode list.
432 */
433unsigned
434x11_context_modes_count(const __GLcontextModes *modes)
435{
436   const __GLcontextModes *mode;
437   int count = 0;
438   for (mode = modes; mode; mode = mode->next)
439      count++;
440   return count;
441}
442
443extern void
444dri2InvalidateBuffers(Display *dpy, XID drawable);
445
446/**
447 * This is called from src/glx/dri2.c.
448 */
449void
450dri2InvalidateBuffers(Display *dpy, XID drawable)
451{
452   __GLXdisplayPrivate *priv = __glXInitialize(dpy);
453   struct x11_screen *xscr = NULL;
454
455   if (priv && priv->xscr)
456      xscr = priv->xscr;
457   if (!xscr || !xscr->dri_invalidate_buffers)
458      return;
459
460   xscr->dri_invalidate_buffers(xscr, drawable, xscr->dri_user_data);
461}
462
463extern unsigned
464dri2GetSwapEventType(Display *dpy, XID drawable);
465
466extern void *
467dri2GetGlxDrawableFromXDrawableId(Display *dpy, XID id);
468
469extern void *
470GetGLXDrawable(Display *dpy, XID drawable);
471
472/**
473 * This is also called from src/glx/dri2.c.
474 */
475unsigned dri2GetSwapEventType(Display *dpy, XID drawable)
476{
477   return 0;
478}
479
480void *
481dri2GetGlxDrawableFromXDrawableId(Display *dpy, XID id)
482{
483   return NULL;
484}
485
486void *
487GetGLXDrawable(Display *dpy, XID drawable)
488{
489   return NULL;
490}
491
492#endif /* GLX_DIRECT_RENDERING */
493