mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 11:26:16 +01:00
b965bb5db1
2000-07-30 Tor Lillqvist <tml@iki.fi> Finally, a new and improved IO Channel and condition watch implementation for Win32. Based on code provided by Craig Setera. When watching file descriptors, for which there is no select() like functionality on Win32 that would work on all Win32 platforms for all types of file descriptors (including anonymous pipes), we start a new thread that blocks while trying to read from the file descriptor. When the read returns, a Win32 Event is signalled that the polling routine eventually notices. Meanwhile, the data being read is stored in a circular buffer, from where the IO channel's read() method picks it up. If the buffer fills up the reading thread has to wait for space becoming available. For this another Win32 Event is used. The IO Channel's read() method signals this when it has read some data out of the buffer. The separate reader thread(s), and the circular buffer(s) with associated events mean lots of possibilities for fun parallellism errors. But it seems to work OK, i.e. GIMP runs. * gmain.c: Small changes to the Win32 polling function. (g_main_win32_get_poll_func): New function. Perhaps it would be a good idea to provide this on all platforms. * giowin32.c: The bulk of the new implementation. (g_io_channel_win32_wait_for_condition): New function. To be used where on Unix one does a select() on the channel's fd, like libgimp's gimp_extension_process(). Could be provided on all platforms. * glib.h: Update documentation for IO Channels on Win32. Remove the declarations for the as of now obsolete old functions related to IO Channels for pipes with "wakeup" messages. * glib.def: Some new functions. * tests/gio-test.c: New file, to test GIOChannel and main loop. * tests/Makefile.am * tests/makefile.mingw.in: Add it.
1441 lines
32 KiB
C
1441 lines
32 KiB
C
/* GLIB - Library of useful routines for C programming
|
|
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
|
|
*
|
|
* gmain.c: Main loop abstraction, timeouts, and idle functions
|
|
* Copyright 1998 Owen Taylor
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/*
|
|
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
|
|
* file for a list of people on the GLib Team. See the ChangeLog
|
|
* files for a list of changes. These files are distributed with
|
|
* GLib at ftp://ftp.gtk.org/pub/gtk/.
|
|
*/
|
|
|
|
/*
|
|
* MT safe
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
/* uncomment the next line to get poll() debugging info */
|
|
/* #define G_MAIN_POLL_DEBUG */
|
|
|
|
#include "glib.h"
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#ifdef HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif /* HAVE_SYS_TIME_H */
|
|
#ifdef GLIB_HAVE_SYS_POLL_H
|
|
# include <sys/poll.h>
|
|
# undef events /* AIX 4.1.5 & 4.3.2 define this for SVR3,4 compatibility */
|
|
# undef revents /* AIX 4.1.5 & 4.3.2 define this for SVR3,4 compatibility */
|
|
#endif /* GLIB_HAVE_SYS_POLL_H */
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif /* HAVE_UNISTD_H */
|
|
#include <errno.h>
|
|
|
|
#ifdef G_OS_WIN32
|
|
#define STRICT
|
|
#include <windows.h>
|
|
#endif /* G_OS_WIN32 */
|
|
|
|
#ifdef G_OS_BEOS
|
|
#include <net/socket.h>
|
|
#endif /* G_OS_BEOS */
|
|
|
|
/* Types */
|
|
|
|
typedef struct _GTimeoutData GTimeoutData;
|
|
typedef struct _GSource GSource;
|
|
typedef struct _GPollRec GPollRec;
|
|
|
|
typedef enum
|
|
{
|
|
G_SOURCE_READY = 1 << G_HOOK_FLAG_USER_SHIFT,
|
|
G_SOURCE_CAN_RECURSE = 1 << (G_HOOK_FLAG_USER_SHIFT + 1)
|
|
} GSourceFlags;
|
|
|
|
struct _GSource
|
|
{
|
|
GHook hook;
|
|
gint priority;
|
|
gpointer source_data;
|
|
};
|
|
|
|
struct _GMainLoop
|
|
{
|
|
gboolean is_running;
|
|
};
|
|
|
|
struct _GTimeoutData
|
|
{
|
|
GTimeVal expiration;
|
|
gint interval;
|
|
GSourceFunc callback;
|
|
};
|
|
|
|
struct _GPollRec
|
|
{
|
|
gint priority;
|
|
GPollFD *fd;
|
|
GPollRec *next;
|
|
};
|
|
|
|
/* Forward declarations */
|
|
|
|
static gint g_source_compare (GHook *a,
|
|
GHook *b);
|
|
static void g_source_destroy_func (GHookList *hook_list,
|
|
GHook *hook);
|
|
static void g_main_poll (gint timeout,
|
|
gboolean use_priority,
|
|
gint priority);
|
|
static void g_main_add_poll_unlocked (gint priority,
|
|
GPollFD *fd);
|
|
static void g_main_wakeup (void);
|
|
|
|
static gboolean g_timeout_prepare (gpointer source_data,
|
|
GTimeVal *current_time,
|
|
gint *timeout,
|
|
gpointer user_data);
|
|
static gboolean g_timeout_check (gpointer source_data,
|
|
GTimeVal *current_time,
|
|
gpointer user_data);
|
|
static gboolean g_timeout_dispatch (gpointer source_data,
|
|
GTimeVal *dispatch_time,
|
|
gpointer user_data);
|
|
static gboolean g_idle_prepare (gpointer source_data,
|
|
GTimeVal *current_time,
|
|
gint *timeout,
|
|
gpointer user_data);
|
|
static gboolean g_idle_check (gpointer source_data,
|
|
GTimeVal *current_time,
|
|
gpointer user_data);
|
|
static gboolean g_idle_dispatch (gpointer source_data,
|
|
GTimeVal *dispatch_time,
|
|
gpointer user_data);
|
|
|
|
/* Data */
|
|
|
|
static GSList *pending_dispatches = NULL;
|
|
static GHookList source_list = { 0 };
|
|
static gint in_check_or_prepare = 0;
|
|
|
|
/* The following lock is used for both the list of sources
|
|
* and the list of poll records
|
|
*/
|
|
G_LOCK_DEFINE_STATIC (main_loop);
|
|
|
|
static GSourceFuncs timeout_funcs =
|
|
{
|
|
g_timeout_prepare,
|
|
g_timeout_check,
|
|
g_timeout_dispatch,
|
|
g_free,
|
|
};
|
|
|
|
static GSourceFuncs idle_funcs =
|
|
{
|
|
g_idle_prepare,
|
|
g_idle_check,
|
|
g_idle_dispatch,
|
|
NULL,
|
|
};
|
|
|
|
static GPollRec *poll_records = NULL;
|
|
static GPollRec *poll_free_list = NULL;
|
|
static GMemChunk *poll_chunk;
|
|
static guint n_poll_records = 0;
|
|
|
|
#ifdef G_THREADS_ENABLED
|
|
#ifndef G_OS_WIN32
|
|
/* this pipe is used to wake up the main loop when a source is added.
|
|
*/
|
|
static gint wake_up_pipe[2] = { -1, -1 };
|
|
#else /* G_OS_WIN32 */
|
|
static HANDLE wake_up_semaphore = NULL;
|
|
#endif /* G_OS_WIN32 */
|
|
static GPollFD wake_up_rec;
|
|
static gboolean poll_waiting = FALSE;
|
|
|
|
/* Flag indicating whether the set of fd's changed during a poll */
|
|
static gboolean poll_changed = FALSE;
|
|
#endif /* G_THREADS_ENABLED */
|
|
|
|
#ifdef HAVE_POLL
|
|
/* SunOS has poll, but doesn't provide a prototype. */
|
|
# if defined (sun) && !defined (__SVR4)
|
|
extern gint poll (GPollFD *ufds, guint nfsd, gint timeout);
|
|
# endif /* !sun */
|
|
static GPollFunc poll_func = (GPollFunc) poll;
|
|
#else /* !HAVE_POLL */
|
|
#ifdef G_OS_WIN32
|
|
|
|
static gint
|
|
g_poll (GPollFD *fds,
|
|
guint nfds,
|
|
gint timeout)
|
|
{
|
|
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
|
|
gboolean poll_msgs = FALSE;
|
|
GPollFD *f;
|
|
DWORD ready;
|
|
MSG msg;
|
|
UINT timer;
|
|
gint nhandles = 0;
|
|
|
|
for (f = fds; f < &fds[nfds]; ++f)
|
|
if (f->fd >= 0)
|
|
{
|
|
if (f->events & G_IO_IN)
|
|
{
|
|
if (f->fd == G_WIN32_MSG_HANDLE)
|
|
poll_msgs = TRUE;
|
|
else
|
|
{
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("g_poll: waiting for %#x\n", f->fd);
|
|
#endif
|
|
handles[nhandles++] = (HANDLE) f->fd;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (timeout == -1)
|
|
timeout = INFINITE;
|
|
|
|
if (poll_msgs)
|
|
{
|
|
/* Waiting for messages, and maybe events */
|
|
if (nhandles == 0)
|
|
{
|
|
if (timeout == INFINITE)
|
|
{
|
|
/* Waiting just for messages, infinite timeout
|
|
* -> Use PeekMessage, then WaitMessage
|
|
*/
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("PeekMessage, then WaitMessage\n");
|
|
#endif
|
|
if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
|
|
ready = WAIT_OBJECT_0;
|
|
else if (!WaitMessage ())
|
|
g_warning ("g_poll: WaitMessage failed");
|
|
ready = WAIT_OBJECT_0;
|
|
}
|
|
else if (timeout == 0)
|
|
{
|
|
/* Waiting just for messages, zero timeout
|
|
* -> Use PeekMessage
|
|
*/
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("PeekMessage\n");
|
|
#endif
|
|
if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
|
|
ready = WAIT_OBJECT_0;
|
|
else
|
|
ready = WAIT_TIMEOUT;
|
|
}
|
|
else
|
|
{
|
|
/* Waiting just for messages, some timeout
|
|
* -> First try PeekMessage, then set a timer, wait for message,
|
|
* kill timer, use PeekMessage
|
|
*/
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("PeekMessage\n");
|
|
#endif
|
|
if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
|
|
ready = WAIT_OBJECT_0;
|
|
else if ((timer = SetTimer (NULL, 0, timeout, NULL)) == 0)
|
|
g_warning ("g_poll: SetTimer failed");
|
|
else
|
|
{
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("WaitMessage\n");
|
|
#endif
|
|
WaitMessage ();
|
|
KillTimer (NULL, timer);
|
|
if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)
|
|
&& msg.message != WM_TIMER)
|
|
ready = WAIT_OBJECT_0;
|
|
else
|
|
ready = WAIT_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Wait for either message or event
|
|
* -> Use MsgWaitForMultipleObjects
|
|
*/
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("MsgWaitForMultipleObjects(%d, %d)\n", nhandles, timeout);
|
|
#endif
|
|
ready = MsgWaitForMultipleObjects (nhandles, handles, FALSE,
|
|
timeout, QS_ALLINPUT);
|
|
|
|
if (ready == WAIT_FAILED)
|
|
g_warning ("g_poll: MsgWaitForMultipleObjects failed");
|
|
}
|
|
}
|
|
else if (nhandles == 0)
|
|
{
|
|
/* Wait for nothing (huh?) */
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
/* Wait for just events
|
|
* -> Use WaitForMultipleObjects
|
|
*/
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("WaitForMultipleObjects(%d, %d)\n", nhandles, timeout);
|
|
#endif
|
|
ready = WaitForMultipleObjects (nhandles, handles, FALSE, timeout);
|
|
if (ready == WAIT_FAILED)
|
|
g_warning ("g_poll: WaitForMultipleObjects failed");
|
|
}
|
|
|
|
for (f = fds; f < &fds[nfds]; ++f)
|
|
f->revents = 0;
|
|
|
|
if (ready == WAIT_FAILED)
|
|
return -1;
|
|
else if (ready == WAIT_TIMEOUT)
|
|
return 0;
|
|
else if (poll_msgs && ready == WAIT_OBJECT_0 + nhandles)
|
|
{
|
|
for (f = fds; f < &fds[nfds]; ++f)
|
|
if (f->fd >= 0)
|
|
{
|
|
if (f->events & G_IO_IN)
|
|
if (f->fd == G_WIN32_MSG_HANDLE)
|
|
f->revents |= G_IO_IN;
|
|
}
|
|
}
|
|
else if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles)
|
|
for (f = fds; f < &fds[nfds]; ++f)
|
|
{
|
|
if ((f->events & G_IO_IN)
|
|
&& f->fd == (gint) handles[ready - WAIT_OBJECT_0])
|
|
{
|
|
f->revents |= G_IO_IN;
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("g_poll: got event %#x\n", f->fd);
|
|
#endif
|
|
#if 0
|
|
ResetEvent ((HANDLE) f->fd);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (ready >= WAIT_OBJECT_0 && ready < WAIT_OBJECT_0 + nhandles)
|
|
return ready - WAIT_OBJECT_0 + 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
#else /* !G_OS_WIN32 */
|
|
|
|
/* The following implementation of poll() comes from the GNU C Library.
|
|
* Copyright (C) 1994, 1996, 1997 Free Software Foundation, Inc.
|
|
*/
|
|
|
|
#include <string.h> /* for bzero on BSD systems */
|
|
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif /* HAVE_SYS_SELECT_H */
|
|
|
|
#ifdef G_OS_BEOS
|
|
#undef NO_FD_SET
|
|
#endif /* G_OS_BEOS */
|
|
|
|
#ifndef NO_FD_SET
|
|
# define SELECT_MASK fd_set
|
|
#else /* !NO_FD_SET */
|
|
# ifndef _AIX
|
|
typedef long fd_mask;
|
|
# endif /* _AIX */
|
|
# ifdef _IBMR2
|
|
# define SELECT_MASK void
|
|
# else /* !_IBMR2 */
|
|
# define SELECT_MASK int
|
|
# endif /* !_IBMR2 */
|
|
#endif /* !NO_FD_SET */
|
|
|
|
static gint
|
|
g_poll (GPollFD *fds,
|
|
guint nfds,
|
|
gint timeout)
|
|
{
|
|
struct timeval tv;
|
|
SELECT_MASK 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 = 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;
|
|
}
|
|
|
|
#endif /* !G_OS_WIN32 */
|
|
|
|
static GPollFunc poll_func = g_poll;
|
|
#endif /* !HAVE_POLL */
|
|
|
|
/* Hooks for adding to the main loop */
|
|
|
|
/* Use knowledge of insert_sorted algorithm here to make
|
|
* sure we insert at the end of equal priority items
|
|
*/
|
|
static gint
|
|
g_source_compare (GHook *a,
|
|
GHook *b)
|
|
{
|
|
GSource *source_a = (GSource *)a;
|
|
GSource *source_b = (GSource *)b;
|
|
|
|
return (source_a->priority < source_b->priority) ? -1 : 1;
|
|
}
|
|
|
|
/* HOLDS: main_loop lock */
|
|
static void
|
|
g_source_destroy_func (GHookList *hook_list,
|
|
GHook *hook)
|
|
{
|
|
GSource *source = (GSource*) hook;
|
|
GDestroyNotify destroy;
|
|
|
|
G_UNLOCK (main_loop);
|
|
|
|
destroy = hook->destroy;
|
|
if (destroy)
|
|
destroy (hook->data);
|
|
|
|
destroy = ((GSourceFuncs*) hook->func)->destroy;
|
|
if (destroy)
|
|
destroy (source->source_data);
|
|
|
|
G_LOCK (main_loop);
|
|
}
|
|
|
|
guint
|
|
g_source_add (gint priority,
|
|
gboolean can_recurse,
|
|
GSourceFuncs *funcs,
|
|
gpointer source_data,
|
|
gpointer user_data,
|
|
GDestroyNotify notify)
|
|
{
|
|
guint return_val;
|
|
GSource *source;
|
|
|
|
G_LOCK (main_loop);
|
|
|
|
if (!source_list.is_setup)
|
|
{
|
|
g_hook_list_init (&source_list, sizeof (GSource));
|
|
|
|
source_list.hook_destroy = G_HOOK_DEFERRED_DESTROY;
|
|
source_list.hook_free = g_source_destroy_func;
|
|
}
|
|
|
|
source = (GSource*) g_hook_alloc (&source_list);
|
|
source->priority = priority;
|
|
source->source_data = source_data;
|
|
source->hook.func = funcs;
|
|
source->hook.data = user_data;
|
|
source->hook.destroy = notify;
|
|
|
|
g_hook_insert_sorted (&source_list,
|
|
(GHook *)source,
|
|
g_source_compare);
|
|
|
|
if (can_recurse)
|
|
source->hook.flags |= G_SOURCE_CAN_RECURSE;
|
|
|
|
return_val = source->hook.hook_id;
|
|
|
|
#ifdef G_THREADS_ENABLED
|
|
/* Now wake up the main loop if it is waiting in the poll() */
|
|
g_main_wakeup ();
|
|
#endif
|
|
|
|
G_UNLOCK (main_loop);
|
|
|
|
return return_val;
|
|
}
|
|
|
|
gboolean
|
|
g_source_remove (guint tag)
|
|
{
|
|
GHook *hook;
|
|
|
|
g_return_val_if_fail (tag > 0, FALSE);
|
|
|
|
G_LOCK (main_loop);
|
|
|
|
hook = g_hook_get (&source_list, tag);
|
|
if (hook)
|
|
g_hook_destroy_link (&source_list, hook);
|
|
|
|
G_UNLOCK (main_loop);
|
|
|
|
return hook != NULL;
|
|
}
|
|
|
|
gboolean
|
|
g_source_remove_by_user_data (gpointer user_data)
|
|
{
|
|
GHook *hook;
|
|
|
|
G_LOCK (main_loop);
|
|
|
|
hook = g_hook_find_data (&source_list, TRUE, user_data);
|
|
if (hook)
|
|
g_hook_destroy_link (&source_list, hook);
|
|
|
|
G_UNLOCK (main_loop);
|
|
|
|
return hook != NULL;
|
|
}
|
|
|
|
static gboolean
|
|
g_source_find_source_data (GHook *hook,
|
|
gpointer data)
|
|
{
|
|
GSource *source = (GSource *)hook;
|
|
|
|
return (source->source_data == data);
|
|
}
|
|
|
|
gboolean
|
|
g_source_remove_by_source_data (gpointer source_data)
|
|
{
|
|
GHook *hook;
|
|
|
|
G_LOCK (main_loop);
|
|
|
|
hook = g_hook_find (&source_list, TRUE,
|
|
g_source_find_source_data, source_data);
|
|
if (hook)
|
|
g_hook_destroy_link (&source_list, hook);
|
|
|
|
G_UNLOCK (main_loop);
|
|
|
|
return hook != NULL;
|
|
}
|
|
|
|
static gboolean
|
|
g_source_find_funcs_user_data (GHook *hook,
|
|
gpointer data)
|
|
{
|
|
gpointer *d = data;
|
|
|
|
return hook->func == d[0] && hook->data == d[1];
|
|
}
|
|
|
|
gboolean
|
|
g_source_remove_by_funcs_user_data (GSourceFuncs *funcs,
|
|
gpointer user_data)
|
|
{
|
|
gpointer d[2];
|
|
GHook *hook;
|
|
|
|
g_return_val_if_fail (funcs != NULL, FALSE);
|
|
|
|
G_LOCK (main_loop);
|
|
|
|
d[0] = funcs;
|
|
d[1] = user_data;
|
|
|
|
hook = g_hook_find (&source_list, TRUE,
|
|
g_source_find_funcs_user_data, d);
|
|
if (hook)
|
|
g_hook_destroy_link (&source_list, hook);
|
|
|
|
G_UNLOCK (main_loop);
|
|
|
|
return hook != NULL;
|
|
}
|
|
|
|
void
|
|
g_get_current_time (GTimeVal *result)
|
|
{
|
|
#ifndef G_OS_WIN32
|
|
g_return_if_fail (result != NULL);
|
|
|
|
/*this is required on alpha, there the timeval structs are int's
|
|
not longs and a cast only would fail horribly*/
|
|
gettimeofday (&r, NULL);
|
|
result->tv_sec = r.tv_sec;
|
|
result->tv_usec = r.tv_usec;
|
|
#else
|
|
/* Avoid calling time() except for the first time.
|
|
* GetTickCount() should be pretty fast and low-level?
|
|
* I could also use ftime() but it seems unnecessarily overheady.
|
|
*/
|
|
static DWORD start_tick = 0;
|
|
static time_t start_time;
|
|
DWORD tick;
|
|
|
|
g_return_if_fail (result != NULL);
|
|
|
|
if (start_tick == 0)
|
|
{
|
|
start_tick = GetTickCount ();
|
|
time (&start_time);
|
|
}
|
|
|
|
tick = GetTickCount ();
|
|
|
|
result->tv_sec = (tick - start_tick) / 1000 + start_time;
|
|
result->tv_usec = ((tick - start_tick) % 1000) * 1000;
|
|
#endif
|
|
}
|
|
|
|
/* Running the main loop */
|
|
|
|
/* HOLDS: main_loop_lock */
|
|
static void
|
|
g_main_dispatch (GTimeVal *dispatch_time)
|
|
{
|
|
while (pending_dispatches != NULL)
|
|
{
|
|
gboolean need_destroy;
|
|
GSource *source = pending_dispatches->data;
|
|
GSList *tmp_list;
|
|
|
|
tmp_list = pending_dispatches;
|
|
pending_dispatches = g_slist_remove_link (pending_dispatches, pending_dispatches);
|
|
g_slist_free_1 (tmp_list);
|
|
|
|
if (G_HOOK_IS_VALID (source))
|
|
{
|
|
gboolean was_in_call;
|
|
gpointer hook_data = source->hook.data;
|
|
gpointer source_data = source->source_data;
|
|
gboolean (*dispatch) (gpointer,
|
|
GTimeVal *,
|
|
gpointer);
|
|
|
|
dispatch = ((GSourceFuncs *) source->hook.func)->dispatch;
|
|
|
|
was_in_call = G_HOOK_IN_CALL (source);
|
|
source->hook.flags |= G_HOOK_FLAG_IN_CALL;
|
|
|
|
G_UNLOCK (main_loop);
|
|
need_destroy = ! dispatch (source_data,
|
|
dispatch_time,
|
|
hook_data);
|
|
G_LOCK (main_loop);
|
|
|
|
if (!was_in_call)
|
|
source->hook.flags &= ~G_HOOK_FLAG_IN_CALL;
|
|
|
|
if (need_destroy && G_HOOK_IS_VALID (source))
|
|
g_hook_destroy_link (&source_list, (GHook *) source);
|
|
}
|
|
|
|
g_hook_unref (&source_list, (GHook*) source);
|
|
}
|
|
}
|
|
|
|
/* g_main_iterate () runs a single iteration of the mainloop, or,
|
|
* if !dispatch checks to see if any sources need dispatching.
|
|
* basic algorithm for dispatch=TRUE:
|
|
*
|
|
* 1) while the list of currently pending sources is non-empty,
|
|
* we call (*dispatch) on those that are !IN_CALL or can_recurse,
|
|
* removing sources from the list after each returns.
|
|
* the return value of (*dispatch) determines whether the source
|
|
* itself is kept alive.
|
|
*
|
|
* 2) call (*prepare) for sources that are not yet SOURCE_READY and
|
|
* are !IN_CALL or can_recurse. a return value of TRUE determines
|
|
* that the source would like to be dispatched immediatedly, it
|
|
* is then flagged as SOURCE_READY.
|
|
*
|
|
* 3) poll with the pollfds from all sources at the priority of the
|
|
* first source flagged as SOURCE_READY. if there are any sources
|
|
* flagged as SOURCE_READY, we use a timeout of 0 or the minimum
|
|
* of all timouts otherwise.
|
|
*
|
|
* 4) for each source !IN_CALL or can_recurse, if SOURCE_READY or
|
|
* (*check) returns true, add the source to the pending list.
|
|
* once one source returns true, stop after checking all sources
|
|
* at that priority.
|
|
*
|
|
* 5) while the list of currently pending sources is non-empty,
|
|
* call (*dispatch) on each source, removing the source
|
|
* after the call.
|
|
*
|
|
*/
|
|
static gboolean
|
|
g_main_iterate (gboolean block,
|
|
gboolean dispatch)
|
|
{
|
|
GHook *hook;
|
|
GTimeVal current_time = { 0, 0 };
|
|
gint n_ready = 0;
|
|
gint current_priority = 0;
|
|
gint timeout;
|
|
gboolean retval = FALSE;
|
|
|
|
g_return_val_if_fail (!block || dispatch, FALSE);
|
|
|
|
g_get_current_time (¤t_time);
|
|
|
|
G_LOCK (main_loop);
|
|
|
|
#ifdef G_THREADS_ENABLED
|
|
if (poll_waiting)
|
|
{
|
|
g_warning("g_main_iterate(): main loop already active in another thread");
|
|
G_UNLOCK (main_loop);
|
|
return FALSE;
|
|
}
|
|
#endif G_THREADS_ENABLED
|
|
|
|
/* If recursing, finish up current dispatch, before starting over */
|
|
if (pending_dispatches)
|
|
{
|
|
if (dispatch)
|
|
g_main_dispatch (¤t_time);
|
|
|
|
G_UNLOCK (main_loop);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Prepare all sources */
|
|
|
|
timeout = block ? -1 : 0;
|
|
|
|
hook = g_hook_first_valid (&source_list, TRUE);
|
|
while (hook)
|
|
{
|
|
GSource *source = (GSource*) hook;
|
|
gint source_timeout = -1;
|
|
|
|
if ((n_ready > 0) && (source->priority > current_priority))
|
|
{
|
|
g_hook_unref (&source_list, hook);
|
|
break;
|
|
}
|
|
if (G_HOOK_IN_CALL (hook) && !(hook->flags & G_SOURCE_CAN_RECURSE))
|
|
{
|
|
hook = g_hook_next_valid (&source_list, hook, TRUE);
|
|
continue;
|
|
}
|
|
|
|
if (!(hook->flags & G_SOURCE_READY))
|
|
{
|
|
gboolean (*prepare) (gpointer source_data,
|
|
GTimeVal *current_time,
|
|
gint *timeout,
|
|
gpointer user_data);
|
|
|
|
prepare = ((GSourceFuncs *) hook->func)->prepare;
|
|
in_check_or_prepare++;
|
|
G_UNLOCK (main_loop);
|
|
|
|
if ((*prepare) (source->source_data, ¤t_time, &source_timeout, source->hook.data))
|
|
hook->flags |= G_SOURCE_READY;
|
|
|
|
G_LOCK (main_loop);
|
|
in_check_or_prepare--;
|
|
}
|
|
|
|
if (hook->flags & G_SOURCE_READY)
|
|
{
|
|
if (!dispatch)
|
|
{
|
|
g_hook_unref (&source_list, hook);
|
|
G_UNLOCK (main_loop);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
n_ready++;
|
|
current_priority = source->priority;
|
|
timeout = 0;
|
|
}
|
|
}
|
|
|
|
if (source_timeout >= 0)
|
|
{
|
|
if (timeout < 0)
|
|
timeout = source_timeout;
|
|
else
|
|
timeout = MIN (timeout, source_timeout);
|
|
}
|
|
|
|
hook = g_hook_next_valid (&source_list, hook, TRUE);
|
|
}
|
|
|
|
/* poll(), if necessary */
|
|
|
|
g_main_poll (timeout, n_ready > 0, current_priority);
|
|
|
|
if (timeout != 0)
|
|
g_get_current_time (¤t_time);
|
|
|
|
/* Check to see what sources need to be dispatched */
|
|
|
|
n_ready = 0;
|
|
|
|
hook = g_hook_first_valid (&source_list, TRUE);
|
|
while (hook)
|
|
{
|
|
GSource *source = (GSource *)hook;
|
|
|
|
if ((n_ready > 0) && (source->priority > current_priority))
|
|
{
|
|
g_hook_unref (&source_list, hook);
|
|
break;
|
|
}
|
|
if (G_HOOK_IN_CALL (hook) && !(hook->flags & G_SOURCE_CAN_RECURSE))
|
|
{
|
|
hook = g_hook_next_valid (&source_list, hook, TRUE);
|
|
continue;
|
|
}
|
|
|
|
if (!(hook->flags & G_SOURCE_READY))
|
|
{
|
|
gboolean (*check) (gpointer source_data,
|
|
GTimeVal *current_time,
|
|
gpointer user_data);
|
|
|
|
check = ((GSourceFuncs *) hook->func)->check;
|
|
in_check_or_prepare++;
|
|
G_UNLOCK (main_loop);
|
|
|
|
if ((*check) (source->source_data, ¤t_time, source->hook.data))
|
|
hook->flags |= G_SOURCE_READY;
|
|
|
|
G_LOCK (main_loop);
|
|
in_check_or_prepare--;
|
|
}
|
|
|
|
if (hook->flags & G_SOURCE_READY)
|
|
{
|
|
if (dispatch)
|
|
{
|
|
hook->flags &= ~G_SOURCE_READY;
|
|
g_hook_ref (&source_list, hook);
|
|
pending_dispatches = g_slist_prepend (pending_dispatches, source);
|
|
current_priority = source->priority;
|
|
n_ready++;
|
|
}
|
|
else
|
|
{
|
|
g_hook_unref (&source_list, hook);
|
|
G_UNLOCK (main_loop);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
hook = g_hook_next_valid (&source_list, hook, TRUE);
|
|
}
|
|
|
|
/* Now invoke the callbacks */
|
|
|
|
if (pending_dispatches)
|
|
{
|
|
pending_dispatches = g_slist_reverse (pending_dispatches);
|
|
g_main_dispatch (¤t_time);
|
|
retval = TRUE;
|
|
}
|
|
|
|
G_UNLOCK (main_loop);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* See if any events are pending
|
|
*/
|
|
gboolean
|
|
g_main_pending (void)
|
|
{
|
|
return in_check_or_prepare ? FALSE : g_main_iterate (FALSE, FALSE);
|
|
}
|
|
|
|
/* Run a single iteration of the mainloop. If block is FALSE,
|
|
* will never block
|
|
*/
|
|
gboolean
|
|
g_main_iteration (gboolean block)
|
|
{
|
|
if (in_check_or_prepare)
|
|
{
|
|
g_warning ("g_main_iteration(): called recursively from within a source's check() or "
|
|
"prepare() member or from a second thread, iteration not possible");
|
|
return FALSE;
|
|
}
|
|
else
|
|
return g_main_iterate (block, TRUE);
|
|
}
|
|
|
|
GMainLoop*
|
|
g_main_new (gboolean is_running)
|
|
{
|
|
GMainLoop *loop;
|
|
|
|
loop = g_new0 (GMainLoop, 1);
|
|
loop->is_running = is_running != FALSE;
|
|
|
|
return loop;
|
|
}
|
|
|
|
void
|
|
g_main_run (GMainLoop *loop)
|
|
{
|
|
g_return_if_fail (loop != NULL);
|
|
|
|
if (in_check_or_prepare)
|
|
{
|
|
g_warning ("g_main_run(): called recursively from within a source's check() or "
|
|
"prepare() member or from a second thread, iteration not possible");
|
|
return;
|
|
}
|
|
|
|
loop->is_running = TRUE;
|
|
while (loop->is_running)
|
|
g_main_iterate (TRUE, TRUE);
|
|
}
|
|
|
|
void
|
|
g_main_quit (GMainLoop *loop)
|
|
{
|
|
g_return_if_fail (loop != NULL);
|
|
|
|
loop->is_running = FALSE;
|
|
}
|
|
|
|
void
|
|
g_main_destroy (GMainLoop *loop)
|
|
{
|
|
g_return_if_fail (loop != NULL);
|
|
|
|
g_free (loop);
|
|
}
|
|
|
|
gboolean
|
|
g_main_is_running (GMainLoop *loop)
|
|
{
|
|
g_return_val_if_fail (loop != NULL, FALSE);
|
|
|
|
return loop->is_running;
|
|
}
|
|
|
|
/* HOLDS: main_loop_lock */
|
|
static void
|
|
g_main_poll (gint timeout,
|
|
gboolean use_priority,
|
|
gint priority)
|
|
{
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
GTimer *poll_timer;
|
|
#endif
|
|
GPollFD *fd_array;
|
|
GPollRec *pollrec;
|
|
gint i;
|
|
gint npoll;
|
|
|
|
#ifdef G_THREADS_ENABLED
|
|
#ifndef G_OS_WIN32
|
|
if (wake_up_pipe[0] < 0)
|
|
{
|
|
if (pipe (wake_up_pipe) < 0)
|
|
g_error ("Cannot create pipe main loop wake-up: %s\n",
|
|
g_strerror (errno));
|
|
|
|
wake_up_rec.fd = wake_up_pipe[0];
|
|
wake_up_rec.events = G_IO_IN;
|
|
g_main_add_poll_unlocked (0, &wake_up_rec);
|
|
}
|
|
#else
|
|
if (wake_up_semaphore == NULL)
|
|
{
|
|
if ((wake_up_semaphore = CreateSemaphore (NULL, 0, 100, NULL)) == NULL)
|
|
g_error ("Cannot create wake-up semaphore: %s", g_win32_error_message (GetLastError ()));
|
|
wake_up_rec.fd = (gint) wake_up_semaphore;
|
|
wake_up_rec.events = G_IO_IN;
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("wake-up semaphore: %#x\n", (guint) wake_up_semaphore);
|
|
#endif
|
|
g_main_add_poll_unlocked (0, &wake_up_rec);
|
|
}
|
|
#endif
|
|
#endif
|
|
fd_array = g_new (GPollFD, n_poll_records);
|
|
|
|
pollrec = poll_records;
|
|
i = 0;
|
|
while (pollrec && (!use_priority || priority >= pollrec->priority))
|
|
{
|
|
if (pollrec->fd->events)
|
|
{
|
|
fd_array[i].fd = pollrec->fd->fd;
|
|
/* In direct contradiction to the Unix98 spec, IRIX runs into
|
|
* difficulty if you pass in POLLERR, POLLHUP or POLLNVAL
|
|
* flags in the events field of the pollfd while it should
|
|
* just ignoring them. So we mask them out here.
|
|
*/
|
|
fd_array[i].events = pollrec->fd->events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
|
|
fd_array[i].revents = 0;
|
|
i++;
|
|
}
|
|
|
|
pollrec = pollrec->next;
|
|
}
|
|
#ifdef G_THREADS_ENABLED
|
|
poll_waiting = TRUE;
|
|
poll_changed = FALSE;
|
|
#endif
|
|
|
|
npoll = i;
|
|
if (npoll || timeout != 0)
|
|
{
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("g_main_poll(%d) timeout: %d\n", npoll, timeout);
|
|
poll_timer = g_timer_new ();
|
|
#endif
|
|
|
|
G_UNLOCK (main_loop);
|
|
if ((*poll_func) (fd_array, npoll, timeout) < 0)
|
|
g_warning ("poll(2) failed due to: %s.",
|
|
g_strerror (errno));
|
|
G_LOCK (main_loop);
|
|
|
|
#ifdef G_MAIN_POLL_DEBUG
|
|
g_print ("g_main_poll(%d) timeout: %d - elapsed %12.10f seconds",
|
|
npoll,
|
|
timeout,
|
|
g_timer_elapsed (poll_timer, NULL));
|
|
g_timer_destroy (poll_timer);
|
|
pollrec = poll_records;
|
|
i = 0;
|
|
while (i < npoll)
|
|
{
|
|
if (pollrec->fd->events)
|
|
{
|
|
if (fd_array[i].revents)
|
|
{
|
|
g_print (" [%d:", fd_array[i].fd);
|
|
if (fd_array[i].revents & G_IO_IN)
|
|
g_print ("i");
|
|
if (fd_array[i].revents & G_IO_OUT)
|
|
g_print ("o");
|
|
if (fd_array[i].revents & G_IO_PRI)
|
|
g_print ("p");
|
|
if (fd_array[i].revents & G_IO_ERR)
|
|
g_print ("e");
|
|
if (fd_array[i].revents & G_IO_HUP)
|
|
g_print ("h");
|
|
if (fd_array[i].revents & G_IO_NVAL)
|
|
g_print ("n");
|
|
g_print ("]");
|
|
}
|
|
i++;
|
|
}
|
|
pollrec = pollrec->next;
|
|
}
|
|
g_print ("\n");
|
|
#endif
|
|
} /* if (npoll || timeout != 0) */
|
|
|
|
#ifdef G_THREADS_ENABLED
|
|
if (!poll_waiting)
|
|
{
|
|
#ifndef G_OS_WIN32
|
|
gchar c;
|
|
read (wake_up_pipe[0], &c, 1);
|
|
#endif
|
|
}
|
|
else
|
|
poll_waiting = FALSE;
|
|
|
|
/* If the set of poll file descriptors changed, bail out
|
|
* and let the main loop rerun
|
|
*/
|
|
if (poll_changed)
|
|
{
|
|
g_free (fd_array);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
pollrec = poll_records;
|
|
i = 0;
|
|
while (i < npoll)
|
|
{
|
|
if (pollrec->fd->events)
|
|
{
|
|
pollrec->fd->revents = fd_array[i].revents;
|
|
i++;
|
|
}
|
|
pollrec = pollrec->next;
|
|
}
|
|
|
|
g_free (fd_array);
|
|
}
|
|
|
|
void
|
|
g_main_add_poll (GPollFD *fd,
|
|
gint priority)
|
|
{
|
|
G_LOCK (main_loop);
|
|
g_main_add_poll_unlocked (priority, fd);
|
|
G_UNLOCK (main_loop);
|
|
}
|
|
|
|
/* HOLDS: main_loop_lock */
|
|
static void
|
|
g_main_add_poll_unlocked (gint priority,
|
|
GPollFD *fd)
|
|
{
|
|
GPollRec *lastrec, *pollrec, *newrec;
|
|
|
|
if (!poll_chunk)
|
|
poll_chunk = g_mem_chunk_create (GPollRec, 32, G_ALLOC_ONLY);
|
|
|
|
if (poll_free_list)
|
|
{
|
|
newrec = poll_free_list;
|
|
poll_free_list = newrec->next;
|
|
}
|
|
else
|
|
newrec = g_chunk_new (GPollRec, poll_chunk);
|
|
|
|
newrec->fd = fd;
|
|
newrec->priority = priority;
|
|
|
|
lastrec = NULL;
|
|
pollrec = poll_records;
|
|
while (pollrec && priority >= pollrec->priority)
|
|
{
|
|
lastrec = pollrec;
|
|
pollrec = pollrec->next;
|
|
}
|
|
|
|
if (lastrec)
|
|
lastrec->next = newrec;
|
|
else
|
|
poll_records = newrec;
|
|
|
|
newrec->next = pollrec;
|
|
|
|
n_poll_records++;
|
|
|
|
#ifdef G_THREADS_ENABLED
|
|
poll_changed = TRUE;
|
|
|
|
/* Now wake up the main loop if it is waiting in the poll() */
|
|
g_main_wakeup ();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
g_main_remove_poll (GPollFD *fd)
|
|
{
|
|
GPollRec *pollrec, *lastrec;
|
|
|
|
G_LOCK (main_loop);
|
|
|
|
lastrec = NULL;
|
|
pollrec = poll_records;
|
|
|
|
while (pollrec)
|
|
{
|
|
if (pollrec->fd == fd)
|
|
{
|
|
if (lastrec != NULL)
|
|
lastrec->next = pollrec->next;
|
|
else
|
|
poll_records = pollrec->next;
|
|
|
|
#ifdef ENABLE_GC_FRIENDLY
|
|
pollrec->fd = NULL;
|
|
#endif /* ENABLE_GC_FRIENDLY */
|
|
|
|
pollrec->next = poll_free_list;
|
|
poll_free_list = pollrec;
|
|
|
|
n_poll_records--;
|
|
break;
|
|
}
|
|
lastrec = pollrec;
|
|
pollrec = pollrec->next;
|
|
}
|
|
|
|
#ifdef G_THREADS_ENABLED
|
|
poll_changed = TRUE;
|
|
|
|
/* Now wake up the main loop if it is waiting in the poll() */
|
|
g_main_wakeup ();
|
|
#endif
|
|
|
|
G_UNLOCK (main_loop);
|
|
}
|
|
|
|
void
|
|
g_main_set_poll_func (GPollFunc func)
|
|
{
|
|
if (func)
|
|
poll_func = func;
|
|
else
|
|
#ifdef HAVE_POLL
|
|
poll_func = (GPollFunc) poll;
|
|
#else
|
|
poll_func = (GPollFunc) g_poll;
|
|
#endif
|
|
}
|
|
|
|
#ifdef G_OS_WIN32
|
|
|
|
/* Useful on other platforms, too? */
|
|
|
|
GPollFunc
|
|
g_main_win32_get_poll_func (void)
|
|
{
|
|
return poll_func;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* Wake the main loop up from a poll() */
|
|
static void
|
|
g_main_wakeup (void)
|
|
{
|
|
#ifdef G_THREADS_ENABLED
|
|
if (poll_waiting)
|
|
{
|
|
poll_waiting = FALSE;
|
|
#ifndef G_OS_WIN32
|
|
write (wake_up_pipe[1], "A", 1);
|
|
#else
|
|
ReleaseSemaphore (wake_up_semaphore, 1, NULL);
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Timeouts */
|
|
|
|
static void
|
|
g_timeout_set_expiration (GTimeoutData *data,
|
|
GTimeVal *current_time)
|
|
{
|
|
guint seconds = data->interval / 1000;
|
|
guint msecs = data->interval - seconds * 1000;
|
|
|
|
data->expiration.tv_sec = current_time->tv_sec + seconds;
|
|
data->expiration.tv_usec = current_time->tv_usec + msecs * 1000;
|
|
if (data->expiration.tv_usec >= 1000000)
|
|
{
|
|
data->expiration.tv_usec -= 1000000;
|
|
data->expiration.tv_sec++;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
g_timeout_prepare (gpointer source_data,
|
|
GTimeVal *current_time,
|
|
gint *timeout,
|
|
gpointer user_data)
|
|
{
|
|
glong msec;
|
|
GTimeoutData *data = source_data;
|
|
|
|
msec = ((data->expiration.tv_sec - current_time->tv_sec) * 1000 +
|
|
(data->expiration.tv_usec - current_time->tv_usec) / 1000);
|
|
|
|
if (msec < 0)
|
|
msec = 0;
|
|
else if (msec > data->interval)
|
|
{
|
|
/* The system time has been set backwards, so we
|
|
* reset the expiration time to now + data->interval;
|
|
* this at least avoids hanging for long periods of time.
|
|
*/
|
|
g_timeout_set_expiration (data, current_time);
|
|
msec = data->interval;
|
|
}
|
|
|
|
*timeout = msec;
|
|
|
|
return msec == 0;
|
|
}
|
|
|
|
static gboolean
|
|
g_timeout_check (gpointer source_data,
|
|
GTimeVal *current_time,
|
|
gpointer user_data)
|
|
{
|
|
GTimeoutData *data = source_data;
|
|
|
|
return ((data->expiration.tv_sec < current_time->tv_sec) ||
|
|
((data->expiration.tv_sec == current_time->tv_sec) &&
|
|
(data->expiration.tv_usec <= current_time->tv_usec)));
|
|
}
|
|
|
|
static gboolean
|
|
g_timeout_dispatch (gpointer source_data,
|
|
GTimeVal *dispatch_time,
|
|
gpointer user_data)
|
|
{
|
|
GTimeoutData *data = source_data;
|
|
|
|
if (data->callback (user_data))
|
|
{
|
|
g_timeout_set_expiration (data, dispatch_time);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
guint
|
|
g_timeout_add_full (gint priority,
|
|
guint interval,
|
|
GSourceFunc function,
|
|
gpointer data,
|
|
GDestroyNotify notify)
|
|
{
|
|
GTimeoutData *timeout_data = g_new (GTimeoutData, 1);
|
|
GTimeVal current_time;
|
|
|
|
timeout_data->interval = interval;
|
|
timeout_data->callback = function;
|
|
g_get_current_time (¤t_time);
|
|
|
|
g_timeout_set_expiration (timeout_data, ¤t_time);
|
|
|
|
return g_source_add (priority, FALSE, &timeout_funcs, timeout_data, data, notify);
|
|
}
|
|
|
|
guint
|
|
g_timeout_add (guint32 interval,
|
|
GSourceFunc function,
|
|
gpointer data)
|
|
{
|
|
return g_timeout_add_full (G_PRIORITY_DEFAULT,
|
|
interval, function, data, NULL);
|
|
}
|
|
|
|
/* Idle functions */
|
|
|
|
static gboolean
|
|
g_idle_prepare (gpointer source_data,
|
|
GTimeVal *current_time,
|
|
gint *timeout,
|
|
gpointer user_data)
|
|
{
|
|
*timeout = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
g_idle_check (gpointer source_data,
|
|
GTimeVal *current_time,
|
|
gpointer user_data)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
g_idle_dispatch (gpointer source_data,
|
|
GTimeVal *dispatch_time,
|
|
gpointer user_data)
|
|
{
|
|
GSourceFunc func = source_data;
|
|
|
|
return func (user_data);
|
|
}
|
|
|
|
guint
|
|
g_idle_add_full (gint priority,
|
|
GSourceFunc function,
|
|
gpointer data,
|
|
GDestroyNotify notify)
|
|
{
|
|
g_return_val_if_fail (function != NULL, 0);
|
|
|
|
return g_source_add (priority, FALSE, &idle_funcs, function, data, notify);
|
|
}
|
|
|
|
guint
|
|
g_idle_add (GSourceFunc function,
|
|
gpointer data)
|
|
{
|
|
return g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, function, data, NULL);
|
|
}
|
|
|
|
gboolean
|
|
g_idle_remove_by_data (gpointer data)
|
|
{
|
|
return g_source_remove_by_funcs_user_data (&idle_funcs, data);
|
|
}
|