1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
11 #pragma implementation "app.h"
15 #include "wx/gdicmn.h"
19 #include "wx/memory.h"
21 #include "wx/settings.h"
22 #include "wx/dialog.h"
24 #if wxUSE_WX_RESOURCES
25 #include "wx/resource.h"
28 #include "wx/module.h"
31 #ifdef __WXUNIVERSAL__
32 #include "wx/univ/theme.h"
33 #include "wx/univ/renderer.h"
37 #include "wx/thread.h"
41 #include "wx/gtk/win_gtk.h"
46 //-----------------------------------------------------------------------------
48 //-----------------------------------------------------------------------------
50 wxApp
*wxTheApp
= (wxApp
*) NULL
;
51 wxAppInitializerFunction
wxAppBase::m_appInitFn
= (wxAppInitializerFunction
) NULL
;
55 bool g_mainThreadLocked
= FALSE
;
56 gint g_pendingTag
= 0;
58 static GtkWidget
*gs_RootWindow
= (GtkWidget
*) NULL
;
60 //-----------------------------------------------------------------------------
62 //-----------------------------------------------------------------------------
64 /* forward declaration */
65 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) );
66 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) );
67 void wxapp_install_idle_handler();
70 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) );
73 //-----------------------------------------------------------------------------
75 //-----------------------------------------------------------------------------
82 //-----------------------------------------------------------------------------
84 //-----------------------------------------------------------------------------
86 static bool gs_inYield
= FALSE
;
91 if ( !wxThread::IsMain() )
93 // can't call gtk_main_iteration() from other threads like this
96 #endif // wxUSE_THREADS
100 wxFAIL_MSG( wxT("wxYield called recursively" ) );
107 // We need to remove idle callbacks or the loop will
109 gtk_idle_remove( wxTheApp
->m_idleTag
);
110 wxTheApp
->m_idleTag
= 0;
114 // disable log flushing from here because a call to wxYield() shouldn't
115 // normally result in message boxes popping up &c
118 while (gtk_events_pending())
119 gtk_main_iteration();
121 /* it's necessary to call ProcessIdle() to update the frames sizes which
122 might have been changed (it also will update other things set from
123 OnUpdateUI() which is a nice (and desired) side effect) */
124 while (wxTheApp
->ProcessIdle()) { }
126 // let the logs be flashed again
134 //-----------------------------------------------------------------------------
136 // Like wxYield, but fails silently if the yield is recursive.
137 //-----------------------------------------------------------------------------
139 bool wxYieldIfNeeded()
147 //-----------------------------------------------------------------------------
149 //-----------------------------------------------------------------------------
154 if (!wxThread::IsMain())
159 wxapp_install_idle_handler();
162 if (!wxThread::IsMain())
167 //-----------------------------------------------------------------------------
169 //-----------------------------------------------------------------------------
171 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) )
173 if (!wxTheApp
) return TRUE
;
175 // when getting called from GDK's time-out handler
176 // we are no longer within GDK's grab on the GUI
177 // thread so we must lock it here ourselves
180 // Sent idle event to all who request them
181 wxTheApp
->ProcessPendingEvents();
185 /* flush the logged messages if any */
187 wxLog::FlushActive();
190 // Release lock again
193 // Return FALSE to indicate that no more idle events are
194 // to be sent (single shot instead of continuous stream)
198 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
204 // don't generate the idle events while the assert modal dialog is shown,
205 // this completely confuses the apps which don't expect to be reentered
206 // from some safely-looking functions
207 if ( wxTheApp
->IsInAssert() )
211 #endif // __WXDEBUG__
213 // when getting called from GDK's time-out handler
214 // we are no longer within GDK's grab on the GUI
215 // thread so we must lock it here ourselves
218 /* Indicate that we are now in idle mode - even so deeply
219 in idle mode that we don't get any idle events anymore.
220 this is like wxMSW where an idle event is sent only
221 once each time after the event queue has been completely
224 wxTheApp
->m_idleTag
= 0;
226 // Sent idle event to all who request them as long as they do
227 while (wxTheApp
->ProcessIdle())
230 // Release lock again
233 // Return FALSE to indicate that no more idle events are
234 // to be sent (single shot instead of continuous stream)
238 void wxapp_install_idle_handler()
240 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, wxT("attempt to install idle handler twice") );
244 if (g_pendingTag
== 0)
245 g_pendingTag
= gtk_idle_add_priority( 900, wxapp_pending_callback
, (gpointer
) NULL
);
247 /* This routine gets called by all event handlers
248 indicating that the idle is over. It may also
249 get called from other thread for sending events
250 to the main thread (and processing these in
251 idle time). Very low priority. */
253 wxTheApp
->m_idleTag
= gtk_idle_add_priority( 1000, wxapp_idle_callback
, (gpointer
) NULL
);
258 static int g_threadUninstallLevel
= 0;
260 void wxapp_install_thread_wakeup()
262 g_threadUninstallLevel
++;
264 if (g_threadUninstallLevel
!= 1) return;
266 if (wxTheApp
->m_wakeUpTimerTag
) return;
268 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 50, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
271 void wxapp_uninstall_thread_wakeup()
273 g_threadUninstallLevel
--;
275 if (g_threadUninstallLevel
!= 0) return;
277 if (!wxTheApp
->m_wakeUpTimerTag
) return;
279 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
280 wxTheApp
->m_wakeUpTimerTag
= 0;
283 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
285 // when getting called from GDK's time-out handler
286 // we are no longer within GDK's grab on the GUI
287 // thread so we must lock it here ourselves
290 wxapp_uninstall_thread_wakeup();
292 // unblock other threads wishing to do some GUI things
295 g_mainThreadLocked
= TRUE
;
297 // wake up other threads
300 // block other thread again
303 g_mainThreadLocked
= FALSE
;
305 wxapp_install_thread_wakeup();
307 // release lock again
313 #endif // wxUSE_THREADS
315 //-----------------------------------------------------------------------------
317 //-----------------------------------------------------------------------------
319 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
321 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
322 EVT_IDLE(wxApp::OnIdle
)
327 m_initialized
= FALSE
;
329 m_isInAssert
= FALSE
;
330 #endif // __WXDEBUG__
333 wxapp_install_idle_handler();
336 m_wakeUpTimerTag
= 0;
337 wxapp_install_thread_wakeup();
340 m_colorCube
= (unsigned char*) NULL
;
342 // this is NULL for a "regular" wxApp, but is set (and freed) by a wxGLApp
343 m_glVisualInfo
= (void *) NULL
;
348 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
351 wxapp_uninstall_thread_wakeup();
354 if (m_colorCube
) free(m_colorCube
);
357 bool wxApp::OnInitGui()
359 if ( !wxAppBase::OnInitGui() )
362 GdkVisual
*visual
= gdk_visual_get_system();
364 // if this is a wxGLApp (derived from wxApp), and we've already
365 // chosen a specific visual, then derive the GdkVisual from that
366 if (m_glVisualInfo
!= NULL
) {
368 /* seems gtk_widget_set_default_visual no longer exists? */
369 GdkVisual
* vis
= gtk_widget_get_default_visual();
371 GdkVisual
* vis
= gdkx_visual_get(
372 ((XVisualInfo
*) m_glVisualInfo
) ->visualid
);
373 gtk_widget_set_default_visual( vis
);
376 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
377 gtk_widget_set_default_colormap( colormap
);
382 /* on some machines, the default visual is just 256 colours, so
383 we make sure we get the best. this can sometimes be wasteful,
384 of course, but what do these guys pay $30.000 for? */
386 else if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
390 /* seems gtk_widget_set_default_visual no longer exists? */
391 GdkVisual
* vis
= gtk_widget_get_default_visual();
393 GdkVisual
* vis
= gdk_visual_get_best();
394 gtk_widget_set_default_visual( vis
);
397 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
398 gtk_widget_set_default_colormap( colormap
);
403 /* Nothing to do for 15, 16, 24, 32 bit displays */
404 if (visual
->depth
> 8) return TRUE
;
406 /* initialize color cube for 8-bit color reduction dithering */
408 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
410 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
412 for (int r
= 0; r
< 32; r
++)
414 for (int g
= 0; g
< 32; g
++)
416 for (int b
= 0; b
< 32; b
++)
418 int rr
= (r
<< 3) | (r
>> 2);
419 int gg
= (g
<< 3) | (g
>> 2);
420 int bb
= (b
<< 3) | (b
>> 2);
424 GdkColor
*colors
= cmap
->colors
;
429 for (int i
= 0; i
< cmap
->size
; i
++)
431 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
432 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
433 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
434 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
437 index
= i
; max
= sum
;
443 #if (GTK_MINOR_VERSION > 0)
444 /* assume 8-bit true or static colors. this really
446 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
447 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
448 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
449 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
451 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
454 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
462 bool wxApp::ProcessIdle()
465 event
.SetEventObject( this );
466 ProcessEvent( event
);
468 return event
.MoreRequested();
471 void wxApp::OnIdle( wxIdleEvent
&event
)
473 static bool s_inOnIdle
= FALSE
;
475 /* Avoid recursion (via ProcessEvent default case) */
481 /* Resend in the main thread events which have been prepared in other
483 ProcessPendingEvents();
485 /* 'Garbage' collection of windows deleted with Close(). */
486 DeletePendingObjects();
488 /* Send OnIdle events to all windows */
489 bool needMore
= SendIdleEvents();
492 event
.RequestMore(TRUE
);
497 bool wxApp::SendIdleEvents()
499 bool needMore
= FALSE
;
501 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
504 wxWindow
* win
= node
->GetData();
505 if (SendIdleEvents(win
))
507 node
= node
->GetNext();
513 bool wxApp::SendIdleEvents( wxWindow
* win
)
515 bool needMore
= FALSE
;
518 event
.SetEventObject(win
);
520 win
->GetEventHandler()->ProcessEvent(event
);
522 win
->OnInternalIdle();
524 if (event
.MoreRequested())
527 wxNode
* node
= win
->GetChildren().First();
530 wxWindow
* win
= (wxWindow
*) node
->Data();
531 if (SendIdleEvents(win
))
539 int wxApp::MainLoop()
545 void wxApp::ExitMainLoop()
547 if (gtk_main_level() > 0)
551 bool wxApp::Initialized()
553 return m_initialized
;
556 bool wxApp::Pending()
558 return (gtk_events_pending() > 0);
561 void wxApp::Dispatch()
563 gtk_main_iteration();
566 void wxApp::DeletePendingObjects()
568 wxNode
*node
= wxPendingDelete
.First();
571 wxObject
*obj
= (wxObject
*)node
->Data();
575 if (wxPendingDelete
.Find(obj
))
578 node
= wxPendingDelete
.First();
582 bool wxApp::Initialize()
584 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
586 wxClassInfo::InitializeClasses();
588 wxSystemSettings::Init();
591 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
594 // GL: I'm annoyed ... I don't know where to put this and I don't want to
595 // create a module for that as it's part of the core.
597 wxPendingEvents
= new wxList();
598 wxPendingEventsLocker
= new wxCriticalSection();
601 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
602 wxTheColourDatabase
->Initialize();
604 wxInitializeStockLists();
605 wxInitializeStockObjects();
607 #if wxUSE_WX_RESOURCES
608 wxInitializeResourceSystem();
611 wxModule::RegisterModules();
612 if (!wxModule::InitializeModules()) return FALSE
;
617 void wxApp::CleanUp()
619 wxModule::CleanUpModules();
621 #if wxUSE_WX_RESOURCES
622 wxCleanUpResourceSystem();
625 if (wxTheColourDatabase
)
626 delete wxTheColourDatabase
;
628 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
630 wxDeleteStockObjects();
632 wxDeleteStockLists();
635 wxTheApp
= (wxApp
*) NULL
;
637 // GL: I'm annoyed ... I don't know where to put this and I don't want to
638 // create a module for that as it's part of the core.
640 delete wxPendingEvents
;
641 delete wxPendingEventsLocker
;
644 wxSystemSettings::Done();
648 wxClassInfo::CleanUpClasses();
650 // check for memory leaks
651 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
652 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
654 wxLogDebug(wxT("There were memory leaks.\n"));
655 wxDebugContext::Dump();
656 wxDebugContext::PrintStatistics();
661 // do this as the very last thing because everything else can log messages
662 wxLog::DontCreateOnDemand();
664 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
670 //-----------------------------------------------------------------------------
671 // Access to the root window global
672 //-----------------------------------------------------------------------------
674 GtkWidget
* wxGetRootWindow()
676 if (gs_RootWindow
== NULL
) {
677 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
678 gtk_widget_realize( gs_RootWindow
);
680 return gs_RootWindow
;
683 //-----------------------------------------------------------------------------
685 //-----------------------------------------------------------------------------
688 int wxEntryStart( int argc
, char *argv
[] )
691 /* GTK 1.2 up to version 1.2.3 has broken threads */
692 if ((gtk_major_version
== 1) &&
693 (gtk_minor_version
== 2) &&
694 (gtk_micro_version
< 4))
696 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
706 // We should have the wxUSE_WCHAR_T test on the _outside_
708 #if defined(__WXGTK20__)
709 // gtk+ 2.0 supports Unicode through UTF-8 strings
710 wxConvCurrent
= &wxConvUTF8
;
712 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
715 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
720 gtk_init( &argc
, &argv
);
722 wxSetDetectableAutoRepeat( TRUE
);
724 if (!wxApp::Initialize())
738 if ( !wxTheApp
->OnInitGui() )
747 void wxEntryCleanup()
750 // flush the logged messages if any
751 wxLog
*log
= wxLog::GetActiveTarget();
752 if (log
!= NULL
&& log
->HasPendingMessages())
755 // continuing to use user defined log target is unsafe from now on because
756 // some resources may be already unavailable, so replace it by something
758 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
770 int wxEntry( int argc
, char *argv
[] )
772 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
773 // This seems to be necessary since there are 'rogue'
774 // objects present at this point (perhaps global objects?)
775 // Setting a checkpoint will ignore them as far as the
776 // memory checking facility is concerned.
777 // Of course you may argue that memory allocated in globals should be
778 // checked, but this is a reasonable compromise.
779 wxDebugContext::SetCheckpoint();
781 int err
= wxEntryStart(argc
, argv
);
787 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
788 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
790 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
792 wxObject
*test_app
= app_ini();
794 wxTheApp
= (wxApp
*) test_app
;
797 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
799 wxTheApp
->argc
= argc
;
801 wxTheApp
->argv
= new wxChar
*[argc
+1];
803 while (mb_argc
< argc
)
805 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
808 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
810 wxTheApp
->argv
= argv
;
813 wxString
name(wxFileNameFromPath(argv
[0]));
814 wxStripExtension( name
);
815 wxTheApp
->SetAppName( name
);
818 retValue
= wxEntryInitGui();
820 // Here frames insert themselves automatically into wxTopLevelWindows by
821 // getting created in OnInit().
824 if ( !wxTheApp
->OnInit() )
830 /* delete pending toplevel windows (typically a single
831 dialog) so that, if there isn't any left, we don't
833 wxTheApp
->DeletePendingObjects();
835 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
837 if (wxTheApp
->Initialized())
841 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
844 /* Forcibly delete the window. */
845 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
846 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
848 topWindow
->Close( TRUE
);
849 wxTheApp
->DeletePendingObjects();
854 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
858 retValue
= wxTheApp
->OnExit();
867 #ifndef __WXUNIVERSAL__
869 #include "wx/gtk/info.xpm"
870 #include "wx/gtk/error.xpm"
871 #include "wx/gtk/question.xpm"
872 #include "wx/gtk/warning.xpm"
874 wxIcon
wxApp::GetStdIcon(int which
) const
878 case wxICON_INFORMATION
:
879 return wxIcon(info_xpm
);
881 case wxICON_QUESTION
:
882 return wxIcon(question_xpm
);
884 case wxICON_EXCLAMATION
:
885 return wxIcon(warning_xpm
);
888 wxFAIL_MSG(wxT("requested non existent standard icon"));
889 // still fall through
892 return wxIcon(error_xpm
);
896 wxIcon
wxApp::GetStdIcon(int which
) const
898 return wxTheme::Get()->GetRenderer()->GetStdIcon(which
);
900 #endif // !__WXUNIVERSAL__
905 void wxApp::OnAssert(const wxChar
*file
, int line
, const wxChar
*msg
)
909 wxAppBase::OnAssert(file
, line
, msg
);
911 m_isInAssert
= FALSE
;
914 #endif // __WXDEBUG__