efl/src/lib/ecore/ecore_glib.c

347 lines
8.8 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include "Ecore.h"
#include "ecore_private.h"
#ifdef HAVE_GLIB
# include <glib.h>
static Eina_Bool _ecore_glib_active = EINA_FALSE;
static Ecore_Select_Function _ecore_glib_select_original;
static GCond *_ecore_glib_cond = NULL;
static GPollFD *_ecore_glib_fds = NULL;
static size_t _ecore_glib_fds_size = 0;
static const size_t ECORE_GLIB_FDS_INITIAL = 128;
static const size_t ECORE_GLIB_FDS_STEP = 8;
static const size_t ECORE_GLIB_FDS_MAX_FREE = 256;
static Eina_Bool
_ecore_glib_fds_resize(size_t size)
{
void *tmp = realloc(_ecore_glib_fds, sizeof(GPollFD) * size);
if (!tmp)
{
ERR("Could not realloc from %zu to %zu buckets.",
_ecore_glib_fds_size, size);
return EINA_FALSE;
}
_ecore_glib_fds = tmp;
_ecore_glib_fds_size = size;
return EINA_TRUE;
}
static int
_ecore_glib_context_query(GMainContext *ctx,
int priority,
int *p_timer)
{
int reqfds;
if (_ecore_glib_fds_size == 0)
{
if (!_ecore_glib_fds_resize(ECORE_GLIB_FDS_INITIAL)) return -1;
}
while (1)
{
size_t size;
reqfds = g_main_context_query
(ctx, priority, p_timer, _ecore_glib_fds, _ecore_glib_fds_size);
if (reqfds <= (int)_ecore_glib_fds_size) break;
size = (1 + reqfds / ECORE_GLIB_FDS_STEP) * ECORE_GLIB_FDS_STEP;
if (!_ecore_glib_fds_resize(size)) return -1;
}
if (reqfds + ECORE_GLIB_FDS_MAX_FREE < _ecore_glib_fds_size)
{
size_t size;
size = (1 + reqfds / ECORE_GLIB_FDS_MAX_FREE) * ECORE_GLIB_FDS_MAX_FREE;
_ecore_glib_fds_resize(size);
}
return reqfds;
}
static int
_ecore_glib_context_poll_from(const GPollFD *pfds,
int count,
fd_set *rfds,
fd_set *wfds,
fd_set *efds)
{
const GPollFD *itr = pfds, *itr_end = pfds + count;
int glib_fds = -1;
for (; itr < itr_end; itr++)
{
if (glib_fds < itr->fd)
glib_fds = itr->fd;
if (itr->events & G_IO_IN)
FD_SET(itr->fd, rfds);
if (itr->events & G_IO_OUT)
FD_SET(itr->fd, wfds);
if (itr->events & (G_IO_HUP | G_IO_ERR))
FD_SET(itr->fd, efds);
}
return glib_fds + 1;
}
static int
_ecore_glib_context_poll_to(GPollFD *pfds,
int count,
const fd_set *rfds,
const fd_set *wfds,
const fd_set *efds,
int ready)
{
GPollFD *itr = pfds, *itr_end = pfds + count;
for (; (itr < itr_end) && (ready > 0); itr++)
{
itr->revents = 0;
if (FD_ISSET(itr->fd, rfds) && (itr->events & G_IO_IN))
{
itr->revents |= G_IO_IN;
ready--;
}
if (FD_ISSET(itr->fd, wfds) && (itr->events & G_IO_OUT))
{
itr->revents |= G_IO_OUT;
ready--;
}
if (FD_ISSET(itr->fd, efds) && (itr->events & (G_IO_HUP | G_IO_ERR)))
{
itr->revents |= G_IO_ERR;
ready--;
}
}
return ready;
}
static int
_ecore_glib_select__locked(GMainContext *ctx,
int ecore_fds,
fd_set *rfds,
fd_set *wfds,
fd_set *efds,
struct timeval *ecore_timeout)
{
int priority, maxfds, glib_fds, reqfds, reqtimeout, ret;
struct timeval *timeout, glib_timeout;
g_main_context_prepare(ctx, &priority);
reqfds = _ecore_glib_context_query(ctx, priority, &reqtimeout);
if (reqfds < 0) goto error;
glib_fds = _ecore_glib_context_poll_from
(_ecore_glib_fds, reqfds, rfds, wfds, efds);
if (reqtimeout == -1)
timeout = ecore_timeout;
else
{
glib_timeout.tv_sec = reqtimeout / 1000;
glib_timeout.tv_usec = (reqtimeout % 1000) * 1000;
if (!ecore_timeout || timercmp(ecore_timeout, &glib_timeout, >))
timeout = &glib_timeout;
else
timeout = ecore_timeout;
}
maxfds = (ecore_fds >= glib_fds) ? ecore_fds : glib_fds;
ret = _ecore_glib_select_original(maxfds, rfds, wfds, efds, timeout);
ret = _ecore_glib_context_poll_to
(_ecore_glib_fds, reqfds, rfds, wfds, efds, ret);
if (g_main_context_check(ctx, priority, _ecore_glib_fds, reqfds))
g_main_context_dispatch(ctx);
return ret;
error:
return _ecore_glib_select_original
(ecore_fds, rfds, wfds, efds, ecore_timeout);
}
static int
_ecore_glib_select(int ecore_fds,
fd_set *rfds,
fd_set *wfds,
fd_set *efds,
struct timeval *ecore_timeout)
{
GStaticMutex lock;
GMutex *mutex;
GMainContext *ctx;
int ret;
g_static_mutex_init(&lock);
mutex = g_static_mutex_get_mutex(&lock);
ctx = g_main_context_default();
if (g_main_context_acquire(ctx))
{
if (mutex) g_mutex_lock(mutex);
}
else
{
if (!_ecore_glib_cond)
_ecore_glib_cond = g_cond_new();
while (!g_main_context_wait(ctx, _ecore_glib_cond, mutex))
g_thread_yield();
}
ret = _ecore_glib_select__locked
(ctx, ecore_fds, rfds, wfds, efds, ecore_timeout);
if (mutex) g_mutex_unlock(mutex);
g_main_context_release(ctx);
g_static_mutex_free(&lock);
return ret;
}
#endif
void
_ecore_glib_init(void)
{
}
void
_ecore_glib_shutdown(void)
{
#ifdef HAVE_GLIB
if (!_ecore_glib_active) return;
_ecore_glib_active = EINA_FALSE;
if (ecore_main_loop_select_func_get() == _ecore_glib_select)
ecore_main_loop_select_func_set(_ecore_glib_select_original);
if (_ecore_glib_fds)
{
free(_ecore_glib_fds);
_ecore_glib_fds = NULL;
}
_ecore_glib_fds_size = 0;
if (_ecore_glib_cond)
{
g_cond_free(_ecore_glib_cond);
_ecore_glib_cond = NULL;
}
#endif
}
/**
* @addtogroup Ecore_Main_Loop_Group
*
* @}
*/
/**
* Request ecore to integrate GLib's main loop.
*
* This will add a small overhead during every main loop interaction
* by checking glib's default main context (used by its main loop). If
* it have events to be checked (timers, file descriptors or idlers),
* then these will be polled alongside with Ecore's own events, then
* dispatched before Ecore's. This is done by calling
* ecore_main_loop_select_func_set().
*
* This will cooperate with previously set
* ecore_main_loop_select_func_set() by calling the old
* function. Similarly, if you want to override
* ecore_main_loop_select_func_set() after main loop is integrated,
* call the new select function set by this call (get it by calling
* ecore_main_loop_select_func_get() right after
* ecore_main_loop_glib_integrate()).
*
* This is useful to use GMainLoop libraries, like GTK, GUPnP,
* LibSoup, GConf and more. Adobe Flash plugin and other plugins
* systems depend on this as well.
*
* Once initialized/integrated, it will be valid until Ecore is
* completely shut down.
*
* Example of use:
* @code
*
* int main(void)
* {
* ecore_init();
* ecore_main_loop_glib_integrate();
*
* // some code here
*
* ecore_main_loop_begin();
*
* ecore_shutdown();
*
* return 0;
* }
*
* @endcode
*
* @note This is only available if Ecore was compiled with GLib support.
* @note You don't need to call this function if Ecore was compiled with
* --enable-glib-integration-always.
*
* @return @c EINA_TRUE on success of @c EINA_FALSE if it failed,
* likely no GLib support in Ecore.
*/
EAPI Eina_Bool
ecore_main_loop_glib_integrate(void)
{
#ifdef HAVE_GLIB
void *func;
if (_ecore_glib_active) return EINA_TRUE;
func = ecore_main_loop_select_func_get();
if (func == _ecore_glib_select) return EINA_TRUE;
_ecore_glib_select_original = func;
ecore_main_loop_select_func_set(_ecore_glib_select);
_ecore_glib_active = EINA_TRUE;
return EINA_TRUE;
#else
ERR("No glib support");
return EINA_FALSE;
#endif
}
Eina_Bool _ecore_glib_always_integrate = 1;
/**
* Disable always integrating glib
*
* If ecore is compiled with --enable-glib-integration-always (to always
* call ecore_main_loop_glib_integrate() when ecore_init() is called), then
* calling this before calling ecore_init() will disable the integration.
* This is for apps that explicitly do not want this to happen for whatever
* reasons they may have.
*/
EAPI void
ecore_main_loop_glib_always_integrate_disable(void)
{
_ecore_glib_always_integrate = 0;
}
/**
* @}
*/