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"
31 #include "wx/thread.h"
39 #include "wx/gtk/win_gtk.h"
41 //-----------------------------------------------------------------------------
43 //-----------------------------------------------------------------------------
45 wxApp
*wxTheApp
= (wxApp
*) NULL
;
46 wxAppInitializerFunction
wxApp::m_appInitFn
= (wxAppInitializerFunction
) NULL
;
49 extern wxList
*wxPendingEvents
;
50 extern wxCriticalSection
*wxPendingEventsLocker
;
52 extern wxResourceCache
*wxTheResourceCache
;
55 unsigned char g_palette
[64*3] =
123 //-----------------------------------------------------------------------------
125 //-----------------------------------------------------------------------------
127 extern void wxFlushResources(void);
129 //-----------------------------------------------------------------------------
131 //-----------------------------------------------------------------------------
138 /* forward declaration */
139 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) );
143 /* it's necessary to call ProcessIdle() to update the frames sizes which
144 might have been changed (it also will update other things set from
145 OnUpdateUI() which is a nice (and desired) side effect) */
146 while (wxTheApp
->ProcessIdle()) { }
149 for ( wxWindowList::Node
*node
= wxTopLevelWindows
.GetFirst();
151 node
= node
->GetNext() )
153 wxWindow
*win
= node
->GetData();
154 win
->OnInternalIdle();
158 if (wxTheApp
->m_idleTag
)
160 /* We need to temporarily remove idle callbacks or the loop will
162 gtk_idle_remove( wxTheApp
->m_idleTag
);
163 wxTheApp
->m_idleTag
= 0;
165 while (gtk_events_pending())
166 gtk_main_iteration();
168 /* re-add idle handler */
169 wxTheApp
->m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
173 while (gtk_events_pending())
174 gtk_main_iteration();
180 gint
wxapp_idle_callback( gpointer
WXUNUSED(data
) )
182 if (!wxTheApp
) return TRUE
;
184 #if (GTK_MINOR_VERSION > 0)
185 /* when getting called from GDK's idle handler we
186 are no longer within GDK's grab on the GUI
187 thread so we must lock it here ourselves */
188 GDK_THREADS_ENTER ();
191 /* sent idle event to all who request them */
192 while (wxTheApp
->ProcessIdle()) { }
194 /* we don't want any more idle events until the next event is
196 gtk_idle_remove( wxTheApp
->m_idleTag
);
197 wxTheApp
->m_idleTag
= 0;
199 /* indicate that we are now in idle mode - even so deeply
200 in idle mode that we don't get any idle events anymore.
201 this is like wxMSW where an idle event is sent only
202 once each time after the event queue has been completely
206 #if (GTK_MINOR_VERSION > 0)
207 /* release lock again */
208 GDK_THREADS_LEAVE ();
214 void wxapp_install_idle_handler()
216 wxASSERT_MSG( wxTheApp
->m_idleTag
== 0, _T("attempt to install idle handler twice") );
218 /* this routine gets called by all event handlers
219 indicating that the idle is over. */
221 wxTheApp
->m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
227 static gint
wxapp_wakeup_timerout_callback( gpointer
WXUNUSED(data
) )
229 gtk_timeout_remove( wxTheApp
->m_wakeUpTimerTag
);
230 wxTheApp
->m_wakeUpTimerTag
= 0;
232 #if (GTK_MINOR_VERSION > 0)
233 /* when getting called from GDK's time-out handler
234 we are no longer within GDK's grab on the GUI
235 thread so we must lock it here ourselves */
236 GDK_THREADS_ENTER ();
239 /* unblock other threads wishing to do some GUI things */
242 /* wake up other threads */
245 /* block other thread again */
248 #if (GTK_MINOR_VERSION > 0)
249 /* release lock again */
250 GDK_THREADS_LEAVE ();
253 wxTheApp
->m_wakeUpTimerTag
= gtk_timeout_add( 20, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
259 //-----------------------------------------------------------------------------
261 //-----------------------------------------------------------------------------
263 IMPLEMENT_DYNAMIC_CLASS(wxApp
,wxEvtHandler
)
265 BEGIN_EVENT_TABLE(wxApp
, wxEvtHandler
)
266 EVT_IDLE(wxApp::OnIdle
)
273 m_topWindow
= (wxWindow
*) NULL
;
274 m_exitOnFrameDelete
= TRUE
;
276 m_idleTag
= gtk_idle_add( wxapp_idle_callback
, (gpointer
) NULL
);
279 m_wakeUpTimerTag
= gtk_timeout_add( 20, wxapp_wakeup_timerout_callback
, (gpointer
) NULL
);
282 m_colorCube
= (unsigned char*) NULL
;
287 if (m_idleTag
) gtk_idle_remove( m_idleTag
);
290 if (m_wakeUpTimerTag
) gtk_timeout_remove( m_wakeUpTimerTag
);
293 if (m_colorCube
) free(m_colorCube
);
296 bool wxApp::OnInitGui()
298 /* Nothing to do for 15, 16, 24, 32 bit displays */
300 GdkVisual
*visual
= gdk_visual_get_system();
301 if (visual
->depth
> 8) return TRUE
;
303 /* this initiates the standard palette as defined by GdkImlib
304 in the GNOME libraries. it ensures that all GNOME applications
305 use the same 64 colormap entries on 8-bit displays so you
306 can use several rather graphics-heavy applications at the
308 NOTE: this doesn't really seem to work this way... */
311 GdkColormap *cmap = gdk_colormap_new( gdk_visual_get_system(), TRUE );
313 for (int i = 0; i < 64; i++)
316 col.red = g_palette[i*3 + 0] << 8;
317 col.green = g_palette[i*3 + 1] << 8;
318 col.blue = g_palette[i*3 + 2] << 8;
321 gdk_color_alloc( cmap, &col );
324 gtk_widget_set_default_colormap( cmap );
327 /* initialize color cube for 8-bit color reduction dithering */
329 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
331 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
333 for (int r
= 0; r
< 32; r
++)
335 for (int g
= 0; g
< 32; g
++)
337 for (int b
= 0; b
< 32; b
++)
339 int rr
= (r
<< 3) | (r
>> 2);
340 int gg
= (g
<< 3) | (g
>> 2);
341 int bb
= (b
<< 3) | (b
>> 2);
345 GdkColor
*colors
= cmap
->colors
;
350 for (int i
= 0; i
< cmap
->size
; i
++)
352 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
353 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
354 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
355 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
358 index
= i
; max
= sum
;
364 /* assume 8-bit true or static colors. this really
366 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
367 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
368 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
369 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
372 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
380 bool wxApp::ProcessIdle()
383 event
.SetEventObject( this );
384 ProcessEvent( event
);
386 return event
.MoreRequested();
389 void wxApp::OnIdle( wxIdleEvent
&event
)
391 static bool inOnIdle
= FALSE
;
393 /* Avoid recursion (via ProcessEvent default case) */
400 /* Resend in the main thread events which have been prepared in other
402 ProcessPendingEvents();
405 /* 'Garbage' collection of windows deleted with Close(). */
406 DeletePendingObjects();
408 /* flush the logged messages if any */
410 wxLog
*log
= wxLog::GetActiveTarget();
411 if (log
!= NULL
&& log
->HasPendingMessages())
415 /* Send OnIdle events to all windows */
416 bool needMore
= SendIdleEvents();
419 event
.RequestMore(TRUE
);
424 bool wxApp::SendIdleEvents()
426 bool needMore
= FALSE
;
428 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
431 wxWindow
* win
= node
->GetData();
432 if (SendIdleEvents(win
))
434 node
= node
->GetNext();
440 bool wxApp::SendIdleEvents( wxWindow
* win
)
442 bool needMore
= FALSE
;
445 event
.SetEventObject(win
);
447 win
->OnInternalIdle();
449 win
->ProcessEvent(event
);
451 if (event
.MoreRequested())
454 wxNode
* node
= win
->GetChildren().First();
457 wxWindow
* win
= (wxWindow
*) node
->Data();
458 if (SendIdleEvents(win
))
466 int wxApp::MainLoop()
472 void wxApp::ExitMainLoop()
477 bool wxApp::Initialized()
479 return m_initialized
;
482 bool wxApp::Pending()
484 return (gtk_events_pending() > 0);
487 void wxApp::Dispatch()
489 gtk_main_iteration();
493 void wxApp::ProcessPendingEvents()
495 wxNode
*node
= wxPendingEvents
->First();
496 wxCriticalSectionLocker
locker(*wxPendingEventsLocker
);
500 wxEvtHandler
*handler
= (wxEvtHandler
*)node
->Data();
502 handler
->ProcessPendingEvents();
506 node
= wxPendingEvents
->First();
509 #endif // wxUSE_THREADS
511 void wxApp::DeletePendingObjects()
513 wxNode
*node
= wxPendingDelete
.First();
516 wxObject
*obj
= (wxObject
*)node
->Data();
520 if (wxPendingDelete
.Find(obj
))
523 node
= wxPendingDelete
.First();
527 wxWindow
*wxApp::GetTopWindow()
531 else if (wxTopLevelWindows
.GetCount() > 0)
532 return wxTopLevelWindows
.GetFirst()->GetData();
537 void wxApp::SetTopWindow( wxWindow
*win
)
542 bool wxApp::Initialize()
544 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
546 wxClassInfo::InitializeClasses();
548 wxSystemSettings::Init();
550 // GL: I'm annoyed ... I don't know where to put this and I don't want to
551 // create a module for that as it's part of the core.
553 wxPendingEvents
= new wxList();
554 wxPendingEventsLocker
= new wxCriticalSection();
558 wxTheFontNameDirectory = new wxFontNameDirectory;
559 wxTheFontNameDirectory->Initialize();
562 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
563 wxTheColourDatabase
->Initialize();
565 wxInitializeStockLists();
566 wxInitializeStockObjects();
568 #if wxUSE_WX_RESOURCES
569 wxTheResourceCache
= new wxResourceCache( wxKEY_STRING
);
571 wxInitializeResourceSystem();
574 wxImage::InitStandardHandlers();
576 /* no global cursor under X
577 g_globalCursor = new wxCursor; */
579 wxModule::RegisterModules();
580 if (!wxModule::InitializeModules()) return FALSE
;
585 void wxApp::CleanUp()
587 wxModule::CleanUpModules();
589 #if wxUSE_WX_RESOURCES
592 if (wxTheResourceCache
)
593 delete wxTheResourceCache
;
594 wxTheResourceCache
= (wxResourceCache
*) NULL
;
596 wxCleanUpResourceSystem();
599 if (wxTheColourDatabase
)
600 delete wxTheColourDatabase
;
601 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
604 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
605 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
608 wxDeleteStockObjects();
610 wxDeleteStockLists();
612 wxImage::CleanUpHandlers();
615 wxTheApp
= (wxApp
*) NULL
;
617 // GL: I'm annoyed ... I don't know where to put this and I don't want to
618 // create a module for that as it's part of the core.
620 delete wxPendingEvents
;
621 delete wxPendingEventsLocker
;
624 wxSystemSettings::Done();
628 wxClassInfo::CleanUpClasses();
630 // check for memory leaks
631 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
632 if (wxDebugContext::CountObjectsLeft() > 0)
634 wxLogDebug(_T("There were memory leaks.\n"));
635 wxDebugContext::Dump();
636 wxDebugContext::PrintStatistics();
641 // do this as the very last thing because everything else can log messages
642 wxLog::DontCreateOnDemand();
644 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
649 wxLog
*wxApp::CreateLogTarget()
655 //-----------------------------------------------------------------------------
657 //-----------------------------------------------------------------------------
659 int wxEntry( int argc
, char *argv
[] )
663 gtk_init( &argc
, &argv
);
665 wxSetDetectableAutoRepeat( TRUE
);
667 if (!wxApp::Initialize())
672 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
673 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
675 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
677 wxObject
*test_app
= app_ini();
679 wxTheApp
= (wxApp
*) test_app
;
682 wxCHECK_MSG( wxTheApp
, -1, _T("wxWindows error: no application object") );
684 wxTheApp
->argc
= argc
;
685 wxTheApp
->argv
= argv
;
687 wxString
name(wxFileNameFromPath(argv
[0]));
688 wxStripExtension( name
);
689 wxTheApp
->SetAppName( name
);
693 if ( !wxTheApp
->OnInitGui() )
696 // Here frames insert themselves automatically into wxTopLevelWindows by
697 // getting created in OnInit().
700 if ( !wxTheApp
->OnInit() )
706 /* delete pending toplevel windows (typically a single
707 dialog) so that, if there isn't any left, we don't
709 wxTheApp
->DeletePendingObjects();
711 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
713 if (wxTheApp
->Initialized())
715 retValue
= wxTheApp
->OnRun();
717 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
720 /* Forcibly delete the window. */
721 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
722 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
724 topWindow
->Close( TRUE
);
725 wxTheApp
->DeletePendingObjects();
730 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
738 // flush the logged messages if any
739 wxLog
*log
= wxLog::GetActiveTarget();
740 if (log
!= NULL
&& log
->HasPendingMessages())
743 // continuing to use user defined log target is unsafe from now on because
744 // some resources may be already unavailable, so replace it by something
746 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);