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"
32 #include "wx/thread.h"
41 #include "wx/gtk/win_gtk.h"
43 //-----------------------------------------------------------------------------
45 //-----------------------------------------------------------------------------
47 wxApp
*wxTheApp
= (wxApp
*) NULL
;
48 wxAppInitializerFunction
wxAppBase::m_appInitFn
= (wxAppInitializerFunction
) NULL
;
52 bool g_mainThreadLocked
= FALSE
;
53 gint g_pendingTag
= 0;
55 static GtkWidget
*gs_RootWindow
= (GtkWidget
*) NULL
;
57 //-----------------------------------------------------------------------------
59 //-----------------------------------------------------------------------------
61 /* forward declaration */
62 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) );
63 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) );
64 void wxapp_install_idle_handler();
67 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) );
70 //-----------------------------------------------------------------------------
72 //-----------------------------------------------------------------------------
79 //-----------------------------------------------------------------------------
81 //-----------------------------------------------------------------------------
83 static bool gs_inYield
= FALSE
;
88 if ( !wxThread::IsMain() )
90 // can't call gtk_main_iteration() from other threads like this
93 #endif // wxUSE_THREADS
97 wxFAIL_MSG( wxT("wxYield called recursively" ) );
104 // We need to remove idle callbacks or the loop will
106 gtk_idle_remove( wxTheApp
->m_idleTag
);
107 wxTheApp
->m_idleTag
= 0;
111 // disable log flushing from here because a call to wxYield() shouldn't
112 // normally result in message boxes popping up &c
115 while (gtk_events_pending())
116 gtk_main_iteration();
118 /* it's necessary to call ProcessIdle() to update the frames sizes which
119 might have been changed (it also will update other things set from
120 OnUpdateUI() which is a nice (and desired) side effect) */
121 while (wxTheApp
->ProcessIdle()) { }
123 // let the logs be flashed again
131 //-----------------------------------------------------------------------------
133 // Like wxYield, but fails silently if the yield is recursive.
134 //-----------------------------------------------------------------------------
136 bool wxYieldIfNeeded()
144 //-----------------------------------------------------------------------------
146 //-----------------------------------------------------------------------------
151 if (!wxThread::IsMain())
156 wxapp_install_idle_handler();
159 if (!wxThread::IsMain())
164 //-----------------------------------------------------------------------------
166 //-----------------------------------------------------------------------------
168 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) )
170 if (!wxTheApp
) return TRUE
;
172 // when getting called from GDK's time-out handler
173 // we are no longer within GDK's grab on the GUI
174 // thread so we must lock it here ourselves
177 // Sent idle event to all who request them
178 wxTheApp
->ProcessPendingEvents();
182 /* flush the logged messages if any */
184 wxLog::FlushActive();
187 // Release lock again
190 // Return FALSE to indicate that no more idle events are
191 // to be sent (single shot instead of continuous stream)
195 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
201 // don't generate the idle events while the assert modal dialog is shown,
202 // this completely confuses the apps which don't expect to be reentered
203 // from some safely-looking functions
204 if ( wxTheApp
->IsInAssert() )
208 #endif // __WXDEBUG__
210 // when getting called from GDK's time-out handler
211 // we are no longer within GDK's grab on the GUI
212 // thread so we must lock it here ourselves
215 /* Indicate that we are now in idle mode - even so deeply
216 in idle mode that we don't get any idle events anymore.
217 this is like wxMSW where an idle event is sent only
218 once each time after the event queue has been completely
221 wxTheApp
->m_idleTag
= 0;
223 // Sent idle event to all who request them as long as they do
224 while (wxTheApp
->ProcessIdle())
227 // Release lock again
230 // Return FALSE to indicate that no more idle events are
231 // to be sent (single shot instead of continuous stream)
235 void wxapp_install_idle_handler()
237 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, wxT("attempt to install idle handler twice") );
241 if (g_pendingTag
== 0)
242 g_pendingTag
= gtk_idle_add_priority( 900, wxapp_pending_callback
, (gpointer
) NULL
);
244 /* This routine gets called by all event handlers
245 indicating that the idle is over. It may also
246 get called from other thread for sending events
247 to the main thread (and processing these in
248 idle time). Very low priority. */
250 wxTheApp
->m_idleTag
= gtk_idle_add_priority( 1000, wxapp_idle_callback
, (gpointer
) NULL
);
255 static int g_threadUninstallLevel
= 0;
257 void wxapp_install_thread_wakeup()
259 g_threadUninstallLevel
++;
261 if (g_threadUninstallLevel
!= 1) return;
263 if (wxTheApp
->m_wakeUpTimerTag
) return;
265 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 50, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
268 void wxapp_uninstall_thread_wakeup()
270 g_threadUninstallLevel
--;
272 if (g_threadUninstallLevel
!= 0) return;
274 if (!wxTheApp
->m_wakeUpTimerTag
) return;
276 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
277 wxTheApp
->m_wakeUpTimerTag
= 0;
280 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
282 // when getting called from GDK's time-out handler
283 // we are no longer within GDK's grab on the GUI
284 // thread so we must lock it here ourselves
287 wxapp_uninstall_thread_wakeup();
289 // unblock other threads wishing to do some GUI things
292 g_mainThreadLocked
= TRUE
;
294 // wake up other threads
297 // block other thread again
300 g_mainThreadLocked
= FALSE
;
302 wxapp_install_thread_wakeup();
304 // release lock again
310 #endif // wxUSE_THREADS
312 //-----------------------------------------------------------------------------
314 //-----------------------------------------------------------------------------
316 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
318 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
319 EVT_IDLE(wxApp::OnIdle
)
324 m_initialized
= FALSE
;
326 m_isInAssert
= FALSE
;
327 #endif // __WXDEBUG__
330 wxapp_install_idle_handler();
333 m_wakeUpTimerTag
= 0;
334 wxapp_install_thread_wakeup();
337 m_colorCube
= (unsigned char*) NULL
;
342 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
345 wxapp_uninstall_thread_wakeup();
348 if (m_colorCube
) free(m_colorCube
);
351 bool wxApp::OnInitGui()
353 if ( !wxAppBase::OnInitGui() )
356 GdkVisual
*visual
= gdk_visual_get_system();
358 /* on some machines, the default visual is just 256 colours, so
359 we make sure we get the best. this can sometimes be wasteful,
360 of course, but what do these guys pay $30.000 for? */
362 if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
366 /* seems gtk_widget_set_default_visual no longer exists? */
367 GdkVisual
* vis
= gtk_widget_get_default_visual();
369 GdkVisual
* vis
= gdk_visual_get_best();
370 gtk_widget_set_default_visual( vis
);
373 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
374 gtk_widget_set_default_colormap( colormap
);
379 /* Nothing to do for 15, 16, 24, 32 bit displays */
380 if (visual
->depth
> 8) return TRUE
;
382 /* initialize color cube for 8-bit color reduction dithering */
384 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
386 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
388 for (int r
= 0; r
< 32; r
++)
390 for (int g
= 0; g
< 32; g
++)
392 for (int b
= 0; b
< 32; b
++)
394 int rr
= (r
<< 3) | (r
>> 2);
395 int gg
= (g
<< 3) | (g
>> 2);
396 int bb
= (b
<< 3) | (b
>> 2);
400 GdkColor
*colors
= cmap
->colors
;
405 for (int i
= 0; i
< cmap
->size
; i
++)
407 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
408 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
409 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
410 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
413 index
= i
; max
= sum
;
419 #if (GTK_MINOR_VERSION > 0)
420 /* assume 8-bit true or static colors. this really
422 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
423 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
424 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
425 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
427 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
430 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
438 bool wxApp::ProcessIdle()
441 event
.SetEventObject( this );
442 ProcessEvent( event
);
444 return event
.MoreRequested();
447 void wxApp::OnIdle( wxIdleEvent
&event
)
449 static bool s_inOnIdle
= FALSE
;
451 /* Avoid recursion (via ProcessEvent default case) */
457 /* Resend in the main thread events which have been prepared in other
459 ProcessPendingEvents();
461 /* 'Garbage' collection of windows deleted with Close(). */
462 DeletePendingObjects();
464 /* Send OnIdle events to all windows */
465 bool needMore
= SendIdleEvents();
468 event
.RequestMore(TRUE
);
473 bool wxApp::SendIdleEvents()
475 bool needMore
= FALSE
;
477 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
480 wxWindow
* win
= node
->GetData();
481 if (SendIdleEvents(win
))
483 node
= node
->GetNext();
489 bool wxApp::SendIdleEvents( wxWindow
* win
)
491 bool needMore
= FALSE
;
494 event
.SetEventObject(win
);
496 win
->GetEventHandler()->ProcessEvent(event
);
498 win
->OnInternalIdle();
500 if (event
.MoreRequested())
503 wxNode
* node
= win
->GetChildren().First();
506 wxWindow
* win
= (wxWindow
*) node
->Data();
507 if (SendIdleEvents(win
))
515 int wxApp::MainLoop()
521 void wxApp::ExitMainLoop()
523 if (gtk_main_level() > 0)
527 bool wxApp::Initialized()
529 return m_initialized
;
532 bool wxApp::Pending()
534 return (gtk_events_pending() > 0);
537 void wxApp::Dispatch()
539 gtk_main_iteration();
542 void wxApp::DeletePendingObjects()
544 wxNode
*node
= wxPendingDelete
.First();
547 wxObject
*obj
= (wxObject
*)node
->Data();
551 if (wxPendingDelete
.Find(obj
))
554 node
= wxPendingDelete
.First();
558 bool wxApp::Initialize()
560 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
562 wxClassInfo::InitializeClasses();
564 wxSystemSettings::Init();
567 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
570 // GL: I'm annoyed ... I don't know where to put this and I don't want to
571 // create a module for that as it's part of the core.
573 wxPendingEvents
= new wxList();
574 wxPendingEventsLocker
= new wxCriticalSection();
577 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
578 wxTheColourDatabase
->Initialize();
580 wxInitializeStockLists();
581 wxInitializeStockObjects();
583 #if wxUSE_WX_RESOURCES
584 wxInitializeResourceSystem();
587 wxModule::RegisterModules();
588 if (!wxModule::InitializeModules()) return FALSE
;
593 void wxApp::CleanUp()
595 wxModule::CleanUpModules();
597 #if wxUSE_WX_RESOURCES
598 wxCleanUpResourceSystem();
601 if (wxTheColourDatabase
)
602 delete wxTheColourDatabase
;
604 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
606 wxDeleteStockObjects();
608 wxDeleteStockLists();
611 wxTheApp
= (wxApp
*) NULL
;
613 // GL: I'm annoyed ... I don't know where to put this and I don't want to
614 // create a module for that as it's part of the core.
616 delete wxPendingEvents
;
617 delete wxPendingEventsLocker
;
620 wxSystemSettings::Done();
624 wxClassInfo::CleanUpClasses();
626 // check for memory leaks
627 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
628 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
630 wxLogDebug(wxT("There were memory leaks.\n"));
631 wxDebugContext::Dump();
632 wxDebugContext::PrintStatistics();
637 // do this as the very last thing because everything else can log messages
638 wxLog::DontCreateOnDemand();
640 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
646 //-----------------------------------------------------------------------------
647 // Access to the root window global
648 //-----------------------------------------------------------------------------
650 GtkWidget
* wxGetRootWindow()
652 if (gs_RootWindow
== NULL
) {
653 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
654 gtk_widget_realize( gs_RootWindow
);
656 return gs_RootWindow
;
659 //-----------------------------------------------------------------------------
661 //-----------------------------------------------------------------------------
664 int wxEntryStart( int argc
, char *argv
[] )
667 /* GTK 1.2 up to version 1.2.3 has broken threads */
668 if ((gtk_major_version
== 1) &&
669 (gtk_minor_version
== 2) &&
670 (gtk_micro_version
< 4))
672 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
682 // We should have the wxUSE_WCHAR_T test on the _outside_
684 #if defined(__WXGTK20__)
685 // gtk+ 2.0 supports Unicode through UTF-8 strings
686 wxConvCurrent
= &wxConvUTF8
;
688 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
691 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
696 gtk_init( &argc
, &argv
);
698 wxSetDetectableAutoRepeat( TRUE
);
700 if (!wxApp::Initialize())
714 if ( !wxTheApp
->OnInitGui() )
723 void wxEntryCleanup()
726 // flush the logged messages if any
727 wxLog
*log
= wxLog::GetActiveTarget();
728 if (log
!= NULL
&& log
->HasPendingMessages())
731 // continuing to use user defined log target is unsafe from now on because
732 // some resources may be already unavailable, so replace it by something
734 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
746 int wxEntry( int argc
, char *argv
[] )
748 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
749 // This seems to be necessary since there are 'rogue'
750 // objects present at this point (perhaps global objects?)
751 // Setting a checkpoint will ignore them as far as the
752 // memory checking facility is concerned.
753 // Of course you may argue that memory allocated in globals should be
754 // checked, but this is a reasonable compromise.
755 wxDebugContext::SetCheckpoint();
757 int err
= wxEntryStart(argc
, argv
);
763 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
764 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
766 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
768 wxObject
*test_app
= app_ini();
770 wxTheApp
= (wxApp
*) test_app
;
773 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
775 wxTheApp
->argc
= argc
;
777 wxTheApp
->argv
= new wxChar
*[argc
+1];
779 while (mb_argc
< argc
)
781 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
784 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
786 wxTheApp
->argv
= argv
;
789 wxString
name(wxFileNameFromPath(argv
[0]));
790 wxStripExtension( name
);
791 wxTheApp
->SetAppName( name
);
794 retValue
= wxEntryInitGui();
796 // Here frames insert themselves automatically into wxTopLevelWindows by
797 // getting created in OnInit().
800 if ( !wxTheApp
->OnInit() )
806 /* delete pending toplevel windows (typically a single
807 dialog) so that, if there isn't any left, we don't
809 wxTheApp
->DeletePendingObjects();
811 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
813 if (wxTheApp
->Initialized())
817 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
820 /* Forcibly delete the window. */
821 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
822 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
824 topWindow
->Close( TRUE
);
825 wxTheApp
->DeletePendingObjects();
830 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
834 retValue
= wxTheApp
->OnExit();
843 #include "wx/gtk/info.xpm"
844 #include "wx/gtk/error.xpm"
845 #include "wx/gtk/question.xpm"
846 #include "wx/gtk/warning.xpm"
849 wxApp::GetStdIcon(int which
) const
853 case wxICON_INFORMATION
:
854 return wxIcon(info_xpm
);
856 case wxICON_QUESTION
:
857 return wxIcon(question_xpm
);
859 case wxICON_EXCLAMATION
:
860 return wxIcon(warning_xpm
);
863 wxFAIL_MSG(wxT("requested non existent standard icon"));
864 // still fall through
867 return wxIcon(error_xpm
);
873 void wxApp::OnAssert(const wxChar
*file
, int line
, const wxChar
*msg
)
877 wxAppBase::OnAssert(file
, line
, msg
);
879 m_isInAssert
= FALSE
;
882 #endif // __WXDEBUG__