svn update: 60286 (latest:60286)
[profile/ivi/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, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data);
65    void (*page_flip_handler)(int fd, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, void *user_data);
66 } drmEventContext;
67
68 static int (*sym_drmClose)       (int fd) = NULL;
69 static int (*sym_drmGetMagic)    (int fd, drm_magic_t * magic) = NULL;
70 static int (*sym_drmWaitVBlank)  (int fd, drmVBlank *vbl) = NULL;
71 static int (*sym_drmHandleEvent) (int fd, drmEventContext *evctx) = NULL;
72
73 //// dri
74
75 static Bool (*sym_DRI2QueryExtension) (Display *display, int *eventBase, int *errorBase) = NULL;
76 static Bool (*sym_DRI2QueryVersion)   (Display *display, int *major, int *minor) = NULL;
77 static Bool (*sym_DRI2Connect)        (Display *display, XID window, char **driverName, char **deviceName) = NULL;
78 static Bool (*sym_DRI2Authenticate)   (Display *display, XID window, drm_magic_t magic) = NULL;
79
80
81 //// dri/drm data needed
82 static int               dri2_event = 0;
83 static int               dri2_error = 0;
84 static int               dri2_major = 0;
85 static int               dri2_minor = 0;
86 static char             *device_name = 0;
87 static char             *driver_name = 0;
88 static drm_magic_t       drm_magic;
89
90 static int               drm_fd = -1;
91 static int               drm_event_is_busy = 0;
92 static int               drm_animators_interval = 1;
93 static drmEventContext   drm_evctx;
94 static Ecore_Fd_Handler *dri_drm_fdh = NULL;
95
96 static void              *dri_lib = NULL;
97 static void              *drm_lib = NULL;
98
99 static Window             dri_drm_vsync_root = 0;
100
101 static void
102 _dri_drm_tick_schedule(void)
103 {
104    drmVBlank vbl;
105    
106    vbl.request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT;
107    vbl.request.sequence = drm_animators_interval;
108    vbl.request.signal = 0;
109    sym_drmWaitVBlank(drm_fd, &vbl);
110 }
111
112 static void
113 _dri_drm_tick_begin(void *data __UNUSED__)
114 {
115    drm_event_is_busy = 1;
116    _dri_drm_tick_schedule();
117 }
118
119 static void
120 _dri_drm_tick_end(void *data __UNUSED__)
121 {
122    drm_event_is_busy = 0;
123 }
124
125 static void
126 _dri_drm_vblank_handler(int fd __UNUSED__, unsigned int frame __UNUSED__, 
127                         unsigned int sec __UNUSED__, 
128                         unsigned int usec __UNUSED__, void *data __UNUSED__)
129 {
130    ecore_animator_custom_tick();
131    if (drm_event_is_busy) _dri_drm_tick_schedule();
132 }
133
134 static Eina_Bool
135 _dri_drm_cb(void *data __UNUSED__, Ecore_Fd_Handler *fd_handler __UNUSED__)
136 {
137    sym_drmHandleEvent(drm_fd, &drm_evctx);
138    return ECORE_CALLBACK_RENEW;
139 }
140
141 // yes. most evil. we dlopen libdrm and libGL etc. to manually find smbols
142 // so we can be as compatible as possible given the whole mess of the
143 // gl/dri/drm etc. world. and handle graceful failure at runtime not
144 // compile time
145 static int
146 _dri_drm_link(void)
147 {
148    const char *drm_libs[] =
149      {
150         "libdrm.so.2",
151         "libdrm.so.1",
152         "libdrm.so.0",
153         "libdrm.so",
154         NULL,
155      };
156    const char *dri_libs[] =
157      {
158         "libdri2.so.2",
159         "libdri2.so.1",
160         "libdri2.so.0",
161         "libdri2.so",
162         "libGL.so.4",
163         "libGL.so.3",
164         "libGL.so.2",
165         "libGL.so.1",
166         "libGL.so.0",
167         "libGL.so",
168         NULL,
169      };
170    int i, fail;
171 #define SYM(lib, xx) \
172    do { \
173       sym_ ## xx = dlsym(lib, #xx); \
174       if (!(sym_ ## xx)) { \
175          fprintf(stderr, "%s\n", dlerror()); \
176          fail = 1; \
177       } \
178    } while (0)
179
180    if (dri_lib) return 1;
181    for (i = 0; drm_libs[i]; i++)
182      {
183         drm_lib = dlopen(drm_libs[i], RTLD_LOCAL | RTLD_LAZY);
184         if (drm_lib)
185           {
186              fail = 0;
187              SYM(drm_lib, drmClose);
188              SYM(drm_lib, drmWaitVBlank);
189              SYM(drm_lib, drmHandleEvent);
190              if (fail)
191                {
192                   dlclose(drm_lib);
193                   drm_lib = NULL;
194                }
195              else break;
196           }
197      }
198    if (!drm_lib) return 0;
199    for (i = 0; dri_libs[i]; i++)
200      {
201         dri_lib = dlopen(dri_libs[i], RTLD_LOCAL | RTLD_LAZY);
202         if (dri_lib)
203           {
204              fail = 0;
205              SYM(dri_lib, DRI2QueryExtension);
206              SYM(dri_lib, DRI2QueryVersion);
207              SYM(dri_lib, DRI2Connect);
208              SYM(dri_lib, DRI2Authenticate);
209              if (fail)
210                {
211                   dlclose(dri_lib);
212                   dri_lib = NULL;
213                }
214              else break;
215           }
216      }
217    if (!dri_lib)
218      {
219         dlclose(drm_lib);
220         drm_lib = NULL;
221         return 0;
222      }
223    return 1;
224 }
225
226 static int
227 _dri_drm_init(void)
228 {
229    if (!sym_DRI2QueryExtension(_ecore_x_disp, &dri2_event, &dri2_error))
230       return 0;
231    if (!sym_DRI2QueryVersion(_ecore_x_disp, &dri2_major, &dri2_minor))
232       return 0;
233    if (dri2_major < 2)
234       return 0;
235    if (!sym_DRI2Connect(_ecore_x_disp, dri_drm_vsync_root, &driver_name, &device_name))
236       return 0;
237    drm_fd = open(device_name, O_RDWR);
238    if (drm_fd < 0)
239       return 0;
240    sym_drmGetMagic(drm_fd, &drm_magic);
241    if (!sym_DRI2Authenticate(_ecore_x_disp, dri_drm_vsync_root, drm_magic))
242      {
243         close(drm_fd);
244         drm_fd = -1;
245         return 0;
246      }
247    memset(&drm_evctx, 0, sizeof(drm_evctx));
248    drm_evctx.version = DRM_EVENT_CONTEXT_VERSION;
249    drm_evctx.vblank_handler = _dri_drm_vblank_handler;
250    drm_evctx.page_flip_handler = NULL;
251    
252    dri_drm_fdh = ecore_main_fd_handler_add(drm_fd, ECORE_FD_READ, 
253                                            _dri_drm_cb, NULL, NULL, NULL);
254    if (!dri_drm_fdh)
255      {
256         close(drm_fd);
257         drm_fd = -1;
258         return 0;
259      }
260    return 1;
261 }
262
263 static void
264 _dri_drm_shutdown(void)
265 {
266    if (drm_fd >= 0)
267      {
268         close(drm_fd);
269         drm_fd = -1;
270      }
271    if (dri_drm_fdh)
272      {
273         ecore_main_fd_handler_del(dri_drm_fdh);
274         dri_drm_fdh = NULL;
275      }
276 }
277 #endif
278
279 EAPI Eina_Bool
280 ecore_x_vsync_animator_tick_source_set(Ecore_X_Window win)
281 {
282 #ifdef ECORE_X_VSYNC_DRI2
283    Ecore_X_Window root;
284    
285    root = ecore_x_window_root_get(win);
286    if (root != dri_drm_vsync_root)
287      {
288         dri_drm_vsync_root = root;
289         if (dri_drm_vsync_root)
290           {
291              if (!_dri_drm_link())
292                {
293                   ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER);
294                   return EINA_FALSE;
295                }
296              _dri_drm_shutdown();
297              if (!_dri_drm_init())
298                {
299                   dri_drm_vsync_root = 0;
300                   ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER);
301                   return EINA_FALSE;
302                }
303              ecore_animator_custom_source_tick_begin_callback_set
304                 (_dri_drm_tick_begin, NULL);
305              ecore_animator_custom_source_tick_end_callback_set
306                 (_dri_drm_tick_end, NULL);
307              ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_CUSTOM);
308           }
309         else
310           {
311              if (drm_fd >= 0)
312                {
313                   _dri_drm_shutdown();
314                   ecore_animator_custom_source_tick_begin_callback_set
315                      (NULL, NULL);
316                   ecore_animator_custom_source_tick_end_callback_set
317                      (NULL, NULL);
318                   ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER);
319                }
320           }
321      }
322    return EINA_TRUE;
323 #else
324    return EINA_FALSE;
325    win = 0;
326 #endif   
327 }