wxDFB: use Unix event loop and timers (fixes #10408)
authorVáclav Slavík <vslavik@fastmail.fm>
Fri, 30 Jan 2009 15:21:47 +0000 (15:21 +0000)
committerVáclav Slavík <vslavik@fastmail.fm>
Fri, 30 Jan 2009 15:21:47 +0000 (15:21 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@58520 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

Makefile.in
build/bakefiles/files.bkl
include/wx/dfb/chkconf.h
include/wx/dfb/evtloop.h
include/wx/dfb/nonownedwnd.h
include/wx/dfb/wrapdfb.h
include/wx/unix/private/timer.h
src/dfb/evtloop.cpp
src/dfb/utils.cpp

index 2f952b9ffef6869ba20b01283937e7529bc58530..fd4fa5affb4eeebe5cec4d7a6b936676b7b9e8ba 100644 (file)
@@ -4363,7 +4363,6 @@ COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS =  \
        monodll_generic_icon.o \
        monodll_generic_imaglist.o \
        monodll_mask.o \
-       monodll_generic_timer.o \
        monodll_dfb_app.o \
        monodll_dfb_bitmap.o \
        monodll_dfb_brush.o \
@@ -5227,7 +5226,6 @@ COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS_1 =  \
        monodll_generic_icon.o \
        monodll_generic_imaglist.o \
        monodll_mask.o \
-       monodll_generic_timer.o \
        monodll_dfb_app.o \
        monodll_dfb_bitmap.o \
        monodll_dfb_brush.o \
@@ -6176,7 +6174,6 @@ COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS_2 =  \
        monolib_generic_icon.o \
        monolib_generic_imaglist.o \
        monolib_mask.o \
-       monolib_generic_timer.o \
        monolib_dfb_app.o \
        monolib_dfb_bitmap.o \
        monolib_dfb_brush.o \
@@ -7040,7 +7037,6 @@ COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS_3 =  \
        monolib_generic_icon.o \
        monolib_generic_imaglist.o \
        monolib_mask.o \
-       monolib_generic_timer.o \
        monolib_dfb_app.o \
        monolib_dfb_bitmap.o \
        monolib_dfb_brush.o \
@@ -8135,7 +8131,6 @@ COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS_4 =  \
        coredll_generic_icon.o \
        coredll_generic_imaglist.o \
        coredll_mask.o \
-       coredll_generic_timer.o \
        coredll_dfb_app.o \
        coredll_dfb_bitmap.o \
        coredll_dfb_brush.o \
@@ -8999,7 +8994,6 @@ COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS_5 =  \
        coredll_generic_icon.o \
        coredll_generic_imaglist.o \
        coredll_mask.o \
-       coredll_generic_timer.o \
        coredll_dfb_app.o \
        coredll_dfb_bitmap.o \
        coredll_dfb_brush.o \
@@ -9646,7 +9640,6 @@ COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS_6 =  \
        corelib_generic_icon.o \
        corelib_generic_imaglist.o \
        corelib_mask.o \
-       corelib_generic_timer.o \
        corelib_dfb_app.o \
        corelib_dfb_bitmap.o \
        corelib_dfb_brush.o \
@@ -10510,7 +10503,6 @@ COND_TOOLKIT_DFB___LOWLEVEL_SRC_OBJECTS_7 =  \
        corelib_generic_icon.o \
        corelib_generic_imaglist.o \
        corelib_mask.o \
-       corelib_generic_timer.o \
        corelib_dfb_app.o \
        corelib_dfb_bitmap.o \
        corelib_dfb_brush.o \
@@ -16944,9 +16936,6 @@ monodll_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONODLL_ODEP)
 @COND_TOOLKIT_MGL_USE_GUI_1@monodll_generic_timer.o: $(srcdir)/src/generic/timer.cpp $(MONODLL_ODEP)
 @COND_TOOLKIT_MGL_USE_GUI_1@   $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/generic/timer.cpp
 
-@COND_TOOLKIT_DFB_USE_GUI_1@monodll_generic_timer.o: $(srcdir)/src/generic/timer.cpp $(MONODLL_ODEP)
-@COND_TOOLKIT_DFB_USE_GUI_1@   $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/generic/timer.cpp
-
 @COND_TOOLKIT_X11_USE_GUI_1@monodll_x11_app.o: $(srcdir)/src/x11/app.cpp $(MONODLL_ODEP)
 @COND_TOOLKIT_X11_USE_GUI_1@   $(CXXC) -c -o $@ $(MONODLL_CXXFLAGS) $(srcdir)/src/x11/app.cpp
 
@@ -21615,9 +21604,6 @@ monolib_sound_sdl.o: $(srcdir)/src/unix/sound_sdl.cpp $(MONOLIB_ODEP)
 @COND_TOOLKIT_MGL_USE_GUI_1@monolib_generic_timer.o: $(srcdir)/src/generic/timer.cpp $(MONOLIB_ODEP)
 @COND_TOOLKIT_MGL_USE_GUI_1@   $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/generic/timer.cpp
 
-@COND_TOOLKIT_DFB_USE_GUI_1@monolib_generic_timer.o: $(srcdir)/src/generic/timer.cpp $(MONOLIB_ODEP)
-@COND_TOOLKIT_DFB_USE_GUI_1@   $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/generic/timer.cpp
-
 @COND_TOOLKIT_X11_USE_GUI_1@monolib_x11_app.o: $(srcdir)/src/x11/app.cpp $(MONOLIB_ODEP)
 @COND_TOOLKIT_X11_USE_GUI_1@   $(CXXC) -c -o $@ $(MONOLIB_CXXFLAGS) $(srcdir)/src/x11/app.cpp
 
@@ -26469,9 +26455,6 @@ coredll_win32.o: $(srcdir)/src/univ/themes/win32.cpp $(COREDLL_ODEP)
 @COND_TOOLKIT_MGL_USE_GUI_1@coredll_generic_timer.o: $(srcdir)/src/generic/timer.cpp $(COREDLL_ODEP)
 @COND_TOOLKIT_MGL_USE_GUI_1@   $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $(srcdir)/src/generic/timer.cpp
 
-@COND_TOOLKIT_DFB_USE_GUI_1@coredll_generic_timer.o: $(srcdir)/src/generic/timer.cpp $(COREDLL_ODEP)
-@COND_TOOLKIT_DFB_USE_GUI_1@   $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $(srcdir)/src/generic/timer.cpp
-
 @COND_TOOLKIT_X11_USE_GUI_1@coredll_x11_app.o: $(srcdir)/src/x11/app.cpp $(COREDLL_ODEP)
 @COND_TOOLKIT_X11_USE_GUI_1@   $(CXXC) -c -o $@ $(COREDLL_CXXFLAGS) $(srcdir)/src/x11/app.cpp
 
@@ -29970,9 +29953,6 @@ corelib_win32.o: $(srcdir)/src/univ/themes/win32.cpp $(CORELIB_ODEP)
 @COND_TOOLKIT_MGL_USE_GUI_1@corelib_generic_timer.o: $(srcdir)/src/generic/timer.cpp $(CORELIB_ODEP)
 @COND_TOOLKIT_MGL_USE_GUI_1@   $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $(srcdir)/src/generic/timer.cpp
 
-@COND_TOOLKIT_DFB_USE_GUI_1@corelib_generic_timer.o: $(srcdir)/src/generic/timer.cpp $(CORELIB_ODEP)
-@COND_TOOLKIT_DFB_USE_GUI_1@   $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $(srcdir)/src/generic/timer.cpp
-
 @COND_TOOLKIT_X11_USE_GUI_1@corelib_x11_app.o: $(srcdir)/src/x11/app.cpp $(CORELIB_ODEP)
 @COND_TOOLKIT_X11_USE_GUI_1@   $(CXXC) -c -o $@ $(CORELIB_CXXFLAGS) $(srcdir)/src/x11/app.cpp
 
index cd4598a3b83032c00aeae0582a6198caee8bbcad..9db7573006edef62d488f8f7511d01a505e07d0c 100644 (file)
@@ -1916,7 +1916,6 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file!
     src/generic/icon.cpp
     src/generic/imaglist.cpp
     src/generic/mask.cpp
-    src/generic/timer.cpp
     src/dfb/app.cpp
     src/dfb/bitmap.cpp
     src/dfb/brush.cpp
index cbc9e7f331e3c03e7ef8e72386e3feb56927b6f1..6e2895a16a3abe81785832a79647282d26ebf38f 100644 (file)
 #   error "wxFileConfig is required by wxDFB port"
 #endif
 
-#if wxUSE_SOCKETS && !wxUSE_SELECT_DISPATCHER
+#if wxUSE_SOCKETS && !wxUSE_CONSOLE_EVENTLOOP
 #   ifdef wxABORT_ON_CONFIG_ERROR
 #       error "wxSocket requires wxSelectDispatcher in wxDFB"
 #   else
-#       undef wxUSE_SELECT_DISPATCHER
-#       define wxUSE_SELECT_DISPATCHER 1
+#       undef wxUSE_CONSOLE_EVENTLOOP
+#       define wxUSE_CONSOLE_EVENTLOOP 1
 #   endif
 #endif
 
index 4c3380819a62f1253346b2558a144bfbe780d567..9b010a10344d4b8523e5e17fdf439671019ad8cf 100644 (file)
 #define _WX_DFB_EVTLOOP_H_
 
 #include "wx/dfb/dfbptr.h"
+#include "wx/unix/evtloop.h"
 
 wxDFB_DECLARE_INTERFACE(IDirectFBEventBuffer);
-struct wxDFBEvent;
 
 // ----------------------------------------------------------------------------
 // wxEventLoop
 // ----------------------------------------------------------------------------
 
-class WXDLLIMPEXP_CORE wxGUIEventLoop : public wxEventLoopManual
+class WXDLLIMPEXP_CORE wxGUIEventLoop : public wxConsoleEventLoop
 {
 public:
     wxGUIEventLoop();
 
-    virtual bool Pending() const;
-    virtual bool Dispatch();
-    virtual int DispatchTimeout(unsigned long timeout);
-
     // returns DirectFB event buffer used by wx
     static wxIDirectFBEventBufferPtr GetDirectFBEventBuffer();
 
+private:
     // wxYield implementation: iterate the loop as long as there are any
     // pending events
     void Yield();
 
-protected:
-    virtual void WakeUp();
-    virtual void OnNextIteration();
-
-    virtual void HandleDFBEvent(const wxDFBEvent& event);
-
-private:
     static void InitBuffer();
     static void CleanUp();
 
-    friend class wxApp; // calls CleanUp() and WakeUp()
+    friend class wxApp; // calls CleanUp()
 
 private:
     static wxIDirectFBEventBufferPtr ms_buffer;
+    static int ms_bufferFd;
 
     DECLARE_NO_COPY_CLASS(wxGUIEventLoop)
 };
index 2363c785e615092e56850048e3f2f425f53d5867..86b7200aa18109d81fcc507ac14b7c03f9ea92b0 100644 (file)
@@ -18,6 +18,7 @@
 wxDFB_DECLARE_INTERFACE(IDirectFBWindow);
 class wxDfbQueuedPaintRequests;
 struct wxDFBWindowEvent;
+class wxDFBEventsHandler;
 
 //-----------------------------------------------------------------------------
 // wxNonOwnedWindow
@@ -118,8 +119,8 @@ private:
     // are we currently painting some area of this TLW?
     bool m_isPainting;
 
-    friend class wxGUIEventLoop; // for HandleDFBWindowEvent
-    friend class wxWindowDFB;    // for SetDfbFocus
+    friend class wxDFBEventsHandler; // for HandleDFBWindowEvent
+    friend class wxWindowDFB;        // for SetDfbFocus
 };
 
 #endif // _WX_DFB_NONOWNEDWND_H_
index da753b6063f23000ba2dd587ade1b1f51560db00..602dd7c5fb7b14e9ba6e54bc6ffec914c6c2ff79 100644 (file)
@@ -368,39 +368,9 @@ struct wxIDirectFBEventBuffer : public wxDfbWrapper<IDirectFBEventBuffer>
 {
     wxIDirectFBEventBuffer(IDirectFBEventBuffer *s) { Init(s); }
 
-    bool WakeUp()
+    bool CreateFileDescriptor(int *ret_fd)
     {
-        return Check(m_ptr->WakeUp(m_ptr));
-    }
-
-    bool HasEvent()
-    {
-        // returns DFB_OK if there is >=1 event, DFB_BUFFEREMPTY otherwise
-        DFBResult r = m_ptr->HasEvent(m_ptr);
-
-        // NB: Check() also returns true for DFB_BUFFEREMPTY, so we can't just
-        //     return it's return value:
-        Check(r);
-        return (r == DFB_OK);
-    }
-
-    bool WaitForEventWithTimeout(unsigned secs, unsigned millisecs)
-    {
-        DFBResult r = m_ptr->WaitForEventWithTimeout(m_ptr, secs, millisecs);
-
-        // DFB_TIMEOUT is not an error in this function:
-        if ( r == DFB_TIMEOUT )
-        {
-            m_lastResult = DFB_TIMEOUT;
-            return true;
-        }
-
-        return Check(r);
-    }
-
-    bool GetEvent(wxDFBEvent& event)
-    {
-        return Check(m_ptr->GetEvent(m_ptr, &event));
+        return Check(m_ptr->CreateFileDescriptor(m_ptr, ret_fd));
     }
 };
 
index 2249fe5c2ead33be2850f96cb4c4a7bcbf5f6dd6..630676cb375c2c79d5553ad5ff49380f2a027b24 100644 (file)
@@ -23,7 +23,9 @@ typedef wxMilliClock_t wxUsecClock_t;
 // wxTimer implementation class for Unix platforms
 // ----------------------------------------------------------------------------
 
-class wxUnixTimerImpl : public wxTimerImpl
+// NB: we have to export at least this symbol from the shared library, because
+//     it's used by wxDFB's wxCore
+class WXDLLIMPEXP_BASE wxUnixTimerImpl : public wxTimerImpl
 {
 public:
     wxUnixTimerImpl(wxTimer *timer);
index 7f7807ac72fefe330aedb15e760d194a92042859..fd3f6b58187419ea1fdf6350225049b60dfd961b 100644 (file)
 #endif
 
 #include "wx/thread.h"
-#include "wx/generic/private/timer.h"
 #include "wx/private/fdiodispatcher.h"
 #include "wx/dfb/private.h"
 #include "wx/nonownedwnd.h"
+#include "wx/buffer.h"
 
 #define TRACE_EVENTS "events"
 
 // implementation
 // ===========================================================================
 
+//-----------------------------------------------------------------------------
+// wxDFBEventsHandler
+//-----------------------------------------------------------------------------
+
+// This handler is installed to process input on DirectFB's events socket (
+// obtained using CreateFileDescriptor()). When IDirectFBEventBuffer is used
+// in this mode, events are written to the file descriptor and we read them
+// in OnReadWaiting() below.
+class wxDFBEventsHandler : public wxFDIOHandler
+{
+public:
+    wxDFBEventsHandler()
+        : m_fd(-1), m_offset(0)
+    {}
+
+    void SetFD(int fd) { m_fd = fd; }
+
+    void Reset()
+    {
+        m_fd = -1;
+        m_offset = 0;
+    }
+
+    // implement wxFDIOHandler pure virtual methods
+    virtual void OnReadWaiting();
+    virtual void OnWriteWaiting()
+        { wxFAIL_MSG("OnWriteWaiting shouldn't be called"); }
+    virtual void OnExceptionWaiting()
+        { wxFAIL_MSG("OnExceptionWaiting shouldn't be called"); }
+
+private:
+    // DirectFB -> wxWidgets events translation
+    void HandleDFBEvent(const wxDFBEvent& event);
+
+    int m_fd;
+    size_t m_offset;
+    DFBEvent m_event;
+};
+
+void wxDFBEventsHandler::OnReadWaiting()
+{
+    for ( ;; )
+    {
+        int size = read(m_fd,
+                        ((char*)&m_event) + m_offset,
+                        sizeof(m_event) - m_offset);
+
+        if ( size == 0 || (size == -1 && (errno == EAGAIN || errno == EINTR)) )
+        {
+            // nothing left in the pipe (EAGAIN is expected for an FD with
+            // O_NONBLOCK)
+            break;
+        }
+
+        if ( size == -1 )
+        {
+            wxLogSysError(_("Failed to read event from DirectFB pipe"));
+            break;
+        }
+
+        size += m_offset;
+        m_offset = 0;
+
+        if ( size != sizeof(m_event) )
+        {
+            m_offset = size;
+            break;
+        }
+
+        HandleDFBEvent(m_event);
+    }
+}
+
+void wxDFBEventsHandler::HandleDFBEvent(const wxDFBEvent& event)
+{
+    switch ( event.GetClass() )
+    {
+        case DFEC_WINDOW:
+        {
+            wxDFBWindowEvent winevent(((const DFBEvent&)event).window);
+            wxNonOwnedWindow::HandleDFBWindowEvent(winevent);
+            break;
+        }
+
+        case DFEC_NONE:
+        case DFEC_INPUT:
+        case DFEC_USER:
+#if wxCHECK_DFB_VERSION(0,9,23)
+        case DFEC_UNIVERSAL:
+#endif
+        {
+            wxLogTrace(TRACE_EVENTS,
+                       "ignoring event of unsupported class %i",
+                       (int)event.GetClass());
+        }
+    }
+}
+
 //-----------------------------------------------------------------------------
 // wxEventLoop initialization
 //-----------------------------------------------------------------------------
 
 wxIDirectFBEventBufferPtr wxGUIEventLoop::ms_buffer;
+int wxGUIEventLoop::ms_bufferFd;
+static wxDFBEventsHandler gs_DFBEventsHandler;
 
 wxGUIEventLoop::wxGUIEventLoop()
 {
+    // Note that this has to be done here so that the buffer is ready when
+    // an event loop runs; GetDirectFBEventBuffer(), which also calls
+    // InitBuffer(), may be called before or after the first wxGUIEventLoop
+    // instance is created.
     if ( !ms_buffer )
         InitBuffer();
 }
@@ -52,13 +156,35 @@ wxGUIEventLoop::wxGUIEventLoop()
 /* static */
 void wxGUIEventLoop::InitBuffer()
 {
+    // create DirectFB events buffer:
     ms_buffer = wxIDirectFB::Get()->CreateEventBuffer();
+
+    // and setup a file descriptor that we can watch for new events:
+
+    ms_buffer->CreateFileDescriptor(&ms_bufferFd);
+    int flags = fcntl(ms_bufferFd, F_GETFL, 0);
+    if ( flags == -1 || fcntl(ms_bufferFd, F_SETFL, flags | O_NONBLOCK) == -1 )
+    {
+        wxLogSysError(_("Failed to switch DirectFB pipe to non-blocking mode"));
+        return;
+    }
+
+    wxFDIODispatcher *dispatcher = wxFDIODispatcher::Get();
+    wxCHECK_RET( dispatcher, "wxDFB requires wxFDIODispatcher" );
+
+    gs_DFBEventsHandler.SetFD(ms_bufferFd);
+    dispatcher->RegisterFD(ms_bufferFd, &gs_DFBEventsHandler, wxFDIO_INPUT);
 }
 
 /* static */
 void wxGUIEventLoop::CleanUp()
 {
+    wxFDIODispatcher *dispatcher = wxFDIODispatcher::Get();
+    wxCHECK_RET( dispatcher, "wxDFB requires wxFDIODispatcher" );
+    dispatcher->UnregisterFD(ms_bufferFd);
+
     ms_buffer.Reset();
+    gs_DFBEventsHandler.Reset();
 }
 
 /* static */
@@ -74,85 +200,6 @@ wxIDirectFBEventBufferPtr wxGUIEventLoop::GetDirectFBEventBuffer()
 // events dispatch and loop handling
 //-----------------------------------------------------------------------------
 
-bool wxGUIEventLoop::Pending() const
-{
-    wxCHECK_MSG( ms_buffer, false, "invalid event buffer" );
-
-    return ms_buffer->HasEvent();
-}
-
-bool wxGUIEventLoop::Dispatch()
-{
-    // NB: we don't block indefinitely waiting for an event, but instead
-    //     time out after a brief period in order to make sure that
-    //     OnNextIteration() will be called frequently enough
-    //
-    // TODO: remove this hack, instead use CreateFileDescriptor() to properly
-    //       multiplex GUI and socket input
-    const int TIMEOUT = 100;
-
-    // treat time out (-1 return value) as normal successful return so that
-    // OnNextIteration() is called
-    return !!DispatchTimeout(TIMEOUT);
-}
-
-int wxGUIEventLoop::DispatchTimeout(unsigned long timeout)
-{
-    wxCHECK_MSG( ms_buffer, 0, "invalid event buffer" );
-
-    // release the GUI mutex so that other threads have a chance to post
-    // events:
-    wxMutexGuiLeave();
-
-    bool rv = ms_buffer->WaitForEventWithTimeout(0, timeout);
-
-    // and acquire it back before calling any event handlers:
-    wxMutexGuiEnter();
-
-    if ( rv )
-    {
-        switch ( ms_buffer->GetLastResult() )
-        {
-            case DFB_OK:
-            {
-                wxDFBEvent e;
-                ms_buffer->GetEvent(e);
-                HandleDFBEvent(e);
-                break;
-            }
-
-            case DFB_TIMEOUT:
-                return -1;
-
-            default:
-                // don't terminate the loop due to errors (they were reported
-                // already by ms_buffer)
-                break;
-        }
-    }
-
-    return 1;
-}
-
-void wxGUIEventLoop::WakeUp()
-{
-    wxCHECK_RET( ms_buffer, "invalid event buffer" );
-
-    ms_buffer->WakeUp();
-}
-
-void wxGUIEventLoop::OnNextIteration()
-{
-#if wxUSE_TIMER
-    wxGenericTimerImpl::NotifyTimers();
-#endif
-
-#if wxUSE_SOCKETS
-    // handle any pending socket events:
-    wxFDIODispatcher::DispatchPending();
-#endif
-}
-
 void wxGUIEventLoop::Yield()
 {
     // process all pending events:
@@ -162,33 +209,3 @@ void wxGUIEventLoop::Yield()
     // handle timers, sockets etc.
     OnNextIteration();
 }
-
-
-//-----------------------------------------------------------------------------
-// DirectFB -> wxWidgets events translation
-//-----------------------------------------------------------------------------
-
-void wxGUIEventLoop::HandleDFBEvent(const wxDFBEvent& event)
-{
-    switch ( event.GetClass() )
-    {
-        case DFEC_WINDOW:
-        {
-            wxDFBWindowEvent winevent(((const DFBEvent&)event).window);
-            wxNonOwnedWindow::HandleDFBWindowEvent(winevent);
-            break;
-        }
-
-        case DFEC_NONE:
-        case DFEC_INPUT:
-        case DFEC_USER:
-#if wxCHECK_DFB_VERSION(0,9,23)
-        case DFEC_UNIVERSAL:
-#endif
-        {
-            wxLogTrace(TRACE_EVENTS,
-                       "ignoring event of unsupported class %i",
-                       (int)event.GetClass());
-        }
-    }
-}
index df106bdad30cebbae716b17e7e3e719c24d7e32f..da9e2baa9d582ed0e07a67f6630fc636373d8d98 100644 (file)
@@ -19,6 +19,7 @@
 #include "wx/evtloop.h"
 #include "wx/apptrait.h"
 #include "wx/unix/execute.h"
+#include "wx/unix/private/timer.h"
 
 #ifndef WX_PRECOMP
     #include "wx/app.h"
@@ -45,6 +46,11 @@ wxEventLoopBase* wxGUIAppTraits::CreateEventLoop()
     return new wxEventLoop;
 }
 
+wxTimerImpl *wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
+{
+    return new wxUnixTimerImpl(timer);
+}
+
 // ----------------------------------------------------------------------------
 // display characteristics
 // ----------------------------------------------------------------------------