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 g_globalCursor
= wxCursor(wxCURSOR_DEFAULT
);
582 wxModule::RegisterModules();
583 if (!wxModule::InitializeModules()) return FALSE
;
588 void wxApp::CleanUp()
590 wxModule::CleanUpModules();
592 #if wxUSE_WX_RESOURCES
595 if (wxTheResourceCache
)
596 delete wxTheResourceCache
;
597 wxTheResourceCache
= (wxResourceCache
*) NULL
;
599 wxCleanUpResourceSystem();
602 if (wxTheColourDatabase
)
603 delete wxTheColourDatabase
;
604 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
607 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
608 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
611 wxDeleteStockObjects();
613 wxDeleteStockLists();
615 wxImage::CleanUpHandlers();
618 wxTheApp
= (wxApp
*) NULL
;
620 // GL: I'm annoyed ... I don't know where to put this and I don't want to
621 // create a module for that as it's part of the core.
623 delete wxPendingEvents
;
624 delete wxPendingEventsLocker
;
627 wxSystemSettings::Done();
631 wxClassInfo::CleanUpClasses();
633 // check for memory leaks
634 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
635 if (wxDebugContext::CountObjectsLeft() > 0)
637 wxLogDebug(_T("There were memory leaks.\n"));
638 wxDebugContext::Dump();
639 wxDebugContext::PrintStatistics();
644 // do this as the very last thing because everything else can log messages
645 wxLog::DontCreateOnDemand();
647 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
652 wxLog
*wxApp::CreateLogTarget()
658 //-----------------------------------------------------------------------------
660 //-----------------------------------------------------------------------------
662 int wxEntry( int argc
, char *argv
[] )
666 if (!wxOKlibc()) wxConvCurrent
= &wxConvLocal
;
668 gtk_init( &argc
, &argv
);
670 wxSetDetectableAutoRepeat( TRUE
);
672 if (!wxApp::Initialize())
677 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
678 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
680 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
682 wxObject
*test_app
= app_ini();
684 wxTheApp
= (wxApp
*) test_app
;
687 wxCHECK_MSG( wxTheApp
, -1, _T("wxWindows error: no application object") );
689 wxTheApp
->argc
= argc
;
690 wxTheApp
->argv
= argv
;
692 wxString
name(wxFileNameFromPath(argv
[0]));
693 wxStripExtension( name
);
694 wxTheApp
->SetAppName( name
);
698 if ( !wxTheApp
->OnInitGui() )
701 // Here frames insert themselves automatically into wxTopLevelWindows by
702 // getting created in OnInit().
705 if ( !wxTheApp
->OnInit() )
711 /* delete pending toplevel windows (typically a single
712 dialog) so that, if there isn't any left, we don't
714 wxTheApp
->DeletePendingObjects();
716 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
718 if (wxTheApp
->Initialized())
720 retValue
= wxTheApp
->OnRun();
722 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
725 /* Forcibly delete the window. */
726 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
727 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
729 topWindow
->Close( TRUE
);
730 wxTheApp
->DeletePendingObjects();
735 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
743 // flush the logged messages if any
744 wxLog
*log
= wxLog::GetActiveTarget();
745 if (log
!= NULL
&& log
->HasPendingMessages())
748 // continuing to use user defined log target is unsafe from now on because
749 // some resources may be already unavailable, so replace it by something
751 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);