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();
554 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
557 // GL: I'm annoyed ... I don't know where to put this and I don't want to
558 // create a module for that as it's part of the core.
560 wxPendingEvents
= new wxList();
561 wxPendingEventsLocker
= new wxCriticalSection();
564 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
565 wxTheColourDatabase
->Initialize();
567 wxInitializeStockLists();
568 wxInitializeStockObjects();
570 #if wxUSE_WX_RESOURCES
571 wxInitializeResourceSystem();
574 wxModule::RegisterModules();
575 if (!wxModule::InitializeModules()) return FALSE
;
580 void wxApp::CleanUp()
582 wxModule::CleanUpModules();
584 #if wxUSE_WX_RESOURCES
585 wxCleanUpResourceSystem();
588 if (wxTheColourDatabase
)
589 delete wxTheColourDatabase
;
591 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
593 wxDeleteStockObjects();
595 wxDeleteStockLists();
598 wxTheApp
= (wxApp
*) NULL
;
600 // GL: I'm annoyed ... I don't know where to put this and I don't want to
601 // create a module for that as it's part of the core.
603 delete wxPendingEvents
;
604 delete wxPendingEventsLocker
;
607 wxSystemSettings::Done();
611 wxClassInfo::CleanUpClasses();
613 // check for memory leaks
614 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
615 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
617 wxLogDebug(wxT("There were memory leaks.\n"));
618 wxDebugContext::Dump();
619 wxDebugContext::PrintStatistics();
624 // do this as the very last thing because everything else can log messages
625 wxLog::DontCreateOnDemand();
627 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
633 //-----------------------------------------------------------------------------
634 // Access to the root window global
635 //-----------------------------------------------------------------------------
637 GtkWidget
* wxGetRootWindow()
639 if (gs_RootWindow
== NULL
) {
640 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
641 gtk_widget_realize( gs_RootWindow
);
643 return gs_RootWindow
;
646 //-----------------------------------------------------------------------------
648 //-----------------------------------------------------------------------------
651 int wxEntryStart( int argc
, char *argv
[] )
654 /* GTK 1.2 up to version 1.2.3 has broken threads */
655 if ((gtk_major_version
== 1) &&
656 (gtk_minor_version
== 2) &&
657 (gtk_micro_version
< 4))
659 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
669 // We should have the wxUSE_WCHAR_T test on the _outside_
671 #if defined(__WXGTK20__)
672 // gtk+ 2.0 supports Unicode through UTF-8 strings
673 wxConvCurrent
= &wxConvUTF8
;
675 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
678 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
683 gtk_init( &argc
, &argv
);
685 wxSetDetectableAutoRepeat( TRUE
);
687 if (!wxApp::Initialize())
701 if ( !wxTheApp
->OnInitGui() )
710 void wxEntryCleanup()
713 // flush the logged messages if any
714 wxLog
*log
= wxLog::GetActiveTarget();
715 if (log
!= NULL
&& log
->HasPendingMessages())
718 // continuing to use user defined log target is unsafe from now on because
719 // some resources may be already unavailable, so replace it by something
721 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
733 int wxEntry( int argc
, char *argv
[] )
735 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
736 // This seems to be necessary since there are 'rogue'
737 // objects present at this point (perhaps global objects?)
738 // Setting a checkpoint will ignore them as far as the
739 // memory checking facility is concerned.
740 // Of course you may argue that memory allocated in globals should be
741 // checked, but this is a reasonable compromise.
742 wxDebugContext::SetCheckpoint();
744 int err
= wxEntryStart(argc
, argv
);
750 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
751 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
753 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
755 wxObject
*test_app
= app_ini();
757 wxTheApp
= (wxApp
*) test_app
;
760 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
762 wxTheApp
->argc
= argc
;
764 wxTheApp
->argv
= new wxChar
*[argc
+1];
766 while (mb_argc
< argc
)
768 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
771 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
773 wxTheApp
->argv
= argv
;
776 wxString
name(wxFileNameFromPath(argv
[0]));
777 wxStripExtension( name
);
778 wxTheApp
->SetAppName( name
);
781 retValue
= wxEntryInitGui();
783 // Here frames insert themselves automatically into wxTopLevelWindows by
784 // getting created in OnInit().
787 if ( !wxTheApp
->OnInit() )
793 /* delete pending toplevel windows (typically a single
794 dialog) so that, if there isn't any left, we don't
796 wxTheApp
->DeletePendingObjects();
798 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
800 if (wxTheApp
->Initialized())
804 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
807 /* Forcibly delete the window. */
808 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
809 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
811 topWindow
->Close( TRUE
);
812 wxTheApp
->DeletePendingObjects();
817 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
821 retValue
= wxTheApp
->OnExit();
830 #include "wx/gtk/info.xpm"
831 #include "wx/gtk/error.xpm"
832 #include "wx/gtk/question.xpm"
833 #include "wx/gtk/warning.xpm"
836 wxApp::GetStdIcon(int which
) const
840 case wxICON_INFORMATION
:
841 return wxIcon(info_xpm
);
843 case wxICON_QUESTION
:
844 return wxIcon(question_xpm
);
846 case wxICON_EXCLAMATION
:
847 return wxIcon(warning_xpm
);
850 wxFAIL_MSG(wxT("requested non existent standard icon"));
851 // still fall through
854 return wxIcon(error_xpm
);