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, "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( 10, 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( 10, 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 */
409 wxLog
*log
= wxLog::GetActiveTarget();
410 if (log
!= NULL
&& log
->HasPendingMessages())
413 /* Send OnIdle events to all windows */
414 bool needMore
= SendIdleEvents();
417 event
.RequestMore(TRUE
);
422 bool wxApp::SendIdleEvents()
424 bool needMore
= FALSE
;
426 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
429 wxWindow
* win
= node
->GetData();
430 if (SendIdleEvents(win
))
432 node
= node
->GetNext();
438 bool wxApp::SendIdleEvents( wxWindow
* win
)
440 bool needMore
= FALSE
;
443 event
.SetEventObject(win
);
445 win
->OnInternalIdle();
447 win
->ProcessEvent(event
);
449 if (event
.MoreRequested())
452 wxNode
* node
= win
->GetChildren().First();
455 wxWindow
* win
= (wxWindow
*) node
->Data();
456 if (SendIdleEvents(win
))
464 int wxApp::MainLoop()
470 void wxApp::ExitMainLoop()
475 bool wxApp::Initialized()
477 return m_initialized
;
480 bool wxApp::Pending()
482 return (gtk_events_pending() > 0);
485 void wxApp::Dispatch()
487 gtk_main_iteration();
491 void wxApp::ProcessPendingEvents()
493 wxNode
*node
= wxPendingEvents
->First();
494 wxCriticalSectionLocker
locker(*wxPendingEventsLocker
);
498 wxEvtHandler
*handler
= (wxEvtHandler
*)node
->Data();
500 handler
->ProcessPendingEvents();
504 node
= wxPendingEvents
->First();
507 #endif // wxUSE_THREADS
509 void wxApp::DeletePendingObjects()
511 wxNode
*node
= wxPendingDelete
.First();
514 wxObject
*obj
= (wxObject
*)node
->Data();
518 if (wxPendingDelete
.Find(obj
))
521 node
= wxPendingDelete
.First();
525 wxWindow
*wxApp::GetTopWindow()
529 else if (wxTopLevelWindows
.GetCount() > 0)
530 return wxTopLevelWindows
.GetFirst()->GetData();
535 void wxApp::SetTopWindow( wxWindow
*win
)
540 bool wxApp::Initialize()
542 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
544 wxClassInfo::InitializeClasses();
546 wxSystemSettings::Init();
548 // GL: I'm annoyed ... I don't know where to put this and I don't want to
549 // create a module for that as it's part of the core.
551 wxPendingEvents
= new wxList();
552 wxPendingEventsLocker
= new wxCriticalSection();
556 wxTheFontNameDirectory = new wxFontNameDirectory;
557 wxTheFontNameDirectory->Initialize();
560 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
561 wxTheColourDatabase
->Initialize();
563 wxInitializeStockLists();
564 wxInitializeStockObjects();
566 #if wxUSE_WX_RESOURCES
567 wxTheResourceCache
= new wxResourceCache( wxKEY_STRING
);
569 wxInitializeResourceSystem();
572 wxImage::InitStandardHandlers();
574 /* no global cursor under X
575 g_globalCursor = new wxCursor; */
577 wxModule::RegisterModules();
578 if (!wxModule::InitializeModules()) return FALSE
;
583 void wxApp::CleanUp()
585 wxModule::CleanUpModules();
587 #if wxUSE_WX_RESOURCES
590 if (wxTheResourceCache
)
591 delete wxTheResourceCache
;
592 wxTheResourceCache
= (wxResourceCache
*) NULL
;
594 wxCleanUpResourceSystem();
597 if (wxTheColourDatabase
)
598 delete wxTheColourDatabase
;
599 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
602 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
603 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
606 wxDeleteStockObjects();
608 wxDeleteStockLists();
610 wxImage::CleanUpHandlers();
613 wxTheApp
= (wxApp
*) NULL
;
615 // GL: I'm annoyed ... I don't know where to put this and I don't want to
616 // create a module for that as it's part of the core.
618 delete wxPendingEvents
;
619 delete wxPendingEventsLocker
;
622 wxSystemSettings::Done();
626 wxClassInfo::CleanUpClasses();
628 // check for memory leaks
629 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
630 if (wxDebugContext::CountObjectsLeft() > 0)
632 wxLogDebug(_T("There were memory leaks.\n"));
633 wxDebugContext::Dump();
634 wxDebugContext::PrintStatistics();
638 // do this as the very last thing because everything else can log messages
639 wxLog::DontCreateOnDemand();
641 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
646 wxLog
*wxApp::CreateLogTarget()
651 //-----------------------------------------------------------------------------
653 //-----------------------------------------------------------------------------
655 int wxEntry( int argc
, char *argv
[] )
659 gtk_init( &argc
, &argv
);
661 wxSetDetectableAutoRepeat( TRUE
);
663 if (!wxApp::Initialize())
668 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
669 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
671 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
673 wxObject
*test_app
= app_ini();
675 wxTheApp
= (wxApp
*) test_app
;
678 wxCHECK_MSG( wxTheApp
, -1, _T("wxWindows error: no application object") );
680 wxTheApp
->argc
= argc
;
681 wxTheApp
->argv
= argv
;
683 wxString
name(wxFileNameFromPath(argv
[0]));
684 wxStripExtension( name
);
685 wxTheApp
->SetAppName( name
);
689 if ( !wxTheApp
->OnInitGui() )
692 // Here frames insert themselves automatically into wxTopLevelWindows by
693 // getting created in OnInit().
696 if ( !wxTheApp
->OnInit() )
702 /* delete pending toplevel windows (typically a single
703 dialog) so that, if there isn't any left, we don't
705 wxTheApp
->DeletePendingObjects();
707 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
709 if (wxTheApp
->Initialized())
711 retValue
= wxTheApp
->OnRun();
713 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
716 /* Forcibly delete the window. */
717 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
718 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
720 topWindow
->Close( TRUE
);
721 wxTheApp
->DeletePendingObjects();
726 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
733 // flush the logged messages if any
734 wxLog
*log
= wxLog::GetActiveTarget();
735 if (log
!= NULL
&& log
->HasPendingMessages())
738 // continuing to use user defined log target is unsafe from now on because
739 // some resources may be already unavailable, so replace it by something
741 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);