/*
* Copyright 2011 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <errno.h>
#include <verto-glib.h>
#define VERTO_MODULE_TYPES
typedef struct {
GMainContext *context;
GMainLoop *loop;
} verto_mod_ctx;
typedef GSource verto_mod_ev;
#include <verto-module.h>
/* While glib has signal support in >=2.29, it does not support many
common signals (like USR*). Therefore, signal support is disabled
until they support them (should be soonish) */
#if GLIB_MAJOR_VERSION >= 999
#if GLIB_MINOR_VERSION >= 29
#ifdef G_OS_UNIX /* Not supported on Windows */
#include <glib-unix.h>
#define HAS_SIGNAL VERTO_EV_TYPE_SIGNAL
#endif
#endif /* GLIB_MINOR_VERSION >= 29 */
#endif /* GLIB_MAJOR_VERSION >= 2 */
#ifndef HAS_SIGNAL
#define HAS_SIGNAL 0
#endif
#define VERTO_GLIB_SUPPORTED_TYPES (VERTO_EV_TYPE_IO \
| VERTO_EV_TYPE_TIMEOUT \
| VERTO_EV_TYPE_IDLE \
| HAS_SIGNAL \
| VERTO_EV_TYPE_CHILD)
typedef gboolean
(*GIOCallback)(gpointer data, GIOCondition condition);
typedef struct GIOSource {
GSource source;
GPollFD fd;
gboolean autoclose;
} GIOSource;
static gboolean
prepare(GSource *source, gint *timeout)
{
(void) source;
*timeout = -1;
return FALSE;
}
static gboolean
check(GSource *source)
{
GIOSource *src = (GIOSource*) source;
return src->fd.revents & src->fd.events;
}
static gboolean
dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
GIOSource *src = (GIOSource*) source;
return ((GIOCallback) callback)(user_data, src->fd.revents);
}
static void
finalize(GSource *source)
{
GIOSource *src = (GIOSource*) source;
if (src->autoclose)
close(src->fd.fd);
}
static GSourceFuncs funcs = { prepare, check, dispatch, finalize, NULL, NULL };
static void *
glib_convert_(GMainContext *mc, GMainLoop *ml)
{
verto_mod_ctx *l = NULL;
l = g_new0(verto_mod_ctx, 1);
if (l) {
if (mc) {
/* Steal references */
l->context = mc;
l->loop = ml ? ml : g_main_loop_new(l->context, FALSE);
if (g_main_context_default() == mc)
g_main_context_ref(mc);
} else {
l->context = g_main_context_ref(g_main_context_default());
l->loop = g_main_loop_new(l->context, FALSE);
}
} else {
g_main_loop_unref(ml);
g_main_context_unref(mc);
}
return l;
}
static verto_mod_ctx *
glib_ctx_new(void) {
return glib_convert_(g_main_context_new(), NULL);
}
static verto_mod_ctx *
glib_ctx_default(void) {
return glib_convert_(g_main_context_default(), NULL);
}
static void
glib_ctx_free(verto_mod_ctx *ctx)
{
g_main_loop_unref(ctx->loop);
g_main_context_unref(ctx->context);
g_free(ctx);
}
static void
glib_ctx_run(verto_mod_ctx *ctx)
{
g_main_loop_run(ctx->loop);
}
static void
glib_ctx_run_once(verto_mod_ctx *ctx)
{
g_main_context_iteration(ctx->context, TRUE);
}
static gboolean
break_callback(gpointer loop)
{
g_main_loop_quit(loop);
return FALSE;
}
static void
glib_ctx_break(verto_mod_ctx *ctx)
{
GSource *src = g_timeout_source_new(0);
g_assert(src);
g_source_set_callback(src, break_callback, ctx->loop, NULL);
g_source_set_priority(src, G_PRIORITY_HIGH);
g_assert(g_source_attach(src, ctx->context) != 0);
g_source_unref(src);
}
static gboolean
glib_callback(gpointer data)
{
gboolean persists = verto_get_flags(data) & VERTO_EV_FLAG_PERSIST;
verto_fire(data);
return persists;
}
static gboolean
glib_callback_io(gpointer data, GIOCondition condition)
{
verto_ev_flag state = VERTO_EV_FLAG_NONE;
if (condition & (G_IO_IN | G_IO_PRI))
state |= VERTO_EV_FLAG_IO_READ;
if (condition & G_IO_OUT)
state |= VERTO_EV_FLAG_IO_WRITE;
if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
state |= VERTO_EV_FLAG_IO_ERROR;
verto_set_fd_state(data, state);
return glib_callback(data);
}
static void
glib_callback_child(GPid pid, gint status, gpointer data)
{
(void) pid;
verto_set_proc_status(data, status);
verto_fire(data);
}
static void
glib_ctx_set_flags(verto_mod_ctx *ctx, const verto_ev *ev, verto_mod_ev *evpriv)
{
(void) ctx;
if (verto_get_flags(ev) & VERTO_EV_FLAG_PRIORITY_HIGH)
g_source_set_priority(evpriv, G_PRIORITY_HIGH);
else if (verto_get_flags(ev) & VERTO_EV_FLAG_PRIORITY_MEDIUM)
g_source_set_priority(evpriv, G_PRIORITY_DEFAULT_IDLE);
else if (verto_get_flags(ev) & VERTO_EV_FLAG_PRIORITY_LOW)
g_source_set_priority(evpriv, G_PRIORITY_LOW);
if (verto_get_type(ev) == VERTO_EV_TYPE_IO) {
((GIOSource*) evpriv)->fd.events = 0;
if (verto_get_flags(ev) & VERTO_EV_FLAG_IO_READ)
((GIOSource*) evpriv)->fd.events |= G_IO_IN | G_IO_PRI | G_IO_ERR |
G_IO_HUP | G_IO_NVAL;
if (verto_get_flags(ev) & VERTO_EV_FLAG_IO_WRITE)
((GIOSource*) evpriv)->fd.events |= G_IO_OUT | G_IO_ERR |
G_IO_HUP | G_IO_NVAL;
}
}
static verto_mod_ev *
glib_ctx_add(verto_mod_ctx *ctx, const verto_ev *ev, verto_ev_flag *flags)
{
verto_mod_ev *evpriv = NULL;
verto_ev_type type = verto_get_type(ev);
*flags |= verto_get_flags(ev) & VERTO_EV_FLAG_PERSIST;
*flags |= verto_get_flags(ev) & VERTO_EV_FLAG_IO_CLOSE_FD;
switch (type) {
case VERTO_EV_TYPE_IO:
evpriv = g_source_new(&funcs, sizeof(GIOSource));
if (evpriv) {
((GIOSource*) evpriv)->fd.fd = verto_get_fd(ev);
((GIOSource*) evpriv)->autoclose =
*flags & VERTO_EV_FLAG_IO_CLOSE_FD;
g_source_add_poll(evpriv, &((GIOSource*) evpriv)->fd);
}
break;
case VERTO_EV_TYPE_TIMEOUT:
evpriv = g_timeout_source_new(verto_get_interval(ev));
break;
case VERTO_EV_TYPE_IDLE:
evpriv = g_idle_source_new();
break;
case VERTO_EV_TYPE_CHILD:
evpriv = g_child_watch_source_new(verto_get_proc(ev));
break;
case VERTO_EV_TYPE_SIGNAL:
/* While glib has signal support in >=2.29, it does not support many
common signals (like USR*). Therefore, signal support is disabled
until they support them (should be soonish) */
#if GLIB_MAJOR_VERSION >= 999
#if GLIB_MINOR_VERSION >= 29
#ifdef G_OS_UNIX /* Not supported on Windows */
evpriv = g_unix_signal_source_new(verto_get_signal(ev));
break;
#endif
#endif /* GLIB_MINOR_VERSION >= 29 */
#endif /* GLIB_MAJOR_VERSION >= 2 */
default:
return NULL; /* Not supported */
}
if (!evpriv)
goto error;
if (type == VERTO_EV_TYPE_IO)
g_source_set_callback(evpriv, (GSourceFunc) glib_callback_io,
(void *) ev, NULL);
else if (type == VERTO_EV_TYPE_CHILD)
g_source_set_callback(evpriv, (GSourceFunc) glib_callback_child,
(void *) ev, NULL);
else
g_source_set_callback(evpriv, glib_callback, (void *) ev, NULL);
glib_ctx_set_flags(ctx, ev, evpriv);
g_source_set_can_recurse(evpriv, FALSE);
if (g_source_attach(evpriv, ctx->context) == 0)
goto error;
return evpriv;
error:
if (evpriv) {
g_source_destroy(evpriv);
g_source_unref(evpriv);
}
return NULL;
}
static void
glib_ctx_del(verto_mod_ctx *ctx, const verto_ev *ev, verto_mod_ev *evpriv)
{
(void) ctx;
if (!ev)
return;
g_source_destroy(evpriv);
g_source_unref(evpriv);
}
#define glib_ctx_reinitialize NULL
VERTO_MODULE(glib, g_main_context_default, VERTO_GLIB_SUPPORTED_TYPES);
verto_ctx *
verto_convert_glib(GMainContext *mc, GMainLoop *ml)
{
return verto_convert(glib, 0, glib_convert_(mc, ml));
}