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 bool wxApp::Yield(bool onlyIfNeeded
)
89 static bool s_inYield
= FALSE
;
95 wxFAIL_MSG( wxT("wxYield called recursively" ) );
102 if ( !wxThread::IsMain() )
104 // can't call gtk_main_iteration() from other threads like this
107 #endif // wxUSE_THREADS
113 // We need to remove idle callbacks or the loop will
115 gtk_idle_remove( m_idleTag
);
120 // disable log flushing from here because a call to wxYield() shouldn't
121 // normally result in message boxes popping up &c
124 while (gtk_events_pending())
125 gtk_main_iteration();
127 /* it's necessary to call ProcessIdle() to update the frames sizes which
128 might have been changed (it also will update other things set from
129 OnUpdateUI() which is a nice (and desired) side effect) */
130 while ( ProcessIdle() )
134 // let the logs be flashed again
142 //-----------------------------------------------------------------------------
144 //-----------------------------------------------------------------------------
149 if (!wxThread::IsMain())
154 wxapp_install_idle_handler();
157 if (!wxThread::IsMain())
162 //-----------------------------------------------------------------------------
164 //-----------------------------------------------------------------------------
166 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) )
168 if (!wxTheApp
) return TRUE
;
170 // when getting called from GDK's time-out handler
171 // we are no longer within GDK's grab on the GUI
172 // thread so we must lock it here ourselves
175 // Sent idle event to all who request them
176 wxTheApp
->ProcessPendingEvents();
180 /* flush the logged messages if any */
182 wxLog::FlushActive();
185 // Release lock again
188 // Return FALSE to indicate that no more idle events are
189 // to be sent (single shot instead of continuous stream)
193 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
199 // don't generate the idle events while the assert modal dialog is shown,
200 // this completely confuses the apps which don't expect to be reentered
201 // from some safely-looking functions
202 if ( wxTheApp
->IsInAssert() )
206 #endif // __WXDEBUG__
208 // when getting called from GDK's time-out handler
209 // we are no longer within GDK's grab on the GUI
210 // thread so we must lock it here ourselves
213 /* Indicate that we are now in idle mode - even so deeply
214 in idle mode that we don't get any idle events anymore.
215 this is like wxMSW where an idle event is sent only
216 once each time after the event queue has been completely
219 wxTheApp
->m_idleTag
= 0;
221 // Sent idle event to all who request them as long as they do
222 while (wxTheApp
->ProcessIdle())
225 // Release lock again
228 // Return FALSE to indicate that no more idle events are
229 // to be sent (single shot instead of continuous stream)
233 void wxapp_install_idle_handler()
235 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, wxT("attempt to install idle handler twice") );
239 if (g_pendingTag
== 0)
240 g_pendingTag
= gtk_idle_add_priority( 900, wxapp_pending_callback
, (gpointer
) NULL
);
242 /* This routine gets called by all event handlers
243 indicating that the idle is over. It may also
244 get called from other thread for sending events
245 to the main thread (and processing these in
246 idle time). Very low priority. */
248 wxTheApp
->m_idleTag
= gtk_idle_add_priority( 1000, wxapp_idle_callback
, (gpointer
) NULL
);
253 static int g_threadUninstallLevel
= 0;
255 void wxapp_install_thread_wakeup()
257 g_threadUninstallLevel
++;
259 if (g_threadUninstallLevel
!= 1) return;
261 if (wxTheApp
->m_wakeUpTimerTag
) return;
263 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 50, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
266 void wxapp_uninstall_thread_wakeup()
268 g_threadUninstallLevel
--;
270 if (g_threadUninstallLevel
!= 0) return;
272 if (!wxTheApp
->m_wakeUpTimerTag
) return;
274 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
275 wxTheApp
->m_wakeUpTimerTag
= 0;
278 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
280 // when getting called from GDK's time-out handler
281 // we are no longer within GDK's grab on the GUI
282 // thread so we must lock it here ourselves
285 wxapp_uninstall_thread_wakeup();
287 // unblock other threads wishing to do some GUI things
290 g_mainThreadLocked
= TRUE
;
292 // wake up other threads
295 // block other thread again
298 g_mainThreadLocked
= FALSE
;
300 wxapp_install_thread_wakeup();
302 // release lock again
308 #endif // wxUSE_THREADS
310 //-----------------------------------------------------------------------------
312 //-----------------------------------------------------------------------------
314 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
316 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
317 EVT_IDLE(wxApp::OnIdle
)
322 m_initialized
= FALSE
;
324 m_isInAssert
= FALSE
;
325 #endif // __WXDEBUG__
328 wxapp_install_idle_handler();
331 m_wakeUpTimerTag
= 0;
332 wxapp_install_thread_wakeup();
335 m_colorCube
= (unsigned char*) NULL
;
337 // this is NULL for a "regular" wxApp, but is set (and freed) by a wxGLApp
338 m_glVisualInfo
= (void *) NULL
;
343 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
346 wxapp_uninstall_thread_wakeup();
349 if (m_colorCube
) free(m_colorCube
);
352 bool wxApp::OnInitGui()
354 if ( !wxAppBase::OnInitGui() )
357 GdkVisual
*visual
= gdk_visual_get_system();
359 // if this is a wxGLApp (derived from wxApp), and we've already
360 // chosen a specific visual, then derive the GdkVisual from that
361 if (m_glVisualInfo
!= NULL
) {
363 /* seems gtk_widget_set_default_visual no longer exists? */
364 GdkVisual
* vis
= gtk_widget_get_default_visual();
366 GdkVisual
* vis
= gdkx_visual_get(
367 ((XVisualInfo
*) m_glVisualInfo
) ->visualid
);
368 gtk_widget_set_default_visual( vis
);
371 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
372 gtk_widget_set_default_colormap( colormap
);
377 /* on some machines, the default visual is just 256 colours, so
378 we make sure we get the best. this can sometimes be wasteful,
379 of course, but what do these guys pay $30.000 for? */
381 else if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
385 /* seems gtk_widget_set_default_visual no longer exists? */
386 GdkVisual
* vis
= gtk_widget_get_default_visual();
388 GdkVisual
* vis
= gdk_visual_get_best();
389 gtk_widget_set_default_visual( vis
);
392 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
393 gtk_widget_set_default_colormap( colormap
);
398 /* Nothing to do for 15, 16, 24, 32 bit displays */
399 if (visual
->depth
> 8) return TRUE
;
401 /* initialize color cube for 8-bit color reduction dithering */
403 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
405 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
407 for (int r
= 0; r
< 32; r
++)
409 for (int g
= 0; g
< 32; g
++)
411 for (int b
= 0; b
< 32; b
++)
413 int rr
= (r
<< 3) | (r
>> 2);
414 int gg
= (g
<< 3) | (g
>> 2);
415 int bb
= (b
<< 3) | (b
>> 2);
419 GdkColor
*colors
= cmap
->colors
;
424 for (int i
= 0; i
< cmap
->size
; i
++)
426 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
427 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
428 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
429 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
432 index
= i
; max
= sum
;
438 #if (GTK_MINOR_VERSION > 0)
439 /* assume 8-bit true or static colors. this really
441 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
442 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
443 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
444 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
446 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
449 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
457 bool wxApp::ProcessIdle()
460 event
.SetEventObject( this );
461 ProcessEvent( event
);
463 return event
.MoreRequested();
466 void wxApp::OnIdle( wxIdleEvent
&event
)
468 static bool s_inOnIdle
= FALSE
;
470 /* Avoid recursion (via ProcessEvent default case) */
476 /* Resend in the main thread events which have been prepared in other
478 ProcessPendingEvents();
480 /* 'Garbage' collection of windows deleted with Close(). */
481 DeletePendingObjects();
483 /* Send OnIdle events to all windows */
484 bool needMore
= SendIdleEvents();
487 event
.RequestMore(TRUE
);
492 bool wxApp::SendIdleEvents()
494 bool needMore
= FALSE
;
496 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
499 wxWindow
* win
= node
->GetData();
500 if (SendIdleEvents(win
))
502 node
= node
->GetNext();
508 bool wxApp::SendIdleEvents( wxWindow
* win
)
510 bool needMore
= FALSE
;
513 event
.SetEventObject(win
);
515 win
->GetEventHandler()->ProcessEvent(event
);
517 win
->OnInternalIdle();
519 if (event
.MoreRequested())
522 wxNode
* node
= win
->GetChildren().First();
525 wxWindow
* win
= (wxWindow
*) node
->Data();
526 if (SendIdleEvents(win
))
534 int wxApp::MainLoop()
540 void wxApp::ExitMainLoop()
542 if (gtk_main_level() > 0)
546 bool wxApp::Initialized()
548 return m_initialized
;
551 bool wxApp::Pending()
553 return (gtk_events_pending() > 0);
556 void wxApp::Dispatch()
558 gtk_main_iteration();
561 void wxApp::DeletePendingObjects()
563 wxNode
*node
= wxPendingDelete
.First();
566 wxObject
*obj
= (wxObject
*)node
->Data();
570 if (wxPendingDelete
.Find(obj
))
573 node
= wxPendingDelete
.First();
577 bool wxApp::Initialize()
579 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
581 wxClassInfo::InitializeClasses();
583 wxSystemSettings::Init();
586 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
589 // GL: I'm annoyed ... I don't know where to put this and I don't want to
590 // create a module for that as it's part of the core.
592 wxPendingEvents
= new wxList();
593 wxPendingEventsLocker
= new wxCriticalSection();
596 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
597 wxTheColourDatabase
->Initialize();
599 wxInitializeStockLists();
600 wxInitializeStockObjects();
602 #if wxUSE_WX_RESOURCES
603 wxInitializeResourceSystem();
606 wxModule::RegisterModules();
607 if (!wxModule::InitializeModules()) return FALSE
;
612 void wxApp::CleanUp()
614 wxModule::CleanUpModules();
616 #if wxUSE_WX_RESOURCES
617 wxCleanUpResourceSystem();
620 if (wxTheColourDatabase
)
621 delete wxTheColourDatabase
;
623 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
625 wxDeleteStockObjects();
627 wxDeleteStockLists();
630 wxTheApp
= (wxApp
*) NULL
;
632 // GL: I'm annoyed ... I don't know where to put this and I don't want to
633 // create a module for that as it's part of the core.
635 delete wxPendingEvents
;
636 delete wxPendingEventsLocker
;
639 wxSystemSettings::Done();
643 wxClassInfo::CleanUpClasses();
645 // check for memory leaks
646 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
647 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
649 wxLogDebug(wxT("There were memory leaks.\n"));
650 wxDebugContext::Dump();
651 wxDebugContext::PrintStatistics();
656 // do this as the very last thing because everything else can log messages
657 wxLog::DontCreateOnDemand();
659 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
665 //-----------------------------------------------------------------------------
666 // Access to the root window global
667 //-----------------------------------------------------------------------------
669 GtkWidget
* wxGetRootWindow()
671 if (gs_RootWindow
== NULL
) {
672 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
673 gtk_widget_realize( gs_RootWindow
);
675 return gs_RootWindow
;
678 //-----------------------------------------------------------------------------
680 //-----------------------------------------------------------------------------
682 // NB: argc and argv may be changed here, pass by reference!
683 int wxEntryStart( int& argc
, char *argv
[] )
686 /* GTK 1.2 up to version 1.2.3 has broken threads */
687 if ((gtk_major_version
== 1) &&
688 (gtk_minor_version
== 2) &&
689 (gtk_micro_version
< 4))
691 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
701 // We should have the wxUSE_WCHAR_T test on the _outside_
703 #if defined(__WXGTK20__)
704 // gtk+ 2.0 supports Unicode through UTF-8 strings
705 wxConvCurrent
= &wxConvUTF8
;
707 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
710 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
715 gtk_init( &argc
, &argv
);
717 wxSetDetectableAutoRepeat( TRUE
);
719 if (!wxApp::Initialize())
733 if ( !wxTheApp
->OnInitGui() )
742 void wxEntryCleanup()
745 // flush the logged messages if any
746 wxLog
*log
= wxLog::GetActiveTarget();
747 if (log
!= NULL
&& log
->HasPendingMessages())
750 // continuing to use user defined log target is unsafe from now on because
751 // some resources may be already unavailable, so replace it by something
753 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
765 int wxEntry( int argc
, char *argv
[] )
767 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
768 // This seems to be necessary since there are 'rogue'
769 // objects present at this point (perhaps global objects?)
770 // Setting a checkpoint will ignore them as far as the
771 // memory checking facility is concerned.
772 // Of course you may argue that memory allocated in globals should be
773 // checked, but this is a reasonable compromise.
774 wxDebugContext::SetCheckpoint();
776 int err
= wxEntryStart(argc
, argv
);
782 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
783 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
785 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
787 wxObject
*test_app
= app_ini();
789 wxTheApp
= (wxApp
*) test_app
;
792 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
794 wxTheApp
->argc
= argc
;
796 wxTheApp
->argv
= new wxChar
*[argc
+1];
798 while (mb_argc
< argc
)
800 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
803 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
805 wxTheApp
->argv
= argv
;
808 wxString
name(wxFileNameFromPath(argv
[0]));
809 wxStripExtension( name
);
810 wxTheApp
->SetAppName( name
);
813 retValue
= wxEntryInitGui();
815 // Here frames insert themselves automatically into wxTopLevelWindows by
816 // getting created in OnInit().
819 if ( !wxTheApp
->OnInit() )
825 /* delete pending toplevel windows (typically a single
826 dialog) so that, if there isn't any left, we don't
828 wxTheApp
->DeletePendingObjects();
830 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
832 if (wxTheApp
->Initialized())
836 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
839 /* Forcibly delete the window. */
840 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
841 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
843 topWindow
->Close( TRUE
);
844 wxTheApp
->DeletePendingObjects();
849 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
853 retValue
= wxTheApp
->OnExit();
862 #ifndef __WXUNIVERSAL__
864 #include "wx/gtk/info.xpm"
865 #include "wx/gtk/error.xpm"
866 #include "wx/gtk/question.xpm"
867 #include "wx/gtk/warning.xpm"
869 wxIcon
wxApp::GetStdIcon(int which
) const
873 case wxICON_INFORMATION
:
874 return wxIcon(info_xpm
);
876 case wxICON_QUESTION
:
877 return wxIcon(question_xpm
);
879 case wxICON_EXCLAMATION
:
880 return wxIcon(warning_xpm
);
883 wxFAIL_MSG(wxT("requested non existent standard icon"));
884 // still fall through
887 return wxIcon(error_xpm
);
891 wxIcon
wxApp::GetStdIcon(int which
) const
893 return wxTheme::Get()->GetRenderer()->GetStdIcon(which
);
895 #endif // !__WXUNIVERSAL__
900 void wxApp::OnAssert(const wxChar
*file
, int line
, const wxChar
*msg
)
904 wxAppBase::OnAssert(file
, line
, msg
);
906 m_isInAssert
= FALSE
;
909 #endif // __WXDEBUG__