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