Back to index

nux  3.0.0
MainLoopGLib.cpp
Go to the documentation of this file.
00001 #include "Nux.h"
00002 #include "Layout.h"
00003 #include "NuxCore/Logger.h"
00004 #include "NuxGraphics/GraphicsEngine.h"
00005 #include "ClientArea.h"
00006 #include "WindowCompositor.h"
00007 #include "TimerProc.h"
00008 #include "SystemThread.h"
00009 #include "FloatingWindow.h"
00010 
00011 #include "WindowThread.h"
00012 
00013 namespace nux
00014 {
00015   namespace
00016   {
00017     logging::Logger logger("nux.windows.thread");
00018   }
00019 
00020   #if (defined(NUX_OS_LINUX) || defined(NUX_USE_GLIB_LOOP_ON_WINDOWS)) && (!defined(NUX_DISABLE_GLIB_LOOP))
00021 
00022   static GMutex *gLibEventMutex = 0;
00023 
00024   static void nux_glib_threads_lock(void)
00025   {
00026     if (gLibEventMutex)
00027       g_mutex_lock(gLibEventMutex);
00028   }
00029 
00030   static void nux_glib_threads_unlock(void)
00031   {
00032     if (gLibEventMutex)
00033       g_mutex_unlock(gLibEventMutex);
00034   }
00035 
00036   struct NuxEventSource
00037   {
00038     GSource source;
00039     GPollFD event_poll_fd;
00040   };
00041 
00042   typedef struct
00043   {
00044     WindowThread *window_thread;
00045     unsigned int id;
00046   } TimeoutData;
00047 
00048   gboolean nux_timeout_dispatch(gpointer user_data)
00049   {
00050     bool repeat = false;
00051     TimeoutData* dd = NUX_STATIC_CAST(TimeoutData*, user_data);
00052     unsigned int return_code = 1;
00053 
00054     dd->window_thread->_inside_timer_loop = true;
00055     repeat = GetTimer().ExecTimerHandler(dd->id)? true : false;
00056     dd->window_thread->_inside_timer_loop = false;
00057 
00058     if (dd->window_thread->IsEmbeddedWindow())
00059     {
00060       dd->window_thread->RedrawRequested.emit();
00061     }
00062     else
00063     {
00064       return_code = dd->window_thread->ExecutionLoop(0);
00065     }
00066 
00067     if ((return_code == 0) && !dd->window_thread->IsEmbeddedWindow())
00068     {
00069       g_main_loop_quit(dd->window_thread->main_loop_glib_);
00070     }
00071 
00072     if (!repeat)
00073       delete dd;
00074 
00075     return repeat;
00076   }
00077 
00078   static gboolean nux_event_prepare(GSource *source, gint *timeout)
00079   {
00080     nux_glib_threads_lock();
00081 
00082     gboolean retval;
00083     *timeout = -1;
00084   #if defined(NUX_OS_WINDOWS)
00085     MSG msg;
00086     retval = PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE) ? TRUE : FALSE;
00087   #elif defined(NUX_OS_LINUX)
00088     retval = GetGraphicsDisplay()->HasXPendingEvent() ? TRUE : FALSE;
00089   #else
00090   #error Not implemented.
00091   #endif
00092 
00093     nux_glib_threads_unlock();
00094     return retval;
00095   }
00096 
00097   static gboolean nux_event_check(GSource *source)
00098   {
00099     nux_glib_threads_lock();
00100 
00101     gboolean retval;
00102     NuxEventSource *event_source = (NuxEventSource*) source;
00103 
00104     if ((event_source->event_poll_fd.revents & G_IO_IN))
00105     {
00106   #if defined(NUX_OS_WINDOWS)
00107       MSG msg;
00108       retval = PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE) ? TRUE : FALSE;
00109   #elif defined(NUX_OS_LINUX)
00110       retval = GetGraphicsDisplay()->HasXPendingEvent() ? TRUE : FALSE;
00111   #else
00112   #error Not implemented.
00113   #endif
00114     }
00115     else
00116     {
00117       retval = FALSE;
00118     }
00119 
00120     nux_glib_threads_unlock();
00121     return retval;
00122   }
00123 
00124   gboolean nux_event_dispatch(GSource *source, GSourceFunc  callback, gpointer user_data)
00125   {
00126     nux_glib_threads_lock();
00127     WindowThread *window_thread = NUX_STATIC_CAST(WindowThread *, user_data);
00128     unsigned int return_code = window_thread->ExecutionLoop(0);
00129 
00130     if (return_code == 0 && !window_thread->IsEmbeddedWindow())
00131     {
00132       g_main_loop_quit(window_thread->main_loop_glib_);
00133     }
00134 
00135     nux_glib_threads_unlock();
00136     return return_code || window_thread->IsEmbeddedWindow();
00137   }
00138 
00139   static GSourceFuncs event_funcs =
00140   {
00141     nux_event_prepare,
00142     nux_event_check,
00143     nux_event_dispatch,
00144     NULL
00145   };
00146 
00147   // Timeline source functions
00148   static gboolean nux_timeline_prepare (GSource  *source, gint *timeout)
00149   {
00150     // right now we are assuming that we are v-synced, so that will handle synchronizations
00151     // we could guess how long we have to wait for the next frame but thats rather ugly
00152     // ideally we need some more API that ensures that timeline/event/re-layout/draws are all in sync
00153 
00154     *timeout = 0;
00155     return TRUE;
00156   }
00157 
00158   static gboolean nux_timeline_check(GSource  *source)
00159   {
00160     return TRUE;
00161   }
00162 
00163   static gboolean nux_timeline_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
00164   {
00165     GTimeVal time_val;
00166     bool has_timelines_left = false;
00167     nux_glib_threads_lock();
00168     g_source_get_current_time(source, &time_val);
00169     WindowThread *window_thread = NUX_STATIC_CAST(WindowThread *, user_data);
00170 
00171     // pump the timelines
00172     has_timelines_left = window_thread->ProcessTimelines(&time_val);
00173 
00174     if (!has_timelines_left)
00175     {
00176       // no timelines left on the stack so lets go ahead and remove the
00177       // master clock, to save on wakeups
00178       window_thread->StopMasterClock();
00179     }
00180 
00181     nux_glib_threads_unlock();
00182     return TRUE;
00183   }
00184 
00185   static GSourceFuncs timeline_funcs =
00186   {
00187     nux_timeline_prepare,
00188     nux_timeline_check,
00189     nux_timeline_dispatch,
00190     NULL
00191   };
00192 
00193   void WindowThread::InitGlibLoop()
00194   {
00195     static bool main_context_created = false;
00196 
00197     if (!IsEmbeddedWindow())
00198     {
00199       static bool gthread_initialized = false;
00200 
00201       if (!gthread_initialized)
00202         g_thread_init(NULL);
00203 
00204       gthread_initialized = true;
00205 
00206       if (((main_loop_glib_context_ == 0) || (main_loop_glib_ == 0)) && (main_context_created == false))
00207       {
00208         //create a context
00209         main_loop_glib_context_ = g_main_context_default();
00210         //create a main loop with context
00211         main_loop_glib_ = g_main_loop_new(main_loop_glib_context_, TRUE);
00212       }
00213       else if ((main_loop_glib_context_ == 0) || (main_loop_glib_ == 0))
00214       {
00215         // Secondary physical windows goes in here
00216         //create a context
00217         main_loop_glib_context_ = g_main_context_new();
00218         //create a main loop with context
00219         main_loop_glib_ = g_main_loop_new(main_loop_glib_context_, TRUE);
00220       }
00221       else
00222       {
00223         return;
00224       }
00225     }
00226 
00227     main_context_created = true;
00228 
00229     gLibEventMutex = 0; //g_mutex_new();
00230 
00231   }
00232 
00233   void WindowThread::RunGlibLoop()
00234   {
00235     GSource *source = g_source_new(&event_funcs, sizeof(NuxEventSource));
00236     NuxEventSource *event_source = (NuxEventSource*) source;
00237 
00238     g_source_set_priority(source, G_PRIORITY_DEFAULT);
00239 
00240 #if defined(NUX_OS_WINDOWS)
00241     event_source->event_poll_fd.fd = G_WIN32_MSG_HANDLE;
00242 #elif defined(NUX_OS_LINUX)
00243     event_source->event_poll_fd.fd = ConnectionNumber(GetGraphicsDisplay().GetX11Display());
00244 #else
00245 #error Not implemented.
00246 #endif
00247 
00248     event_source->event_poll_fd.events = G_IO_IN;
00249 
00250     g_source_add_poll(source, &event_source->event_poll_fd);
00251     g_source_set_can_recurse(source, TRUE);
00252     g_source_set_callback(source, 0, this, 0);
00253 
00254     if (IsEmbeddedWindow())
00255       g_source_attach(source, NULL);
00256     else
00257       g_source_attach(source, main_loop_glib_context_);
00258 
00259     if (_Timelines->size() > 0)
00260       StartMasterClock();
00261 
00262     if (!IsEmbeddedWindow())
00263     {
00264       g_main_loop_run(main_loop_glib_);
00265       g_main_loop_unref(main_loop_glib_);
00266     }
00267   }
00268 
00269   void WindowThread::CleanupGlibLoop()
00270   {
00271     g_source_remove_by_funcs_user_data(&event_funcs, this);
00272   }
00273 
00274   unsigned int WindowThread::AddGLibTimeout(unsigned int duration)
00275   {
00276     if (IsEmbeddedWindow())
00277     {
00278       TimeoutData* dd = new TimeoutData;
00279       dd->window_thread = this;
00280       dd->id = g_timeout_add(duration, nux_timeout_dispatch, dd);
00281 
00282       return dd->id;
00283     }
00284     else
00285     {
00286       if ((main_loop_glib_context_ == 0) || (main_loop_glib_ == 0))
00287       {
00288         //LOG_WARNING(logger) << "Trying to set a timeout before GLib Context is created.\n";
00289         return 0;
00290       }
00291 
00292       GSource *timeout_source;
00293 
00294       //create a new time-out source
00295       timeout_source = g_timeout_source_new(duration);
00296 
00297       TimeoutData* dd = new TimeoutData;
00298       dd->window_thread = this;
00299       dd->id = 0;
00300       //set the callback for this source
00301       g_source_set_callback(timeout_source, nux_timeout_dispatch, dd, NULL);
00302 
00303       //attach source to context
00304       dd->id = g_source_attach(timeout_source, main_loop_glib_context_);
00305 
00306       return dd->id;
00307     }
00308   }
00309 
00310   bool WindowThread::AddChildWindowGlibLoop(WindowThread* wnd_thread)
00311   {
00312 #if defined(NUX_OS_WINDOWS)
00313     if (wnd_thread == NULL)
00314     {
00315       return false;
00316     }
00317 
00318     if (main_loop_glib_context_ == NULL)
00319     {
00320       InitGlibLoop();
00321     }
00322 
00323     GSource *source = g_source_new(&event_funcs, sizeof(NuxEventSource));
00324     NuxEventSource *event_source = (NuxEventSource*) source;
00325     g_source_set_priority(source, G_PRIORITY_DEFAULT);
00326 
00327     event_source->event_poll_fd.fd = (int)((int*)wnd_thread->GetThreadHandle());
00328     event_source->event_poll_fd.events = G_IO_IN;
00329 
00330     g_source_add_poll(source, &event_source->event_poll_fd);
00331     g_source_set_can_recurse(source, TRUE);
00332     g_source_set_callback(source, 0, this, 0);
00333 
00334     if (IsEmbeddedWindow())
00335       g_source_attach(source, NULL);
00336     else
00337       g_source_attach(source, main_loop_glib_context_);
00338 #endif
00339 
00340     return true;
00341   }
00342 
00343   void WindowThread::StartMasterClock()
00344   {
00345     // if we are not embedded and don't have a context yet
00346     if (!IsEmbeddedWindow() && main_loop_glib_context_ == 0)
00347       return;
00348 
00349     if (_MasterClock == NULL)
00350     {
00351       GTimeVal time_val;
00352       // make a source for our master clock
00353       _MasterClock = g_source_new(&timeline_funcs, sizeof(GSource));
00354 
00355       g_source_set_priority(_MasterClock, G_PRIORITY_DEFAULT + 10);
00356       g_source_set_callback(_MasterClock, 0, this, 0);
00357       g_source_set_can_recurse(_MasterClock, TRUE);
00358 
00359       if (IsEmbeddedWindow())
00360         g_source_attach(_MasterClock, NULL);
00361       else if (main_loop_glib_context_ != 0)
00362         g_source_attach(_MasterClock, main_loop_glib_context_);
00363 
00364 
00365       g_get_current_time(&time_val);
00366       last_timeline_frame_time_sec_ = time_val.tv_sec;
00367       last_timeline_frame_time_usec_ = time_val.tv_usec;
00368     }
00369   }
00370 
00371   void WindowThread::StopMasterClock()
00372   {
00373     if (_MasterClock)
00374     {
00375       g_source_remove(g_source_get_id(_MasterClock));
00376       _MasterClock = NULL;
00377     }
00378   }
00379 
00380   void WindowThread::StopGLibLoop()
00381   {
00382     // woo no more main loop! this is prolly bad for nux, so erm
00383     // FIXME!! - Jay take a look at this, make sure just quitting the mainloop
00384     // is an idea that makes sense(needed for testing)
00385     if (!IsEmbeddedWindow())
00386       g_main_loop_quit(main_loop_glib_);
00387   }
00388 
00389 #endif
00390 }