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
) )
197 if (!wxTheApp
) return TRUE
;
199 // when getting called from GDK's time-out handler
200 // we are no longer within GDK's grab on the GUI
201 // thread so we must lock it here ourselves
204 /* Indicate that we are now in idle mode - even so deeply
205 in idle mode that we don't get any idle events anymore.
206 this is like wxMSW where an idle event is sent only
207 once each time after the event queue has been completely
210 wxTheApp
->m_idleTag
= 0;
212 // Sent idle event to all who request them
213 while (wxTheApp
->ProcessIdle()) { }
215 // Release lock again
218 // Return FALSE to indicate that no more idle events are
219 // to be sent (single shot instead of continuous stream)
223 void wxapp_install_idle_handler()
225 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, wxT("attempt to install idle handler twice") );
229 if (g_pendingTag
== 0)
230 g_pendingTag
= gtk_idle_add_priority( 900, wxapp_pending_callback
, (gpointer
) NULL
);
232 /* This routine gets called by all event handlers
233 indicating that the idle is over. It may also
234 get called from other thread for sending events
235 to the main thread (and processing these in
236 idle time). Very low priority. */
238 wxTheApp
->m_idleTag
= gtk_idle_add_priority( 1000, wxapp_idle_callback
, (gpointer
) NULL
);
243 static int g_threadUninstallLevel
= 0;
245 void wxapp_install_thread_wakeup()
247 g_threadUninstallLevel
++;
249 if (g_threadUninstallLevel
!= 1) return;
251 if (wxTheApp
->m_wakeUpTimerTag
) return;
253 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 50, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
256 void wxapp_uninstall_thread_wakeup()
258 g_threadUninstallLevel
--;
260 if (g_threadUninstallLevel
!= 0) return;
262 if (!wxTheApp
->m_wakeUpTimerTag
) return;
264 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
265 wxTheApp
->m_wakeUpTimerTag
= 0;
268 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
270 // when getting called from GDK's time-out handler
271 // we are no longer within GDK's grab on the GUI
272 // thread so we must lock it here ourselves
275 wxapp_uninstall_thread_wakeup();
277 // unblock other threads wishing to do some GUI things
280 g_mainThreadLocked
= TRUE
;
282 // wake up other threads
285 // block other thread again
288 g_mainThreadLocked
= FALSE
;
290 wxapp_install_thread_wakeup();
292 // release lock again
298 #endif // wxUSE_THREADS
300 //-----------------------------------------------------------------------------
302 //-----------------------------------------------------------------------------
304 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
306 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
307 EVT_IDLE(wxApp::OnIdle
)
314 m_topWindow
= (wxWindow
*) NULL
;
315 m_exitOnFrameDelete
= TRUE
;
318 wxapp_install_idle_handler();
321 m_wakeUpTimerTag
= 0;
322 wxapp_install_thread_wakeup();
325 m_colorCube
= (unsigned char*) NULL
;
327 m_useBestVisual
= FALSE
;
332 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
335 wxapp_uninstall_thread_wakeup();
338 if (m_colorCube
) free(m_colorCube
);
341 bool wxApp::OnInitGui()
343 GdkVisual
*visual
= gdk_visual_get_system();
345 /* on some machines, the default visual is just 256 colours, so
346 we make sure we get the best. this can sometimes be wasteful,
347 of course, but what do these guys pay $30.000 for? */
349 if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
353 /* seems gtk_widget_set_default_visual no longer exists? */
354 GdkVisual
* vis
= gtk_widget_get_default_visual();
356 GdkVisual
* vis
= gdk_visual_get_best();
357 gtk_widget_set_default_visual( vis
);
360 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
361 gtk_widget_set_default_colormap( colormap
);
366 /* Nothing to do for 15, 16, 24, 32 bit displays */
367 if (visual
->depth
> 8) return TRUE
;
369 /* initialize color cube for 8-bit color reduction dithering */
371 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
373 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
375 for (int r
= 0; r
< 32; r
++)
377 for (int g
= 0; g
< 32; g
++)
379 for (int b
= 0; b
< 32; b
++)
381 int rr
= (r
<< 3) | (r
>> 2);
382 int gg
= (g
<< 3) | (g
>> 2);
383 int bb
= (b
<< 3) | (b
>> 2);
387 GdkColor
*colors
= cmap
->colors
;
392 for (int i
= 0; i
< cmap
->size
; i
++)
394 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
395 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
396 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
397 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
400 index
= i
; max
= sum
;
406 #if (GTK_MINOR_VERSION > 0)
407 /* assume 8-bit true or static colors. this really
409 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
410 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
411 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
412 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
414 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
417 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
425 bool wxApp::ProcessIdle()
428 event
.SetEventObject( this );
429 ProcessEvent( event
);
431 return event
.MoreRequested();
434 void wxApp::OnIdle( wxIdleEvent
&event
)
436 static bool s_inOnIdle
= FALSE
;
438 /* Avoid recursion (via ProcessEvent default case) */
444 /* Resend in the main thread events which have been prepared in other
446 ProcessPendingEvents();
448 /* 'Garbage' collection of windows deleted with Close(). */
449 DeletePendingObjects();
451 /* Send OnIdle events to all windows */
452 bool needMore
= SendIdleEvents();
455 event
.RequestMore(TRUE
);
460 bool wxApp::SendIdleEvents()
462 bool needMore
= FALSE
;
464 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
467 wxWindow
* win
= node
->GetData();
468 if (SendIdleEvents(win
))
470 node
= node
->GetNext();
476 bool wxApp::SendIdleEvents( wxWindow
* win
)
478 bool needMore
= FALSE
;
481 event
.SetEventObject(win
);
483 win
->GetEventHandler()->ProcessEvent(event
);
485 win
->OnInternalIdle();
487 if (event
.MoreRequested())
490 wxNode
* node
= win
->GetChildren().First();
493 wxWindow
* win
= (wxWindow
*) node
->Data();
494 if (SendIdleEvents(win
))
502 int wxApp::MainLoop()
508 void wxApp::ExitMainLoop()
510 if (gtk_main_level() > 0)
514 bool wxApp::Initialized()
516 return m_initialized
;
519 bool wxApp::Pending()
521 return (gtk_events_pending() > 0);
524 void wxApp::Dispatch()
526 gtk_main_iteration();
529 void wxApp::DeletePendingObjects()
531 wxNode
*node
= wxPendingDelete
.First();
534 wxObject
*obj
= (wxObject
*)node
->Data();
538 if (wxPendingDelete
.Find(obj
))
541 node
= wxPendingDelete
.First();
545 bool wxApp::Initialize()
547 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
549 wxClassInfo::InitializeClasses();
551 wxSystemSettings::Init();
553 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
555 // GL: I'm annoyed ... I don't know where to put this and I don't want to
556 // create a module for that as it's part of the core.
558 wxPendingEvents
= new wxList();
559 wxPendingEventsLocker
= new wxCriticalSection();
562 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
563 wxTheColourDatabase
->Initialize();
565 wxInitializeStockLists();
566 wxInitializeStockObjects();
568 #if wxUSE_WX_RESOURCES
569 wxInitializeResourceSystem();
572 wxModule::RegisterModules();
573 if (!wxModule::InitializeModules()) return FALSE
;
578 void wxApp::CleanUp()
580 wxModule::CleanUpModules();
582 #if wxUSE_WX_RESOURCES
583 wxCleanUpResourceSystem();
586 if (wxTheColourDatabase
)
587 delete wxTheColourDatabase
;
589 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
591 wxDeleteStockObjects();
593 wxDeleteStockLists();
596 wxTheApp
= (wxApp
*) NULL
;
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 delete wxPendingEvents
;
602 delete wxPendingEventsLocker
;
605 wxSystemSettings::Done();
609 wxClassInfo::CleanUpClasses();
611 // check for memory leaks
612 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
613 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
615 wxLogDebug(wxT("There were memory leaks.\n"));
616 wxDebugContext::Dump();
617 wxDebugContext::PrintStatistics();
622 // do this as the very last thing because everything else can log messages
623 wxLog::DontCreateOnDemand();
625 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
631 //-----------------------------------------------------------------------------
632 // Access to the root window global
633 //-----------------------------------------------------------------------------
635 GtkWidget
* wxGetRootWindow()
637 if (gs_RootWindow
== NULL
) {
638 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
639 gtk_widget_realize( gs_RootWindow
);
641 return gs_RootWindow
;
644 //-----------------------------------------------------------------------------
646 //-----------------------------------------------------------------------------
649 int wxEntryStart( int argc
, char *argv
[] )
652 /* GTK 1.2 up to version 1.2.3 has broken threads */
653 if ((gtk_major_version
== 1) &&
654 (gtk_minor_version
== 2) &&
655 (gtk_micro_version
< 4))
657 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
667 // We should have the wxUSE_WCHAR_T test on the _outside_
669 #if defined(__WXGTK20__)
670 // gtk+ 2.0 supports Unicode through UTF-8 strings
671 wxConvCurrent
= &wxConvUTF8
;
673 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
676 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
681 gtk_init( &argc
, &argv
);
683 wxSetDetectableAutoRepeat( TRUE
);
685 if (!wxApp::Initialize())
699 if ( !wxTheApp
->OnInitGui() )
708 void wxEntryCleanup()
711 // flush the logged messages if any
712 wxLog
*log
= wxLog::GetActiveTarget();
713 if (log
!= NULL
&& log
->HasPendingMessages())
716 // continuing to use user defined log target is unsafe from now on because
717 // some resources may be already unavailable, so replace it by something
719 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
731 int wxEntry( int argc
, char *argv
[] )
733 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
734 // This seems to be necessary since there are 'rogue'
735 // objects present at this point (perhaps global objects?)
736 // Setting a checkpoint will ignore them as far as the
737 // memory checking facility is concerned.
738 // Of course you may argue that memory allocated in globals should be
739 // checked, but this is a reasonable compromise.
740 wxDebugContext::SetCheckpoint();
742 int err
= wxEntryStart(argc
, argv
);
748 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
749 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
751 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
753 wxObject
*test_app
= app_ini();
755 wxTheApp
= (wxApp
*) test_app
;
758 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
760 wxTheApp
->argc
= argc
;
762 wxTheApp
->argv
= new wxChar
*[argc
+1];
764 while (mb_argc
< argc
)
766 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
769 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
771 wxTheApp
->argv
= argv
;
774 wxString
name(wxFileNameFromPath(argv
[0]));
775 wxStripExtension( name
);
776 wxTheApp
->SetAppName( name
);
779 retValue
= wxEntryInitGui();
781 // Here frames insert themselves automatically into wxTopLevelWindows by
782 // getting created in OnInit().
785 if ( !wxTheApp
->OnInit() )
791 /* delete pending toplevel windows (typically a single
792 dialog) so that, if there isn't any left, we don't
794 wxTheApp
->DeletePendingObjects();
796 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
798 if (wxTheApp
->Initialized())
802 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
805 /* Forcibly delete the window. */
806 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
807 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
809 topWindow
->Close( TRUE
);
810 wxTheApp
->DeletePendingObjects();
815 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
819 retValue
= wxTheApp
->OnExit();
828 #include "wx/gtk/info.xpm"
829 #include "wx/gtk/error.xpm"
830 #include "wx/gtk/question.xpm"
831 #include "wx/gtk/warning.xpm"
834 wxApp::GetStdIcon(int which
) const
838 case wxICON_INFORMATION
:
839 return wxIcon(info_xpm
);
841 case wxICON_QUESTION
:
842 return wxIcon(question_xpm
);
844 case wxICON_EXCLAMATION
:
845 return wxIcon(warning_xpm
);
848 wxFAIL_MSG(wxT("requested non existent standard icon"));
849 // still fall through
852 return wxIcon(error_xpm
);