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"
42 #include "wx/gtk/win_gtk.h"
44 //-----------------------------------------------------------------------------
46 //-----------------------------------------------------------------------------
48 wxApp
*wxTheApp
= (wxApp
*) NULL
;
49 wxAppInitializerFunction
wxAppBase::m_appInitFn
= (wxAppInitializerFunction
) NULL
;
53 bool g_mainThreadLocked
= FALSE
;
54 gint g_pendingTag
= 0;
56 static GtkWidget
*gs_RootWindow
= (GtkWidget
*) NULL
;
58 //-----------------------------------------------------------------------------
60 //-----------------------------------------------------------------------------
62 /* forward declaration */
63 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) );
64 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) );
65 void wxapp_install_idle_handler();
68 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) );
71 //-----------------------------------------------------------------------------
73 //-----------------------------------------------------------------------------
80 //-----------------------------------------------------------------------------
82 //-----------------------------------------------------------------------------
84 static bool gs_inYield
= FALSE
;
89 if ( !wxThread::IsMain() )
91 // can't call gtk_main_iteration() from other threads like this
94 #endif // wxUSE_THREADS
98 wxFAIL_MSG( wxT("wxYield called recursively" ) );
105 // We need to remove idle callbacks or the loop will
107 gtk_idle_remove( wxTheApp
->m_idleTag
);
108 wxTheApp
->m_idleTag
= 0;
112 // disable log flushing from here because a call to wxYield() shouldn't
113 // normally result in message boxes popping up &c
116 while (gtk_events_pending())
117 gtk_main_iteration();
119 /* it's necessary to call ProcessIdle() to update the frames sizes which
120 might have been changed (it also will update other things set from
121 OnUpdateUI() which is a nice (and desired) side effect) */
122 while (wxTheApp
->ProcessIdle()) { }
124 // let the logs be flashed again
132 //-----------------------------------------------------------------------------
134 // Like wxYield, but fails silently if the yield is recursive.
135 //-----------------------------------------------------------------------------
137 bool wxYieldIfNeeded()
145 //-----------------------------------------------------------------------------
147 //-----------------------------------------------------------------------------
152 if (!wxThread::IsMain())
157 wxapp_install_idle_handler();
160 if (!wxThread::IsMain())
165 //-----------------------------------------------------------------------------
167 //-----------------------------------------------------------------------------
169 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) )
171 if (!wxTheApp
) return TRUE
;
173 // when getting called from GDK's time-out handler
174 // we are no longer within GDK's grab on the GUI
175 // thread so we must lock it here ourselves
178 // Sent idle event to all who request them
179 wxTheApp
->ProcessPendingEvents();
183 /* flush the logged messages if any */
185 wxLog::FlushActive();
188 // Release lock again
191 // Return FALSE to indicate that no more idle events are
192 // to be sent (single shot instead of continuous stream)
196 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
202 // don't generate the idle events while the assert modal dialog is shown,
203 // this completely confuses the apps which don't expect to be reentered
204 // from some safely-looking functions
205 if ( wxTheApp
->IsInAssert() )
209 #endif // __WXDEBUG__
211 // when getting called from GDK's time-out handler
212 // we are no longer within GDK's grab on the GUI
213 // thread so we must lock it here ourselves
216 /* Indicate that we are now in idle mode - even so deeply
217 in idle mode that we don't get any idle events anymore.
218 this is like wxMSW where an idle event is sent only
219 once each time after the event queue has been completely
222 wxTheApp
->m_idleTag
= 0;
224 // Sent idle event to all who request them as long as they do
225 while (wxTheApp
->ProcessIdle())
228 // Release lock again
231 // Return FALSE to indicate that no more idle events are
232 // to be sent (single shot instead of continuous stream)
236 void wxapp_install_idle_handler()
238 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, wxT("attempt to install idle handler twice") );
242 if (g_pendingTag
== 0)
243 g_pendingTag
= gtk_idle_add_priority( 900, wxapp_pending_callback
, (gpointer
) NULL
);
245 /* This routine gets called by all event handlers
246 indicating that the idle is over. It may also
247 get called from other thread for sending events
248 to the main thread (and processing these in
249 idle time). Very low priority. */
251 wxTheApp
->m_idleTag
= gtk_idle_add_priority( 1000, wxapp_idle_callback
, (gpointer
) NULL
);
256 static int g_threadUninstallLevel
= 0;
258 void wxapp_install_thread_wakeup()
260 g_threadUninstallLevel
++;
262 if (g_threadUninstallLevel
!= 1) return;
264 if (wxTheApp
->m_wakeUpTimerTag
) return;
266 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 50, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
269 void wxapp_uninstall_thread_wakeup()
271 g_threadUninstallLevel
--;
273 if (g_threadUninstallLevel
!= 0) return;
275 if (!wxTheApp
->m_wakeUpTimerTag
) return;
277 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
278 wxTheApp
->m_wakeUpTimerTag
= 0;
281 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
283 // when getting called from GDK's time-out handler
284 // we are no longer within GDK's grab on the GUI
285 // thread so we must lock it here ourselves
288 wxapp_uninstall_thread_wakeup();
290 // unblock other threads wishing to do some GUI things
293 g_mainThreadLocked
= TRUE
;
295 // wake up other threads
298 // block other thread again
301 g_mainThreadLocked
= FALSE
;
303 wxapp_install_thread_wakeup();
305 // release lock again
311 #endif // wxUSE_THREADS
313 //-----------------------------------------------------------------------------
315 //-----------------------------------------------------------------------------
317 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
319 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
320 EVT_IDLE(wxApp::OnIdle
)
325 m_initialized
= FALSE
;
327 m_isInAssert
= FALSE
;
328 #endif // __WXDEBUG__
331 wxapp_install_idle_handler();
334 m_wakeUpTimerTag
= 0;
335 wxapp_install_thread_wakeup();
338 m_colorCube
= (unsigned char*) NULL
;
340 // this is NULL for a "regular" wxApp, but is set (and freed) by a wxGLApp
341 m_glVisualInfo
= (void *) NULL
;
346 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
349 wxapp_uninstall_thread_wakeup();
352 if (m_colorCube
) free(m_colorCube
);
355 bool wxApp::OnInitGui()
357 if ( !wxAppBase::OnInitGui() )
360 GdkVisual
*visual
= gdk_visual_get_system();
362 // if this is a wxGLApp (derived from wxApp), and we've already
363 // chosen a specific visual, then derive the GdkVisual from that
364 if (m_glVisualInfo
!= NULL
) {
366 /* seems gtk_widget_set_default_visual no longer exists? */
367 GdkVisual
* vis
= gtk_widget_get_default_visual();
369 GdkVisual
* vis
= gdkx_visual_get(
370 ((XVisualInfo
*) m_glVisualInfo
) ->visualid
);
371 gtk_widget_set_default_visual( vis
);
374 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
375 gtk_widget_set_default_colormap( colormap
);
380 /* on some machines, the default visual is just 256 colours, so
381 we make sure we get the best. this can sometimes be wasteful,
382 of course, but what do these guys pay $30.000 for? */
384 else if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
388 /* seems gtk_widget_set_default_visual no longer exists? */
389 GdkVisual
* vis
= gtk_widget_get_default_visual();
391 GdkVisual
* vis
= gdk_visual_get_best();
392 gtk_widget_set_default_visual( vis
);
395 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
396 gtk_widget_set_default_colormap( colormap
);
401 /* Nothing to do for 15, 16, 24, 32 bit displays */
402 if (visual
->depth
> 8) return TRUE
;
404 /* initialize color cube for 8-bit color reduction dithering */
406 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
408 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
410 for (int r
= 0; r
< 32; r
++)
412 for (int g
= 0; g
< 32; g
++)
414 for (int b
= 0; b
< 32; b
++)
416 int rr
= (r
<< 3) | (r
>> 2);
417 int gg
= (g
<< 3) | (g
>> 2);
418 int bb
= (b
<< 3) | (b
>> 2);
422 GdkColor
*colors
= cmap
->colors
;
427 for (int i
= 0; i
< cmap
->size
; i
++)
429 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
430 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
431 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
432 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
435 index
= i
; max
= sum
;
441 #if (GTK_MINOR_VERSION > 0)
442 /* assume 8-bit true or static colors. this really
444 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
445 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
446 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
447 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
449 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
452 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
460 bool wxApp::ProcessIdle()
463 event
.SetEventObject( this );
464 ProcessEvent( event
);
466 return event
.MoreRequested();
469 void wxApp::OnIdle( wxIdleEvent
&event
)
471 static bool s_inOnIdle
= FALSE
;
473 /* Avoid recursion (via ProcessEvent default case) */
479 /* Resend in the main thread events which have been prepared in other
481 ProcessPendingEvents();
483 /* 'Garbage' collection of windows deleted with Close(). */
484 DeletePendingObjects();
486 /* Send OnIdle events to all windows */
487 bool needMore
= SendIdleEvents();
490 event
.RequestMore(TRUE
);
495 bool wxApp::SendIdleEvents()
497 bool needMore
= FALSE
;
499 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
502 wxWindow
* win
= node
->GetData();
503 if (SendIdleEvents(win
))
505 node
= node
->GetNext();
511 bool wxApp::SendIdleEvents( wxWindow
* win
)
513 bool needMore
= FALSE
;
516 event
.SetEventObject(win
);
518 win
->GetEventHandler()->ProcessEvent(event
);
520 win
->OnInternalIdle();
522 if (event
.MoreRequested())
525 wxNode
* node
= win
->GetChildren().First();
528 wxWindow
* win
= (wxWindow
*) node
->Data();
529 if (SendIdleEvents(win
))
537 int wxApp::MainLoop()
543 void wxApp::ExitMainLoop()
545 if (gtk_main_level() > 0)
549 bool wxApp::Initialized()
551 return m_initialized
;
554 bool wxApp::Pending()
556 return (gtk_events_pending() > 0);
559 void wxApp::Dispatch()
561 gtk_main_iteration();
564 void wxApp::DeletePendingObjects()
566 wxNode
*node
= wxPendingDelete
.First();
569 wxObject
*obj
= (wxObject
*)node
->Data();
573 if (wxPendingDelete
.Find(obj
))
576 node
= wxPendingDelete
.First();
580 bool wxApp::Initialize()
582 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
584 wxClassInfo::InitializeClasses();
586 wxSystemSettings::Init();
589 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
592 // GL: I'm annoyed ... I don't know where to put this and I don't want to
593 // create a module for that as it's part of the core.
595 wxPendingEvents
= new wxList();
596 wxPendingEventsLocker
= new wxCriticalSection();
599 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
600 wxTheColourDatabase
->Initialize();
602 wxInitializeStockLists();
603 wxInitializeStockObjects();
605 #if wxUSE_WX_RESOURCES
606 wxInitializeResourceSystem();
609 wxModule::RegisterModules();
610 if (!wxModule::InitializeModules()) return FALSE
;
615 void wxApp::CleanUp()
617 wxModule::CleanUpModules();
619 #if wxUSE_WX_RESOURCES
620 wxCleanUpResourceSystem();
623 if (wxTheColourDatabase
)
624 delete wxTheColourDatabase
;
626 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
628 wxDeleteStockObjects();
630 wxDeleteStockLists();
633 wxTheApp
= (wxApp
*) NULL
;
635 // GL: I'm annoyed ... I don't know where to put this and I don't want to
636 // create a module for that as it's part of the core.
638 delete wxPendingEvents
;
639 delete wxPendingEventsLocker
;
642 wxSystemSettings::Done();
646 wxClassInfo::CleanUpClasses();
648 // check for memory leaks
649 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
650 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
652 wxLogDebug(wxT("There were memory leaks.\n"));
653 wxDebugContext::Dump();
654 wxDebugContext::PrintStatistics();
659 // do this as the very last thing because everything else can log messages
660 wxLog::DontCreateOnDemand();
662 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
668 //-----------------------------------------------------------------------------
669 // Access to the root window global
670 //-----------------------------------------------------------------------------
672 GtkWidget
* wxGetRootWindow()
674 if (gs_RootWindow
== NULL
) {
675 gs_RootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
676 gtk_widget_realize( gs_RootWindow
);
678 return gs_RootWindow
;
681 //-----------------------------------------------------------------------------
683 //-----------------------------------------------------------------------------
686 int wxEntryStart( int argc
, char *argv
[] )
689 /* GTK 1.2 up to version 1.2.3 has broken threads */
690 if ((gtk_major_version
== 1) &&
691 (gtk_minor_version
== 2) &&
692 (gtk_micro_version
< 4))
694 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
704 // We should have the wxUSE_WCHAR_T test on the _outside_
706 #if defined(__WXGTK20__)
707 // gtk+ 2.0 supports Unicode through UTF-8 strings
708 wxConvCurrent
= &wxConvUTF8
;
710 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
713 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
718 gtk_init( &argc
, &argv
);
720 wxSetDetectableAutoRepeat( TRUE
);
722 if (!wxApp::Initialize())
736 if ( !wxTheApp
->OnInitGui() )
745 void wxEntryCleanup()
748 // flush the logged messages if any
749 wxLog
*log
= wxLog::GetActiveTarget();
750 if (log
!= NULL
&& log
->HasPendingMessages())
753 // continuing to use user defined log target is unsafe from now on because
754 // some resources may be already unavailable, so replace it by something
756 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
768 int wxEntry( int argc
, char *argv
[] )
770 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
771 // This seems to be necessary since there are 'rogue'
772 // objects present at this point (perhaps global objects?)
773 // Setting a checkpoint will ignore them as far as the
774 // memory checking facility is concerned.
775 // Of course you may argue that memory allocated in globals should be
776 // checked, but this is a reasonable compromise.
777 wxDebugContext::SetCheckpoint();
779 int err
= wxEntryStart(argc
, argv
);
785 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
786 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
788 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
790 wxObject
*test_app
= app_ini();
792 wxTheApp
= (wxApp
*) test_app
;
795 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
797 wxTheApp
->argc
= argc
;
799 wxTheApp
->argv
= new wxChar
*[argc
+1];
801 while (mb_argc
< argc
)
803 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
806 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
808 wxTheApp
->argv
= argv
;
811 wxString
name(wxFileNameFromPath(argv
[0]));
812 wxStripExtension( name
);
813 wxTheApp
->SetAppName( name
);
816 retValue
= wxEntryInitGui();
818 // Here frames insert themselves automatically into wxTopLevelWindows by
819 // getting created in OnInit().
822 if ( !wxTheApp
->OnInit() )
828 /* delete pending toplevel windows (typically a single
829 dialog) so that, if there isn't any left, we don't
831 wxTheApp
->DeletePendingObjects();
833 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
835 if (wxTheApp
->Initialized())
839 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
842 /* Forcibly delete the window. */
843 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
844 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
846 topWindow
->Close( TRUE
);
847 wxTheApp
->DeletePendingObjects();
852 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
856 retValue
= wxTheApp
->OnExit();
865 #include "wx/gtk/info.xpm"
866 #include "wx/gtk/error.xpm"
867 #include "wx/gtk/question.xpm"
868 #include "wx/gtk/warning.xpm"
871 wxApp::GetStdIcon(int which
) const
875 case wxICON_INFORMATION
:
876 return wxIcon(info_xpm
);
878 case wxICON_QUESTION
:
879 return wxIcon(question_xpm
);
881 case wxICON_EXCLAMATION
:
882 return wxIcon(warning_xpm
);
885 wxFAIL_MSG(wxT("requested non existent standard icon"));
886 // still fall through
889 return wxIcon(error_xpm
);
895 void wxApp::OnAssert(const wxChar
*file
, int line
, const wxChar
*msg
)
899 wxAppBase::OnAssert(file
, line
, msg
);
901 m_isInAssert
= FALSE
;
904 #endif // __WXDEBUG__