4296bb2dc11c929652e443500ec1a22c4228c58c
[framework/uifw/ecore.git] / src / lib / ecore_x / xlib / ecore_x_vsync.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif /* ifdef HAVE_CONFIG_H */
4
5 #include "Ecore.h"
6 #include "ecore_x_private.h"
7 #include "Ecore_X.h"
8
9 #include <string.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <dlfcn.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17
18 #define ECORE_X_VSYNC_DRI2 1
19
20 #ifdef ECORE_X_VSYNC_DRI2
21 // relevant header bits of dri/drm inlined here to avoid needing external
22 // headers to build
23 /// drm
24 typedef unsigned int drm_magic_t;
25
26 typedef enum
27 {
28    DRM_VBLANK_ABSOLUTE = 0x00000000,
29    DRM_VBLANK_RELATIVE = 0x00000001,
30    DRM_VBLANK_EVENT = 0x04000000,
31    DRM_VBLANK_FLIP = 0x08000000,
32    DRM_VBLANK_NEXTONMISS = 0x10000000,
33    DRM_VBLANK_SECONDARY = 0x20000000,
34    DRM_VBLANK_SIGNAL = 0x40000000
35 }
36 drmVBlankSeqType;
37
38 typedef struct _drmVBlankReq
39 {
40    drmVBlankSeqType type;
41    unsigned int     sequence;
42    unsigned long    signal;
43 } drmVBlankReq;
44
45 typedef struct _drmVBlankReply
46 {
47    drmVBlankSeqType type;
48    unsigned int     sequence;
49    long             tval_sec;
50    long             tval_usec;
51 } drmVBlankReply;
52
53 typedef union _drmVBlank
54 {
55    drmVBlankReq   request;
56    drmVBlankReply reply;
57 } drmVBlank;
58
59 #define DRM_EVENT_CONTEXT_VERSION 2
60
61 typedef struct _drmEventContext
62 {
63    int version;
64    void (*vblank_handler)(int fd,
65                           unsigned int sequence,
66                           unsigned int tv_sec,
67                           unsigned int tv_usec,
68                           void *user_data);
69    void (*page_flip_handler)(int fd,
70                              unsigned int sequence,
71                              unsigned int tv_sec,
72                              unsigned int tv_usec,
73                              void *user_data);
74 } drmEventContext;
75
76 static int (*sym_drmClose)(int fd) = NULL;
77 static int (*sym_drmGetMagic)(int fd,
78                               drm_magic_t *magic) = NULL;
79 static int (*sym_drmWaitVBlank)(int fd,
80                                 drmVBlank *vbl) = NULL;
81 static int (*sym_drmHandleEvent)(int fd,
82                                  drmEventContext *evctx) = NULL;
83
84 //// dri
85
86 static Bool (*sym_DRI2QueryExtension)(Display *display,
87                                       int *eventBase,
88                                       int *errorBase) = NULL;
89 static Bool (*sym_DRI2QueryVersion)(Display *display,
90                                     int *major,
91                                     int *minor) = NULL;
92 static Bool (*sym_DRI2Connect)(Display *display,
93                                XID window,
94                                char **driverName,
95                                char **deviceName) = NULL;
96 static Bool (*sym_DRI2Authenticate)(Display *display,
97                                     XID window,
98                                     drm_magic_t magic) = NULL;
99
100 //// dri/drm data needed
101 static int dri2_event = 0;
102 static int dri2_error = 0;
103 static int dri2_major = 0;
104 static int dri2_minor = 0;
105 static char *device_name = 0;
106 static char *driver_name = 0;
107 static drm_magic_t drm_magic;
108
109 static int drm_fd = -1;
110 static int drm_event_is_busy = 0;
111 static int drm_animators_interval = 1;
112 static drmEventContext drm_evctx;
113 static Ecore_Fd_Handler *dri_drm_fdh = NULL;
114
115 static void *dri_lib = NULL;
116 static void *drm_lib = NULL;
117
118 static Window dri_drm_vsync_root = 0;
119
120 static void
121 _dri_drm_tick_schedule(void)
122 {
123    drmVBlank vbl;
124
125    vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
126    vbl.request.sequence = drm_animators_interval;
127    vbl.request.signal = 0;
128    sym_drmWaitVBlank(drm_fd, &vbl);
129 }
130
131 static void
132 _dri_drm_tick_begin(void *data __UNUSED__)
133 {
134    drm_event_is_busy = 1;
135    _dri_drm_tick_schedule();
136 }
137
138 static void
139 _dri_drm_tick_end(void *data __UNUSED__)
140 {
141    drm_event_is_busy = 0;
142 }
143
144 static void
145 _dri_drm_vblank_handler(int fd __UNUSED__,
146                         unsigned int frame __UNUSED__,
147                         unsigned int sec __UNUSED__,
148                         unsigned int usec __UNUSED__,
149                         void *data __UNUSED__)
150 {
151    ecore_animator_custom_tick();
152    if (drm_event_is_busy) _dri_drm_tick_schedule();
153 }
154
155 static Eina_Bool
156 _dri_drm_cb(void *data __UNUSED__,
157             Ecore_Fd_Handler *fd_handler __UNUSED__)
158 {
159    sym_drmHandleEvent(drm_fd, &drm_evctx);
160    return ECORE_CALLBACK_RENEW;
161 }
162
163 // yes. most evil. we dlopen libdrm and libGL etc. to manually find smbols
164 // so we can be as compatible as possible given the whole mess of the
165 // gl/dri/drm etc. world. and handle graceful failure at runtime not
166 // compile time
167 static int
168 _dri_drm_link(void)
169 {
170    const char *drm_libs[] =
171    {
172       "libdrm.so.2",
173       "libdrm.so.1",
174       "libdrm.so.0",
175       "libdrm.so",
176       NULL,
177    };
178    const char *dri_libs[] =
179    {
180       "libdri2.so.2",
181       "libdri2.so.1",
182       "libdri2.so.0",
183       "libdri2.so",
184       "libGL.so.4",
185       "libGL.so.3",
186       "libGL.so.2",
187       "libGL.so.1",
188       "libGL.so.0",
189       "libGL.so",
190       NULL,
191    };
192    int i, fail;
193 #define SYM(lib, xx)                            \
194   do {                                          \
195        sym_ ## xx = dlsym(lib, #xx);            \
196        if (!(sym_ ## xx)) {                     \
197             fprintf(stderr, "%s\n", dlerror()); \
198             fail = 1;                           \
199          }                                      \
200     } while (0)
201
202    if (dri_lib) return 1;
203    for (i = 0; drm_libs[i]; i++)
204      {
205         drm_lib = dlopen(drm_libs[i], RTLD_LOCAL | RTLD_LAZY);
206         if (drm_lib)
207           {
208              fail = 0;
209              SYM(drm_lib, drmClose);
210              SYM(drm_lib, drmWaitVBlank);
211              SYM(drm_lib, drmHandleEvent);
212              if (fail)
213                {
214                   dlclose(drm_lib);
215                   drm_lib = NULL;
216                }
217              else break;
218           }
219      }
220    if (!drm_lib) return 0;
221    for (i = 0; dri_libs[i]; i++)
222      {
223         dri_lib = dlopen(dri_libs[i], RTLD_LOCAL | RTLD_LAZY);
224         if (dri_lib)
225           {
226              fail = 0;
227              SYM(dri_lib, DRI2QueryExtension);
228              SYM(dri_lib, DRI2QueryVersion);
229              SYM(dri_lib, DRI2Connect);
230              SYM(dri_lib, DRI2Authenticate);
231              if (fail)
232                {
233                   dlclose(dri_lib);
234                   dri_lib = NULL;
235                }
236              else break;
237           }
238      }
239    if (!dri_lib)
240      {
241         dlclose(drm_lib);
242         drm_lib = NULL;
243         return 0;
244      }
245    return 1;
246 }
247
248 static int
249 _dri_drm_init(void)
250 {
251    if (!sym_DRI2QueryExtension(_ecore_x_disp, &dri2_event, &dri2_error))
252      return 0;
253    if (!sym_DRI2QueryVersion(_ecore_x_disp, &dri2_major, &dri2_minor))
254      return 0;
255    if (dri2_major < 2)
256      return 0;
257    if (!sym_DRI2Connect(_ecore_x_disp, dri_drm_vsync_root, &driver_name, &device_name))
258      return 0;
259    drm_fd = open(device_name, O_RDWR);
260    if (drm_fd < 0)
261      return 0;
262    sym_drmGetMagic(drm_fd, &drm_magic);
263    if (!sym_DRI2Authenticate(_ecore_x_disp, dri_drm_vsync_root, drm_magic))
264      {
265         close(drm_fd);
266         drm_fd = -1;
267         return 0;
268      }
269    memset(&drm_evctx, 0, sizeof(drm_evctx));
270    drm_evctx.version = DRM_EVENT_CONTEXT_VERSION;
271    drm_evctx.vblank_handler = _dri_drm_vblank_handler;
272    drm_evctx.page_flip_handler = NULL;
273
274    dri_drm_fdh = ecore_main_fd_handler_add(drm_fd, ECORE_FD_READ,
275                                            _dri_drm_cb, NULL, NULL, NULL);
276    if (!dri_drm_fdh)
277      {
278         close(drm_fd);
279         drm_fd = -1;
280         return 0;
281      }
282    return 1;
283 }
284
285 static void
286 _dri_drm_shutdown(void)
287 {
288    if (drm_fd >= 0)
289      {
290         close(drm_fd);
291         drm_fd = -1;
292      }
293    if (dri_drm_fdh)
294      {
295         ecore_main_fd_handler_del(dri_drm_fdh);
296         dri_drm_fdh = NULL;
297      }
298 }
299
300 #endif
301
302 EAPI Eina_Bool
303 ecore_x_vsync_animator_tick_source_set(Ecore_X_Window win)
304 {
305 #ifdef ECORE_X_VSYNC_DRI2
306    Ecore_X_Window root;
307
308    root = ecore_x_window_root_get(win);
309    if (root != dri_drm_vsync_root)
310      {
311         dri_drm_vsync_root = root;
312         if (dri_drm_vsync_root)
313           {
314              if (!_dri_drm_link())
315                {
316                   ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER);
317                   return EINA_FALSE;
318                }
319              _dri_drm_shutdown();
320              if (!_dri_drm_init())
321                {
322                   dri_drm_vsync_root = 0;
323                   ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER);
324                   return EINA_FALSE;
325                }
326              ecore_animator_custom_source_tick_begin_callback_set
327                (_dri_drm_tick_begin, NULL);
328              ecore_animator_custom_source_tick_end_callback_set
329                (_dri_drm_tick_end, NULL);
330              ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_CUSTOM);
331           }
332         else
333           {
334              if (drm_fd >= 0)
335                {
336                   _dri_drm_shutdown();
337                   ecore_animator_custom_source_tick_begin_callback_set
338                     (NULL, NULL);
339                   ecore_animator_custom_source_tick_end_callback_set
340                     (NULL, NULL);
341                   ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER);
342                }
343           }
344      }
345    return EINA_TRUE;
346 #else
347    return EINA_FALSE;
348    win = 0;
349 #endif
350 }
351