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
wxApp::m_appInitFn
= (wxAppInitializerFunction
) NULL
;
51 extern wxList
*wxPendingEvents
;
52 extern wxCriticalSection
*wxPendingEventsLocker
;
54 extern wxResourceCache
*wxTheResourceCache
;
57 unsigned char g_palette
[64*3] =
125 //-----------------------------------------------------------------------------
127 //-----------------------------------------------------------------------------
129 extern void wxFlushResources(void);
131 //-----------------------------------------------------------------------------
133 //-----------------------------------------------------------------------------
140 /* forward declaration */
141 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) );
145 /* it's necessary to call ProcessIdle() to update the frames sizes which
146 might have been changed (it also will update other things set from
147 OnUpdateUI() which is a nice (and desired) side effect) */
148 while (wxTheApp
->ProcessIdle()) { }
151 for ( wxWindowList::Node
*node
= wxTopLevelWindows
.GetFirst();
153 node
= node
->GetNext() )
155 wxWindow
*win
= node
->GetData();
156 win
->OnInternalIdle();
160 if (wxTheApp
->m_idleTag
)
162 /* We need to temporarily remove idle callbacks or the loop will
164 gtk_idle_remove( wxTheApp
->m_idleTag
);
165 wxTheApp
->m_idleTag
= 0;
167 while (gtk_events_pending())
168 gtk_main_iteration();
170 /* re-add idle handler */
171 wxTheApp
->m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
175 while (gtk_events_pending())
176 gtk_main_iteration();
182 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
184 if (!wxTheApp
) return TRUE
;
186 #if (GTK_MINOR_VERSION > 0)
187 /* when getting called from GDK's idle handler we
188 are no longer within GDK's grab on the GUI
189 thread so we must lock it here ourselves */
190 GDK_THREADS_ENTER ();
193 /* sent idle event to all who request them */
194 while (wxTheApp
->ProcessIdle()) { }
196 /* we don't want any more idle events until the next event is
198 gtk_idle_remove( wxTheApp
->m_idleTag
);
199 wxTheApp
->m_idleTag
= 0;
201 /* indicate that we are now in idle mode - even so deeply
202 in idle mode that we don't get any idle events anymore.
203 this is like wxMSW where an idle event is sent only
204 once each time after the event queue has been completely
208 #if (GTK_MINOR_VERSION > 0)
209 /* release lock again */
210 GDK_THREADS_LEAVE ();
216 void wxapp_install_idle_handler()
218 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, _T("attempt to install idle handler twice") );
220 /* this routine gets called by all event handlers
221 indicating that the idle is over. */
223 wxTheApp
->m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
229 static gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
231 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
232 wxTheApp
->m_wakeUpTimerTag
= 0;
234 #if (GTK_MINOR_VERSION > 0)
235 /* when getting called from GDK's time-out handler
236 we are no longer within GDK's grab on the GUI
237 thread so we must lock it here ourselves */
238 GDK_THREADS_ENTER ();
241 /* unblock other threads wishing to do some GUI things */
244 /* wake up other threads */
247 /* block other thread again */
250 #if (GTK_MINOR_VERSION > 0)
251 /* release lock again */
252 GDK_THREADS_LEAVE ();
255 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 20, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
261 //-----------------------------------------------------------------------------
263 //-----------------------------------------------------------------------------
265 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
267 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
268 EVT_IDLE(wxApp::OnIdle
)
275 m_topWindow
= (wxWindow
*) NULL
;
276 m_exitOnFrameDelete
= TRUE
;
278 m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
281 m_wakeUpTimerTag
= gtk_timeout_add( 20, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
284 m_colorCube
= (unsigned char*) NULL
;
289 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
292 if (m_wakeUpTimerTag
) gtk_timeout_remove( m_wakeUpTimerTag
);
295 if (m_colorCube
) free(m_colorCube
);
298 bool wxApp::OnInitGui()
300 GdkVisual
*visual
= gdk_visual_get_system();
302 /* on some machines, the default visual is just 256 colours, so
303 we make sure we get the best. this can sometimes be wasteful,
304 of course, but what do these guys pay $30.000 for? */
306 if (gdk_visual_get_best() != gdk_visual_get_system())
308 GdkVisual* vis = gdk_visual_get_best();
309 gtk_widget_set_default_visual( vis );
311 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
312 gtk_widget_set_default_colormap( colormap );
318 /* Nothing to do for 15, 16, 24, 32 bit displays */
319 if (visual
->depth
> 8) return TRUE
;
321 /* this initiates the standard palette as defined by GdkImlib
322 in the GNOME libraries. it ensures that all GNOME applications
323 use the same 64 colormap entries on 8-bit displays so you
324 can use several rather graphics-heavy applications at the
326 NOTE: this doesn't really seem to work this way... */
329 GdkColormap *cmap = gdk_colormap_new( gdk_visual_get_system(), TRUE );
331 for (int i = 0; i < 64; i++)
334 col.red = g_palette[i*3 + 0] << 8;
335 col.green = g_palette[i*3 + 1] << 8;
336 col.blue = g_palette[i*3 + 2] << 8;
339 gdk_color_alloc( cmap, &col );
342 gtk_widget_set_default_colormap( cmap );
345 /* initialize color cube for 8-bit color reduction dithering */
347 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
349 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
351 for (int r
= 0; r
< 32; r
++)
353 for (int g
= 0; g
< 32; g
++)
355 for (int b
= 0; b
< 32; b
++)
357 int rr
= (r
<< 3) | (r
>> 2);
358 int gg
= (g
<< 3) | (g
>> 2);
359 int bb
= (b
<< 3) | (b
>> 2);
363 GdkColor
*colors
= cmap
->colors
;
368 for (int i
= 0; i
< cmap
->size
; i
++)
370 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
371 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
372 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
373 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
376 index
= i
; max
= sum
;
382 #if (GTK_MINOR_VERSION > 0)
383 /* assume 8-bit true or static colors. this really
385 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
386 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
387 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
388 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
390 wxFAIL_MSG( _T("Unsupported graphics hardware") );
393 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
401 bool wxApp::ProcessIdle()
404 event
.SetEventObject( this );
405 ProcessEvent( event
);
407 return event
.MoreRequested();
410 void wxApp::OnIdle( wxIdleEvent
&event
)
412 static bool inOnIdle
= FALSE
;
414 /* Avoid recursion (via ProcessEvent default case) */
421 /* Resend in the main thread events which have been prepared in other
423 ProcessPendingEvents();
426 /* 'Garbage' collection of windows deleted with Close(). */
427 DeletePendingObjects();
429 /* flush the logged messages if any */
431 wxLog
*log
= wxLog::GetActiveTarget();
432 if (log
!= NULL
&& log
->HasPendingMessages())
436 /* Send OnIdle events to all windows */
437 bool needMore
= SendIdleEvents();
440 event
.RequestMore(TRUE
);
445 bool wxApp::SendIdleEvents()
447 bool needMore
= FALSE
;
449 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
452 wxWindow
* win
= node
->GetData();
453 if (SendIdleEvents(win
))
455 node
= node
->GetNext();
461 bool wxApp::SendIdleEvents( wxWindow
* win
)
463 bool needMore
= FALSE
;
466 event
.SetEventObject(win
);
468 win
->OnInternalIdle();
470 win
->ProcessEvent(event
);
472 if (event
.MoreRequested())
475 wxNode
* node
= win
->GetChildren().First();
478 wxWindow
* win
= (wxWindow
*) node
->Data();
479 if (SendIdleEvents(win
))
487 int wxApp::MainLoop()
493 void wxApp::ExitMainLoop()
498 bool wxApp::Initialized()
500 return m_initialized
;
503 bool wxApp::Pending()
505 return (gtk_events_pending() > 0);
508 void wxApp::Dispatch()
510 gtk_main_iteration();
514 void wxApp::ProcessPendingEvents()
516 wxNode
*node
= wxPendingEvents
->First();
517 wxCriticalSectionLocker
locker(*wxPendingEventsLocker
);
521 wxEvtHandler
*handler
= (wxEvtHandler
*)node
->Data();
523 handler
->ProcessPendingEvents();
527 node
= wxPendingEvents
->First();
530 #endif // wxUSE_THREADS
532 void wxApp::DeletePendingObjects()
534 wxNode
*node
= wxPendingDelete
.First();
537 wxObject
*obj
= (wxObject
*)node
->Data();
541 if (wxPendingDelete
.Find(obj
))
544 node
= wxPendingDelete
.First();
548 wxWindow
*wxApp::GetTopWindow()
552 else if (wxTopLevelWindows
.GetCount() > 0)
553 return wxTopLevelWindows
.GetFirst()->GetData();
558 void wxApp::SetTopWindow( wxWindow
*win
)
563 bool wxApp::Initialize()
565 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
567 wxClassInfo::InitializeClasses();
569 wxSystemSettings::Init();
571 // GL: I'm annoyed ... I don't know where to put this and I don't want to
572 // create a module for that as it's part of the core.
574 wxPendingEvents
= new wxList();
575 wxPendingEventsLocker
= new wxCriticalSection();
579 wxTheFontNameDirectory = new wxFontNameDirectory;
580 wxTheFontNameDirectory->Initialize();
583 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
584 wxTheColourDatabase
->Initialize();
586 wxInitializeStockLists();
587 wxInitializeStockObjects();
589 #if wxUSE_WX_RESOURCES
590 wxTheResourceCache
= new wxResourceCache( wxKEY_STRING
);
592 wxInitializeResourceSystem();
595 wxImage::InitStandardHandlers();
597 wxModule::RegisterModules();
598 if (!wxModule::InitializeModules()) return FALSE
;
603 void wxApp::CleanUp()
605 wxModule::CleanUpModules();
607 #if wxUSE_WX_RESOURCES
610 if (wxTheResourceCache
)
611 delete wxTheResourceCache
;
612 wxTheResourceCache
= (wxResourceCache
*) NULL
;
614 wxCleanUpResourceSystem();
617 if (wxTheColourDatabase
)
618 delete wxTheColourDatabase
;
619 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
622 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
623 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
626 wxDeleteStockObjects();
628 wxDeleteStockLists();
630 wxImage::CleanUpHandlers();
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() > 0)
652 wxLogDebug(_T("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
);
667 wxLog
*wxApp::CreateLogTarget()
673 //-----------------------------------------------------------------------------
675 //-----------------------------------------------------------------------------
677 int wxEntry( int argc
, char *argv
[] )
681 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
683 gtk_init( &argc
, &argv
);
685 wxSetDetectableAutoRepeat( TRUE
);
687 if (!wxApp::Initialize())
692 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
693 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
695 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
697 wxObject
*test_app
= app_ini();
699 wxTheApp
= (wxApp
*) test_app
;
702 wxCHECK_MSG( wxTheApp
, -1, _T("wxWindows error: no application object") );
704 wxTheApp
->argc
= argc
;
705 wxTheApp
->argv
= argv
;
707 wxString
name(wxFileNameFromPath(argv
[0]));
708 wxStripExtension( name
);
709 wxTheApp
->SetAppName( name
);
713 if ( !wxTheApp
->OnInitGui() )
716 // Here frames insert themselves automatically into wxTopLevelWindows by
717 // getting created in OnInit().
720 if ( !wxTheApp
->OnInit() )
726 /* delete pending toplevel windows (typically a single
727 dialog) so that, if there isn't any left, we don't
729 wxTheApp
->DeletePendingObjects();
731 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
733 if (wxTheApp
->Initialized())
735 retValue
= wxTheApp
->OnRun();
737 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
740 /* Forcibly delete the window. */
741 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
742 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
744 topWindow
->Close( TRUE
);
745 wxTheApp
->DeletePendingObjects();
750 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
758 // flush the logged messages if any
759 wxLog
*log
= wxLog::GetActiveTarget();
760 if (log
!= NULL
&& log
->HasPendingMessages())
763 // continuing to use user defined log target is unsafe from now on because
764 // some resources may be already unavailable, so replace it by something
766 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);