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 //-----------------------------------------------------------------------------
66 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) );
67 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) );
70 void wxapp_install_thread_wakeup();
71 void wxapp_uninstall_thread_wakeup();
72 void wxapp_install_idle_handler();
74 //-----------------------------------------------------------------------------
76 //-----------------------------------------------------------------------------
83 //-----------------------------------------------------------------------------
85 //-----------------------------------------------------------------------------
87 bool wxApp::Yield(bool onlyIfNeeded
)
90 static bool s_inYield
= FALSE
;
96 wxFAIL_MSG( wxT("wxYield called recursively" ) );
103 if ( !wxThread::IsMain() )
105 // can't call gtk_main_iteration() from other threads like this
108 #endif // wxUSE_THREADS
114 // We need to remove idle callbacks or the loop will
116 gtk_idle_remove( m_idleTag
);
121 // disable log flushing from here because a call to wxYield() shouldn't
122 // normally result in message boxes popping up &c
125 while (gtk_events_pending())
126 gtk_main_iteration();
128 /* it's necessary to call ProcessIdle() to update the frames sizes which
129 might have been changed (it also will update other things set from
130 OnUpdateUI() which is a nice (and desired) side effect) */
131 while ( ProcessIdle() )
135 // let the logs be flashed again
143 //-----------------------------------------------------------------------------
145 //-----------------------------------------------------------------------------
150 if (!wxThread::IsMain())
155 wxapp_install_idle_handler();
158 if (!wxThread::IsMain())
163 //-----------------------------------------------------------------------------
165 //-----------------------------------------------------------------------------
167 void wxapp_install_idle_handler()
169 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, wxT("attempt to install idle handler twice") );
173 if (g_pendingTag
== 0)
174 g_pendingTag
= gtk_idle_add_priority( 900, wxapp_pending_callback
, (gpointer
) NULL
);
176 /* This routine gets called by all event handlers
177 indicating that the idle is over. It may also
178 get called from other thread for sending events
179 to the main thread (and processing these in
180 idle time). Very low priority. */
182 wxTheApp
->m_idleTag
= gtk_idle_add_priority( 1000, wxapp_idle_callback
, (gpointer
) NULL
);
185 // the callback functions must be extern "C" to comply with GTK+ declarations
189 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) )
191 if (!wxTheApp
) return TRUE
;
193 // when getting called from GDK's time-out handler
194 // we are no longer within GDK's grab on the GUI
195 // thread so we must lock it here ourselves
198 // Sent idle event to all who request them
199 wxTheApp
->ProcessPendingEvents();
203 /* flush the logged messages if any */
205 wxLog::FlushActive();
208 // Release lock again
211 // Return FALSE to indicate that no more idle events are
212 // to be sent (single shot instead of continuous stream)
216 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
222 // don't generate the idle events while the assert modal dialog is shown,
223 // this completely confuses the apps which don't expect to be reentered
224 // from some safely-looking functions
225 if ( wxTheApp
->IsInAssert() )
229 #endif // __WXDEBUG__
231 // when getting called from GDK's time-out handler
232 // we are no longer within GDK's grab on the GUI
233 // thread so we must lock it here ourselves
236 /* Indicate that we are now in idle mode - even so deeply
237 in idle mode that we don't get any idle events anymore.
238 this is like wxMSW where an idle event is sent only
239 once each time after the event queue has been completely
242 wxTheApp
->m_idleTag
= 0;
244 // Sent idle event to all who request them as long as they do
245 while (wxTheApp
->ProcessIdle())
248 // Release lock again
251 // Return FALSE to indicate that no more idle events are
252 // to be sent (single shot instead of continuous stream)
258 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
260 // when getting called from GDK's time-out handler
261 // we are no longer within GDK's grab on the GUI
262 // thread so we must lock it here ourselves
265 wxapp_uninstall_thread_wakeup();
267 // unblock other threads wishing to do some GUI things
270 g_mainThreadLocked
= TRUE
;
272 // wake up other threads
275 // block other thread again
278 g_mainThreadLocked
= FALSE
;
280 wxapp_install_thread_wakeup();
282 // release lock again
288 #endif // wxUSE_THREADS
294 static int g_threadUninstallLevel
= 0;
296 void wxapp_install_thread_wakeup()
298 g_threadUninstallLevel
++;
300 if (g_threadUninstallLevel
!= 1) return;
302 if (wxTheApp
->m_wakeUpTimerTag
) return;
304 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 50, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
307 void wxapp_uninstall_thread_wakeup()
309 g_threadUninstallLevel
--;
311 if (g_threadUninstallLevel
!= 0) return;
313 if (!wxTheApp
->m_wakeUpTimerTag
) return;
315 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
316 wxTheApp
->m_wakeUpTimerTag
= 0;
319 #endif // wxUSE_THREADS
321 //-----------------------------------------------------------------------------
323 //-----------------------------------------------------------------------------
325 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
327 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
328 EVT_IDLE(wxApp::OnIdle
)
333 m_initialized
= FALSE
;
335 m_isInAssert
= FALSE
;
336 #endif // __WXDEBUG__
339 wxapp_install_idle_handler();
342 m_wakeUpTimerTag
= 0;
343 wxapp_install_thread_wakeup();
346 m_colorCube
= (unsigned char*) NULL
;
348 // this is NULL for a "regular" wxApp, but is set (and freed) by a wxGLApp
349 m_glVisualInfo
= (void *) NULL
;
354 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
357 wxapp_uninstall_thread_wakeup();
360 if (m_colorCube
) free(m_colorCube
);
363 bool wxApp::OnInitGui()
365 if ( !wxAppBase::OnInitGui() )
368 GdkVisual
*visual
= gdk_visual_get_system();
370 // if this is a wxGLApp (derived from wxApp), and we've already
371 // chosen a specific visual, then derive the GdkVisual from that
372 if (m_glVisualInfo
!= NULL
) {
374 /* seems gtk_widget_set_default_visual no longer exists? */
375 GdkVisual
* vis
= gtk_widget_get_default_visual();
377 GdkVisual
* vis
= gdkx_visual_get(
378 ((XVisualInfo
*) m_glVisualInfo
) ->visualid
);
379 gtk_widget_set_default_visual( vis
);
382 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
383 gtk_widget_set_default_colormap( colormap
);
388 /* on some machines, the default visual is just 256 colours, so
389 we make sure we get the best. this can sometimes be wasteful,
390 of course, but what do these guys pay $30.000 for? */
392 else if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
396 /* seems gtk_widget_set_default_visual no longer exists? */
397 GdkVisual
* vis
= gtk_widget_get_default_visual();
399 GdkVisual
* vis
= gdk_visual_get_best();
400 gtk_widget_set_default_visual( vis
);
403 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
404 gtk_widget_set_default_colormap( colormap
);
409 /* Nothing to do for 15, 16, 24, 32 bit displays */
410 if (visual
->depth
> 8) return TRUE
;
412 /* initialize color cube for 8-bit color reduction dithering */
414 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
416 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
418 for (int r
= 0; r
< 32; r
++)
420 for (int g
= 0; g
< 32; g
++)
422 for (int b
= 0; b
< 32; b
++)
424 int rr
= (r
<< 3) | (r
>> 2);
425 int gg
= (g
<< 3) | (g
>> 2);
426 int bb
= (b
<< 3) | (b
>> 2);
430 GdkColor
*colors
= cmap
->colors
;
435 for (int i
= 0; i
< cmap
->size
; i
++)
437 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
438 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
439 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
440 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
443 index
= i
; max
= sum
;
449 #if (GTK_MINOR_VERSION > 0)
450 /* assume 8-bit true or static colors. this really
452 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
453 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
454 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
455 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
457 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
460 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
468 bool wxApp::ProcessIdle()
471 event
.SetEventObject( this );
472 ProcessEvent( event
);
474 return event
.MoreRequested();
477 void wxApp::OnIdle( wxIdleEvent
&event
)
479 static bool s_inOnIdle
= FALSE
;
481 /* Avoid recursion (via ProcessEvent default case) */
487 /* Resend in the main thread events which have been prepared in other
489 ProcessPendingEvents();
491 /* 'Garbage' collection of windows deleted with Close(). */
492 DeletePendingObjects();
494 /* Send OnIdle events to all windows */
495 bool needMore
= SendIdleEvents();
498 event
.RequestMore(TRUE
);
503 bool wxApp::SendIdleEvents()
505 bool needMore
= FALSE
;
507 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
510 wxWindow
* win
= node
->GetData();
511 if (SendIdleEvents(win
))
513 node
= node
->GetNext();
519 bool wxApp::SendIdleEvents( wxWindow
* win
)
521 bool needMore
= FALSE
;
524 event
.SetEventObject(win
);
526 win
->GetEventHandler()->ProcessEvent(event
);
528 win
->OnInternalIdle();
530 if (event
.MoreRequested())
533 wxNode
* node
= win
->GetChildren().First();
536 wxWindow
* win
= (wxWindow
*) node
->Data();
537 if (SendIdleEvents(win
))
545 int wxApp::MainLoop()
551 void wxApp::ExitMainLoop()
553 if (gtk_main_level() > 0)
557 bool wxApp::Initialized()
559 return m_initialized
;
562 bool wxApp::Pending()
564 return (gtk_events_pending() > 0);
567 void wxApp::Dispatch()
569 gtk_main_iteration();
572 void wxApp::DeletePendingObjects()
574 wxNode
*node
= wxPendingDelete
.First();
577 wxObject
*obj
= (wxObject
*)node
->Data();
581 if (wxPendingDelete
.Find(obj
))
584 node
= wxPendingDelete
.First();
588 bool wxApp::Initialize()
590 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
592 wxClassInfo::InitializeClasses();
595 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
598 // GL: I'm annoyed ... I don't know where to put this and I don't want to
599 // create a module for that as it's part of the core.
601 wxPendingEvents
= new wxList();
602 wxPendingEventsLocker
= new wxCriticalSection();
605 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
606 wxTheColourDatabase
->Initialize();
608 wxInitializeStockLists();
609 wxInitializeStockObjects();
611 #if wxUSE_WX_RESOURCES
612 wxInitializeResourceSystem();
615 wxModule::RegisterModules();
616 if (!wxModule::InitializeModules()) return FALSE
;
621 void wxApp::CleanUp()
623 wxModule::CleanUpModules();
625 #if wxUSE_WX_RESOURCES
626 wxCleanUpResourceSystem();
629 if (wxTheColourDatabase
)
630 delete wxTheColourDatabase
;
632 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
634 wxDeleteStockObjects();
636 wxDeleteStockLists();
639 wxTheApp
= (wxApp
*) NULL
;
641 // GL: I'm annoyed ... I don't know where to put this and I don't want to
642 // create a module for that as it's part of the core.
644 delete wxPendingEvents
;
645 delete wxPendingEventsLocker
;
650 wxClassInfo::CleanUpClasses();
652 // check for memory leaks
653 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
654 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
656 wxLogDebug(wxT("There were memory leaks.\n"));
657 wxDebugContext::Dump();
658 wxDebugContext::PrintStatistics();
663 // do this as the very last thing because everything else can log messages
664 wxLog::DontCreateOnDemand();
666 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
672 //-----------------------------------------------------------------------------
673 // Access to the root window global
674 //-----------------------------------------------------------------------------
676 GtkWidget
* wxGetRootWindow()
678 if (gs_RootWindow
== NULL
) {
679 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
680 gtk_widget_realize( gs_RootWindow
);
682 return gs_RootWindow
;
685 //-----------------------------------------------------------------------------
687 //-----------------------------------------------------------------------------
689 // NB: argc and argv may be changed here, pass by reference!
690 int wxEntryStart( int& argc
, char *argv
[] )
693 /* GTK 1.2 up to version 1.2.3 has broken threads */
694 if ((gtk_major_version
== 1) &&
695 (gtk_minor_version
== 2) &&
696 (gtk_micro_version
< 4))
698 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
708 // We should have the wxUSE_WCHAR_T test on the _outside_
710 #if defined(__WXGTK20__)
711 // gtk+ 2.0 supports Unicode through UTF-8 strings
712 wxConvCurrent
= &wxConvUTF8
;
714 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
717 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
722 gtk_init( &argc
, &argv
);
724 wxSetDetectableAutoRepeat( TRUE
);
726 if (!wxApp::Initialize())
740 if ( !wxTheApp
->OnInitGui() )
749 void wxEntryCleanup()
752 // flush the logged messages if any
753 wxLog
*log
= wxLog::GetActiveTarget();
754 if (log
!= NULL
&& log
->HasPendingMessages())
757 // continuing to use user defined log target is unsafe from now on because
758 // some resources may be already unavailable, so replace it by something
760 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
772 int wxEntry( int argc
, char *argv
[] )
774 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
775 // This seems to be necessary since there are 'rogue'
776 // objects present at this point (perhaps global objects?)
777 // Setting a checkpoint will ignore them as far as the
778 // memory checking facility is concerned.
779 // Of course you may argue that memory allocated in globals should be
780 // checked, but this is a reasonable compromise.
781 wxDebugContext::SetCheckpoint();
783 int err
= wxEntryStart(argc
, argv
);
789 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
790 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
792 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
794 wxObject
*test_app
= app_ini();
796 wxTheApp
= (wxApp
*) test_app
;
799 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
801 wxTheApp
->argc
= argc
;
803 wxTheApp
->argv
= new wxChar
*[argc
+1];
805 while (mb_argc
< argc
)
807 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
810 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
812 wxTheApp
->argv
= argv
;
815 wxString
name(wxFileNameFromPath(argv
[0]));
816 wxStripExtension( name
);
817 wxTheApp
->SetAppName( name
);
820 retValue
= wxEntryInitGui();
822 // Here frames insert themselves automatically into wxTopLevelWindows by
823 // getting created in OnInit().
826 if ( !wxTheApp
->OnInit() )
832 /* delete pending toplevel windows (typically a single
833 dialog) so that, if there isn't any left, we don't
835 wxTheApp
->DeletePendingObjects();
837 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
839 if (wxTheApp
->Initialized())
843 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
846 /* Forcibly delete the window. */
847 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
848 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
850 topWindow
->Close( TRUE
);
851 wxTheApp
->DeletePendingObjects();
856 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
860 retValue
= wxTheApp
->OnExit();
869 #ifndef __WXUNIVERSAL__
871 #include "wx/gtk/info.xpm"
872 #include "wx/gtk/error.xpm"
873 #include "wx/gtk/question.xpm"
874 #include "wx/gtk/warning.xpm"
876 wxIcon
wxApp::GetStdIcon(int which
) const
880 case wxICON_INFORMATION
:
881 return wxIcon(info_xpm
);
883 case wxICON_QUESTION
:
884 return wxIcon(question_xpm
);
886 case wxICON_EXCLAMATION
:
887 return wxIcon(warning_xpm
);
890 wxFAIL_MSG(wxT("requested non existent standard icon"));
891 // still fall through
894 return wxIcon(error_xpm
);
898 wxIcon
wxApp::GetStdIcon(int which
) const
900 return wxTheme::Get()->GetRenderer()->GetStdIcon(which
);
902 #endif // !__WXUNIVERSAL__
907 void wxApp::OnAssert(const wxChar
*file
, int line
, const wxChar
*msg
)
911 wxAppBase::OnAssert(file
, line
, msg
);
913 m_isInAssert
= FALSE
;
916 #endif // __WXDEBUG__