1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
11 #pragma implementation "appbase.h"
12 #pragma implementation "app.h"
16 #include "wx/gdicmn.h"
20 #include "wx/memory.h"
22 #include "wx/settings.h"
23 #include "wx/dialog.h"
25 #if wxUSE_WX_RESOURCES
26 #include "wx/resource.h"
29 #include "wx/module.h"
33 #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
;
52 extern wxList
*wxPendingEvents
;
53 extern wxCriticalSection
*wxPendingEventsLocker
;
55 extern wxResourceCache
*wxTheResourceCache
;
58 unsigned char g_palette
[64*3] =
126 //-----------------------------------------------------------------------------
128 //-----------------------------------------------------------------------------
130 extern void wxFlushResources(void);
132 //-----------------------------------------------------------------------------
134 //-----------------------------------------------------------------------------
141 /* forward declaration */
142 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) );
146 bool has_idle
= (wxTheApp
->m_idleTag
!= 0);
150 /* We need to temporarily remove idle callbacks or the loop will
152 gtk_idle_remove( wxTheApp
->m_idleTag
);
153 wxTheApp
->m_idleTag
= 0;
156 while (gtk_events_pending())
157 gtk_main_iteration();
159 /* it's necessary to call ProcessIdle() to update the frames sizes which
160 might have been changed (it also will update other things set from
161 OnUpdateUI() which is a nice (and desired) side effect) */
162 while (wxTheApp
->ProcessIdle()) { }
166 /* re-add idle handler */
167 wxTheApp
->m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
173 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
175 if (!wxTheApp
) return TRUE
;
177 #if (GTK_MINOR_VERSION > 0)
178 /* when getting called from GDK's idle handler we
179 are no longer within GDK's grab on the GUI
180 thread so we must lock it here ourselves */
181 GDK_THREADS_ENTER ();
184 /* sent idle event to all who request them */
185 while (wxTheApp
->ProcessIdle()) { }
187 /* we don't want any more idle events until the next event is
189 gtk_idle_remove( wxTheApp
->m_idleTag
);
190 wxTheApp
->m_idleTag
= 0;
192 /* indicate that we are now in idle mode - even so deeply
193 in idle mode that we don't get any idle events anymore.
194 this is like wxMSW where an idle event is sent only
195 once each time after the event queue has been completely
199 #if (GTK_MINOR_VERSION > 0)
200 /* release lock again */
201 GDK_THREADS_LEAVE ();
207 void wxapp_install_idle_handler()
209 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, _T("attempt to install idle handler twice") );
211 /* this routine gets called by all event handlers
212 indicating that the idle is over. */
214 wxTheApp
->m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
221 /* forward declaration */
222 static gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) );
224 void wxapp_install_thread_wakeup()
226 if (wxTheApp
->m_wakeUpTimerTag
) return;
228 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 100, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
231 void wxapp_uninstall_thread_wakeup()
233 if (!wxTheApp
->m_wakeUpTimerTag
) return;
235 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
236 wxTheApp
->m_wakeUpTimerTag
= 0;
239 static gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
241 wxapp_uninstall_thread_wakeup();
243 #if (GTK_MINOR_VERSION > 0)
244 // when getting called from GDK's time-out handler
245 // we are no longer within GDK's grab on the GUI
246 // thread so we must lock it here ourselves
247 GDK_THREADS_ENTER ();
250 // unblock other threads wishing to do some GUI things
253 // wake up other threads
256 // block other thread again
259 #if (GTK_MINOR_VERSION > 0)
260 // release lock again
261 GDK_THREADS_LEAVE ();
264 wxapp_install_thread_wakeup();
270 //-----------------------------------------------------------------------------
272 //-----------------------------------------------------------------------------
274 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
276 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
277 EVT_IDLE(wxApp::OnIdle
)
284 m_topWindow
= (wxWindow
*) NULL
;
285 m_exitOnFrameDelete
= TRUE
;
287 m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
290 m_wakeUpTimerTag
= 0;
291 wxapp_install_thread_wakeup();
294 m_colorCube
= (unsigned char*) NULL
;
299 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
302 wxapp_uninstall_thread_wakeup();
305 if (m_colorCube
) free(m_colorCube
);
308 bool wxApp::OnInitGui()
310 GdkVisual
*visual
= gdk_visual_get_system();
312 /* on some machines, the default visual is just 256 colours, so
313 we make sure we get the best. this can sometimes be wasteful,
314 of course, but what do these guys pay $30.000 for? */
316 if (gdk_visual_get_best() != gdk_visual_get_system())
318 GdkVisual* vis = gdk_visual_get_best();
319 gtk_widget_set_default_visual( vis );
321 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
322 gtk_widget_set_default_colormap( colormap );
328 /* Nothing to do for 15, 16, 24, 32 bit displays */
329 if (visual
->depth
> 8) return TRUE
;
331 /* this initiates the standard palette as defined by GdkImlib
332 in the GNOME libraries. it ensures that all GNOME applications
333 use the same 64 colormap entries on 8-bit displays so you
334 can use several rather graphics-heavy applications at the
336 NOTE: this doesn't really seem to work this way... */
339 GdkColormap *cmap = gdk_colormap_new( gdk_visual_get_system(), TRUE );
341 for (int i = 0; i < 64; i++)
344 col.red = g_palette[i*3 + 0] << 8;
345 col.green = g_palette[i*3 + 1] << 8;
346 col.blue = g_palette[i*3 + 2] << 8;
349 gdk_color_alloc( cmap, &col );
352 gtk_widget_set_default_colormap( cmap );
355 /* initialize color cube for 8-bit color reduction dithering */
357 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
359 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
361 for (int r
= 0; r
< 32; r
++)
363 for (int g
= 0; g
< 32; g
++)
365 for (int b
= 0; b
< 32; b
++)
367 int rr
= (r
<< 3) | (r
>> 2);
368 int gg
= (g
<< 3) | (g
>> 2);
369 int bb
= (b
<< 3) | (b
>> 2);
373 GdkColor
*colors
= cmap
->colors
;
378 for (int i
= 0; i
< cmap
->size
; i
++)
380 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
381 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
382 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
383 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
386 index
= i
; max
= sum
;
392 #if (GTK_MINOR_VERSION > 0)
393 /* assume 8-bit true or static colors. this really
395 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
396 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
397 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
398 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
400 wxFAIL_MSG( _T("Unsupported graphics hardware") );
403 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
411 bool wxApp::ProcessIdle()
414 event
.SetEventObject( this );
415 ProcessEvent( event
);
417 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
420 wxWindow
* win
= node
->GetData();
421 win
->OnInternalIdle();
422 node
= node
->GetNext();
425 return event
.MoreRequested();
428 void wxApp::OnIdle( wxIdleEvent
&event
)
430 static bool s_inOnIdle
= FALSE
;
432 /* Avoid recursion (via ProcessEvent default case) */
439 /* Resend in the main thread events which have been prepared in other
441 ProcessPendingEvents();
444 /* 'Garbage' collection of windows deleted with Close(). */
445 DeletePendingObjects();
447 /* flush the logged messages if any */
449 wxLog
*log
= wxLog::GetActiveTarget();
450 if (log
!= NULL
&& log
->HasPendingMessages())
454 /* Send OnIdle events to all windows */
455 bool needMore
= SendIdleEvents();
458 event
.RequestMore(TRUE
);
463 bool wxApp::SendIdleEvents()
465 bool needMore
= FALSE
;
467 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
470 wxWindow
* win
= node
->GetData();
471 if (SendIdleEvents(win
))
473 node
= node
->GetNext();
479 bool wxApp::SendIdleEvents( wxWindow
* win
)
481 bool needMore
= FALSE
;
484 event
.SetEventObject(win
);
486 win
->ProcessEvent(event
);
488 if (event
.MoreRequested())
491 wxNode
* node
= win
->GetChildren().First();
494 wxWindow
* win
= (wxWindow
*) node
->Data();
495 if (SendIdleEvents(win
))
503 int wxApp::MainLoop()
509 void wxApp::ExitMainLoop()
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();
530 void wxApp::ProcessPendingEvents()
532 wxNode
*node
= wxPendingEvents
->First();
533 wxCriticalSectionLocker
locker(*wxPendingEventsLocker
);
537 wxEvtHandler
*handler
= (wxEvtHandler
*)node
->Data();
539 handler
->ProcessPendingEvents();
543 node
= wxPendingEvents
->First();
546 #endif // wxUSE_THREADS
548 void wxApp::DeletePendingObjects()
550 wxNode
*node
= wxPendingDelete
.First();
553 wxObject
*obj
= (wxObject
*)node
->Data();
557 if (wxPendingDelete
.Find(obj
))
560 node
= wxPendingDelete
.First();
564 bool wxApp::Initialize()
566 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
568 wxClassInfo::InitializeClasses();
570 wxSystemSettings::Init();
572 // GL: I'm annoyed ... I don't know where to put this and I don't want to
573 // create a module for that as it's part of the core.
575 wxPendingEvents
= new wxList();
576 wxPendingEventsLocker
= new wxCriticalSection();
580 wxTheFontNameDirectory = new wxFontNameDirectory;
581 wxTheFontNameDirectory->Initialize();
584 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
585 wxTheColourDatabase
->Initialize();
587 wxInitializeStockLists();
588 wxInitializeStockObjects();
590 #if wxUSE_WX_RESOURCES
591 wxTheResourceCache
= new wxResourceCache( wxKEY_STRING
);
593 wxInitializeResourceSystem();
596 wxImage::InitStandardHandlers();
598 wxModule::RegisterModules();
599 if (!wxModule::InitializeModules()) return FALSE
;
604 void wxApp::CleanUp()
606 wxModule::CleanUpModules();
608 #if wxUSE_WX_RESOURCES
611 if (wxTheResourceCache
)
612 delete wxTheResourceCache
;
613 wxTheResourceCache
= (wxResourceCache
*) NULL
;
615 wxCleanUpResourceSystem();
618 if (wxTheColourDatabase
)
619 delete wxTheColourDatabase
;
620 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
623 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
624 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
627 wxDeleteStockObjects();
629 wxDeleteStockLists();
631 wxImage::CleanUpHandlers();
634 wxTheApp
= (wxApp
*) NULL
;
636 // GL: I'm annoyed ... I don't know where to put this and I don't want to
637 // create a module for that as it's part of the core.
639 delete wxPendingEvents
;
640 delete wxPendingEventsLocker
;
643 wxSystemSettings::Done();
647 wxClassInfo::CleanUpClasses();
649 // check for memory leaks
650 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
651 if (wxDebugContext::CountObjectsLeft() > 0)
653 wxLogDebug(_T("There were memory leaks.\n"));
654 wxDebugContext::Dump();
655 wxDebugContext::PrintStatistics();
660 // do this as the very last thing because everything else can log messages
661 wxLog::DontCreateOnDemand();
663 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
670 //-----------------------------------------------------------------------------
672 //-----------------------------------------------------------------------------
674 int wxEntry( int argc
, char *argv
[] )
678 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
680 gtk_init( &argc
, &argv
);
682 wxSetDetectableAutoRepeat( TRUE
);
684 if (!wxApp::Initialize())
689 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
690 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
692 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
694 wxObject
*test_app
= app_ini();
696 wxTheApp
= (wxApp
*) test_app
;
699 wxCHECK_MSG( wxTheApp
, -1, _T("wxWindows error: no application object") );
701 wxTheApp
->argc
= argc
;
703 wxTheApp
->argv
= new wxChar
*[argc
+1];
705 while (mb_argc
< argc
) {
706 wxTheApp
->argv
[mb_argc
] = wxStrdup(wxConvLibc
.cMB2WX(argv
[mb_argc
]));
709 wxTheApp
->argv
[mb_argc
] = (wxChar
*)NULL
;
711 wxTheApp
->argv
= argv
;
714 wxString
name(wxFileNameFromPath(argv
[0]));
715 wxStripExtension( name
);
716 wxTheApp
->SetAppName( name
);
720 if ( !wxTheApp
->OnInitGui() )
723 // Here frames insert themselves automatically into wxTopLevelWindows by
724 // getting created in OnInit().
727 if ( !wxTheApp
->OnInit() )
733 /* delete pending toplevel windows (typically a single
734 dialog) so that, if there isn't any left, we don't
736 wxTheApp
->DeletePendingObjects();
738 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
740 if (wxTheApp
->Initialized())
742 retValue
= wxTheApp
->OnRun();
744 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
747 /* Forcibly delete the window. */
748 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
749 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
751 topWindow
->Close( TRUE
);
752 wxTheApp
->DeletePendingObjects();
757 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
765 // flush the logged messages if any
766 wxLog
*log
= wxLog::GetActiveTarget();
767 if (log
!= NULL
&& log
->HasPendingMessages())
770 // continuing to use user defined log target is unsafe from now on because
771 // some resources may be already unavailable, so replace it by something
773 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);
783 #include "wx/gtk/info.xpm"
784 #include "wx/gtk/error.xpm"
785 #include "wx/gtk/question.xpm"
786 #include "wx/gtk/warning.xpm"
789 wxApp::GetStdIcon(int which
) const
793 case wxICON_INFORMATION
:
794 return wxIcon(info_xpm
);
796 case wxICON_QUESTION
:
797 return wxIcon(question_xpm
);
799 case wxICON_EXCLAMATION
:
800 return wxIcon(warning_xpm
);
803 wxFAIL_MSG(_T("requested non existent standard icon"));
804 // still fall through
807 return wxIcon(error_xpm
);