diff --git a/packages/gtk/gst-gtk.c b/packages/gtk/gst-gtk.c index 46b2cf7..91ba8f5 100644 --- a/packages/gtk/gst-gtk.c +++ b/packages/gtk/gst-gtk.c @@ -53,7 +53,7 @@ #include "gstpub.h" #include -#include +#include #include #include #include @@ -62,6 +62,10 @@ #include +#ifdef G_WIN32_MSG_HANDLE +#include +#endif + #ifdef STDC_HEADERS #include #include @@ -719,46 +723,175 @@ static GCond *cond; static GCond *cond_dispatch; static volatile gboolean queued; +#ifdef G_WIN32_MSG_HANDLE +static gint +gst_gtk_poll (GPollFD *fds, + guint nfds, + gint timeout) +{ + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + gint win32_timeout; + gint poll_msgs = -1; + GPollFD *f; + DWORD ready; + gint nhandles = 0; + + for (f = fds; f < &fds[nfds]; ++f) + { + HANDLE h; + assert (f->fd >= 0); + if (f->fd == G_WIN32_MSG_HANDLE) + { + assert (poll_msgs == -1 && nhandles == f - fds); + poll_msgs = nhandles; +#if 1 + continue; +#else + /* Once the VM will host the event loop, it will be possible to + have a MsgWaitForMultipleObjects call in the VM thread and use + the result to wake up this side of the loop. For now resort + to polling; messages are checked by the VM thread every 20ms + (in the GTK check function, called by g_main_context_check). */ + h = hWokenUpEvent; +#endif + } + else + h = (HANDLE) f->fd; + if (nhandles == MAXIMUM_WAIT_OBJECTS) + { + g_warning (G_STRLOC ": Too many handles to wait for!\n"); + break; + } + handles[nhandles++] = (HANDLE) f->fd; + } + + if (nhandles == 0) + { + /* Wait for nothing (huh?) */ + return 0; + } + + /* If the VM were idling, it could in principle use MsgWaitForMultipleObjects + and tell us when it gets a message on its queue. This would remove the + need for polling. However, we cannot implement this until the main + loop is moved inside the VM. */ + if (poll_msgs != -1 /* && !idle */ ) + win32_timeout = (timeout == -1 || timeout > 20) ? 20 : timeout; + else + win32_timeout = (timeout == -1) ? INFINITE : timeout; + + ready = WaitForMultipleObjects (nhandles, handles, FALSE, win32_timeout); + if (ready == WAIT_FAILED) + { + gchar *emsg = g_win32_error_message (GetLastError ()); + g_warning (G_STRLOC ": WaitForMultipleObjects() failed: %s", emsg); + g_free (emsg); + } + + for (f = fds; f < &fds[nfds]; ++f) + f->revents = 0; + +#if 1 + if (poll_msgs != -1) + { + if (ready >= WAIT_OBJECT_0 + poll_msgs + && ready <= WAIT_OBJECT_0 + nhandles) + ready++; + + else if (ready == WAIT_TIMEOUT + && win32_timeout != INFINITE) + ready = WAIT_OBJECT_0 + poll_msgs; + } +#endif + + if (ready == WAIT_FAILED) + return -1; + if (ready == WAIT_TIMEOUT) + return 0; + + f = &fds[ready - WAIT_OBJECT_0]; + if (f->events & (G_IO_IN | G_IO_OUT)) + { + if (f->events & G_IO_IN) + f->revents |= G_IO_IN; + else + f->revents |= G_IO_OUT; + } + + return 1; +} + +#if 0 +/* libgst should have something like this: */ + +static gint +_gst_pause () +{ + idle = true; + ResetEvent (hWakeUpEvent); + MsgWaitForMultipleObjects (1, &hWakeUpEvent, FALSE, INFINITE, QS_ALLEVENTS); + SetEvent (hWokenUpEvent); +} + +static gint +_gst_wakeup () +{ + idle = false; + SetEvent (hWakeUpEvent); +} +#endif +#else +#define gst_gtk_poll g_poll +#endif + + static void main_context_acquire_wait (GMainContext *context) { - g_mutex_lock (mutex); - g_main_context_wait (context, cond, mutex); - - /* No need to keep the mutex except during g_main_context_acquire_wait - and g_main_context_release_signal, i.e. except while we operate on - cond. */ - g_mutex_unlock (mutex); + while (!g_main_context_wait (context, cond, mutex)); } static void -main_context_release_signal (GMainContext *context) +main_context_signal (GMainContext *context) { - g_mutex_lock (mutex); - g_main_context_release (context); - /* Restart the polling thread. Note that #iterate is asynchronous, so this might execute before the Smalltalk code finishes running! This allows debugging GTK+ signal handlers. */ + g_mutex_lock (mutex); queued = false; g_cond_broadcast (cond_dispatch); g_mutex_unlock (mutex); } +static GPollFD *fds; +static int allocated_nfds, nfds; +static int maxprio; + static void main_context_iterate (GMainContext *context) { + g_mutex_lock (mutex); + if (!fds) + { + g_mutex_unlock (mutex); + return; + } + + /* No need to keep the mutex except during g_main_context_acquire_wait + and g_main_context_release_signal, i.e. except while we operate on + cond. */ main_context_acquire_wait (context); + g_mutex_unlock (mutex); + g_main_context_check (context, maxprio, fds, nfds); g_main_context_dispatch (context); - main_context_release_signal (context); + g_main_context_release (context); + main_context_signal (context); } static gpointer main_loop_thread (gpointer semaphore) { OOP semaphoreOOP = semaphore; - static GPollFD *fds; - static int allocated_nfds; GMainContext *context = g_main_loop_get_context (loop); if (!fds) @@ -774,9 +907,9 @@ main_loop_thread (gpointer semaphore) g_mutex_lock (mutex); while (g_main_loop_is_running (loop)) { - int nfds, maxprio, timeout; + int timeout; - g_main_context_wait (context, cond, mutex); + main_context_acquire_wait (context); g_main_context_prepare (context, &maxprio); while ((nfds = g_main_context_query (context, maxprio, &timeout, fds, allocated_nfds)) @@ -788,18 +921,14 @@ main_loop_thread (gpointer semaphore) } /* Release the context so that the other thread can dispatch while - this one polls. */ - g_main_context_release (context); + this one polls. g_main_context_release unlocks the mutex for us. */ g_mutex_unlock (mutex); - - g_poll (fds, nfds, timeout); - - g_mutex_lock (mutex); - g_main_context_wait (context, cond, mutex); - g_main_context_check (context, maxprio, fds, nfds); g_main_context_release (context); + gst_gtk_poll (fds, nfds, timeout); + /* Dispatch on the other thread and wait for it to rendez-vous. */ + g_mutex_lock (mutex); queued = true; _gst_vm_proxy->asyncSignal (semaphoreOOP);