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). But we
131 // call ProcessIdle() only once since this is not meant for longish
132 // background jobs (controlled by wxIdleEvent::RequestMore() and the
133 // return value of Processidle().
136 // let the logs be flashed again
144 //-----------------------------------------------------------------------------
146 //-----------------------------------------------------------------------------
151 if (!wxThread::IsMain())
156 wxapp_install_idle_handler();
159 if (!wxThread::IsMain())
164 //-----------------------------------------------------------------------------
166 //-----------------------------------------------------------------------------
168 void wxapp_install_idle_handler()
170 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, wxT("attempt to install idle handler twice") );
174 if (g_pendingTag
== 0)
175 g_pendingTag
= gtk_idle_add_priority( 900, wxapp_pending_callback
, (gpointer
) NULL
);
177 /* This routine gets called by all event handlers
178 indicating that the idle is over. It may also
179 get called from other thread for sending events
180 to the main thread (and processing these in
181 idle time). Very low priority. */
183 wxTheApp
->m_idleTag
= gtk_idle_add_priority( 1000, wxapp_idle_callback
, (gpointer
) NULL
);
186 // the callback functions must be extern "C" to comply with GTK+ declarations
190 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) )
192 if (!wxTheApp
) return TRUE
;
194 // When getting called from GDK's time-out handler
195 // we are no longer within GDK's grab on the GUI
196 // thread so we must lock it here ourselves.
199 // Sent idle event to all who request them.
200 wxTheApp
->ProcessPendingEvents();
204 // Flush the logged messages if any.
206 wxLog::FlushActive();
209 // Release lock again
212 // Return FALSE to indicate that no more idle events are
213 // to be sent (single shot instead of continuous stream)
217 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
223 // don't generate the idle events while the assert modal dialog is shown,
224 // this completely confuses the apps which don't expect to be reentered
225 // from some safely-looking functions
226 if ( wxTheApp
->IsInAssert() )
230 #endif // __WXDEBUG__
232 // When getting called from GDK's time-out handler
233 // we are no longer within GDK's grab on the GUI
234 // thread so we must lock it here ourselves.
237 // Indicate that we are now in idle mode and event handlers
238 // will have to reinstall the idle handler again.
240 wxTheApp
->m_idleTag
= 0;
242 // Sent idle event to all who request them as long as
243 // no events have popped up in the event queue.
244 while (wxTheApp
->ProcessIdle() && (gtk_events_pending() == 0))
247 // Release lock again
250 // Return FALSE to indicate that no more idle events are
251 // to be sent (single shot instead of continuous stream).
257 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
259 // when getting called from GDK's time-out handler
260 // we are no longer within GDK's grab on the GUI
261 // thread so we must lock it here ourselves
264 wxapp_uninstall_thread_wakeup();
266 // unblock other threads wishing to do some GUI things
269 g_mainThreadLocked
= TRUE
;
271 // wake up other threads
274 // block other thread again
277 g_mainThreadLocked
= FALSE
;
279 wxapp_install_thread_wakeup();
281 // release lock again
287 #endif // wxUSE_THREADS
293 static int g_threadUninstallLevel
= 0;
295 void wxapp_install_thread_wakeup()
297 g_threadUninstallLevel
++;
299 if (g_threadUninstallLevel
!= 1) return;
301 if (wxTheApp
->m_wakeUpTimerTag
) return;
303 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 50, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
306 void wxapp_uninstall_thread_wakeup()
308 g_threadUninstallLevel
--;
310 if (g_threadUninstallLevel
!= 0) return;
312 if (!wxTheApp
->m_wakeUpTimerTag
) return;
314 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
315 wxTheApp
->m_wakeUpTimerTag
= 0;
318 #endif // wxUSE_THREADS
320 //-----------------------------------------------------------------------------
321 // Access to the root window global
322 //-----------------------------------------------------------------------------
324 GtkWidget
* wxGetRootWindow()
326 if (gs_RootWindow
== NULL
)
328 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
329 gtk_widget_realize( gs_RootWindow
);
331 return gs_RootWindow
;
334 //-----------------------------------------------------------------------------
336 //-----------------------------------------------------------------------------
338 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
340 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
341 EVT_IDLE(wxApp::OnIdle
)
346 m_initialized
= FALSE
;
348 m_isInAssert
= FALSE
;
349 #endif // __WXDEBUG__
352 wxapp_install_idle_handler();
355 m_wakeUpTimerTag
= 0;
356 wxapp_install_thread_wakeup();
359 m_colorCube
= (unsigned char*) NULL
;
361 // this is NULL for a "regular" wxApp, but is set (and freed) by a wxGLApp
362 m_glVisualInfo
= (void *) NULL
;
367 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
370 wxapp_uninstall_thread_wakeup();
373 if (m_colorCube
) free(m_colorCube
);
376 bool wxApp::OnInitGui()
378 if ( !wxAppBase::OnInitGui() )
381 GdkVisual
*visual
= gdk_visual_get_system();
383 // if this is a wxGLApp (derived from wxApp), and we've already
384 // chosen a specific visual, then derive the GdkVisual from that
385 if (m_glVisualInfo
!= NULL
)
388 // seems gtk_widget_set_default_visual no longer exists?
389 GdkVisual
* vis
= gtk_widget_get_default_visual();
391 GdkVisual
* vis
= gdkx_visual_get(
392 ((XVisualInfo
*) m_glVisualInfo
) ->visualid
);
393 gtk_widget_set_default_visual( vis
);
396 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
397 gtk_widget_set_default_colormap( colormap
);
402 // On some machines, the default visual is just 256 colours, so
403 // we make sure we get the best. This can sometimes be wasteful.
406 if ((gdk_visual_get_best() != gdk_visual_get_system()) && (m_useBestVisual
))
409 /* seems gtk_widget_set_default_visual no longer exists? */
410 GdkVisual
* vis
= gtk_widget_get_default_visual();
412 GdkVisual
* vis
= gdk_visual_get_best();
413 gtk_widget_set_default_visual( vis
);
416 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
417 gtk_widget_set_default_colormap( colormap
);
422 // Nothing to do for 15, 16, 24, 32 bit displays
423 if (visual
->depth
> 8) return TRUE
;
425 // initialize color cube for 8-bit color reduction dithering
427 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
429 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
431 for (int r
= 0; r
< 32; r
++)
433 for (int g
= 0; g
< 32; g
++)
435 for (int b
= 0; b
< 32; b
++)
437 int rr
= (r
<< 3) | (r
>> 2);
438 int gg
= (g
<< 3) | (g
>> 2);
439 int bb
= (b
<< 3) | (b
>> 2);
443 GdkColor
*colors
= cmap
->colors
;
448 for (int i
= 0; i
< cmap
->size
; i
++)
450 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
451 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
452 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
453 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
456 index
= i
; max
= sum
;
462 // assume 8-bit true or static colors. this really exists
463 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
464 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
465 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
466 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
468 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
476 GdkVisual
*wxApp::GetGdkVisual()
478 GdkVisual
*visual
= NULL
;
481 visual
= gdkx_visual_get( ((XVisualInfo
*) m_glVisualInfo
)->visualid
);
483 visual
= gdk_window_get_visual( wxGetRootWindow()->window
);
490 bool wxApp::ProcessIdle()
493 event
.SetEventObject( this );
494 ProcessEvent( event
);
496 return event
.MoreRequested();
499 void wxApp::OnIdle( wxIdleEvent
&event
)
501 static bool s_inOnIdle
= FALSE
;
503 // Avoid recursion (via ProcessEvent default case)
509 // Resend in the main thread events which have been prepared in other
511 ProcessPendingEvents();
513 // 'Garbage' collection of windows deleted with Close()
514 DeletePendingObjects();
516 // Send OnIdle events to all windows
517 bool needMore
= SendIdleEvents();
520 event
.RequestMore(TRUE
);
525 bool wxApp::SendIdleEvents()
527 bool needMore
= FALSE
;
529 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
532 wxWindow
* win
= node
->GetData();
533 if (SendIdleEvents(win
))
535 node
= node
->GetNext();
541 bool wxApp::SendIdleEvents( wxWindow
* win
)
543 bool needMore
= FALSE
;
546 event
.SetEventObject(win
);
548 win
->GetEventHandler()->ProcessEvent(event
);
550 win
->OnInternalIdle();
552 if (event
.MoreRequested())
555 wxNode
* node
= win
->GetChildren().First();
558 wxWindow
* win
= (wxWindow
*) node
->Data();
559 if (SendIdleEvents(win
))
567 int wxApp::MainLoop()
573 void wxApp::ExitMainLoop()
575 if (gtk_main_level() > 0)
579 bool wxApp::Initialized()
581 return m_initialized
;
584 bool wxApp::Pending()
586 return (gtk_events_pending() > 0);
589 void wxApp::Dispatch()
591 gtk_main_iteration();
594 void wxApp::DeletePendingObjects()
596 wxNode
*node
= wxPendingDelete
.First();
599 wxObject
*obj
= (wxObject
*)node
->Data();
603 if (wxPendingDelete
.Find(obj
))
606 node
= wxPendingDelete
.First();
610 bool wxApp::Initialize()
612 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
614 wxClassInfo::InitializeClasses();
617 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
620 // GL: I'm annoyed ... I don't know where to put this and I don't want to
621 // create a module for that as it's part of the core.
623 wxPendingEvents
= new wxList();
624 wxPendingEventsLocker
= new wxCriticalSection();
627 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
628 wxTheColourDatabase
->Initialize();
630 wxInitializeStockLists();
631 wxInitializeStockObjects();
633 #if wxUSE_WX_RESOURCES
634 wxInitializeResourceSystem();
637 wxModule::RegisterModules();
638 if (!wxModule::InitializeModules()) return FALSE
;
643 void wxApp::CleanUp()
645 wxModule::CleanUpModules();
647 #if wxUSE_WX_RESOURCES
648 wxCleanUpResourceSystem();
651 if (wxTheColourDatabase
)
652 delete wxTheColourDatabase
;
654 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
656 wxDeleteStockObjects();
658 wxDeleteStockLists();
661 wxTheApp
= (wxApp
*) NULL
;
663 // GL: I'm annoyed ... I don't know where to put this and I don't want to
664 // create a module for that as it's part of the core.
666 delete wxPendingEvents
;
667 delete wxPendingEventsLocker
;
672 wxClassInfo::CleanUpClasses();
674 // check for memory leaks
675 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
676 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
678 wxLogDebug(wxT("There were memory leaks.\n"));
679 wxDebugContext::Dump();
680 wxDebugContext::PrintStatistics();
685 // do this as the very last thing because everything else can log messages
686 wxLog::DontCreateOnDemand();
688 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
694 //-----------------------------------------------------------------------------
696 //-----------------------------------------------------------------------------
698 // NB: argc and argv may be changed here, pass by reference!
699 int wxEntryStart( int& argc
, char *argv
[] )
702 // GTK 1.2 up to version 1.2.3 has broken threads
703 if ((gtk_major_version
== 1) &&
704 (gtk_minor_version
== 2) &&
705 (gtk_micro_version
< 4))
707 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
717 // We should have the wxUSE_WCHAR_T test on the _outside_
719 #if defined(__WXGTK20__)
720 // gtk+ 2.0 supports Unicode through UTF-8 strings
721 wxConvCurrent
= &wxConvUTF8
;
723 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
726 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
731 gtk_init( &argc
, &argv
);
733 wxSetDetectableAutoRepeat( TRUE
);
735 if (!wxApp::Initialize())
749 if ( !wxTheApp
->OnInitGui() )
758 void wxEntryCleanup()
761 // flush the logged messages if any
762 wxLog
*log
= wxLog::GetActiveTarget();
763 if (log
!= NULL
&& log
->HasPendingMessages())
766 // continuing to use user defined log target is unsafe from now on because
767 // some resources may be already unavailable, so replace it by something
769 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
780 int wxEntry( int argc
, char *argv
[] )
782 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
783 // This seems to be necessary since there are 'rogue'
784 // objects present at this point (perhaps global objects?)
785 // Setting a checkpoint will ignore them as far as the
786 // memory checking facility is concerned.
787 // Of course you may argue that memory allocated in globals should be
788 // checked, but this is a reasonable compromise.
789 wxDebugContext::SetCheckpoint();
791 int err
= wxEntryStart(argc
, argv
);
797 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
798 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
800 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
802 wxObject
*test_app
= app_ini();
804 wxTheApp
= (wxApp
*) test_app
;
807 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
809 wxTheApp
->argc
= argc
;
811 wxTheApp
->argv
= new wxChar
*[argc
+1];
813 while (mb_argc
< argc
)
815 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
818 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
820 wxTheApp
->argv
= argv
;
823 wxString
name(wxFileNameFromPath(argv
[0]));
824 wxStripExtension( name
);
825 wxTheApp
->SetAppName( name
);
828 retValue
= wxEntryInitGui();
830 // Here frames insert themselves automatically into wxTopLevelWindows by
831 // getting created in OnInit().
834 if ( !wxTheApp
->OnInit() )
840 /* delete pending toplevel windows (typically a single
841 dialog) so that, if there isn't any left, we don't
843 wxTheApp
->DeletePendingObjects();
845 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
847 if (wxTheApp
->Initialized())
851 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
854 /* Forcibly delete the window. */
855 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
856 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
858 topWindow
->Close( TRUE
);
859 wxTheApp
->DeletePendingObjects();
864 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
868 retValue
= wxTheApp
->OnExit();
877 #ifndef __WXUNIVERSAL__
879 // XPM hack: make the arrays const
880 #define static static const
882 #include "wx/gtk/info.xpm"
883 #include "wx/gtk/error.xpm"
884 #include "wx/gtk/question.xpm"
885 #include "wx/gtk/warning.xpm"
889 wxIcon
wxApp::GetStdIcon(int which
) const
893 case wxICON_INFORMATION
:
894 return wxIcon(info_xpm
);
896 case wxICON_QUESTION
:
897 return wxIcon(question_xpm
);
899 case wxICON_EXCLAMATION
:
900 return wxIcon(warning_xpm
);
903 wxFAIL_MSG(wxT("requested non existent standard icon"));
904 // still fall through
907 return wxIcon(error_xpm
);
911 wxIcon
wxApp::GetStdIcon(int which
) const
913 return wxTheme::Get()->GetRenderer()->GetStdIcon(which
);
915 #endif // !__WXUNIVERSAL__
920 void wxApp::OnAssert(const wxChar
*file
, int line
, const wxChar
*msg
)
924 wxAppBase::OnAssert(file
, line
, msg
);
926 m_isInAssert
= FALSE
;
929 #endif // __WXDEBUG__