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 GtkWidget
*wxRootWindow
= (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 //-----------------------------------------------------------------------------
86 static bool s_inYield
= FALSE
;
89 wxFAIL_MSG( wxT("wxYield called recursively" ) );
96 // We need to remove idle callbacks or the loop will
98 gtk_idle_remove( wxTheApp
->m_idleTag
);
99 wxTheApp
->m_idleTag
= 0;
103 while (gtk_events_pending())
104 gtk_main_iteration();
106 // disable log flushing from here because a call to wxYield() shouldn't
107 // normally result in message boxes popping up &c
110 /* it's necessary to call ProcessIdle() to update the frames sizes which
111 might have been changed (it also will update other things set from
112 OnUpdateUI() which is a nice (and desired) side effect) */
113 while (wxTheApp
->ProcessIdle()) { }
115 // let the logs be flashed again
125 //-----------------------------------------------------------------------------
127 //-----------------------------------------------------------------------------
132 if (!wxThread::IsMain())
137 wxapp_install_idle_handler();
140 if (!wxThread::IsMain())
145 //-----------------------------------------------------------------------------
147 //-----------------------------------------------------------------------------
149 gint
wxapp_pending_callback( gpointer
WXUNUSED(data
) )
151 if (!wxTheApp
) return TRUE
;
153 // when getting called from GDK's time-out handler
154 // we are no longer within GDK's grab on the GUI
155 // thread so we must lock it here ourselves
158 // Sent idle event to all who request them
159 wxTheApp
->ProcessPendingEvents();
163 /* flush the logged messages if any */
165 wxLog::FlushActive();
168 // Release lock again
171 // Return FALSE to indicate that no more idle events are
172 // to be sent (single shot instead of continuous stream)
176 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
178 if (!wxTheApp
) return TRUE
;
180 // when getting called from GDK's time-out handler
181 // we are no longer within GDK's grab on the GUI
182 // thread so we must lock it here ourselves
185 /* Indicate that we are now in idle mode - even so deeply
186 in idle mode that we don't get any idle events anymore.
187 this is like wxMSW where an idle event is sent only
188 once each time after the event queue has been completely
191 wxTheApp
->m_idleTag
= 0;
193 // Sent idle event to all who request them
194 while (wxTheApp
->ProcessIdle()) { }
196 // Release lock again
199 // Return FALSE to indicate that no more idle events are
200 // to be sent (single shot instead of continuous stream)
204 void wxapp_install_idle_handler()
206 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, wxT("attempt to install idle handler twice") );
210 if (g_pendingTag
== 0)
211 g_pendingTag
= gtk_idle_add_priority( 900, wxapp_pending_callback
, (gpointer
) NULL
);
213 /* This routine gets called by all event handlers
214 indicating that the idle is over. It may also
215 get called from other thread for sending events
216 to the main thread (and processing these in
217 idle time). Very low priority. */
219 wxTheApp
->m_idleTag
= gtk_idle_add_priority( 1000, wxapp_idle_callback
, (gpointer
) NULL
);
224 static int g_threadUninstallLevel
= 0;
226 void wxapp_install_thread_wakeup()
228 g_threadUninstallLevel
++;
230 if (g_threadUninstallLevel
!= 1) return;
232 if (wxTheApp
->m_wakeUpTimerTag
) return;
234 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 50, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
237 void wxapp_uninstall_thread_wakeup()
239 g_threadUninstallLevel
--;
241 if (g_threadUninstallLevel
!= 0) return;
243 if (!wxTheApp
->m_wakeUpTimerTag
) return;
245 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
246 wxTheApp
->m_wakeUpTimerTag
= 0;
249 gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
251 // when getting called from GDK's time-out handler
252 // we are no longer within GDK's grab on the GUI
253 // thread so we must lock it here ourselves
256 wxapp_uninstall_thread_wakeup();
258 // unblock other threads wishing to do some GUI things
261 g_mainThreadLocked
= TRUE
;
263 // wake up other threads
266 // block other thread again
269 g_mainThreadLocked
= FALSE
;
271 wxapp_install_thread_wakeup();
273 // release lock again
279 #endif // wxUSE_THREADS
281 //-----------------------------------------------------------------------------
283 //-----------------------------------------------------------------------------
285 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
287 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
288 EVT_IDLE(wxApp::OnIdle
)
295 m_topWindow
= (wxWindow
*) NULL
;
296 m_exitOnFrameDelete
= TRUE
;
299 wxapp_install_idle_handler();
302 m_wakeUpTimerTag
= 0;
303 wxapp_install_thread_wakeup();
306 m_colorCube
= (unsigned char*) NULL
;
308 m_useBestVisual
= FALSE
;
313 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
316 wxapp_uninstall_thread_wakeup();
319 if (m_colorCube
) free(m_colorCube
);
322 bool wxApp::OnInitGui()
324 GdkVisual
*visual
= gdk_visual_get_system();
326 /* on some machines, the default visual is just 256 colours, so
327 we make sure we get the best. this can sometimes be wasteful,
328 of course, but what do these guys pay $30.000 for? */
330 if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
334 /* seems gtk_widget_set_default_visual no longer exists? */
335 GdkVisual
* vis
= gtk_widget_get_default_visual();
337 GdkVisual
* vis
= gdk_visual_get_best();
338 gtk_widget_set_default_visual( vis
);
341 GdkColormap
*colormap
= gdk_colormap_new( vis
, FALSE
);
342 gtk_widget_set_default_colormap( colormap
);
347 /* Nothing to do for 15, 16, 24, 32 bit displays */
348 if (visual
->depth
> 8) return TRUE
;
350 /* initialize color cube for 8-bit color reduction dithering */
352 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
354 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
356 for (int r
= 0; r
< 32; r
++)
358 for (int g
= 0; g
< 32; g
++)
360 for (int b
= 0; b
< 32; b
++)
362 int rr
= (r
<< 3) | (r
>> 2);
363 int gg
= (g
<< 3) | (g
>> 2);
364 int bb
= (b
<< 3) | (b
>> 2);
368 GdkColor
*colors
= cmap
->colors
;
373 for (int i
= 0; i
< cmap
->size
; i
++)
375 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
376 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
377 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
378 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
381 index
= i
; max
= sum
;
387 #if (GTK_MINOR_VERSION > 0)
388 /* assume 8-bit true or static colors. this really
390 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
391 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
392 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
393 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
395 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
398 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
406 bool wxApp::ProcessIdle()
409 event
.SetEventObject( this );
410 ProcessEvent( event
);
412 return event
.MoreRequested();
415 void wxApp::OnIdle( wxIdleEvent
&event
)
417 static bool s_inOnIdle
= FALSE
;
419 /* Avoid recursion (via ProcessEvent default case) */
425 /* Resend in the main thread events which have been prepared in other
427 ProcessPendingEvents();
429 /* 'Garbage' collection of windows deleted with Close(). */
430 DeletePendingObjects();
432 /* Send OnIdle events to all windows */
433 bool needMore
= SendIdleEvents();
436 event
.RequestMore(TRUE
);
441 bool wxApp::SendIdleEvents()
443 bool needMore
= FALSE
;
445 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
448 wxWindow
* win
= node
->GetData();
449 if (SendIdleEvents(win
))
451 node
= node
->GetNext();
457 bool wxApp::SendIdleEvents( wxWindow
* win
)
459 bool needMore
= FALSE
;
462 event
.SetEventObject(win
);
464 win
->GetEventHandler()->ProcessEvent(event
);
466 win
->OnInternalIdle();
468 if (event
.MoreRequested())
471 wxNode
* node
= win
->GetChildren().First();
474 wxWindow
* win
= (wxWindow
*) node
->Data();
475 if (SendIdleEvents(win
))
483 int wxApp::MainLoop()
489 void wxApp::ExitMainLoop()
491 if (gtk_main_level() > 0)
495 bool wxApp::Initialized()
497 return m_initialized
;
500 bool wxApp::Pending()
502 return (gtk_events_pending() > 0);
505 void wxApp::Dispatch()
507 gtk_main_iteration();
510 void wxApp::DeletePendingObjects()
512 wxNode
*node
= wxPendingDelete
.First();
515 wxObject
*obj
= (wxObject
*)node
->Data();
519 if (wxPendingDelete
.Find(obj
))
522 node
= wxPendingDelete
.First();
526 bool wxApp::Initialize()
528 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
530 wxClassInfo::InitializeClasses();
532 wxSystemSettings::Init();
534 // GL: I'm annoyed ... I don't know where to put this and I don't want to
535 // create a module for that as it's part of the core.
537 wxPendingEvents
= new wxList();
538 wxPendingEventsLocker
= new wxCriticalSection();
541 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
542 wxTheColourDatabase
->Initialize();
544 wxInitializeStockLists();
545 wxInitializeStockObjects();
547 #if wxUSE_WX_RESOURCES
548 wxInitializeResourceSystem();
551 wxModule::RegisterModules();
552 if (!wxModule::InitializeModules()) return FALSE
;
557 void wxApp::CleanUp()
559 wxModule::CleanUpModules();
561 #if wxUSE_WX_RESOURCES
562 wxCleanUpResourceSystem();
565 if (wxTheColourDatabase
)
566 delete wxTheColourDatabase
;
568 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
570 wxDeleteStockObjects();
572 wxDeleteStockLists();
575 wxTheApp
= (wxApp
*) NULL
;
577 // GL: I'm annoyed ... I don't know where to put this and I don't want to
578 // create a module for that as it's part of the core.
580 delete wxPendingEvents
;
581 delete wxPendingEventsLocker
;
584 wxSystemSettings::Done();
588 wxClassInfo::CleanUpClasses();
590 // check for memory leaks
591 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
592 if (wxDebugContext::CountObjectsLeft(TRUE
) > 0)
594 wxLogDebug(wxT("There were memory leaks.\n"));
595 wxDebugContext::Dump();
596 wxDebugContext::PrintStatistics();
601 // do this as the very last thing because everything else can log messages
602 wxLog::DontCreateOnDemand();
604 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
610 //-----------------------------------------------------------------------------
612 //-----------------------------------------------------------------------------
615 int wxEntryStart( int argc
, char *argv
[] )
618 /* GTK 1.2 up to version 1.2.3 has broken threads */
619 if ((gtk_major_version
== 1) &&
620 (gtk_minor_version
== 2) &&
621 (gtk_micro_version
< 4))
623 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
633 // We should have the wxUSE_WCHAR_T test on the _outside_
635 #if defined(__WXGTK20__)
636 // gtk+ 2.0 supports Unicode through UTF-8 strings
637 wxConvCurrent
= &wxConvUTF8
;
639 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
642 if (!wxOKlibc()) wxConvCurrent
= (wxMBConv
*) NULL
;
647 gtk_init( &argc
, &argv
);
649 wxSetDetectableAutoRepeat( TRUE
);
651 if (!wxApp::Initialize())
664 if ( !wxTheApp
->OnInitGui() )
667 wxRootWindow
= gtk_window_new( GTK_WINDOW_TOPLEVEL
);
668 gtk_widget_realize( wxRootWindow
);
674 void wxEntryCleanup()
677 // flush the logged messages if any
678 wxLog
*log
= wxLog::GetActiveTarget();
679 if (log
!= NULL
&& log
->HasPendingMessages())
682 // continuing to use user defined log target is unsafe from now on because
683 // some resources may be already unavailable, so replace it by something
685 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
697 int wxEntry( int argc
, char *argv
[] )
699 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
700 // This seems to be necessary since there are 'rogue'
701 // objects present at this point (perhaps global objects?)
702 // Setting a checkpoint will ignore them as far as the
703 // memory checking facility is concerned.
704 // Of course you may argue that memory allocated in globals should be
705 // checked, but this is a reasonable compromise.
706 wxDebugContext::SetCheckpoint();
708 int err
= wxEntryStart(argc
, argv
);
714 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
715 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
717 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
719 wxObject
*test_app
= app_ini();
721 wxTheApp
= (wxApp
*) test_app
;
724 wxCHECK_MSG( wxTheApp
, -1, wxT("wxWindows error: no application object") );
726 wxTheApp
->argc
= argc
;
728 wxTheApp
->argv
= new wxChar
*[argc
+1];
730 while (mb_argc
< argc
)
732 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
735 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
737 wxTheApp
->argv
= argv
;
740 wxString
name(wxFileNameFromPath(argv
[0]));
741 wxStripExtension( name
);
742 wxTheApp
->SetAppName( name
);
745 retValue
= wxEntryInitGui();
747 // Here frames insert themselves automatically into wxTopLevelWindows by
748 // getting created in OnInit().
751 if ( !wxTheApp
->OnInit() )
757 /* delete pending toplevel windows (typically a single
758 dialog) so that, if there isn't any left, we don't
760 wxTheApp
->DeletePendingObjects();
762 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
764 if (wxTheApp
->Initialized())
768 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
771 /* Forcibly delete the window. */
772 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
773 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
775 topWindow
->Close( TRUE
);
776 wxTheApp
->DeletePendingObjects();
781 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
785 retValue
= wxTheApp
->OnExit();
794 #include "wx/gtk/info.xpm"
795 #include "wx/gtk/error.xpm"
796 #include "wx/gtk/question.xpm"
797 #include "wx/gtk/warning.xpm"
800 wxApp::GetStdIcon(int which
) const
804 case wxICON_INFORMATION
:
805 return wxIcon(info_xpm
);
807 case wxICON_QUESTION
:
808 return wxIcon(question_xpm
);
810 case wxICON_EXCLAMATION
:
811 return wxIcon(warning_xpm
);
814 wxFAIL_MSG(wxT("requested non existent standard icon"));
815 // still fall through
818 return wxIcon(error_xpm
);