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 //-----------------------------------------------------------------------------
322 // Access to the root window global
323 //-----------------------------------------------------------------------------
325 GtkWidget
* wxGetRootWindow()
327 if (gs_RootWindow
== NULL
)
329 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
330 gtk_widget_realize( gs_RootWindow
);
332 return gs_RootWindow
;
335 //-----------------------------------------------------------------------------
337 //-----------------------------------------------------------------------------
339 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
341 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
342 EVT_IDLE(wxApp::OnIdle
)
347 m_initialized
= FALSE
;
349 m_isInAssert
= FALSE
;
350 #endif // __WXDEBUG__
353 wxapp_install_idle_handler();
356 m_wakeUpTimerTag
= 0;
357 wxapp_install_thread_wakeup();
360 m_colorCube
= (unsigned char*) NULL
;
362 // this is NULL for a "regular" wxApp, but is set (and freed) by a wxGLApp
363 m_glVisualInfo
= (void *) NULL
;
368 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
371 wxapp_uninstall_thread_wakeup();
374 if (m_colorCube
) free(m_colorCube
);
377 bool wxApp::OnInitGui()
379 if ( !wxAppBase::OnInitGui() )
382 GdkVisual
*visual
= gdk_visual_get_system();
384 // if this is a wxGLApp (derived from wxApp), and we've already
385 // chosen a specific visual, then derive the GdkVisual from that
386 if (m_glVisualInfo
!= NULL
)
389 // seems gtk_widget_set_default_visual no longer exists?
390 GdkVisual
* vis
= gtk_widget_get_default_visual();
392 GdkVisual
* vis
= gdkx_visual_get(
393 ((XVisualInfo
*) m_glVisualInfo
) ->visualid
);
394 gtk_widget_set_default_visual( vis
);
397 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
398 gtk_widget_set_default_colormap( colormap
);
403 // On some machines, the default visual is just 256 colours, so
404 // we make sure we get the best. This can sometimes be wasteful.
407 if ((gdk_visual_get_best() != gdk_visual_get_system()) && (m_useBestVisual
))
410 /* seems gtk_widget_set_default_visual no longer exists? */
411 GdkVisual
* vis
= gtk_widget_get_default_visual();
413 GdkVisual
* vis
= gdk_visual_get_best();
414 gtk_widget_set_default_visual( vis
);
417 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
418 gtk_widget_set_default_colormap( colormap
);
423 // Nothing to do for 15, 16, 24, 32 bit displays
424 if (visual
->depth
> 8) return TRUE
;
426 // initialize color cube for 8-bit color reduction dithering
428 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
430 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
432 for (int r
= 0; r
< 32; r
++)
434 for (int g
= 0; g
< 32; g
++)
436 for (int b
= 0; b
< 32; b
++)
438 int rr
= (r
<< 3) | (r
>> 2);
439 int gg
= (g
<< 3) | (g
>> 2);
440 int bb
= (b
<< 3) | (b
>> 2);
444 GdkColor
*colors
= cmap
->colors
;
449 for (int i
= 0; i
< cmap
->size
; i
++)
451 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
452 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
453 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
454 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
457 index
= i
; max
= sum
;
463 // assume 8-bit true or static colors. this really exists
464 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
465 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
466 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
467 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
469 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
477 GdkVisual
*wxApp::GetGdkVisual()
479 GdkVisual
*visual
= NULL
;
482 visual
= gdkx_visual_get( ((XVisualInfo
*) m_glVisualInfo
)->visualid
);
484 visual
= gdk_window_get_visual( wxGetRootWindow()->window
);
491 bool wxApp::ProcessIdle()
494 event
.SetEventObject( this );
495 ProcessEvent( event
);
497 return event
.MoreRequested();
500 void wxApp::OnIdle( wxIdleEvent
&event
)
502 static bool s_inOnIdle
= FALSE
;
504 /* Avoid recursion (via ProcessEvent default case) */
510 /* Resend in the main thread events which have been prepared in other
512 ProcessPendingEvents();
514 /* 'Garbage' collection of windows deleted with Close(). */
515 DeletePendingObjects();
517 /* Send OnIdle events to all windows */
518 bool needMore
= SendIdleEvents();
521 event
.RequestMore(TRUE
);
526 bool wxApp::SendIdleEvents()
528 bool needMore
= FALSE
;
530 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
533 wxWindow
* win
= node
->GetData();
534 if (SendIdleEvents(win
))
536 node
= node
->GetNext();
542 bool wxApp::SendIdleEvents( wxWindow
* win
)
544 bool needMore
= FALSE
;
547 event
.SetEventObject(win
);
549 win
->GetEventHandler()->ProcessEvent(event
);
551 win
->OnInternalIdle();
553 if (event
.MoreRequested())
556 wxNode
* node
= win
->GetChildren().First();
559 wxWindow
* win
= (wxWindow
*) node
->Data();
560 if (SendIdleEvents(win
))
568 int wxApp::MainLoop()
574 void wxApp::ExitMainLoop()
576 if (gtk_main_level() > 0)
580 bool wxApp::Initialized()
582 return m_initialized
;
585 bool wxApp::Pending()
587 return (gtk_events_pending() > 0);
590 void wxApp::Dispatch()
592 gtk_main_iteration();
595 void wxApp::DeletePendingObjects()
597 wxNode
*node
= wxPendingDelete
.First();
600 wxObject
*obj
= (wxObject
*)node
->Data();
604 if (wxPendingDelete
.Find(obj
))
607 node
= wxPendingDelete
.First();
611 bool wxApp::Initialize()
613 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
615 wxClassInfo::InitializeClasses();
618 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
621 // GL: I'm annoyed ... I don't know where to put this and I don't want to
622 // create a module for that as it's part of the core.
624 wxPendingEvents
= new wxList();
625 wxPendingEventsLocker
= new wxCriticalSection();
628 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
629 wxTheColourDatabase
->Initialize();
631 wxInitializeStockLists();
632 wxInitializeStockObjects();
634 #if wxUSE_WX_RESOURCES
635 wxInitializeResourceSystem();
638 wxModule::RegisterModules();
639 if (!wxModule::InitializeModules()) return FALSE
;
644 void wxApp::CleanUp()
646 wxModule::CleanUpModules();
648 #if wxUSE_WX_RESOURCES
649 wxCleanUpResourceSystem();
652 if (wxTheColourDatabase
)
653 delete wxTheColourDatabase
;
655 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
657 wxDeleteStockObjects();
659 wxDeleteStockLists();
662 wxTheApp
= (wxApp
*) NULL
;
664 // GL: I'm annoyed ... I don't know where to put this and I don't want to
665 // create a module for that as it's part of the core.
667 delete wxPendingEvents
;
668 delete wxPendingEventsLocker
;
673 wxClassInfo::CleanUpClasses();
675 // check for memory leaks
676 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
677 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
679 wxLogDebug(wxT("There were memory leaks.\n"));
680 wxDebugContext::Dump();
681 wxDebugContext::PrintStatistics();
686 // do this as the very last thing because everything else can log messages
687 wxLog::DontCreateOnDemand();
689 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
695 //-----------------------------------------------------------------------------
697 //-----------------------------------------------------------------------------
699 // NB: argc and argv may be changed here, pass by reference!
700 int wxEntryStart( int& argc
, char *argv
[] )
703 // GTK 1.2 up to version 1.2.3 has broken threads
704 if ((gtk_major_version
== 1) &&
705 (gtk_minor_version
== 2) &&
706 (gtk_micro_version
< 4))
708 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
718 // We should have the wxUSE_WCHAR_T test on the _outside_
720 #if defined(__WXGTK20__)
721 // gtk+ 2.0 supports Unicode through UTF-8 strings
722 wxConvCurrent
= &wxConvUTF8
;
724 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
727 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
732 gtk_init( &argc
, &argv
);
734 wxSetDetectableAutoRepeat( TRUE
);
736 if (!wxApp::Initialize())
750 if ( !wxTheApp
->OnInitGui() )
759 void wxEntryCleanup()
762 // flush the logged messages if any
763 wxLog
*log
= wxLog::GetActiveTarget();
764 if (log
!= NULL
&& log
->HasPendingMessages())
767 // continuing to use user defined log target is unsafe from now on because
768 // some resources may be already unavailable, so replace it by something
770 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
781 int wxEntry( int argc
, char *argv
[] )
783 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
784 // This seems to be necessary since there are 'rogue'
785 // objects present at this point (perhaps global objects?)
786 // Setting a checkpoint will ignore them as far as the
787 // memory checking facility is concerned.
788 // Of course you may argue that memory allocated in globals should be
789 // checked, but this is a reasonable compromise.
790 wxDebugContext::SetCheckpoint();
792 int err
= wxEntryStart(argc
, argv
);
798 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
799 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
801 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
803 wxObject
*test_app
= app_ini();
805 wxTheApp
= (wxApp
*) test_app
;
808 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
810 wxTheApp
->argc
= argc
;
812 wxTheApp
->argv
= new wxChar
*[argc
+1];
814 while (mb_argc
< argc
)
816 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
819 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
821 wxTheApp
->argv
= argv
;
824 wxString
name(wxFileNameFromPath(argv
[0]));
825 wxStripExtension( name
);
826 wxTheApp
->SetAppName( name
);
829 retValue
= wxEntryInitGui();
831 // Here frames insert themselves automatically into wxTopLevelWindows by
832 // getting created in OnInit().
835 if ( !wxTheApp
->OnInit() )
841 /* delete pending toplevel windows (typically a single
842 dialog) so that, if there isn't any left, we don't
844 wxTheApp
->DeletePendingObjects();
846 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
848 if (wxTheApp
->Initialized())
852 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
855 /* Forcibly delete the window. */
856 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
857 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
859 topWindow
->Close( TRUE
);
860 wxTheApp
->DeletePendingObjects();
865 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
869 retValue
= wxTheApp
->OnExit();
878 #ifndef __WXUNIVERSAL__
880 // XPM hack: make the arrays const
881 #define static static const
883 #include "wx/gtk/info.xpm"
884 #include "wx/gtk/error.xpm"
885 #include "wx/gtk/question.xpm"
886 #include "wx/gtk/warning.xpm"
890 wxIcon
wxApp::GetStdIcon(int which
) const
894 case wxICON_INFORMATION
:
895 return wxIcon(info_xpm
);
897 case wxICON_QUESTION
:
898 return wxIcon(question_xpm
);
900 case wxICON_EXCLAMATION
:
901 return wxIcon(warning_xpm
);
904 wxFAIL_MSG(wxT("requested non existent standard icon"));
905 // still fall through
908 return wxIcon(error_xpm
);
912 wxIcon
wxApp::GetStdIcon(int which
) const
914 return wxTheme::Get()->GetRenderer()->GetStdIcon(which
);
916 #endif // !__WXUNIVERSAL__
921 void wxApp::OnAssert(const wxChar
*file
, int line
, const wxChar
*msg
)
925 wxAppBase::OnAssert(file
, line
, msg
);
927 m_isInAssert
= FALSE
;
930 #endif // __WXDEBUG__