/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */ /************************************************ rbglib_mainloop.c - $Author: ktou $ $Date: 2007/10/03 11:06:01 $ Copyright (C) 2005,2006 Masao Mutoh ************************************************/ #include "global.h" #include /*****************************************/ GType g_main_loop_get_type(void) { static GType our_type = 0; if (our_type == 0) our_type = g_boxed_type_register_static ("GMainLoop", (GBoxedCopyFunc)g_main_loop_ref, (GBoxedFreeFunc)g_main_loop_unref); return our_type; } /*****************************************/ #define _SELF(s) ((GMainLoop*)RVAL2BOXED(s, G_TYPE_MAIN_LOOP)) /*****************************************/ static VALUE rbglib_main_threads; /* We can't use rbglib_poll() on native Win32. Because GPollFD doesn't have file descriptor but HANDLE. */ #ifndef G_OS_WIN32 #define USE_POLL_FUNC #endif /* This function is very important to work signals with Ruby. */ #ifdef USE_POLL_FUNC static GPollFunc default_poll_func; static gint rbglib_poll (GPollFD *fds, guint nfds, gint timeout) { struct timeval tv; fd_set rset, wset, xset; GPollFD *f; int ready; int maxfd = 0; FD_ZERO (&rset); FD_ZERO (&wset); FD_ZERO (&xset); for (f = fds; f < &fds[nfds]; ++f) { if (f->fd >= 0) { if (f->events & G_IO_IN) FD_SET (f->fd, &rset); if (f->events & G_IO_OUT) FD_SET (f->fd, &wset); if (f->events & G_IO_PRI) FD_SET (f->fd, &xset); if (f->fd > maxfd && (f->events & (G_IO_IN|G_IO_OUT|G_IO_PRI))) maxfd = f->fd; } } tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; ready = rb_thread_select (maxfd + 1, &rset, &wset, &xset, timeout == -1 ? NULL : &tv); if (ready > 0) { for (f = fds; f < &fds[nfds]; ++f) { f->revents = 0; if (f->fd >= 0) { if (FD_ISSET (f->fd, &rset)) f->revents |= G_IO_IN; if (FD_ISSET (f->fd, &wset)) f->revents |= G_IO_OUT; if (FD_ISSET (f->fd, &xset)) f->revents |= G_IO_PRI; } } } return ready; } static void restore_poll_func(VALUE data) { if (g_main_context_get_poll_func(NULL) == (GPollFunc)rbglib_poll) { g_main_context_set_poll_func(NULL, default_poll_func); } } #else /* !USE_POLL_FUNC */ static guint idle_id = 0; static gboolean idle(gpointer data) { struct timeval wait; wait.tv_sec = 0; wait.tv_usec = 10000; /* 10ms */ #ifdef CHECK_INTS CHECK_INTS; #endif if (!rb_thread_critical) rb_thread_wait_for(wait); return TRUE; } void rbg_remove_internal_poll_func(VALUE data) { g_source_remove(idle_id); } void rbg_set_internal_poll_func(void) { idle_id = g_idle_add((GSourceFunc)idle, (gpointer)NULL); } #endif /* !USE_POLL_FUNC */ /*****************************************/ static VALUE ml_initialize(int argc, VALUE *argv, VALUE self) { VALUE context, is_running; GMainLoop *loop; GMainContext *main_context = NULL; rb_scan_args(argc, argv, "02", &context, &is_running); if (!NIL_P(context)) main_context = RVAL2BOXED(context, G_TYPE_MAIN_CONTEXT); loop = g_main_loop_new(main_context, RVAL2CBOOL(is_running)); G_INITIALIZE(self, loop); return Qnil; } /* * An empty timeout */ static gint empty_timeout_func(gpointer data) { return TRUE; } static VALUE ml_run(self) VALUE self; { rb_ary_push(rbglib_main_threads, rb_thread_current()); /* This forces the custom g_poll function to be called * with a minimum timeout of 100ms so that the GMainLoop * iterates from time to time even if there is no event. * Another way could be to add a wakeup pipe to the selectable * fds and than wake up the select only when needed. */ g_timeout_add(100, empty_timeout_func, NULL); g_main_loop_run(_SELF(self)); return self; } static VALUE ml_quit(self) VALUE self; { VALUE thread = rb_ary_pop(rbglib_main_threads); g_main_loop_quit(_SELF(self)); if (NIL_P(thread)){ rb_warning("GLib::MainLoop#quit was called incorrectly."); } else { rb_thread_wakeup(thread); } return Qnil; } static VALUE ml_is_running(self) VALUE self; { return CBOOL2RVAL(g_main_loop_is_running(_SELF(self))); } static VALUE ml_get_context(self) VALUE self; { return BOXED2RVAL(g_main_loop_get_context(_SELF(self)), G_TYPE_MAIN_CONTEXT); } void Init_glib_main_loop() { VALUE ml = G_DEF_CLASS(G_TYPE_MAIN_LOOP, "MainLoop", mGLib); rb_define_method(ml, "initialize", ml_initialize, -1); rb_define_method(ml, "run", ml_run, 0); rb_define_method(ml, "quit", ml_quit, 0); rb_define_method(ml, "running?", ml_is_running, 0); rb_define_method(ml, "context", ml_get_context, 0); rb_global_variable(&rbglib_main_threads); rbglib_main_threads = rb_ary_new(); #ifdef USE_POLL_FUNC default_poll_func = g_main_context_get_poll_func(NULL); g_main_context_set_poll_func(NULL, (GPollFunc)rbglib_poll); rb_set_end_proc(restore_poll_func, Qnil); #else rbg_set_internal_poll_func(); rb_set_end_proc(rbg_remove_internal_poll_func, Qnil); #endif }