/* Hey EMACS -*- linux-c -*- */ /* $Id$ */ /* TiEmu - an TI emulator * * Copyright (c) 2000-2001, Thomas Corvazier, Romain Lievin * Copyright (c) 2001-2003, Romain Lievin * Copyright (c) 2003, Julien Blache * Copyright (c) 2004, Romain Liévin * Copyright (c) 2005, Romain Liévin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /* Custom g_timeout_xxx support. Timeout support provided by GLib is unusable for our job because: - we could start a new timeout from callback but this doesn't work because timeout finalizing wan be done at _any_ time, - if we want to modify timeout value on the fly, we can't, - it does not try to 'catch up' time lost in delays. Solution used: I have re-implemented GLib timeout but this version catch up time lost in delays (time spend in callback). Widely taken from gmain.c of GLib. */ #ifdef HAVE_CONFIG_H #include #endif #include typedef struct { GSource source; GTimeVal expiration; guint interval; } GTimeoutSource; static void g_timeout_set_expiration (GTimeoutSource *timeout_source, GTimeVal *current_time) { guint seconds = timeout_source->interval / 1000; guint msecs = timeout_source->interval - seconds * 1000; timeout_source->expiration.tv_sec = current_time->tv_sec + seconds; timeout_source->expiration.tv_usec = current_time->tv_usec + msecs * 1000; if (timeout_source->expiration.tv_usec >= 1000000) { timeout_source->expiration.tv_usec -= 1000000; timeout_source->expiration.tv_sec++; } } static gboolean g_timeout_prepare (GSource *source, gint *timeout) { glong sec; glong msec; GTimeVal current_time; GTimeoutSource *timeout_source = (GTimeoutSource *)source; g_source_get_current_time (source, ¤t_time); sec = timeout_source->expiration.tv_sec - current_time.tv_sec; msec = (timeout_source->expiration.tv_usec - current_time.tv_usec) / 1000; /* We do the following in a rather convoluted fashion to deal with * the fact that we don't have an integral type big enough to hold * the difference of two timevals in millseconds. */ if (sec < 0 || (sec == 0 && msec < 0)) msec = 0; else { glong interval_sec = timeout_source->interval / 1000; glong interval_msec = timeout_source->interval % 1000; if (msec < 0) { msec += 1000; sec -= 1; } if (sec > interval_sec || (sec == interval_sec && msec > interval_msec)) { /* The system time has been set backwards, so we * reset the expiration time to now + timeout_source->interval; * this at least avoids hanging for long periods of time. */ g_timeout_set_expiration (timeout_source, ¤t_time); msec = MIN (G_MAXINT, timeout_source->interval); } else { msec = MIN (G_MAXINT, (guint)msec + 1000 * (guint)sec); } } *timeout = (gint)msec; return msec == 0; } static gboolean g_timeout_check (GSource *source) { GTimeVal current_time; GTimeoutSource *timeout_source = (GTimeoutSource *)source; g_source_get_current_time (source, ¤t_time); return ((timeout_source->expiration.tv_sec < current_time.tv_sec) || ((timeout_source->expiration.tv_sec == current_time.tv_sec) && (timeout_source->expiration.tv_usec <= current_time.tv_usec))); } static gboolean g_timeout_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { #if 0 // as GLib GTimeoutSource *timeout_source = (GTimeoutSource *)source; if (!callback) { g_warning ("Timeout source dispatched without callback\n" "You must call g_source_set_callback()."); return FALSE; } if (callback (user_data)) { GTimeVal current_time; g_source_get_current_time (source, ¤t_time); g_timeout_set_expiration (timeout_source, ¤t_time); return TRUE; } else return FALSE; #else GTimeoutSource *timeout_source = (GTimeoutSource *)source; GTimeVal current_time; g_source_get_current_time (source, ¤t_time); g_timeout_set_expiration (timeout_source, ¤t_time); return (callback (user_data)); #endif } GSourceFuncs g_timeout2_funcs = { g_timeout_prepare, g_timeout_check, g_timeout_dispatch, NULL }; GSource * g_timeout2_source_new (guint interval) { GSource *source = g_source_new (&g_timeout2_funcs, sizeof (GTimeoutSource)); GTimeoutSource *timeout_source = (GTimeoutSource *)source; GTimeVal current_time; timeout_source->interval = interval; g_get_current_time (¤t_time); g_timeout_set_expiration (timeout_source, ¤t_time); return source; } guint g_timeout2_add_full (gint priority, guint interval, GSourceFunc function, gpointer data, GDestroyNotify notify) { GSource *source; guint id; g_return_val_if_fail (function != NULL, 0); source = g_timeout2_source_new (interval); if (priority != G_PRIORITY_DEFAULT) g_source_set_priority (source, priority); g_source_set_callback (source, function, data, notify); id = g_source_attach (source, NULL); g_source_unref (source); return id; } guint g_timeout2_add (guint32 interval, GSourceFunc function, gpointer data) { return g_timeout2_add_full (G_PRIORITY_DEFAULT, interval, function, data, NULL); } // --- void g_timeout2_set_interval(guint source_id, guint32 interval) { GSource *source = g_main_context_find_source_by_id(g_main_context_default(), source_id); GTimeoutSource *timeout_source = (GTimeoutSource *)source; timeout_source->interval = interval; }