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 /* Nothing to do for 15, 16, 24, 32 bit displays */
302 GdkVisual
*visual
= gdk_visual_get_system();
303 if (visual
->depth
> 8) return TRUE
;
305 /* this initiates the standard palette as defined by GdkImlib
306 in the GNOME libraries. it ensures that all GNOME applications
307 use the same 64 colormap entries on 8-bit displays so you
308 can use several rather graphics-heavy applications at the
310 NOTE: this doesn't really seem to work this way... */
313 GdkColormap *cmap = gdk_colormap_new( gdk_visual_get_system(), TRUE );
315 for (int i = 0; i < 64; i++)
318 col.red = g_palette[i*3 + 0] << 8;
319 col.green = g_palette[i*3 + 1] << 8;
320 col.blue = g_palette[i*3 + 2] << 8;
323 gdk_color_alloc( cmap, &col );
326 gtk_widget_set_default_colormap( cmap );
329 /* initialize color cube for 8-bit color reduction dithering */
331 GdkColormap
*cmap
= gtk_widget_get_default_colormap();
333 m_colorCube
= (unsigned char*)malloc(32 * 32 * 32);
335 for (int r
= 0; r
< 32; r
++)
337 for (int g
= 0; g
< 32; g
++)
339 for (int b
= 0; b
< 32; b
++)
341 int rr
= (r
<< 3) | (r
>> 2);
342 int gg
= (g
<< 3) | (g
>> 2);
343 int bb
= (b
<< 3) | (b
>> 2);
347 GdkColor
*colors
= cmap
->colors
;
352 for (int i
= 0; i
< cmap
->size
; i
++)
354 int rdiff
= ((rr
<< 8) - colors
[i
].red
);
355 int gdiff
= ((gg
<< 8) - colors
[i
].green
);
356 int bdiff
= ((bb
<< 8) - colors
[i
].blue
);
357 int sum
= ABS (rdiff
) + ABS (gdiff
) + ABS (bdiff
);
360 index
= i
; max
= sum
;
366 /* assume 8-bit true or static colors. this really
368 GdkVisual
* vis
= gdk_colormap_get_visual( cmap
);
369 index
= (r
>> (5 - vis
->red_prec
)) << vis
->red_shift
;
370 index
|= (g
>> (5 - vis
->green_prec
)) << vis
->green_shift
;
371 index
|= (b
>> (5 - vis
->blue_prec
)) << vis
->blue_shift
;
374 m_colorCube
[ (r
*1024) + (g
*32) + b
] = index
;
382 bool wxApp::ProcessIdle()
385 event
.SetEventObject( this );
386 ProcessEvent( event
);
388 return event
.MoreRequested();
391 void wxApp::OnIdle( wxIdleEvent
&event
)
393 static bool inOnIdle
= FALSE
;
395 /* Avoid recursion (via ProcessEvent default case) */
402 /* Resend in the main thread events which have been prepared in other
404 ProcessPendingEvents();
407 /* 'Garbage' collection of windows deleted with Close(). */
408 DeletePendingObjects();
410 /* flush the logged messages if any */
412 wxLog
*log
= wxLog::GetActiveTarget();
413 if (log
!= NULL
&& log
->HasPendingMessages())
417 /* Send OnIdle events to all windows */
418 bool needMore
= SendIdleEvents();
421 event
.RequestMore(TRUE
);
426 bool wxApp::SendIdleEvents()
428 bool needMore
= FALSE
;
430 wxWindowList::Node
* node
= wxTopLevelWindows
.GetFirst();
433 wxWindow
* win
= node
->GetData();
434 if (SendIdleEvents(win
))
436 node
= node
->GetNext();
442 bool wxApp::SendIdleEvents( wxWindow
* win
)
444 bool needMore
= FALSE
;
447 event
.SetEventObject(win
);
449 win
->OnInternalIdle();
451 win
->ProcessEvent(event
);
453 if (event
.MoreRequested())
456 wxNode
* node
= win
->GetChildren().First();
459 wxWindow
* win
= (wxWindow
*) node
->Data();
460 if (SendIdleEvents(win
))
468 int wxApp::MainLoop()
474 void wxApp::ExitMainLoop()
479 bool wxApp::Initialized()
481 return m_initialized
;
484 bool wxApp::Pending()
486 return (gtk_events_pending() > 0);
489 void wxApp::Dispatch()
491 gtk_main_iteration();
495 void wxApp::ProcessPendingEvents()
497 wxNode
*node
= wxPendingEvents
->First();
498 wxCriticalSectionLocker
locker(*wxPendingEventsLocker
);
502 wxEvtHandler
*handler
= (wxEvtHandler
*)node
->Data();
504 handler
->ProcessPendingEvents();
508 node
= wxPendingEvents
->First();
511 #endif // wxUSE_THREADS
513 void wxApp::DeletePendingObjects()
515 wxNode
*node
= wxPendingDelete
.First();
518 wxObject
*obj
= (wxObject
*)node
->Data();
522 if (wxPendingDelete
.Find(obj
))
525 node
= wxPendingDelete
.First();
529 wxWindow
*wxApp::GetTopWindow()
533 else if (wxTopLevelWindows
.GetCount() > 0)
534 return wxTopLevelWindows
.GetFirst()->GetData();
539 void wxApp::SetTopWindow( wxWindow
*win
)
544 bool wxApp::Initialize()
546 wxBuffer
= new wxChar
[BUFSIZ
+ 512];
548 wxClassInfo::InitializeClasses();
550 wxSystemSettings::Init();
552 // GL: I'm annoyed ... I don't know where to put this and I don't want to
553 // create a module for that as it's part of the core.
555 wxPendingEvents
= new wxList();
556 wxPendingEventsLocker
= new wxCriticalSection();
560 wxTheFontNameDirectory = new wxFontNameDirectory;
561 wxTheFontNameDirectory->Initialize();
564 wxTheColourDatabase
= new wxColourDatabase( wxKEY_STRING
);
565 wxTheColourDatabase
->Initialize();
567 wxInitializeStockLists();
568 wxInitializeStockObjects();
570 #if wxUSE_WX_RESOURCES
571 wxTheResourceCache
= new wxResourceCache( wxKEY_STRING
);
573 wxInitializeResourceSystem();
576 wxImage::InitStandardHandlers();
578 /* no global cursor under X
579 g_globalCursor = new wxCursor; */
581 wxModule::RegisterModules();
582 if (!wxModule::InitializeModules()) return FALSE
;
587 void wxApp::CleanUp()
589 wxModule::CleanUpModules();
591 #if wxUSE_WX_RESOURCES
594 if (wxTheResourceCache
)
595 delete wxTheResourceCache
;
596 wxTheResourceCache
= (wxResourceCache
*) NULL
;
598 wxCleanUpResourceSystem();
601 if (wxTheColourDatabase
)
602 delete wxTheColourDatabase
;
603 wxTheColourDatabase
= (wxColourDatabase
*) NULL
;
606 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
607 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
610 wxDeleteStockObjects();
612 wxDeleteStockLists();
614 wxImage::CleanUpHandlers();
617 wxTheApp
= (wxApp
*) NULL
;
619 // GL: I'm annoyed ... I don't know where to put this and I don't want to
620 // create a module for that as it's part of the core.
622 delete wxPendingEvents
;
623 delete wxPendingEventsLocker
;
626 wxSystemSettings::Done();
630 wxClassInfo::CleanUpClasses();
632 // check for memory leaks
633 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
634 if (wxDebugContext::CountObjectsLeft() > 0)
636 wxLogDebug(_T("There were memory leaks.\n"));
637 wxDebugContext::Dump();
638 wxDebugContext::PrintStatistics();
643 // do this as the very last thing because everything else can log messages
644 wxLog::DontCreateOnDemand();
646 wxLog
*oldLog
= wxLog::SetActiveTarget( (wxLog
*) NULL
);
651 wxLog
*wxApp::CreateLogTarget()
657 //-----------------------------------------------------------------------------
659 //-----------------------------------------------------------------------------
661 int wxEntry( int argc
, char *argv
[] )
665 if (!wxOKlibc()) wxConv_current
= &wxConv_local
;
667 gtk_init( &argc
, &argv
);
669 wxSetDetectableAutoRepeat( TRUE
);
671 if (!wxApp::Initialize())
676 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
677 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
679 wxAppInitializerFunction app_ini
= wxApp::GetInitializerFunction();
681 wxObject
*test_app
= app_ini();
683 wxTheApp
= (wxApp
*) test_app
;
686 wxCHECK_MSG( wxTheApp
, -1, _T("wxWindows error: no application object") );
688 wxTheApp
->argc
= argc
;
689 wxTheApp
->argv
= argv
;
691 wxString
name(wxFileNameFromPath(argv
[0]));
692 wxStripExtension( name
);
693 wxTheApp
->SetAppName( name
);
697 if ( !wxTheApp
->OnInitGui() )
700 // Here frames insert themselves automatically into wxTopLevelWindows by
701 // getting created in OnInit().
704 if ( !wxTheApp
->OnInit() )
710 /* delete pending toplevel windows (typically a single
711 dialog) so that, if there isn't any left, we don't
713 wxTheApp
->DeletePendingObjects();
715 wxTheApp
->m_initialized
= wxTopLevelWindows
.GetCount() != 0;
717 if (wxTheApp
->Initialized())
719 retValue
= wxTheApp
->OnRun();
721 wxWindow
*topWindow
= wxTheApp
->GetTopWindow();
724 /* Forcibly delete the window. */
725 if (topWindow
->IsKindOf(CLASSINFO(wxFrame
)) ||
726 topWindow
->IsKindOf(CLASSINFO(wxDialog
)) )
728 topWindow
->Close( TRUE
);
729 wxTheApp
->DeletePendingObjects();
734 wxTheApp
->SetTopWindow( (wxWindow
*) NULL
);
742 // flush the logged messages if any
743 wxLog
*log
= wxLog::GetActiveTarget();
744 if (log
!= NULL
&& log
->HasPendingMessages())
747 // continuing to use user defined log target is unsafe from now on because
748 // some resources may be already unavailable, so replace it by something
750 wxLog
*oldlog
= wxLog::SetActiveTarget(new wxLogStderr
);