1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk/evtloop.cpp 
   3 // Purpose:     implements wxEventLoop for GTK+ 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  27 #include "wx/evtloop.h" 
  28 #include "wx/evtloopsrc.h" 
  38 // ============================================================================ 
  39 // wxEventLoop implementation 
  40 // ============================================================================ 
  42 extern GtkWidget 
*wxGetRootWindow(); 
  44 // ---------------------------------------------------------------------------- 
  45 // wxEventLoop running and exiting 
  46 // ---------------------------------------------------------------------------- 
  48 wxGUIEventLoop::wxGUIEventLoop() 
  53 int wxGUIEventLoop::Run() 
  55     // event loops are not recursive, you need to create another loop! 
  56     wxCHECK_MSG( !IsRunning(), -1, "can't reenter a message loop" ); 
  58     wxEventLoopActivator 
activate(this); 
  67 void wxGUIEventLoop::Exit(int rc
) 
  69     wxCHECK_RET( IsRunning(), "can't call Exit() if not running" ); 
  76 void wxGUIEventLoop::WakeUp() 
  78     // TODO: idle events handling should really be done by wxEventLoop itself 
  79     //       but for now it's completely in gtk/app.cpp so just call there when 
  80     //       we have wxTheApp and hope that it doesn't matter that we do 
  81     //       nothing when we don't... 
  83         wxTheApp
->WakeUpIdle(); 
  86 // ---------------------------------------------------------------------------- 
  87 // wxEventLoop adding & removing sources 
  88 // ---------------------------------------------------------------------------- 
  90 #if wxUSE_EVENTLOOP_SOURCE 
  94 static gboolean 
wx_on_channel_event(GIOChannel 
* WXUNUSED_UNLESS_DEBUG(channel
), 
  95                                     GIOCondition condition
, 
  98     wxLogTrace(wxTRACE_EVT_SOURCE
, 
  99                "wx_on_channel_event, fd=%d, condition=%08x", 
 100                g_io_channel_unix_get_fd(channel
), condition
); 
 102     wxEventLoopSourceHandler 
* const 
 103         handler 
= static_cast<wxEventLoopSourceHandler 
*>(data
); 
 105     if (condition 
& G_IO_IN 
|| condition 
& G_IO_PRI
) 
 106         handler
->OnReadWaiting(); 
 107     if (condition 
& G_IO_OUT
) 
 108         handler
->OnWriteWaiting(); 
 109     else if (condition 
& G_IO_ERR 
|| condition 
& G_IO_NVAL
) 
 110         handler
->OnExceptionWaiting(); 
 112     // we never want to remove source here, so always return true 
 118 wxGUIEventLoop::AddSourceForFD(int fd
, 
 119                                wxEventLoopSourceHandler 
*handler
, 
 122     wxCHECK_MSG( fd 
!= -1, NULL
, "can't monitor invalid fd" ); 
 125     if (flags 
& wxEVENT_SOURCE_INPUT
) 
 126         condition 
|= G_IO_IN 
| G_IO_PRI
; 
 127     if (flags 
& wxEVENT_SOURCE_OUTPUT
) 
 128         condition 
|= G_IO_OUT
; 
 129     if (flags 
& wxEVENT_SOURCE_EXCEPTION
) 
 130         condition 
|= G_IO_ERR 
| G_IO_HUP 
| G_IO_NVAL
; 
 132     GIOChannel
* channel 
= g_io_channel_unix_new(fd
); 
 133     const unsigned sourceId  
= g_io_add_watch
 
 136                                 (GIOCondition
)condition
, 
 137                                 &wx_on_channel_event
, 
 140     // it was ref'd by g_io_add_watch() so we can unref it here 
 141     g_io_channel_unref(channel
); 
 146     wxLogTrace(wxTRACE_EVT_SOURCE
, 
 147                "Adding event loop source for fd=%d with GTK id=%u", 
 151     return new wxGTKEventLoopSource(sourceId
, handler
, flags
); 
 154 wxGTKEventLoopSource::~wxGTKEventLoopSource() 
 156     wxLogTrace(wxTRACE_EVT_SOURCE
, 
 157                "Removing event loop source with GTK id=%u", m_sourceId
); 
 159     g_source_remove(m_sourceId
); 
 162 #endif // wxUSE_EVENTLOOP_SOURCE 
 164 // ---------------------------------------------------------------------------- 
 165 // wxEventLoop message processing dispatching 
 166 // ---------------------------------------------------------------------------- 
 168 bool wxGUIEventLoop::Pending() const 
 172         // this avoids false positives from our idle source 
 173         return wxTheApp
->EventsPending(); 
 176     return gtk_events_pending() != 0; 
 179 bool wxGUIEventLoop::Dispatch() 
 181     wxCHECK_MSG( IsRunning(), false, wxT("can't call Dispatch() if not running") ); 
 183     // gtk_main_iteration() returns TRUE only if gtk_main_quit() was called 
 184     return !gtk_main_iteration(); 
 188 static gboolean 
wx_event_loop_timeout(void* data
) 
 190     bool* expired 
= static_cast<bool*>(data
); 
 193     // return FALSE to remove this timeout 
 198 int wxGUIEventLoop::DispatchTimeout(unsigned long timeout
) 
 200     bool expired 
= false; 
 201     const unsigned id 
= g_timeout_add(timeout
, wx_event_loop_timeout
, &expired
); 
 202     bool quit 
= gtk_main_iteration() != 0; 
 212 //----------------------------------------------------------------------------- 
 214 //----------------------------------------------------------------------------- 
 217 static void wxgtk_main_do_event(GdkEvent
* event
, void* data
) 
 219     // categorize the GDK event according to wxEventCategory. 
 220     // See http://library.gnome.org/devel/gdk/unstable/gdk-Events.html#GdkEventType 
 223     // NOTE: GDK_* constants which were not present in the GDK2.0 can be tested for 
 224     //       only at compile-time; when running the program (compiled with a recent GDK) 
 225     //       on a system with an older GDK lib we can be sure there won't be problems 
 226     //       because event->type will never assume those values corresponding to 
 227     //       new event types (since new event types are always added in GDK with non 
 228     //       conflicting values for ABI compatibility). 
 230     wxEventCategory cat 
= wxEVT_CATEGORY_UNKNOWN
; 
 233     case GDK_SELECTION_REQUEST
: 
 234     case GDK_SELECTION_NOTIFY
: 
 235     case GDK_SELECTION_CLEAR
: 
 236 #if GTK_CHECK_VERSION(2,6,0) 
 237     case GDK_OWNER_CHANGE
: 
 239         cat 
= wxEVT_CATEGORY_CLIPBOARD
; 
 243     case GDK_KEY_RELEASE
: 
 244     case GDK_BUTTON_PRESS
: 
 245     case GDK_2BUTTON_PRESS
: 
 246     case GDK_3BUTTON_PRESS
: 
 247     case GDK_BUTTON_RELEASE
: 
 248     case GDK_SCROLL
:        // generated from mouse buttons 
 249     case GDK_CLIENT_EVENT
: 
 250         cat 
= wxEVT_CATEGORY_USER_INPUT
; 
 253     case GDK_PROXIMITY_IN
: 
 254     case GDK_PROXIMITY_OUT
: 
 256     case GDK_MOTION_NOTIFY
: 
 257     case GDK_ENTER_NOTIFY
: 
 258     case GDK_LEAVE_NOTIFY
: 
 259     case GDK_VISIBILITY_NOTIFY
: 
 260     case GDK_PROPERTY_NOTIFY
: 
 262     case GDK_FOCUS_CHANGE
: 
 264     case GDK_WINDOW_STATE
: 
 276     case GDK_DRAG_MOTION
: 
 277     case GDK_DRAG_STATUS
: 
 279     case GDK_DROP_FINISHED
: 
 280 #if GTK_CHECK_VERSION(2,8,0) 
 281     case GDK_GRAB_BROKEN
: 
 283 #if GTK_CHECK_VERSION(2,14,0) 
 286         cat 
= wxEVT_CATEGORY_UI
; 
 290         cat 
= wxEVT_CATEGORY_UNKNOWN
; 
 294     wxGUIEventLoop
* evtloop 
= static_cast<wxGUIEventLoop
*>(data
); 
 296     // is this event allowed now? 
 297     if (evtloop
->IsEventAllowedInsideYield(cat
)) 
 298         gtk_main_do_event(event
);         // process it now 
 299     else if (event
->type 
!= GDK_NOTHING
) 
 300         evtloop
->StoreGdkEventForLaterProcessing(gdk_event_copy(event
)); 
 301             // process it later (but make a copy; the caller will free the event pointer) 
 305 bool wxGUIEventLoop::YieldFor(long eventsToProcess
) 
 308     if ( !wxThread::IsMain() ) 
 310         // can't call gtk_main_iteration() from other threads like this 
 313 #endif // wxUSE_THREADS 
 315     m_isInsideYield 
= true; 
 316     m_eventsToProcessInsideYield 
= eventsToProcess
; 
 319     // disable log flushing from here because a call to wxYield() shouldn't 
 320     // normally result in message boxes popping up &c 
 324     // temporarily replace the global GDK event handler with our function, which 
 325     // categorizes the events and using m_eventsToProcessInsideYield decides 
 326     // if an event should be processed immediately or not 
 327     // NOTE: this approach is better than using gdk_display_get_event() because 
 328     //       gtk_main_iteration() does more than just calling gdk_display_get_event() 
 329     //       and then call gtk_main_do_event()! 
 330     //       In particular in this way we also process input from sources like 
 331     //       GIOChannels (this is needed for e.g. wxGUIAppTraits::WaitForChild). 
 332     gdk_event_handler_set(wxgtk_main_do_event
, this, NULL
); 
 333     while (Pending())   // avoid false positives from our idle source 
 334         gtk_main_iteration(); 
 335     gdk_event_handler_set ((GdkEventFunc
)gtk_main_do_event
, NULL
, NULL
); 
 337     if (eventsToProcess 
!= wxEVT_CATEGORY_CLIPBOARD
) 
 339         // It's necessary to call ProcessIdle() to update the frames sizes which 
 340         // might have been changed (it also will update other things set from 
 341         // OnUpdateUI() which is a nice (and desired) side effect). But we 
 342         // call ProcessIdle() only once since this is not meant for longish 
 343         // background jobs (controlled by wxIdleEvent::RequestMore() and the 
 344         // return value of Processidle(). 
 345         ProcessIdle();      // ProcessIdle() also calls ProcessPendingEvents() 
 347     //else: if we are inside ~wxClipboardSync() and we call ProcessIdle() and 
 348     //      the user app contains an UI update handler which calls wxClipboard::IsSupported, 
 349     //      then we fall into a never-ending loop... 
 351     // put all unprocessed GDK events back in the queue 
 352     GdkDisplay
* disp 
= gtk_widget_get_display(wxGetRootWindow()); 
 353     for (size_t i
=0; i
<m_arrGdkEvents
.GetCount(); i
++) 
 355         GdkEvent
* ev 
= (GdkEvent
*)m_arrGdkEvents
[i
]; 
 357         // NOTE: gdk_display_put_event makes a copy of the event passed to it 
 358         gdk_display_put_event(disp
, ev
); 
 362     m_arrGdkEvents
.Clear(); 
 365     // let the logs be flashed again 
 369     m_isInsideYield 
= false;