/* * 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 #include #define VERTO_MODULE_TYPES typedef struct { GMainContext *context; GMainLoop *loop; } verto_mod_ctx; typedef GSource verto_mod_ev; #include /* 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 #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)); }