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 while (gtk_events_pending())
112 gtk_main_iteration();
114 // disable log flushing from here because a call to wxYield() shouldn't
115 // normally result in message boxes popping up &c
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 if ( wxTheApp
->IsInAssert() )
203 // don't generate the idle events while the assert modal dialog is
204 // shown, this completely confuses the apps which don't expect to be
205 // reentered from some safely-looking functions
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
224 while (wxTheApp
->ProcessIdle()) { }
226 // Release lock again
229 // Return FALSE to indicate that no more idle events are
230 // to be sent (single shot instead of continuous stream)
234 void wxapp_install_idle_handler()
236 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, wxT("attempt to install idle handler twice") );
240 if (g_pendingTag
== 0)
241 g_pendingTag
= gtk_idle_add_priority( 900, wxapp_pending_callback
, (gpointer
) NULL
);
243 /* This routine gets called by all event handlers
244 indicating that the idle is over. It may also
245 get called from other thread for sending events
246 to the main thread (and processing these in
247 idle time). Very low priority. */
249 wxTheApp
->m_idleTag
= gtk_idle_add_priority( 1000, wxapp_idle_callback
, (gpointer
) NULL
);
254 static int g_threadUninstallLevel
= 0;
256 void wxapp_install_thread_wakeup()
258 g_threadUninstallLevel
++;
260 if (g_threadUninstallLevel
!= 1) return;
262 if (wxTheApp
->m_wakeUpTimerTag
) return;
264 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 50, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
267 void wxapp_uninstall_thread_wakeup()
269 g_threadUninstallLevel
--;
271 if (g_threadUninstallLevel
!= 0) return;
273 if (!wxTheApp
->m_wakeUpTimerTag
) return;
275 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
276 wxTheApp
->m_wakeUpTimerTag
= 0;
279 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
281 // when getting called from GDK's time-out handler
282 // we are no longer within GDK's grab on the GUI
283 // thread so we must lock it here ourselves
286 wxapp_uninstall_thread_wakeup();
288 // unblock other threads wishing to do some GUI things
291 g_mainThreadLocked
= TRUE
;
293 // wake up other threads
296 // block other thread again
299 g_mainThreadLocked
= FALSE
;
301 wxapp_install_thread_wakeup();
303 // release lock again
309 #endif // wxUSE_THREADS
311 //-----------------------------------------------------------------------------
313 //-----------------------------------------------------------------------------
315 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
317 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
318 EVT_IDLE(wxApp::OnIdle
)
323 m_initialized
= FALSE
;
325 m_isInAssert
= FALSE
;
326 #endif // __WXDEBUG__
329 wxapp_install_idle_handler();
332 m_wakeUpTimerTag
= 0;
333 wxapp_install_thread_wakeup();
336 m_colorCube
= (unsigned char*) NULL
;
341 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
344 wxapp_uninstall_thread_wakeup();
347 if (m_colorCube
) free(m_colorCube
);
350 bool wxApp::OnInitGui()
352 if ( !wxAppBase::OnInitGui() )
355 GdkVisual
*visual
= gdk_visual_get_system();
357 /* on some machines, the default visual is just 256 colours, so
358 we make sure we get the best. this can sometimes be wasteful,
359 of course, but what do these guys pay $30.000 for? */
361 if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
365 /* seems gtk_widget_set_default_visual no longer exists? */
366 GdkVisual
* vis
= gtk_widget_get_default_visual();
368 GdkVisual
* vis
= gdk_visual_get_best();
369 gtk_widget_set_default_visual( vis
);
372 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
373 gtk_widget_set_default_colormap( colormap
);
378 /* Nothing to do for 15, 16, 24, 32 bit displays */
379 if (visual
->depth
> 8) return TRUE
;
381 /* initialize color cube for 8-bit color reduction dithering */
383 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
385 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
387 for (int r
= 0; r
< 32; r
++)
389 for (int g
= 0; g
< 32; g
++)
391 for (int b
= 0; b
< 32; b
++)
393 int rr
= (r
<< 3) | (r
>> 2);
394 int gg
= (g
<< 3) | (g
>> 2);
395 int bb
= (b
<< 3) | (b
>> 2);
399 GdkColor
*colors
= cmap
->colors
;
404 for (int i
= 0; i
< cmap
->size
; i
++)
406 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
407 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
408 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
409 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
412 index
= i
; max
= sum
;
418 #if (GTK_MINOR_VERSION > 0)
419 /* assume 8-bit true or static colors. this really
421 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
422 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
423 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
424 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
426 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
429 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
437 bool wxApp::ProcessIdle()
440 event
.SetEventObject( this );
441 ProcessEvent( event
);
443 return event
.MoreRequested();
446 void wxApp::OnIdle( wxIdleEvent
&event
)
448 static bool s_inOnIdle
= FALSE
;
450 /* Avoid recursion (via ProcessEvent default case) */
456 /* Resend in the main thread events which have been prepared in other
458 ProcessPendingEvents();
460 /* 'Garbage' collection of windows deleted with Close(). */
461 DeletePendingObjects();
463 /* Send OnIdle events to all windows */
464 bool needMore
= SendIdleEvents();
467 event
.RequestMore(TRUE
);
472 bool wxApp::SendIdleEvents()
474 bool needMore
= FALSE
;
476 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
479 wxWindow
* win
= node
->GetData();
480 if (SendIdleEvents(win
))
482 node
= node
->GetNext();
488 bool wxApp::SendIdleEvents( wxWindow
* win
)
490 bool needMore
= FALSE
;
493 event
.SetEventObject(win
);
495 win
->GetEventHandler()->ProcessEvent(event
);
497 win
->OnInternalIdle();
499 if (event
.MoreRequested())
502 wxNode
* node
= win
->GetChildren().First();
505 wxWindow
* win
= (wxWindow
*) node
->Data();
506 if (SendIdleEvents(win
))
514 int wxApp::MainLoop()
520 void wxApp::ExitMainLoop()
522 if (gtk_main_level() > 0)
526 bool wxApp::Initialized()
528 return m_initialized
;
531 bool wxApp::Pending()
533 return (gtk_events_pending() > 0);
536 void wxApp::Dispatch()
538 gtk_main_iteration();
541 void wxApp::DeletePendingObjects()
543 wxNode
*node
= wxPendingDelete
.First();
546 wxObject
*obj
= (wxObject
*)node
->Data();
550 if (wxPendingDelete
.Find(obj
))
553 node
= wxPendingDelete
.First();
557 bool wxApp::Initialize()
559 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
561 wxClassInfo::InitializeClasses();
563 wxSystemSettings::Init();
566 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
569 // GL: I'm annoyed ... I don't know where to put this and I don't want to
570 // create a module for that as it's part of the core.
572 wxPendingEvents
= new wxList();
573 wxPendingEventsLocker
= new wxCriticalSection();
576 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
577 wxTheColourDatabase
->Initialize();
579 wxInitializeStockLists();
580 wxInitializeStockObjects();
582 #if wxUSE_WX_RESOURCES
583 wxInitializeResourceSystem();
586 wxModule::RegisterModules();
587 if (!wxModule::InitializeModules()) return FALSE
;
592 void wxApp::CleanUp()
594 wxModule::CleanUpModules();
596 #if wxUSE_WX_RESOURCES
597 wxCleanUpResourceSystem();
600 if (wxTheColourDatabase
)
601 delete wxTheColourDatabase
;
603 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
605 wxDeleteStockObjects();
607 wxDeleteStockLists();
610 wxTheApp
= (wxApp
*) NULL
;
612 // GL: I'm annoyed ... I don't know where to put this and I don't want to
613 // create a module for that as it's part of the core.
615 delete wxPendingEvents
;
616 delete wxPendingEventsLocker
;
619 wxSystemSettings::Done();
623 wxClassInfo::CleanUpClasses();
625 // check for memory leaks
626 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
627 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
629 wxLogDebug(wxT("There were memory leaks.\n"));
630 wxDebugContext::Dump();
631 wxDebugContext::PrintStatistics();
636 // do this as the very last thing because everything else can log messages
637 wxLog::DontCreateOnDemand();
639 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
645 //-----------------------------------------------------------------------------
646 // Access to the root window global
647 //-----------------------------------------------------------------------------
649 GtkWidget
* wxGetRootWindow()
651 if (gs_RootWindow
== NULL
) {
652 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
653 gtk_widget_realize( gs_RootWindow
);
655 return gs_RootWindow
;
658 //-----------------------------------------------------------------------------
660 //-----------------------------------------------------------------------------
663 int wxEntryStart( int argc
, char *argv
[] )
666 /* GTK 1.2 up to version 1.2.3 has broken threads */
667 if ((gtk_major_version
== 1) &&
668 (gtk_minor_version
== 2) &&
669 (gtk_micro_version
< 4))
671 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
681 // We should have the wxUSE_WCHAR_T test on the _outside_
683 #if defined(__WXGTK20__)
684 // gtk+ 2.0 supports Unicode through UTF-8 strings
685 wxConvCurrent
= &wxConvUTF8
;
687 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
690 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
695 gtk_init( &argc
, &argv
);
697 wxSetDetectableAutoRepeat( TRUE
);
699 if (!wxApp::Initialize())
713 if ( !wxTheApp
->OnInitGui() )
722 void wxEntryCleanup()
725 // flush the logged messages if any
726 wxLog
*log
= wxLog::GetActiveTarget();
727 if (log
!= NULL
&& log
->HasPendingMessages())
730 // continuing to use user defined log target is unsafe from now on because
731 // some resources may be already unavailable, so replace it by something
733 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
745 int wxEntry( int argc
, char *argv
[] )
747 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
748 // This seems to be necessary since there are 'rogue'
749 // objects present at this point (perhaps global objects?)
750 // Setting a checkpoint will ignore them as far as the
751 // memory checking facility is concerned.
752 // Of course you may argue that memory allocated in globals should be
753 // checked, but this is a reasonable compromise.
754 wxDebugContext::SetCheckpoint();
756 int err
= wxEntryStart(argc
, argv
);
762 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
763 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
765 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
767 wxObject
*test_app
= app_ini();
769 wxTheApp
= (wxApp
*) test_app
;
772 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
774 wxTheApp
->argc
= argc
;
776 wxTheApp
->argv
= new wxChar
*[argc
+1];
778 while (mb_argc
< argc
)
780 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
783 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
785 wxTheApp
->argv
= argv
;
788 wxString
name(wxFileNameFromPath(argv
[0]));
789 wxStripExtension( name
);
790 wxTheApp
->SetAppName( name
);
793 retValue
= wxEntryInitGui();
795 // Here frames insert themselves automatically into wxTopLevelWindows by
796 // getting created in OnInit().
799 if ( !wxTheApp
->OnInit() )
805 /* delete pending toplevel windows (typically a single
806 dialog) so that, if there isn't any left, we don't
808 wxTheApp
->DeletePendingObjects();
810 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
812 if (wxTheApp
->Initialized())
816 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
819 /* Forcibly delete the window. */
820 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
821 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
823 topWindow
->Close( TRUE
);
824 wxTheApp
->DeletePendingObjects();
829 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
833 retValue
= wxTheApp
->OnExit();
842 #include "wx/gtk/info.xpm"
843 #include "wx/gtk/error.xpm"
844 #include "wx/gtk/question.xpm"
845 #include "wx/gtk/warning.xpm"
848 wxApp::GetStdIcon(int which
) const
852 case wxICON_INFORMATION
:
853 return wxIcon(info_xpm
);
855 case wxICON_QUESTION
:
856 return wxIcon(question_xpm
);
858 case wxICON_EXCLAMATION
:
859 return wxIcon(warning_xpm
);
862 wxFAIL_MSG(wxT("requested non existent standard icon"));
863 // still fall through
866 return wxIcon(error_xpm
);
872 void wxApp::OnAssert(const wxChar
*file
, int line
, const wxChar
*msg
)
876 wxAppBase::OnAssert(file
, line
, msg
);
878 m_isInAssert
= FALSE
;
881 #endif // __WXDEBUG__