Tizen 2.1 base
[framework/uifw/ecore.git] / src / lib / ecore_x / xcb / ecore_xcb_vsync.c
1 #include "ecore_xcb_private.h"
2 # include <fcntl.h>
3 # include <dlfcn.h>
4 # include <X11/Xlib-xcb.h>
5
6 #define ECORE_XCB_VSYNC_DRI2      1
7 #define DRM_EVENT_CONTEXT_VERSION 2
8
9 #ifdef ECORE_XCB_VSYNC_DRI2
10
11 /* relevant header bits of dri/drm inlined here to avoid needing external */
12 /* headers to build drm */
13 typedef unsigned int drm_magic_t;
14
15 typedef enum
16 {
17    DRM_VBLANK_ABSOLUTE = 0x00000000,
18    DRM_VBLANK_RELATIVE = 0x00000001,
19    DRM_VBLANK_EVENT = 0x04000000,
20    DRM_VBLANK_FLIP = 0x08000000,
21    DRM_VBLANK_NEXTONMISS = 0x10000000,
22    DRM_VBLANK_SECONDARY = 0x20000000,
23    DRM_VBLANK_SIGNAL = 0x40000000
24 } drmVBlankSeqType;
25
26 typedef struct _drmVBlankReq
27 {
28    drmVBlankSeqType type;
29    unsigned int     sequence;
30    unsigned long    signal;
31 } drmVBlankReq;
32
33 typedef struct _drmVBlankReply
34 {
35    drmVBlankSeqType type;
36    unsigned int     sequence;
37    long             tval_sec, tval_usec;
38 } drmVBlankReply;
39
40 typedef union _drmVBlank
41 {
42    drmVBlankReq   request;
43    drmVBlankReply reply;
44 } drmVBlank;
45
46 typedef struct _drmEventContext
47 {
48    int version;
49    void (*vblank_handler)(int          fd,
50                           unsigned int sequence,
51                           unsigned int tv_sec,
52                           unsigned int tv_usec,
53                           void        *user_data);
54    void (*page_flip_handler)(int          fd,
55                              unsigned int sequence,
56                              unsigned int tv_sec,
57                              unsigned int tv_usec,
58                              void        *user_data);
59 } drmEventContext;
60
61 static int (*sym_drmClose)(int fd) = NULL;
62 static int (*sym_drmGetMagic)(int          fd,
63                               drm_magic_t *magic) = NULL;
64 static int (*sym_drmWaitVBlank)(int        fd,
65                                 drmVBlank *vbl) = NULL;
66 static int (*sym_drmHandleEvent)(int              fd,
67                                  drmEventContext *evctx) = NULL;
68
69 /* dri */
70 static Bool (*sym_DRI2QueryExtension)(Display *display,
71                                       int     *eventBase,
72                                       int     *errorBase) = NULL;
73 static Bool (*sym_DRI2QueryVersion)(Display *display,
74                                     int     *major,
75                                     int     *minor) = NULL;
76 static Bool (*sym_DRI2Connect)(Display *display,
77                                XID      window,
78                                char   **driverName,
79                                char   **deviceName) = NULL;
80 static Bool (*sym_DRI2Authenticate)(Display    *display,
81                                     XID         window,
82                                     drm_magic_t magic) = NULL;
83
84 /* local function prototypes */
85 static Eina_Bool _ecore_xcb_dri_link(void);
86 static Eina_Bool _ecore_xcb_dri_start(void);
87 static void      _ecore_xcb_dri_shutdown(void);
88
89 static Eina_Bool _ecore_xcb_dri_cb(void             *data __UNUSED__,
90                                    Ecore_Fd_Handler *fdh __UNUSED__);
91 static void      _ecore_xcb_dri_tick_begin(void *data __UNUSED__);
92 static void      _ecore_xcb_dri_tick_end(void *data __UNUSED__);
93 static void      _ecore_xcb_dri_tick_schedule(void);
94 static void      _ecore_xcb_dri_vblank_handler(int          fd __UNUSED__,
95                                                unsigned int frame __UNUSED__,
96                                                unsigned int sec __UNUSED__,
97                                                unsigned int usec __UNUSED__,
98                                                void        *data __UNUSED__);
99
100 /* local variables */
101 static Ecore_X_Window _vsync_root = 0;
102 static int _drm_fd = -1;
103 static Ecore_Fd_Handler *_drm_fdh = NULL;
104 static unsigned int _drm_magic = 0;
105 static Eina_Bool _drm_event_busy = EINA_FALSE;
106 static void *_drm_lib = NULL;
107 static void *_dri_lib = NULL;
108 static drmEventContext _drm_evctx;
109 #endif
110
111 void
112 _ecore_xcb_dri_init(void)
113 {
114    LOGFN(__FILE__, __LINE__, __FUNCTION__);
115 }
116
117 void
118 _ecore_xcb_dri_finalize(void)
119 {
120    LOGFN(__FILE__, __LINE__, __FUNCTION__);
121 }
122
123 EAPI Eina_Bool
124 ecore_x_vsync_animator_tick_source_set(Ecore_X_Window win)
125 {
126 #ifdef ECORE_XCB_VSYNC_DRI2
127    Ecore_X_Window root;
128 #endif
129
130    LOGFN(__FILE__, __LINE__, __FUNCTION__);
131    CHECK_XCB_CONN;
132
133 #ifdef ECORE_XCB_VSYNC_DRI2
134    root = ecore_x_window_root_get(win);
135    if (root != _vsync_root)
136      {
137         _vsync_root = root;
138         if (_vsync_root)
139           {
140              if (!_ecore_xcb_dri_link())
141                {
142                   ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER);
143                   return EINA_FALSE;
144                }
145              _ecore_xcb_dri_shutdown();
146              if (!_ecore_xcb_dri_start())
147                {
148                   _vsync_root = 0;
149                   ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER);
150                   return EINA_FALSE;
151                }
152              ecore_animator_custom_source_tick_begin_callback_set
153                (_ecore_xcb_dri_tick_begin, NULL);
154              ecore_animator_custom_source_tick_end_callback_set
155                (_ecore_xcb_dri_tick_end, NULL);
156              ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_CUSTOM);
157           }
158         else
159           {
160              if (_drm_fd >= 0)
161                {
162                   _ecore_xcb_dri_shutdown();
163                   ecore_animator_custom_source_tick_begin_callback_set
164                     (NULL, NULL);
165                   ecore_animator_custom_source_tick_end_callback_set
166                     (NULL, NULL);
167                   ecore_animator_source_set(ECORE_ANIMATOR_SOURCE_TIMER);
168                }
169           }
170      }
171    return EINA_TRUE;
172 #else
173    return EINA_FALSE;
174    win = 0;
175 #endif
176 }
177
178 /* local functions */
179 #ifdef ECORE_XCB_VSYNC_DRI2
180 static Eina_Bool
181 _ecore_xcb_dri_link(void)
182 {
183    const char *_drm_libs[] =
184    {
185       "libdrm.so.2",
186       "libdrm.so.1",
187       "libdrm.so.0",
188       "libdrm.so",
189       NULL,
190    };
191    const char *_dri_libs[] =
192    {
193       "libdri2.so.2",
194       "libdri2.so.1",
195       "libdri2.so.0",
196       "libdri2.so",
197       "libGL.so.4",
198       "libGL.so.3",
199       "libGL.so.2",
200       "libGL.so.1",
201       "libGL.so.0",
202       "libGL.so",
203       NULL,
204    };
205    int i = 0, fail = 0;
206
207    LOGFN(__FILE__, __LINE__, __FUNCTION__);
208
209 # define SYM(lib, xx)                           \
210   do {                                          \
211        sym_## xx = dlsym(lib, #xx);             \
212        if (!(sym_## xx)) {                      \
213             fprintf(stderr, "%s\n", dlerror()); \
214             fail = 1;                           \
215          }                                      \
216     } while (0);
217
218    if (_drm_lib) return EINA_TRUE;
219
220    for (i = 0; _drm_libs[i]; i++)
221      {
222         _drm_lib = dlopen(_drm_libs[i], (RTLD_LOCAL | RTLD_LAZY));
223         if (_drm_lib)
224           {
225              fail = 0;
226              SYM(_drm_lib, drmClose);
227              SYM(_drm_lib, drmGetMagic);
228              SYM(_drm_lib, drmWaitVBlank);
229              SYM(_drm_lib, drmHandleEvent);
230              if (fail)
231                {
232                   dlclose(_drm_lib);
233                   _drm_lib = NULL;
234                }
235              else
236                break;
237           }
238      }
239    if (!_drm_lib) return EINA_FALSE;
240    for (i = 0; _dri_libs[i]; i++)
241      {
242         if ((_dri_lib = dlopen(_dri_libs[i], (RTLD_LOCAL | RTLD_LAZY))))
243           {
244              fail = 0;
245              SYM(_dri_lib, DRI2QueryExtension);
246              SYM(_dri_lib, DRI2QueryVersion);
247              SYM(_dri_lib, DRI2Connect);
248              SYM(_dri_lib, DRI2Authenticate);
249              if (fail)
250                {
251                   dlclose(_dri_lib);
252                   _dri_lib = NULL;
253                }
254              else
255                break;
256           }
257      }
258    if (!_dri_lib)
259      {
260         dlclose(_drm_lib);
261         _drm_lib = NULL;
262         return EINA_FALSE;
263      }
264
265    return EINA_TRUE;
266 }
267
268 static Eina_Bool
269 _ecore_xcb_dri_start(void)
270 {
271    Ecore_X_Display *disp;
272    int _dri2_event = 0, _dri2_error = 0;
273    int _dri2_major = 0, _dri2_minor = 0;
274    char *device = NULL, *driver = NULL;
275
276    disp = ecore_x_display_get();
277    if (!sym_DRI2QueryExtension(disp, &_dri2_event, &_dri2_error))
278      return 0;
279    if (!sym_DRI2QueryVersion(disp, &_dri2_major, &_dri2_minor))
280      return 0;
281    if (_dri2_major < 2) return 0;
282    if (!sym_DRI2Connect(disp, _vsync_root, &driver, &device))
283      return 0;
284
285    _drm_fd = open(device, O_RDWR);
286    if (_drm_fd < 0) return 0;
287
288    sym_drmGetMagic(_drm_fd, &_drm_magic);
289    if (!sym_DRI2Authenticate(disp, _vsync_root, _drm_magic))
290      {
291         close(_drm_fd);
292         _drm_fd = -1;
293         return EINA_FALSE;
294      }
295
296    memset(&_drm_evctx, 0, sizeof(_drm_evctx));
297    _drm_evctx.version = DRM_EVENT_CONTEXT_VERSION;
298    _drm_evctx.vblank_handler = _ecore_xcb_dri_vblank_handler;
299    _drm_evctx.page_flip_handler = NULL;
300
301    _drm_fdh = ecore_main_fd_handler_add(_drm_fd, ECORE_FD_READ,
302                                         _ecore_xcb_dri_cb, NULL, NULL, NULL);
303    if (!_drm_fdh)
304      {
305         close(_drm_fd);
306         _drm_fd = -1;
307         return EINA_FALSE;
308      }
309
310    return EINA_TRUE;
311 }
312
313 static void
314 _ecore_xcb_dri_shutdown(void)
315 {
316    if (_drm_fd >= 0)
317      {
318         close(_drm_fd);
319         _drm_fd = -1;
320      }
321    if (_drm_fdh)
322      {
323         ecore_main_fd_handler_del(_drm_fdh);
324         _drm_fdh = NULL;
325      }
326 }
327
328 static Eina_Bool
329 _ecore_xcb_dri_cb(void             *data __UNUSED__,
330                   Ecore_Fd_Handler *fdh __UNUSED__)
331 {
332    sym_drmHandleEvent(_drm_fd, &_drm_evctx);
333    return ECORE_CALLBACK_RENEW;
334 }
335
336 static void
337 _ecore_xcb_dri_tick_begin(void *data __UNUSED__)
338 {
339    _drm_event_busy = EINA_TRUE;
340    _ecore_xcb_dri_tick_schedule();
341 }
342
343 static void
344 _ecore_xcb_dri_tick_end(void *data __UNUSED__)
345 {
346    _drm_event_busy = EINA_FALSE;
347 }
348
349 static void
350 _ecore_xcb_dri_tick_schedule(void)
351 {
352    drmVBlank vbl;
353
354    LOGFN(__FILE__, __LINE__, __FUNCTION__);
355    CHECK_XCB_CONN;
356
357    vbl.request.type = (DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT);
358    vbl.request.sequence = 1;
359    vbl.request.signal = 0;
360
361    sym_drmWaitVBlank(_drm_fd, &vbl);
362 }
363
364 static void
365 _ecore_xcb_dri_vblank_handler(int          fd __UNUSED__,
366                               unsigned int frame __UNUSED__,
367                               unsigned int sec __UNUSED__,
368                               unsigned int usec __UNUSED__,
369                               void        *data __UNUSED__)
370 {
371    ecore_animator_custom_tick();
372    if (_drm_event_busy) _ecore_xcb_dri_tick_schedule();
373 }
374
375 #endif