1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include "evas_common_private.h"
6 #include "evas_xlib_dri_image.h"
7 #include "../software_generic/evas_native_common.h"
8 
9 # include <dlfcn.h> /* dlopen,dlclose,etc */
10 # include <sys/types.h>
11 # include <sys/stat.h>
12 # include <fcntl.h>
13 
14 static Eina_Bool tried = EINA_FALSE;
15 ////////////////////////////////////
16 //libdrm.so.2
17 static void *drm_lib = NULL;
18 
19 typedef unsigned int drm_magic_t;
20 static int (*sym_drmGetMagic)(int fd, drm_magic_t *magic) = NULL;
21 
22 ////////////////////////////////////
23 // libtbm.so.1
24 #define TBM_DEVICE_CPU   1
25 #define TBM_OPTION_READ  (1 << 0)
26 #define TBM_OPTION_WRITE (1 << 1)
27 static void *lib_tbm = NULL;
28 
29 static tbm_bo (*sym_tbm_bo_import)(tbm_bufmgr bufmgr, unsigned int key) = NULL;
30 static tbm_bo_handle (*sym_tbm_bo_map)(tbm_bo bo, int device, int opt) = NULL;
31 static int (*sym_tbm_bo_unmap)(tbm_bo bo) = NULL;
32 static void (*sym_tbm_bo_unref)(tbm_bo bo) = NULL;
33 static tbm_bufmgr (*sym_tbm_bufmgr_init)(int fd) = NULL;
34 static void (*sym_tbm_bufmgr_deinit)(tbm_bufmgr bufmgr) = NULL;
35 
36 // legacy compatibility
37 static void *(*sym_drm_slp_bo_map)(tbm_bo bo, int device, int opt) = NULL;
38 static int (*sym_drm_slp_bo_unmap)(tbm_bo bo, int device) = NULL;
39 static tbm_bufmgr (*sym_drm_slp_bufmgr_init)(int fd, void *arg) = NULL;
40 
41 ////////////////////////////////////
42 // libdri2.so.0
43 #define DRI2BufferFrontLeft 0
44 static void *dri_lib = NULL;
45 
46 typedef unsigned long long CD64;
47 
48 static DRI2Buffer *(*sym_DRI2GetBuffers)(Display * display, XID drawable, int *width, int *height, unsigned int *attachments, int count, int *outCount) = NULL;
49 static Bool (*sym_DRI2QueryExtension)(Display *display, int *eventBase, int *errorBase) = NULL;
50 static Bool (*sym_DRI2QueryVersion)(Display *display, int *major, int *minor) = NULL;
51 static Bool (*sym_DRI2Connect)(Display *display, XID window, char **driverName, char **deviceName) = NULL;
52 static Bool (*sym_DRI2Authenticate)(Display *display, XID window, unsigned int magic) = NULL;
53 static void (*sym_DRI2CreateDrawable)(Display *display, XID drawable) = NULL;
54 static void (*sym_DRI2DestroyDrawable)(Display *display, XID handle) = NULL;
55 
56 ////////////////////////////////////
57 // libXfixes.so.3
58 static void *xfixes_lib = NULL;
59 
60 static Bool (*sym_XFixesQueryExtension)(Display *display, int *event_base_return, int *error_base_return) = NULL;
61 static Status (*sym_XFixesQueryVersion)(Display *display, int *major_version_return, int *minor_version_return) = NULL;
62 static XID (*sym_XFixesCreateRegion)(Display *display, XRectangle *rectangles, int nrectangles) = NULL;
63 static void (*sym_XFixesDestroyRegion)(Display *dpy, XID region) = NULL;
64 
65 static int inits = 0;
66 static int xfixes_ev_base = 0, xfixes_err_base = 0;
67 static int xfixes_major = 0, xfixes_minor = 0;
68 static int dri2_ev_base = 0, dri2_err_base = 0;
69 static int dri2_major = 0, dri2_minor = 0;
70 static int drm_fd = -1;
71 static tbm_bufmgr bufmgr = NULL;
72 static int exim_debug = -1;
73 static Eina_Bool use_cache = EINA_TRUE;
74 static Eina_Bool slp_mode = EINA_FALSE;
75 
76 static Eina_Bool
_drm_init(Display * disp,int scr)77 _drm_init(Display *disp, int scr)
78 {
79    char *drv_name = NULL, *dev_name = NULL;
80    drm_magic_t magic = 0;
81 
82    if (xfixes_lib) return EINA_TRUE;
83    if ((tried) && (!xfixes_lib)) return EINA_FALSE;
84    if (tried) return EINA_FALSE;
85    tried = EINA_TRUE;
86    drm_lib = dlopen("libdrm.so.2", RTLD_NOW | RTLD_LOCAL);
87    if (!drm_lib)
88      {
89         ERR("Can't load libdrm.so.2");
90         goto err;
91      }
92    slp_mode = EINA_FALSE;
93    lib_tbm = dlopen("libtbm.so.1", RTLD_NOW | RTLD_LOCAL);
94    if (!lib_tbm)
95      {
96         ERR("Can't load libtbm.so.1");
97         lib_tbm = dlopen("libdrm_slp.so.1", RTLD_NOW | RTLD_LOCAL);
98         if (lib_tbm) slp_mode = EINA_TRUE;
99         else goto err;
100      }
101    dri_lib = dlopen("libdri2.so.0", RTLD_NOW | RTLD_LOCAL);
102    if (!dri_lib)
103      {
104         ERR("Can't load libdri2.so.0");
105         goto err;
106      }
107 
108    xfixes_lib = dlopen("libXfixes.so.3", RTLD_NOW | RTLD_LOCAL);
109    if (!xfixes_lib)
110      {
111         ERR("Can't load libXfixes.so.3");
112         goto err;
113      }
114 
115 #define SYM(l, x)                         \
116   do { sym_ ## x = dlsym(l, #x);          \
117        if (!sym_ ## x) {                  \
118             ERR("Can't load symbol " #x); \
119             goto err;                     \
120          }                                \
121     } while (0)
122 
123    SYM(drm_lib, drmGetMagic);
124 
125    if (!slp_mode)
126      {
127         SYM(lib_tbm, tbm_bo_import);
128         SYM(lib_tbm, tbm_bo_map);
129         SYM(lib_tbm, tbm_bo_unmap);
130         SYM(lib_tbm, tbm_bo_unref);
131         SYM(lib_tbm, tbm_bufmgr_init);
132         SYM(lib_tbm, tbm_bufmgr_deinit);
133      }
134    else
135      {
136         // Looking up the legacy DRM SLP symbols. I don't believe this will
137         // ever happen, this code is here "just in case".
138         sym_tbm_bo_import = dlsym(lib_tbm, "drm_slp_bo_import");
139         sym_drm_slp_bo_map = dlsym(lib_tbm, "drm_slp_bo_map");
140         sym_drm_slp_bo_unmap = dlsym(lib_tbm, "drm_slp_bo_unmap");
141         sym_tbm_bo_unref = dlsym(lib_tbm, "drm_slp_bo_unref");
142         sym_drm_slp_bufmgr_init = dlsym(lib_tbm, "drm_slp_bufmgr_init");
143         sym_tbm_bufmgr_deinit = dlsym(lib_tbm, "drm_slp_bufmgr_destroy");
144         if (!sym_tbm_bo_import || !sym_drm_slp_bo_map || !sym_drm_slp_bo_unmap ||
145             !sym_tbm_bo_unref || !sym_drm_slp_bufmgr_init || !sym_tbm_bufmgr_deinit)
146           {
147              ERR("Can't load symbols from libdrm_slp.so.1");
148              goto err;
149           }
150      }
151 
152    SYM(dri_lib, DRI2GetBuffers);
153    SYM(dri_lib, DRI2QueryExtension);
154    SYM(dri_lib, DRI2QueryVersion);
155    SYM(dri_lib, DRI2Connect);
156    SYM(dri_lib, DRI2Authenticate);
157    SYM(dri_lib, DRI2CreateDrawable);
158    SYM(dri_lib, DRI2DestroyDrawable);
159 
160    SYM(xfixes_lib, XFixesQueryExtension);
161    SYM(xfixes_lib, XFixesQueryVersion);
162    SYM(xfixes_lib, XFixesCreateRegion);
163    SYM(xfixes_lib, XFixesDestroyRegion);
164    if (!sym_XFixesQueryExtension(disp, &xfixes_ev_base, &xfixes_err_base))
165      {
166         if (exim_debug) ERR("XFixes extension not in xserver");
167         goto err;
168      }
169    sym_XFixesQueryVersion(disp, &xfixes_major, &xfixes_minor);
170 
171    if (!sym_DRI2QueryExtension(disp, &dri2_ev_base, &dri2_err_base))
172      {
173         if (exim_debug) ERR("DRI2 extension not in xserver");
174         goto err;
175      }
176    if (!sym_DRI2QueryVersion(disp, &dri2_major, &dri2_minor))
177      {
178         if (exim_debug) ERR("DRI2 query version failed");
179         goto err;
180      }
181    if (dri2_minor < 99)
182      {
183         if (exim_debug)
184           ERR("Not supported by DRI2 version(%i.%i)",
185               dri2_major, dri2_minor);
186         goto err;
187      }
188    if (!sym_DRI2Connect(disp, RootWindow(disp, scr), &drv_name, &dev_name))
189      {
190         if (exim_debug) ERR("DRI2 connect failed on screen %i", scr);
191         goto err;
192      }
193    if (!dev_name)
194      {
195         if (exim_debug) ERR("DRI2 connect - cannot find dev name");
196         goto err;
197      }
198    drm_fd = open(dev_name, O_RDWR);
199    if (drm_fd < 0)
200      {
201         if (exim_debug) ERR("DRM FD open of '%s' failed", dev_name);
202         goto err;
203      }
204    if (sym_drmGetMagic(drm_fd, &magic))
205      {
206         if (exim_debug) ERR("DRM get magic failed");
207         goto err;
208      }
209    if (!sym_DRI2Authenticate(disp, RootWindow(disp, scr),
210                              (unsigned int)magic))
211      {
212         if (exim_debug) ERR("DRI2 authenticate failed with magic 0x%x on screen %i", (unsigned int)magic, scr);
213         goto err;
214      }
215    if (!slp_mode)
216      bufmgr = sym_tbm_bufmgr_init(drm_fd);
217    else
218      bufmgr = sym_drm_slp_bufmgr_init(drm_fd, NULL);
219    if (!bufmgr)
220      {
221         if (exim_debug) ERR("DRM bufmgr init failed");
222         goto err;
223      }
224    if (drv_name)
225      {
226         XFree(drv_name);
227      }
228    if (dev_name)
229      {
230         XFree(dev_name);
231      }
232    return EINA_TRUE;
233 err:
234    if (drm_fd >= 0)
235      {
236         close(drm_fd);
237         drm_fd = -1;
238      }
239    if (drm_lib)
240      {
241         dlclose(drm_lib);
242         drm_lib = NULL;
243      }
244    if (lib_tbm)
245      {
246         dlclose(lib_tbm);
247         lib_tbm = NULL;
248      }
249    if (dri_lib)
250      {
251         dlclose(dri_lib);
252         dri_lib = NULL;
253      }
254    if (xfixes_lib)
255      {
256         dlclose(xfixes_lib);
257         xfixes_lib = NULL;
258      }
259    if (drv_name)
260      {
261         XFree(drv_name);
262      }
263    if (dev_name)
264      {
265         XFree(dev_name);
266      }
267    return EINA_FALSE;
268 }
269 
270 static void
_drm_shutdown(void)271 _drm_shutdown(void)
272 {
273    if (bufmgr)
274      {
275         sym_tbm_bufmgr_deinit(bufmgr);
276         bufmgr = NULL;
277      }
278    if (drm_fd >= 0) close(drm_fd);
279    tried = EINA_FALSE;
280    drm_fd = -1;
281    dlclose(lib_tbm);
282    lib_tbm = NULL;
283    dlclose(dri_lib);
284    dri_lib = NULL;
285    dlclose(xfixes_lib);
286    xfixes_lib = NULL;
287 }
288 
289 static Eina_Bool
_drm_setup(Display * disp,Evas_DRI_Image * exim)290 _drm_setup(Display *disp, Evas_DRI_Image *exim)
291 {
292    sym_DRI2CreateDrawable(disp, exim->draw);
293    return EINA_TRUE;
294 }
295 
296 static void
_drm_cleanup(Evas_DRI_Image * exim)297 _drm_cleanup(Evas_DRI_Image *exim)
298 {
299    sym_DRI2DestroyDrawable(exim->dis, exim->draw);
300 }
301 
302 Eina_Bool
evas_xlib_image_dri_init(Evas_DRI_Image * exim,Display * display)303 evas_xlib_image_dri_init(Evas_DRI_Image *exim,
304                          Display *display)
305 {
306    exim->dis = display;
307    if (inits <= 0)
308      {
309         if (!_drm_init(display, 0)) return EINA_FALSE;
310      }
311    inits++;
312 
313    if (!_drm_setup(display, exim))
314      {
315         inits--;
316         if (inits == 0) _drm_shutdown();
317         free(exim);
318         return EINA_FALSE;
319      }
320 
321    if (getenv("EVAS_NO_DRI2_CACHE"))
322      {
323         use_cache = EINA_FALSE;
324      }
325    return EINA_TRUE;
326 }
327 
328 Eina_Bool
evas_xlib_image_dri_used()329 evas_xlib_image_dri_used()
330 {
331    if (inits > 0) return EINA_TRUE;
332    return EINA_FALSE;
333 }
334 
335 void
evas_xlib_image_buffer_unmap(Evas_DRI_Image * exim)336 evas_xlib_image_buffer_unmap(Evas_DRI_Image *exim)
337 {
338    if (!slp_mode)
339      sym_tbm_bo_unmap(exim->buf_bo);
340    else
341      sym_drm_slp_bo_unmap(exim->buf_bo, TBM_DEVICE_CPU);
342    if (exim_debug) DBG("Unmap buffer name %i\n", exim->buf->name);
343    free(exim->buf);
344    exim->buf = NULL;
345    exim->buf_data = NULL;
346 }
347 
348 Eina_Bool
_evas_xlib_image_cache_import(Evas_DRI_Image * exim)349 _evas_xlib_image_cache_import(Evas_DRI_Image *exim)
350 {
351    DRI2BufferFlags *flags;
352    exim->buf_bo = NULL;
353    flags = (DRI2BufferFlags *)(&(exim->buf->flags));
354    if (!flags->data.is_reused)
355      {
356         if (exim_debug) DBG("Buffer cache not reused - clear cache\n");
357         if (exim->buf_cache)
358           {
359              sym_tbm_bo_unref(exim->buf_cache->buf_bo);
360              free(exim->buf_cache);
361           }
362      }
363    else
364      {
365         if (exim->buf_cache && exim->buf_cache->name == exim->buf->name)
366           {
367              if (exim_debug) DBG("Cached buf name %i found\n", exim->buf_cache->name);
368              exim->buf_bo = exim->buf_cache->buf_bo;
369           }
370         else
371           {
372              if (exim->buf_cache)
373                {
374                   sym_tbm_bo_unref(exim->buf_cache->buf_bo);
375                   free(exim->buf_cache);
376                }
377           }
378      }
379 
380    if (!exim->buf_bo)
381      {
382         exim->buf_bo = sym_tbm_bo_import(bufmgr, exim->buf->name);
383         if (!exim->buf_bo) return EINA_FALSE;
384         // cache the buf entry
385         exim->buf_cache = calloc(1, sizeof(Buffer));
386         if (!exim->buf_cache) return EINA_FALSE;
387         exim->buf_cache->name = exim->buf->name;
388         exim->buf_cache->buf_bo = exim->buf_bo;
389         if (exim_debug) DBG("Buffer cache added name %i\n", exim->buf_cache->name);
390      }
391    return EINA_TRUE;
392 }
393 
394 Eina_Bool
_evas_xlib_image_no_cache_import(Evas_DRI_Image * exim)395 _evas_xlib_image_no_cache_import(Evas_DRI_Image *exim)
396 {
397    if (exim->buf_bo) sym_tbm_bo_unref(exim->buf_bo);
398    exim->buf_bo = sym_tbm_bo_import(bufmgr, exim->buf->name);
399    if (!exim->buf_bo) return EINA_FALSE;
400    return EINA_TRUE;
401 }
402 
403 Eina_Bool
_evas_xlib_image_x_free(Display * d)404 _evas_xlib_image_x_free(Display *d)
405 {
406    XUngrabServer(d);
407    XSync(d, 0);
408    return EINA_FALSE;
409 }
410 
411 Eina_Bool
evas_xlib_image_get_buffers(RGBA_Image * im)412 evas_xlib_image_get_buffers(RGBA_Image *im)
413 {
414    Native *n = NULL;
415    Display *d;
416    Evas_DRI_Image *exim;
417 
418    if (im->native.data)
419      n = im->native.data;
420    if (!n) return EINA_FALSE;
421 
422    exim = n->ns_data.x11.exim;
423    d = n->ns_data.x11.display;
424 
425    if (!exim) return EINA_FALSE;
426 
427    unsigned int attach = DRI2BufferFrontLeft;
428    int num;
429    tbm_bo_handle bo_handle;
430 
431    XGrabServer(d);
432    exim->buf = sym_DRI2GetBuffers(d, exim->draw,
433                                   &(exim->buf_w), &(exim->buf_h),
434                                   &attach, 1, &num);
435 
436    if (!exim->buf) return _evas_xlib_image_x_free(d);
437    if (!exim->buf->name) return _evas_xlib_image_x_free(d);
438 
439    if (use_cache)
440      {
441         if (!_evas_xlib_image_cache_import(exim)) return _evas_xlib_image_x_free(d);
442      }
443    else
444      {
445         if (!_evas_xlib_image_no_cache_import(exim)) return _evas_xlib_image_x_free(d);
446      }
447 
448    if (!slp_mode)
449      {
450         bo_handle = sym_tbm_bo_map(exim->buf_bo, TBM_DEVICE_CPU, TBM_OPTION_READ | TBM_OPTION_WRITE);
451         if (bo_handle.ptr == NULL) return _evas_xlib_image_x_free(d);
452         exim->buf_data = bo_handle.ptr;
453      }
454    else
455      {
456         exim->buf_data = sym_drm_slp_bo_map(exim->buf_bo, TBM_DEVICE_CPU, TBM_OPTION_READ | TBM_OPTION_WRITE);
457      }
458    if (!exim->buf_data)
459      {
460         ERR("Buffer map name %i failed", exim->buf->name);
461         return _evas_xlib_image_x_free(d);
462      }
463 
464    XUngrabServer(d);
465    XSync(d, 0);
466 
467    im->image.data = exim->buf_data;
468    im->cache_entry.w = exim->buf->pitch / 4;
469 
470    evas_xlib_image_buffer_unmap(exim);
471 
472    return EINA_TRUE;
473 }
474 
475 void
evas_xlib_image_dri_free(Evas_DRI_Image * exim)476 evas_xlib_image_dri_free(Evas_DRI_Image *exim)
477 {
478    if (use_cache)
479      {
480         if (exim->buf_cache)
481           {
482              if (exim_debug) DBG("Cached buf name %i freed\n", exim->buf_cache->name);
483              sym_tbm_bo_unref(exim->buf_cache->buf_bo);
484              free(exim->buf_cache);
485           }
486      }
487    else
488      {
489         if (exim->buf_bo) sym_tbm_bo_unref(exim->buf_bo);
490      }
491 
492    _drm_cleanup(exim);
493    free(exim);
494    inits--;
495    if (inits == 0) _drm_shutdown();
496 }
497 
498 Evas_DRI_Image *
evas_xlib_image_dri_new(int w,int h,Visual * vis,int depth)499 evas_xlib_image_dri_new(int w, int h, Visual *vis, int depth)
500 {
501    Evas_DRI_Image *exim;
502 
503    exim = calloc(1, sizeof(Evas_DRI_Image));
504    if (!exim)
505      return NULL;
506 
507    exim->w = w;
508    exim->h = h;
509    exim->visual = vis;
510    exim->depth = depth;
511    return exim;
512 }
513 
514 static void
_native_bind_cb(void * image,int x EINA_UNUSED,int y EINA_UNUSED,int w EINA_UNUSED,int h EINA_UNUSED)515 _native_bind_cb(void *image, int x EINA_UNUSED, int y EINA_UNUSED, int w EINA_UNUSED, int h EINA_UNUSED)
516 {
517    RGBA_Image *im = image;
518    Native *n = im->native.data;
519 
520    if ((n) && (n->ns.type == EVAS_NATIVE_SURFACE_X11))
521      {
522         if (evas_xlib_image_get_buffers(im))
523           {
524              evas_common_image_colorspace_dirty(im);
525           }
526      }
527 }
528 
529 static void
_native_free_cb(void * image)530 _native_free_cb(void *image)
531 {
532    RGBA_Image *im = image;
533    Native *n = im->native.data;
534    if (!n) return;
535    if (n->ns_data.x11.exim)
536      {
537         evas_xlib_image_dri_free(n->ns_data.x11.exim);
538         n->ns_data.x11.exim = NULL;
539      }
540    n->ns_data.x11.visual = NULL;
541    n->ns_data.x11.display = NULL;
542 
543    im->native.data = NULL;
544    im->native.func.bind = NULL;
545    im->native.func.free = NULL;
546    im->image.data = NULL;
547    free(n);
548 }
549 
550 void *
evas_xlib_image_dri_native_set(void * data,void * image,void * native)551 evas_xlib_image_dri_native_set(void *data, void *image, void *native)
552 {
553    Display *d = NULL;
554    Visual *vis = NULL;
555    Pixmap pm = 0;
556    Native *n = NULL;
557    RGBA_Image *im = image;
558    int w, h;
559    Evas_DRI_Image *exim;
560    Evas_Native_Surface *ns = native;
561    Outbuf *ob = (Outbuf *)data;
562 
563    Window wdum;
564    int idum;
565    unsigned int uidum, depth = 0;
566 
567    if (!ns || ns->type != EVAS_NATIVE_SURFACE_X11)
568      return NULL;
569 
570    d = ob->priv.x11.xlib.disp;
571    vis = ns->data.x11.visual;
572    pm = ns->data.x11.pixmap;
573    if (!pm) return NULL;
574 
575    XGetGeometry(d, pm, &wdum, &idum, &idum, &uidum, &uidum, &uidum, &depth);
576 
577    w = im->cache_entry.w;
578    h = im->cache_entry.h;
579 
580    exim = evas_xlib_image_dri_new(w, h, vis, depth);
581 
582    if (!exim)
583      {
584         ERR("evas_xlib_image_dri_new failed.");
585         return NULL;
586      }
587 
588    exim->draw = (Drawable)ns->data.x11.pixmap;
589 
590    n = calloc(1, sizeof(Native));
591    if (!n)
592      {
593         evas_xlib_image_dri_free(exim);
594         return NULL;
595      }
596 
597    memcpy(&(n->ns), ns, sizeof(Evas_Native_Surface));
598    n->ns_data.x11.pixmap = pm;
599    n->ns_data.x11.visual = vis;
600    n->ns_data.x11.display = d;
601    n->ns_data.x11.exim = exim;
602    im->native.data = n;
603    im->native.func.bind = _native_bind_cb;
604    im->native.func.free = _native_free_cb;
605 
606    if (evas_xlib_image_dri_init(exim, d)) evas_xlib_image_get_buffers(im);
607    else return NULL;
608    return im;
609 }
610 
611