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 extern wxCursor g_globalCursor
;
49 wxApp
*wxTheApp
= (wxApp
*) NULL
;
50 wxAppInitializerFunction
wxApp::m_appInitFn
= (wxAppInitializerFunction
) NULL
;
53 extern wxList
*wxPendingEvents
;
54 extern wxCriticalSection
*wxPendingEventsLocker
;
56 extern wxResourceCache
*wxTheResourceCache
;
59 unsigned char g_palette
[64*3] =
127 //-----------------------------------------------------------------------------
129 //-----------------------------------------------------------------------------
131 extern void wxFlushResources(void);
133 //-----------------------------------------------------------------------------
135 //-----------------------------------------------------------------------------
142 /* forward declaration */
143 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) );
147 /* it's necessary to call ProcessIdle() to update the frames sizes which
148 might have been changed (it also will update other things set from
149 OnUpdateUI() which is a nice (and desired) side effect) */
150 while (wxTheApp
->ProcessIdle()) { }
153 for ( wxWindowList::Node
*node
= wxTopLevelWindows
.GetFirst();
155 node
= node
->GetNext() )
157 wxWindow
*win
= node
->GetData();
158 win
->OnInternalIdle();
162 if (wxTheApp
->m_idleTag
)
164 /* We need to temporarily remove idle callbacks or the loop will
166 gtk_idle_remove( wxTheApp
->m_idleTag
);
167 wxTheApp
->m_idleTag
= 0;
169 while (gtk_events_pending())
170 gtk_main_iteration();
172 /* re-add idle handler */
173 wxTheApp
->m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
177 while (gtk_events_pending())
178 gtk_main_iteration();
184 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
186 if (!wxTheApp
) return TRUE
;
188 #if (GTK_MINOR_VERSION > 0)
189 /* when getting called from GDK's idle handler we
190 are no longer within GDK's grab on the GUI
191 thread so we must lock it here ourselves */
192 GDK_THREADS_ENTER ();
195 /* sent idle event to all who request them */
196 while (wxTheApp
->ProcessIdle()) { }
198 /* we don't want any more idle events until the next event is
200 gtk_idle_remove( wxTheApp
->m_idleTag
);
201 wxTheApp
->m_idleTag
= 0;
203 /* indicate that we are now in idle mode - even so deeply
204 in idle mode that we don't get any idle events anymore.
205 this is like wxMSW where an idle event is sent only
206 once each time after the event queue has been completely
210 #if (GTK_MINOR_VERSION > 0)
211 /* release lock again */
212 GDK_THREADS_LEAVE ();
218 void wxapp_install_idle_handler()
220 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, _T("attempt to install idle handler twice") );
222 /* this routine gets called by all event handlers
223 indicating that the idle is over. */
225 wxTheApp
->m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
231 static gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
233 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
234 wxTheApp
->m_wakeUpTimerTag
= 0;
236 #if (GTK_MINOR_VERSION > 0)
237 /* when getting called from GDK's time-out handler
238 we are no longer within GDK's grab on the GUI
239 thread so we must lock it here ourselves */
240 GDK_THREADS_ENTER ();
243 /* unblock other threads wishing to do some GUI things */
246 /* wake up other threads */
249 /* block other thread again */
252 #if (GTK_MINOR_VERSION > 0)
253 /* release lock again */
254 GDK_THREADS_LEAVE ();
257 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 20, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
263 //-----------------------------------------------------------------------------
265 //-----------------------------------------------------------------------------
267 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
269 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
270 EVT_IDLE(wxApp::OnIdle
)
277 m_topWindow
= (wxWindow
*) NULL
;
278 m_exitOnFrameDelete
= TRUE
;
280 m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
283 m_wakeUpTimerTag
= gtk_timeout_add( 20, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
286 m_colorCube
= (unsigned char*) NULL
;
291 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
294 if (m_wakeUpTimerTag
) gtk_timeout_remove( m_wakeUpTimerTag
);
297 if (m_colorCube
) free(m_colorCube
);
300 bool wxApp::OnInitGui()
302 /* Nothing to do for 15, 16, 24, 32 bit displays */
304 GdkVisual
*visual
= gdk_visual_get_system();
305 if (visual
->depth
> 8) return TRUE
;
307 /* this initiates the standard palette as defined by GdkImlib
308 in the GNOME libraries. it ensures that all GNOME applications
309 use the same 64 colormap entries on 8-bit displays so you
310 can use several rather graphics-heavy applications at the
312 NOTE: this doesn't really seem to work this way... */
315 GdkColormap *cmap = gdk_colormap_new( gdk_visual_get_system(), TRUE );
317 for (int i = 0; i < 64; i++)
320 col.red = g_palette[i*3 + 0] << 8;
321 col.green = g_palette[i*3 + 1] << 8;
322 col.blue = g_palette[i*3 + 2] << 8;
325 gdk_color_alloc( cmap, &col );
328 gtk_widget_set_default_colormap( cmap );
331 /* initialize color cube for 8-bit color reduction dithering */
333 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
335 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
337 for (int r
= 0; r
< 32; r
++)
339 for (int g
= 0; g
< 32; g
++)
341 for (int b
= 0; b
< 32; b
++)
343 int rr
= (r
<< 3) | (r
>> 2);
344 int gg
= (g
<< 3) | (g
>> 2);
345 int bb
= (b
<< 3) | (b
>> 2);
349 GdkColor
*colors
= cmap
->colors
;
354 for (int i
= 0; i
< cmap
->size
; i
++)
356 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
357 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
358 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
359 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
362 index
= i
; max
= sum
;
368 /* assume 8-bit true or static colors. this really
370 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
371 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
372 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
373 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
376 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
384 bool wxApp::ProcessIdle()
387 event
.SetEventObject( this );
388 ProcessEvent( event
);
390 return event
.MoreRequested();
393 void wxApp::OnIdle( wxIdleEvent
&event
)
395 static bool inOnIdle
= FALSE
;
397 /* Avoid recursion (via ProcessEvent default case) */
404 /* Resend in the main thread events which have been prepared in other
406 ProcessPendingEvents();
409 /* 'Garbage' collection of windows deleted with Close(). */
410 DeletePendingObjects();
412 /* flush the logged messages if any */
414 wxLog
*log
= wxLog::GetActiveTarget();
415 if (log
!= NULL
&& log
->HasPendingMessages())
419 /* Send OnIdle events to all windows */
420 bool needMore
= SendIdleEvents();
423 event
.RequestMore(TRUE
);
428 bool wxApp::SendIdleEvents()
430 bool needMore
= FALSE
;
432 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
435 wxWindow
* win
= node
->GetData();
436 if (SendIdleEvents(win
))
438 node
= node
->GetNext();
444 bool wxApp::SendIdleEvents( wxWindow
* win
)
446 bool needMore
= FALSE
;
449 event
.SetEventObject(win
);
451 win
->OnInternalIdle();
453 win
->ProcessEvent(event
);
455 if (event
.MoreRequested())
458 wxNode
* node
= win
->GetChildren().First();
461 wxWindow
* win
= (wxWindow
*) node
->Data();
462 if (SendIdleEvents(win
))
470 int wxApp::MainLoop()
476 void wxApp::ExitMainLoop()
481 bool wxApp::Initialized()
483 return m_initialized
;
486 bool wxApp::Pending()
488 return (gtk_events_pending() > 0);
491 void wxApp::Dispatch()
493 gtk_main_iteration();
497 void wxApp::ProcessPendingEvents()
499 wxNode
*node
= wxPendingEvents
->First();
500 wxCriticalSectionLocker
locker(*wxPendingEventsLocker
);
504 wxEvtHandler
*handler
= (wxEvtHandler
*)node
->Data();
506 handler
->ProcessPendingEvents();
510 node
= wxPendingEvents
->First();
513 #endif // wxUSE_THREADS
515 void wxApp::DeletePendingObjects()
517 wxNode
*node
= wxPendingDelete
.First();
520 wxObject
*obj
= (wxObject
*)node
->Data();
524 if (wxPendingDelete
.Find(obj
))
527 node
= wxPendingDelete
.First();
531 wxWindow
*wxApp::GetTopWindow()
535 else if (wxTopLevelWindows
.GetCount() > 0)
536 return wxTopLevelWindows
.GetFirst()->GetData();
541 void wxApp::SetTopWindow( wxWindow
*win
)
546 bool wxApp::Initialize()
548 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
550 wxClassInfo::InitializeClasses();
552 wxSystemSettings::Init();
554 // GL: I'm annoyed ... I don't know where to put this and I don't want to
555 // create a module for that as it's part of the core.
557 wxPendingEvents
= new wxList();
558 wxPendingEventsLocker
= new wxCriticalSection();
562 wxTheFontNameDirectory = new wxFontNameDirectory;
563 wxTheFontNameDirectory->Initialize();
566 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
567 wxTheColourDatabase
->Initialize();
569 wxInitializeStockLists();
570 wxInitializeStockObjects();
572 #if wxUSE_WX_RESOURCES
573 wxTheResourceCache
= new wxResourceCache( wxKEY_STRING
);
575 wxInitializeResourceSystem();
578 wxImage::InitStandardHandlers();
580 // Not needed, leave it as default. (KB)
581 // g_globalCursor = wxCursor(wxCURSOR_DEFAULT);
583 wxModule::RegisterModules();
584 if (!wxModule::InitializeModules()) return FALSE
;
589 void wxApp::CleanUp()
591 wxModule::CleanUpModules();
593 #if wxUSE_WX_RESOURCES
596 if (wxTheResourceCache
)
597 delete wxTheResourceCache
;
598 wxTheResourceCache
= (wxResourceCache
*) NULL
;
600 wxCleanUpResourceSystem();
603 if (wxTheColourDatabase
)
604 delete wxTheColourDatabase
;
605 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
608 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
609 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
612 wxDeleteStockObjects();
614 wxDeleteStockLists();
616 wxImage::CleanUpHandlers();
619 wxTheApp
= (wxApp
*) NULL
;
621 // GL: I'm annoyed ... I don't know where to put this and I don't want to
622 // create a module for that as it's part of the core.
624 delete wxPendingEvents
;
625 delete wxPendingEventsLocker
;
628 wxSystemSettings::Done();
632 wxClassInfo::CleanUpClasses();
634 // check for memory leaks
635 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
636 if (wxDebugContext::CountObjectsLeft() > 0)
638 wxLogDebug(_T("There were memory leaks.\n"));
639 wxDebugContext::Dump();
640 wxDebugContext::PrintStatistics();
645 // do this as the very last thing because everything else can log messages
646 wxLog::DontCreateOnDemand();
648 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
653 wxLog
*wxApp::CreateLogTarget()
659 //-----------------------------------------------------------------------------
661 //-----------------------------------------------------------------------------
663 int wxEntry( int argc
, char *argv
[] )
667 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
669 gtk_init( &argc
, &argv
);
671 wxSetDetectableAutoRepeat( TRUE
);
673 if (!wxApp::Initialize())
678 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
679 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
681 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
683 wxObject
*test_app
= app_ini();
685 wxTheApp
= (wxApp
*) test_app
;
688 wxCHECK_MSG( wxTheApp
, -1, _T("wxWindows error: no application object") );
690 wxTheApp
->argc
= argc
;
691 wxTheApp
->argv
= argv
;
693 wxString
name(wxFileNameFromPath(argv
[0]));
694 wxStripExtension( name
);
695 wxTheApp
->SetAppName( name
);
699 if ( !wxTheApp
->OnInitGui() )
702 // Here frames insert themselves automatically into wxTopLevelWindows by
703 // getting created in OnInit().
706 if ( !wxTheApp
->OnInit() )
712 /* delete pending toplevel windows (typically a single
713 dialog) so that, if there isn't any left, we don't
715 wxTheApp
->DeletePendingObjects();
717 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
719 if (wxTheApp
->Initialized())
721 retValue
= wxTheApp
->OnRun();
723 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
726 /* Forcibly delete the window. */
727 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
728 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
730 topWindow
->Close( TRUE
);
731 wxTheApp
->DeletePendingObjects();
736 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
744 // flush the logged messages if any
745 wxLog
*log
= wxLog::GetActiveTarget();
746 if (log
!= NULL
&& log
->HasPendingMessages())
749 // continuing to use user defined log target is unsafe from now on because
750 // some resources may be already unavailable, so replace it by something
752 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);