svn update: 48958 (latest:48959)
[framework/uifw/ecore.git] / src / lib / ecore / ecore_glib.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4
5 #ifdef HAVE_CONFIG_H
6 # include <config.h>
7 #endif
8
9 #include <stdio.h>
10
11 #include "Ecore.h"
12 #include "ecore_private.h"
13
14 #ifdef HAVE_GLIB
15 #include <glib.h>
16
17 static Eina_Bool _ecore_glib_active = EINA_FALSE;
18 static int (*_ecore_glib_select_original)(int, fd_set*, fd_set*, fd_set*, struct timeval *);
19 static GCond *_ecore_glib_cond = NULL;
20 static GPollFD *_ecore_glib_fds = NULL;
21 static size_t _ecore_glib_fds_size = 0;
22 static const size_t ECORE_GLIB_FDS_INITIAL = 128;
23 static const size_t ECORE_GLIB_FDS_STEP = 8;
24 static const size_t ECORE_GLIB_FDS_MAX_FREE = 256;
25
26 static Eina_Bool
27 _ecore_glib_fds_resize(size_t size)
28 {
29    void *tmp = realloc(_ecore_glib_fds, sizeof(GPollFD) * size);
30    
31    if (!tmp)
32      {
33         ERR("Could not realloc from %zu to %zu buckets.",
34             _ecore_glib_fds_size, size);
35         return EINA_FALSE;
36      }
37    
38    _ecore_glib_fds = tmp;
39    _ecore_glib_fds_size = size;
40    return EINA_TRUE;
41 }
42
43 static int
44 _ecore_glib_context_query(GMainContext *ctx, int priority, int *p_timer)
45 {
46    int reqfds;
47
48    if (_ecore_glib_fds_size == 0)
49      {
50         if (!_ecore_glib_fds_resize(ECORE_GLIB_FDS_INITIAL)) return -1;
51      }
52    
53    while (1)
54      {
55         size_t size;
56         
57         reqfds = g_main_context_query
58           (ctx, priority, p_timer, _ecore_glib_fds, _ecore_glib_fds_size);
59         if (reqfds <= (int)_ecore_glib_fds_size) break;
60
61         size = (1 + reqfds / ECORE_GLIB_FDS_STEP) * ECORE_GLIB_FDS_STEP;
62         if (!_ecore_glib_fds_resize(size)) return -1;
63      }
64
65    if (reqfds + ECORE_GLIB_FDS_MAX_FREE < _ecore_glib_fds_size)
66      {
67         size_t size;
68
69         size = (1 + reqfds / ECORE_GLIB_FDS_MAX_FREE) * ECORE_GLIB_FDS_MAX_FREE;
70         _ecore_glib_fds_resize(size);
71      }
72
73    return reqfds;
74 }
75
76 static int
77 _ecore_glib_context_poll_from(const GPollFD *pfds, int count, fd_set *rfds, fd_set *wfds, fd_set *efds)
78 {
79    const GPollFD *itr = pfds, *itr_end = pfds + count;
80    int glib_fds = -1;
81    
82    for (; itr < itr_end; itr++)
83      {
84         if (glib_fds < itr->fd)
85           glib_fds = itr->fd;
86
87         if (itr->events & G_IO_IN)
88           FD_SET(itr->fd, rfds);
89         if (itr->events & G_IO_OUT)
90           FD_SET(itr->fd, wfds);
91         if (itr->events & (G_IO_HUP | G_IO_ERR))
92           FD_SET(itr->fd, efds);
93      }
94
95    return glib_fds + 1;
96 }
97
98 static int
99 _ecore_glib_context_poll_to(GPollFD *pfds, int count, const fd_set *rfds, const fd_set *wfds, const fd_set *efds, int ready)
100 {
101    GPollFD *itr = pfds, *itr_end = pfds + count;
102    
103    for (; itr < itr_end && ready > 0; itr++)
104      {
105         itr->revents = 0;
106         if (FD_ISSET(itr->fd, rfds))
107           {
108              itr->revents |= G_IO_IN;
109              ready--;
110           }
111         if (FD_ISSET(itr->fd, wfds))
112           {
113              itr->revents |= G_IO_OUT;
114              ready--;
115           }
116         if (FD_ISSET(itr->fd, efds))
117           {
118              itr->revents |= G_IO_ERR;
119              ready--;
120           }
121      }
122    return ready;
123 }
124
125 static int
126 _ecore_glib_select__locked(GMainContext *ctx, int ecore_fds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *ecore_timeout)
127 {
128    int priority, maxfds, glib_fds, reqfds, reqtimeout, ret;
129    struct timeval *timeout, glib_timeout;
130
131    g_main_context_prepare(ctx, &priority);
132    reqfds = _ecore_glib_context_query(ctx, priority, &reqtimeout);
133    if (reqfds < 0) goto error;
134
135    glib_fds = _ecore_glib_context_poll_from
136      (_ecore_glib_fds, reqfds, rfds, wfds, efds);
137
138    if (reqtimeout == -1)
139      timeout = ecore_timeout;
140    else
141      {
142         glib_timeout.tv_sec = reqtimeout / 1000;
143         glib_timeout.tv_usec = (reqtimeout % 1000) * 1000;
144         
145         if (!ecore_timeout || timercmp(ecore_timeout, &glib_timeout, >))
146           timeout = &glib_timeout;
147         else
148           timeout = ecore_timeout;
149      }
150
151    maxfds = (ecore_fds >= glib_fds) ? ecore_fds : glib_fds;
152    ret = _ecore_glib_select_original(maxfds, rfds, wfds, efds, timeout);
153
154    ret = _ecore_glib_context_poll_to
155      (_ecore_glib_fds, reqfds, rfds, wfds, efds, ret);
156
157    if (g_main_context_check(ctx, priority, _ecore_glib_fds, reqfds))
158      g_main_context_dispatch(ctx);
159
160    return ret;
161
162  error:
163    return _ecore_glib_select_original
164      (ecore_fds, rfds, wfds, efds, ecore_timeout);
165 }
166
167 static int
168 _ecore_glib_select(int ecore_fds, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *ecore_timeout)
169 {
170    GStaticMutex lock = G_STATIC_MUTEX_INIT;
171    GMutex *mutex = g_static_mutex_get_mutex(&lock);
172    GMainContext *ctx = g_main_context_default();
173    int ret;
174
175    if (g_main_context_acquire(ctx))
176      g_mutex_lock(mutex);
177    else
178      {
179         if (!_ecore_glib_cond)
180           _ecore_glib_cond = g_cond_new();
181
182         while (!g_main_context_wait(ctx, _ecore_glib_cond, mutex))
183           g_thread_yield();
184      }
185
186    ret = _ecore_glib_select__locked
187      (ctx, ecore_fds, rfds, wfds, efds, ecore_timeout);
188
189    g_mutex_unlock(mutex);
190    g_main_context_release(ctx);
191
192    return ret;
193 }
194 #endif
195
196 void
197 _ecore_glib_init(void)
198 {
199 }
200
201 void
202 _ecore_glib_shutdown(void)
203 {
204 #ifdef HAVE_GLIB
205    if (!_ecore_glib_active) return;
206    _ecore_glib_active = EINA_FALSE;
207
208    if (ecore_main_loop_select_func_get() == _ecore_glib_select)
209      ecore_main_loop_select_func_set(_ecore_glib_select_original);
210    
211    if (_ecore_glib_fds)
212      {
213         free(_ecore_glib_fds);
214         _ecore_glib_fds = NULL;
215      }
216    _ecore_glib_fds_size = 0;
217
218    if (_ecore_glib_cond)
219      {
220         g_cond_free(_ecore_glib_cond);
221         _ecore_glib_cond = NULL;
222      }
223 #endif
224 }
225
226 /**
227  * Request ecore to integrate GLib's main loop.
228  *
229  * This will add a small overhead during every main loop interaction
230  * by checking glib's default main context (used by its main loop). If
231  * it have events to be checked (timers, file descriptors or idlers),
232  * then these will be polled alongside with Ecore's own events, then
233  * dispatched before Ecore's. This is done by calling
234  * ecore_main_loop_select_func_set().
235  *
236  * This will cooperate with previously set
237  * ecore_main_loop_select_func_set() by calling the old
238  * function. Similarly, if you want to override
239  * ecore_main_loop_select_func_set() after main loop is integrated,
240  * call the new select function set by this call (get it by calling
241  * ecore_main_loop_select_func_get() right after
242  * ecore_main_loop_glib_integrate()).
243  *
244  * This is useful to use GMainLoop libraries, like GTK, GUPnP,
245  * LibSoup, GConf and more. Adobe Flash plugin and other plugins
246  * systems depend on this as well.
247  *
248  * Once initialized/integrated, it will be valid until Ecore is
249  * completely shut down.
250  *
251  * @note this is only available if Ecore was compiled with GLib support.
252  *
253  * @return @c EINA_TRUE on success of @c EINA_FALSE if it failed,
254  *         likely no GLib support in Ecore.
255  */
256 EAPI Eina_Bool
257 ecore_main_loop_glib_integrate(void)
258 {
259 #ifdef HAVE_GLIB
260    void *func;
261
262    if (_ecore_glib_active) return EINA_TRUE;
263    func = ecore_main_loop_select_func_get();
264    if (func == _ecore_glib_select) return EINA_TRUE;
265    _ecore_glib_select_original = func;
266    ecore_main_loop_select_func_set(_ecore_glib_select);
267    _ecore_glib_active = EINA_TRUE;
268    return EINA_TRUE;
269 #else
270    fputs("ERROR: no glib support in ecore.\n", stderr);
271    return EINA_FALSE;
272 #endif
273 }
274
275 Eina_Bool _ecore_glib_always_integrate = 1;
276
277 /**
278  * Disable always integrating glib
279  * 
280  * If ecore is compiled with --enable-glib-integration-always (to always
281  * call ecore_main_loop_glib_integrate() when ecore_init() is called), then
282  * calling this before calling ecore_init() will disable the integration.
283  * This is for apps that explicitly do not want this to happen for whatever
284  * reasons they may have.
285  */
286 EAPI void
287 ecore_main_loop_glib_always_integrate_disable(void)
288 {
289    _ecore_glib_always_integrate = 0;
290 }