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
)
313 wxapp_install_idle_handler();
316 m_wakeUpTimerTag
= 0;
317 wxapp_install_thread_wakeup();
320 m_colorCube
= (unsigned char*) NULL
;
325 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
328 wxapp_uninstall_thread_wakeup();
331 if (m_colorCube
) free(m_colorCube
);
334 bool wxApp::OnInitGui()
336 if ( !wxAppBase::OnInitGui() )
339 GdkVisual
*visual
= gdk_visual_get_system();
341 /* on some machines, the default visual is just 256 colours, so
342 we make sure we get the best. this can sometimes be wasteful,
343 of course, but what do these guys pay $30.000 for? */
345 if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
349 /* seems gtk_widget_set_default_visual no longer exists? */
350 GdkVisual
* vis
= gtk_widget_get_default_visual();
352 GdkVisual
* vis
= gdk_visual_get_best();
353 gtk_widget_set_default_visual( vis
);
356 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
357 gtk_widget_set_default_colormap( colormap
);
362 /* Nothing to do for 15, 16, 24, 32 bit displays */
363 if (visual
->depth
> 8) return TRUE
;
365 /* initialize color cube for 8-bit color reduction dithering */
367 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
369 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
371 for (int r
= 0; r
< 32; r
++)
373 for (int g
= 0; g
< 32; g
++)
375 for (int b
= 0; b
< 32; b
++)
377 int rr
= (r
<< 3) | (r
>> 2);
378 int gg
= (g
<< 3) | (g
>> 2);
379 int bb
= (b
<< 3) | (b
>> 2);
383 GdkColor
*colors
= cmap
->colors
;
388 for (int i
= 0; i
< cmap
->size
; i
++)
390 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
391 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
392 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
393 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
396 index
= i
; max
= sum
;
402 #if (GTK_MINOR_VERSION > 0)
403 /* assume 8-bit true or static colors. this really
405 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
406 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
407 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
408 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
410 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
413 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
421 bool wxApp::ProcessIdle()
424 event
.SetEventObject( this );
425 ProcessEvent( event
);
427 return event
.MoreRequested();
430 void wxApp::OnIdle( wxIdleEvent
&event
)
432 static bool s_inOnIdle
= FALSE
;
434 /* Avoid recursion (via ProcessEvent default case) */
440 /* Resend in the main thread events which have been prepared in other
442 ProcessPendingEvents();
444 /* 'Garbage' collection of windows deleted with Close(). */
445 DeletePendingObjects();
447 /* Send OnIdle events to all windows */
448 bool needMore
= SendIdleEvents();
451 event
.RequestMore(TRUE
);
456 bool wxApp::SendIdleEvents()
458 bool needMore
= FALSE
;
460 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
463 wxWindow
* win
= node
->GetData();
464 if (SendIdleEvents(win
))
466 node
= node
->GetNext();
472 bool wxApp::SendIdleEvents( wxWindow
* win
)
474 bool needMore
= FALSE
;
477 event
.SetEventObject(win
);
479 win
->GetEventHandler()->ProcessEvent(event
);
481 win
->OnInternalIdle();
483 if (event
.MoreRequested())
486 wxNode
* node
= win
->GetChildren().First();
489 wxWindow
* win
= (wxWindow
*) node
->Data();
490 if (SendIdleEvents(win
))
498 int wxApp::MainLoop()
504 void wxApp::ExitMainLoop()
506 if (gtk_main_level() > 0)
510 bool wxApp::Initialized()
512 return m_initialized
;
515 bool wxApp::Pending()
517 return (gtk_events_pending() > 0);
520 void wxApp::Dispatch()
522 gtk_main_iteration();
525 void wxApp::DeletePendingObjects()
527 wxNode
*node
= wxPendingDelete
.First();
530 wxObject
*obj
= (wxObject
*)node
->Data();
534 if (wxPendingDelete
.Find(obj
))
537 node
= wxPendingDelete
.First();
541 bool wxApp::Initialize()
543 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
545 wxClassInfo::InitializeClasses();
547 wxSystemSettings::Init();
550 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
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
);