x11_screen.c revision 87290a383be2eeffef4407eeb17c1070d1122b2f
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#ifdef GLX_DIRECT_RENDERING
43   /* dummy base class */
44   struct __GLXDRIdisplayRec base;
45#endif
46
47   Display *dpy;
48   int number;
49
50   /*
51    * This is used to fetch GLX visuals/fbconfigs.  It steals code from GLX.
52    * It might be better to rewrite the part in Xlib or XCB.
53    */
54   __GLXdisplayPrivate *glx_dpy;
55
56   int dri_major, dri_minor;
57   char *dri_driver;
58   char *dri_device;
59   int dri_fd;
60
61   x11_drawable_invalidate_buffers dri_invalidate_buffers;
62   void *dri_user_data;
63
64   XVisualInfo *visuals;
65   int num_visuals;
66
67   /* cached values for x11_drawable_get_depth */
68   Drawable last_drawable;
69   unsigned int last_depth;
70};
71
72
73/**
74 * Create a X11 screen.
75 */
76struct x11_screen *
77x11_screen_create(Display *dpy, int screen)
78{
79   struct x11_screen *xscr;
80
81   if (screen >= ScreenCount(dpy))
82      return NULL;
83
84   xscr = CALLOC_STRUCT(x11_screen);
85   if (xscr) {
86      xscr->dpy = dpy;
87      xscr->number = screen;
88
89      xscr->dri_major = -1;
90      xscr->dri_fd = -1;
91   }
92   return xscr;
93}
94
95/**
96 * Destroy a X11 screen.
97 */
98void
99x11_screen_destroy(struct x11_screen *xscr)
100{
101   if (xscr->dri_fd >= 0)
102      close(xscr->dri_fd);
103   if (xscr->dri_driver)
104      Xfree(xscr->dri_driver);
105   if (xscr->dri_device)
106      Xfree(xscr->dri_device);
107
108#ifdef GLX_DIRECT_RENDERING
109   /* xscr->glx_dpy will be destroyed with the X display */
110   if (xscr->glx_dpy)
111      xscr->glx_dpy->dri2Display = NULL;
112#endif
113
114   if (xscr->visuals)
115      XFree(xscr->visuals);
116   FREE(xscr);
117}
118
119#ifdef GLX_DIRECT_RENDERING
120
121static boolean
122x11_screen_init_dri2(struct x11_screen *xscr)
123{
124   if (xscr->dri_major < 0) {
125      int eventBase, errorBase;
126
127      if (!DRI2QueryExtension(xscr->dpy, &eventBase, &errorBase) ||
128          !DRI2QueryVersion(xscr->dpy, &xscr->dri_major, &xscr->dri_minor))
129         xscr->dri_major = -1;
130   }
131   return (xscr->dri_major >= 0);
132}
133
134static boolean
135x11_screen_init_glx(struct x11_screen *xscr)
136{
137   if (!xscr->glx_dpy)
138      xscr->glx_dpy = __glXInitialize(xscr->dpy);
139   return (xscr->glx_dpy != NULL);
140}
141
142#endif /* GLX_DIRECT_RENDERING */
143
144/**
145 * Return true if the screen supports the extension.
146 */
147boolean
148x11_screen_support(struct x11_screen *xscr, enum x11_screen_extension ext)
149{
150   boolean supported = FALSE;
151
152   switch (ext) {
153   case X11_SCREEN_EXTENSION_XSHM:
154      supported = XShmQueryExtension(xscr->dpy);
155      break;
156#ifdef GLX_DIRECT_RENDERING
157   case X11_SCREEN_EXTENSION_GLX:
158      supported = x11_screen_init_glx(xscr);
159      break;
160   case X11_SCREEN_EXTENSION_DRI2:
161      supported = x11_screen_init_dri2(xscr);
162      break;
163#endif
164   default:
165      break;
166   }
167
168   return supported;
169}
170
171/**
172 * Return the X visuals.
173 */
174const XVisualInfo *
175x11_screen_get_visuals(struct x11_screen *xscr, int *num_visuals)
176{
177   if (!xscr->visuals) {
178      XVisualInfo vinfo_template;
179      vinfo_template.screen = xscr->number;
180      xscr->visuals = XGetVisualInfo(xscr->dpy, VisualScreenMask,
181            &vinfo_template, &xscr->num_visuals);
182   }
183
184   if (num_visuals)
185      *num_visuals = xscr->num_visuals;
186   return xscr->visuals;
187}
188
189/**
190 * Return the depth of a drawable.
191 *
192 * Unlike other drawable functions, the drawable needs not be a DRI2 drawable.
193 */
194uint
195x11_drawable_get_depth(struct x11_screen *xscr, Drawable drawable)
196{
197   unsigned int depth;
198
199   if (drawable != xscr->last_drawable) {
200      Window root;
201      int x, y;
202      unsigned int w, h, border;
203      Status ok;
204
205      ok = XGetGeometry(xscr->dpy, drawable, &root,
206            &x, &y, &w, &h, &border, &depth);
207      if (!ok)
208         depth = 0;
209
210      xscr->last_drawable = drawable;
211      xscr->last_depth = depth;
212   }
213   else {
214      depth = xscr->last_depth;
215   }
216
217   return depth;
218}
219
220#ifdef GLX_DIRECT_RENDERING
221
222/**
223 * Return the GLX fbconfigs.
224 */
225const __GLcontextModes *
226x11_screen_get_glx_configs(struct x11_screen *xscr)
227{
228   return (x11_screen_init_glx(xscr))
229      ? xscr->glx_dpy->screenConfigs[xscr->number]->configs
230      : NULL;
231}
232
233/**
234 * Return the GLX visuals.
235 */
236const __GLcontextModes *
237x11_screen_get_glx_visuals(struct x11_screen *xscr)
238{
239   return (x11_screen_init_glx(xscr))
240      ? xscr->glx_dpy->screenConfigs[xscr->number]->visuals
241      : NULL;
242}
243
244/**
245 * Probe the screen for the DRI2 driver name.
246 */
247const char *
248x11_screen_probe_dri2(struct x11_screen *xscr, int *major, int *minor)
249{
250   if (!x11_screen_init_dri2(xscr))
251      return NULL;
252
253   /* get the driver name and the device name */
254   if (!xscr->dri_driver) {
255      if (!DRI2Connect(xscr->dpy, RootWindow(xscr->dpy, xscr->number),
256               &xscr->dri_driver, &xscr->dri_device))
257         xscr->dri_driver = xscr->dri_device = NULL;
258   }
259   if (major)
260      *major = xscr->dri_major;
261   if (minor)
262      *minor = xscr->dri_minor;
263
264   return xscr->dri_driver;
265}
266
267/**
268 * Enable DRI2 and returns the file descriptor of the DRM device.  The file
269 * descriptor will be closed automatically when the screen is destoryed.
270 */
271int
272x11_screen_enable_dri2(struct x11_screen *xscr,
273                       x11_drawable_invalidate_buffers invalidate_buffers,
274                       void *user_data)
275{
276   if (xscr->dri_fd < 0) {
277      int fd;
278      drm_magic_t magic;
279
280      /* get the driver name and the device name first */
281      if (!x11_screen_probe_dri2(xscr, NULL, NULL))
282         return -1;
283
284      fd = open(xscr->dri_device, O_RDWR);
285      if (fd < 0) {
286         _eglLog(_EGL_WARNING, "failed to open %s", xscr->dri_device);
287         return -1;
288      }
289
290      memset(&magic, 0, sizeof(magic));
291      if (drmGetMagic(fd, &magic)) {
292         _eglLog(_EGL_WARNING, "failed to get magic");
293         close(fd);
294         return -1;
295      }
296
297      if (!DRI2Authenticate(xscr->dpy,
298               RootWindow(xscr->dpy, xscr->number), magic)) {
299         _eglLog(_EGL_WARNING, "failed to authenticate magic");
300         close(fd);
301         return -1;
302      }
303
304      if (!x11_screen_init_glx(xscr)) {
305         _eglLog(_EGL_WARNING, "failed to initialize GLX");
306         close(fd);
307         return -1;
308      }
309      if (xscr->glx_dpy->dri2Display) {
310         _eglLog(_EGL_WARNING,
311               "display is already managed by another x11 screen");
312         close(fd);
313         return -1;
314      }
315
316      xscr->glx_dpy->dri2Display = (__GLXDRIdisplay *) xscr;
317      xscr->dri_invalidate_buffers = invalidate_buffers;
318      xscr->dri_user_data = user_data;
319
320      xscr->dri_fd = fd;
321   }
322
323   return xscr->dri_fd;
324}
325
326/**
327 * Create/Destroy the DRI drawable.
328 */
329void
330x11_drawable_enable_dri2(struct x11_screen *xscr,
331                         Drawable drawable, boolean on)
332{
333   if (on)
334      DRI2CreateDrawable(xscr->dpy, drawable);
335   else
336      DRI2DestroyDrawable(xscr->dpy, drawable);
337}
338
339/**
340 * Copy between buffers of the DRI2 drawable.
341 */
342void
343x11_drawable_copy_buffers(struct x11_screen *xscr, Drawable drawable,
344                          int x, int y, int width, int height,
345                          int src_buf, int dst_buf)
346{
347   XRectangle rect;
348   XserverRegion region;
349
350   rect.x = x;
351   rect.y = y;
352   rect.width = width;
353   rect.height = height;
354
355   region = XFixesCreateRegion(xscr->dpy, &rect, 1);
356   DRI2CopyRegion(xscr->dpy, drawable, region, dst_buf, src_buf);
357   XFixesDestroyRegion(xscr->dpy, region);
358}
359
360/**
361 * Get the buffers of the DRI2 drawable.  The returned array should be freed.
362 */
363struct x11_drawable_buffer *
364x11_drawable_get_buffers(struct x11_screen *xscr, Drawable drawable,
365                         int *width, int *height, unsigned int *attachments,
366                         boolean with_format, int num_ins, int *num_outs)
367{
368   DRI2Buffer *dri2bufs;
369
370   if (with_format)
371      dri2bufs = DRI2GetBuffersWithFormat(xscr->dpy, drawable, width, height,
372            attachments, num_ins, num_outs);
373   else
374      dri2bufs = DRI2GetBuffers(xscr->dpy, drawable, width, height,
375            attachments, num_ins, num_outs);
376
377   return (struct x11_drawable_buffer *) dri2bufs;
378}
379
380/**
381 * Create a mode list of the given size.
382 */
383__GLcontextModes *
384x11_context_modes_create(unsigned count)
385{
386   const size_t size = sizeof(__GLcontextModes);
387   __GLcontextModes *base = NULL;
388   __GLcontextModes **next;
389   unsigned i;
390
391   next = &base;
392   for (i = 0; i < count; i++) {
393      *next = (__GLcontextModes *) CALLOC(1, size);
394      if (*next == NULL) {
395         x11_context_modes_destroy(base);
396         base = NULL;
397         break;
398      }
399      next = &((*next)->next);
400   }
401
402   return base;
403}
404
405/**
406 * Destroy a mode list.
407 */
408void
409x11_context_modes_destroy(__GLcontextModes *modes)
410{
411   while (modes != NULL) {
412      __GLcontextModes *next = modes->next;
413      FREE(modes);
414      modes = next;
415   }
416}
417
418/**
419 * Return the number of the modes in the mode list.
420 */
421unsigned
422x11_context_modes_count(const __GLcontextModes *modes)
423{
424   const __GLcontextModes *mode;
425   int count = 0;
426   for (mode = modes; mode; mode = mode->next)
427      count++;
428   return count;
429}
430
431/**
432 * This is called from src/glx/dri2.c.
433 */
434void
435dri2InvalidateBuffers(Display *dpy, XID drawable)
436{
437   __GLXdisplayPrivate *priv = __glXInitialize(dpy);
438   struct x11_screen *xscr = NULL;
439
440   if (priv && priv->dri2Display)
441      xscr = (struct x11_screen *) priv->dri2Display;
442   if (!xscr || !xscr->dri_invalidate_buffers)
443      return;
444
445   xscr->dri_invalidate_buffers(xscr, drawable, xscr->dri_user_data);
446}
447
448#endif /* GLX_DIRECT_RENDERING */
449