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 // GL: I'm annoyed ... I don't know where to put this and I don't want to
554 // create a module for that as it's part of the core.
556 wxPendingEvents
= new wxList();
557 wxPendingEventsLocker
= new wxCriticalSection();
560 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
561 wxTheColourDatabase
->Initialize();
563 wxInitializeStockLists();
564 wxInitializeStockObjects();
566 #if wxUSE_WX_RESOURCES
567 wxInitializeResourceSystem();
570 wxModule::RegisterModules();
571 if (!wxModule::InitializeModules()) return FALSE
;
576 void wxApp::CleanUp()
578 wxModule::CleanUpModules();
580 #if wxUSE_WX_RESOURCES
581 wxCleanUpResourceSystem();
584 if (wxTheColourDatabase
)
585 delete wxTheColourDatabase
;
587 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
589 wxDeleteStockObjects();
591 wxDeleteStockLists();
594 wxTheApp
= (wxApp
*) NULL
;
596 // GL: I'm annoyed ... I don't know where to put this and I don't want to
597 // create a module for that as it's part of the core.
599 delete wxPendingEvents
;
600 delete wxPendingEventsLocker
;
603 wxSystemSettings::Done();
607 wxClassInfo::CleanUpClasses();
609 // check for memory leaks
610 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
611 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
613 wxLogDebug(wxT("There were memory leaks.\n"));
614 wxDebugContext::Dump();
615 wxDebugContext::PrintStatistics();
620 // do this as the very last thing because everything else can log messages
621 wxLog::DontCreateOnDemand();
623 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
629 //-----------------------------------------------------------------------------
630 // Access to the root window global
631 //-----------------------------------------------------------------------------
633 GtkWidget
* wxGetRootWindow()
635 if (gs_RootWindow
== NULL
) {
636 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
637 gtk_widget_realize( gs_RootWindow
);
639 return gs_RootWindow
;
642 //-----------------------------------------------------------------------------
644 //-----------------------------------------------------------------------------
647 int wxEntryStart( int argc
, char *argv
[] )
650 /* GTK 1.2 up to version 1.2.3 has broken threads */
651 if ((gtk_major_version
== 1) &&
652 (gtk_minor_version
== 2) &&
653 (gtk_micro_version
< 4))
655 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
665 // We should have the wxUSE_WCHAR_T test on the _outside_
667 #if defined(__WXGTK20__)
668 // gtk+ 2.0 supports Unicode through UTF-8 strings
669 wxConvCurrent
= &wxConvUTF8
;
671 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
674 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
679 gtk_init( &argc
, &argv
);
681 wxSetDetectableAutoRepeat( TRUE
);
683 if (!wxApp::Initialize())
697 if ( !wxTheApp
->OnInitGui() )
706 void wxEntryCleanup()
709 // flush the logged messages if any
710 wxLog
*log
= wxLog::GetActiveTarget();
711 if (log
!= NULL
&& log
->HasPendingMessages())
714 // continuing to use user defined log target is unsafe from now on because
715 // some resources may be already unavailable, so replace it by something
717 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
729 int wxEntry( int argc
, char *argv
[] )
731 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
732 // This seems to be necessary since there are 'rogue'
733 // objects present at this point (perhaps global objects?)
734 // Setting a checkpoint will ignore them as far as the
735 // memory checking facility is concerned.
736 // Of course you may argue that memory allocated in globals should be
737 // checked, but this is a reasonable compromise.
738 wxDebugContext::SetCheckpoint();
740 int err
= wxEntryStart(argc
, argv
);
746 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
747 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
749 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
751 wxObject
*test_app
= app_ini();
753 wxTheApp
= (wxApp
*) test_app
;
756 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
758 wxTheApp
->argc
= argc
;
760 wxTheApp
->argv
= new wxChar
*[argc
+1];
762 while (mb_argc
< argc
)
764 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
767 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
769 wxTheApp
->argv
= argv
;
772 wxString
name(wxFileNameFromPath(argv
[0]));
773 wxStripExtension( name
);
774 wxTheApp
->SetAppName( name
);
777 retValue
= wxEntryInitGui();
779 // Here frames insert themselves automatically into wxTopLevelWindows by
780 // getting created in OnInit().
783 if ( !wxTheApp
->OnInit() )
789 /* delete pending toplevel windows (typically a single
790 dialog) so that, if there isn't any left, we don't
792 wxTheApp
->DeletePendingObjects();
794 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
796 if (wxTheApp
->Initialized())
800 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
803 /* Forcibly delete the window. */
804 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
805 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
807 topWindow
->Close( TRUE
);
808 wxTheApp
->DeletePendingObjects();
813 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
817 retValue
= wxTheApp
->OnExit();
826 #include "wx/gtk/info.xpm"
827 #include "wx/gtk/error.xpm"
828 #include "wx/gtk/question.xpm"
829 #include "wx/gtk/warning.xpm"
832 wxApp::GetStdIcon(int which
) const
836 case wxICON_INFORMATION
:
837 return wxIcon(info_xpm
);
839 case wxICON_QUESTION
:
840 return wxIcon(question_xpm
);
842 case wxICON_EXCLAMATION
:
843 return wxIcon(warning_xpm
);
846 wxFAIL_MSG(wxT("requested non existent standard icon"));
847 // still fall through
850 return wxIcon(error_xpm
);