]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/app.cpp
Distrib things.
[wxWidgets.git] / src / gtk1 / app.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: app.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling, Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "app.h"
12 #endif
13
14 #include "wx/app.h"
15 #include "wx/gdicmn.h"
16 #include "wx/utils.h"
17 #include "wx/intl.h"
18 #include "wx/log.h"
19 #include "wx/memory.h"
20 #include "wx/font.h"
21 #include "wx/settings.h"
22 #include "wx/dialog.h"
23
24 #if wxUSE_WX_RESOURCES
25 #include "wx/resource.h"
26 #endif
27
28 #include "wx/module.h"
29 #include "wx/image.h"
30
31 #if wxUSE_THREADS
32 #include "wx/thread.h"
33 #endif
34
35 #include <unistd.h>
36
37 #include <glib.h>
38 #include <gdk/gdk.h>
39 #include <gtk/gtk.h>
40
41 #include "wx/gtk/win_gtk.h"
42
43 //-----------------------------------------------------------------------------
44 // global data
45 //-----------------------------------------------------------------------------
46
47 wxApp *wxTheApp = (wxApp *) NULL;
48 wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
49
50 extern bool g_isIdle;
51
52 //-----------------------------------------------------------------------------
53 // local functions
54 //-----------------------------------------------------------------------------
55
56 /* forward declaration */
57 gint wxapp_idle_callback( gpointer WXUNUSED(data) );
58 void wxapp_install_idle_handler();
59
60 #if wxUSE_THREADS
61 gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) );
62 #endif
63
64 //-----------------------------------------------------------------------------
65 // wxExit
66 //-----------------------------------------------------------------------------
67
68 void wxExit()
69 {
70 gtk_main_quit();
71 }
72
73 //-----------------------------------------------------------------------------
74 // wxYield
75 //-----------------------------------------------------------------------------
76
77 bool wxYield()
78 {
79 bool has_idle = (wxTheApp->m_idleTag != 0);
80
81 if (has_idle)
82 {
83 /* We need to temporarily remove idle callbacks or the loop will
84 never finish. */
85 gtk_idle_remove( wxTheApp->m_idleTag );
86 wxTheApp->m_idleTag = 0;
87 }
88
89 while (gtk_events_pending())
90 gtk_main_iteration();
91
92 /* it's necessary to call ProcessIdle() to update the frames sizes which
93 might have been changed (it also will update other things set from
94 OnUpdateUI() which is a nice (and desired) side effect) */
95 while (wxTheApp->ProcessIdle()) { }
96
97 if (has_idle)
98 {
99 /* re-add idle handler */
100 wxTheApp->m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
101 }
102
103 return TRUE;
104 }
105
106 //-----------------------------------------------------------------------------
107 // wxWakeUpIdle
108 //-----------------------------------------------------------------------------
109
110 void wxWakeUpIdle()
111 {
112 #if wxUSE_THREADS
113 if (!wxThread::IsMain())
114 wxMutexGuiEnter();
115 #endif
116
117 if (g_isIdle)
118 wxapp_install_idle_handler();
119
120 #if wxUSE_THREADS
121 if (!wxThread::IsMain())
122 wxMutexGuiLeave();
123 #endif
124 }
125
126 //-----------------------------------------------------------------------------
127 // local functions
128 //-----------------------------------------------------------------------------
129
130 gint wxapp_idle_callback( gpointer WXUNUSED(data) )
131 {
132 if (!wxTheApp) return TRUE;
133
134 // when getting called from GDK's time-out handler
135 // we are no longer within GDK's grab on the GUI
136 // thread so we must lock it here ourselves
137 gdk_threads_enter();
138
139 /* sent idle event to all who request them */
140 while (wxTheApp->ProcessIdle()) { }
141
142 /* we don't want any more idle events until the next event is
143 sent to wxGTK */
144 gtk_idle_remove( wxTheApp->m_idleTag );
145 wxTheApp->m_idleTag = 0;
146
147 /* indicate that we are now in idle mode - even so deeply
148 in idle mode that we don't get any idle events anymore.
149 this is like wxMSW where an idle event is sent only
150 once each time after the event queue has been completely
151 emptied */
152 g_isIdle = TRUE;
153
154 // release lock again
155 gdk_threads_leave();
156
157 return TRUE;
158 }
159
160 void wxapp_install_idle_handler()
161 {
162 wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
163
164 /* This routine gets called by all event handlers
165 indicating that the idle is over. It may also
166 get called from other thread for sending events
167 to the main thread (and processing these in
168 idle time). */
169
170 wxTheApp->m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
171
172 g_isIdle = FALSE;
173 }
174
175 #if wxUSE_THREADS
176
177 void wxapp_install_thread_wakeup()
178 {
179 if (wxTheApp->m_wakeUpTimerTag) return;
180
181 wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 50, wxapp_wakeup_timerout_callback, (gpointer) NULL );
182 }
183
184 void wxapp_uninstall_thread_wakeup()
185 {
186 if (!wxTheApp->m_wakeUpTimerTag) return;
187
188 gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
189 wxTheApp->m_wakeUpTimerTag = 0;
190 }
191
192 gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
193 {
194 // when getting called from GDK's time-out handler
195 // we are no longer within GDK's grab on the GUI
196 // thread so we must lock it here ourselves
197 gdk_threads_enter();
198
199 wxapp_uninstall_thread_wakeup();
200
201 // unblock other threads wishing to do some GUI things
202 wxMutexGuiLeave();
203
204 // wake up other threads
205 wxUsleep( 1 );
206
207 // block other thread again
208 wxMutexGuiEnter();
209
210 wxapp_install_thread_wakeup();
211
212 // release lock again
213 gdk_threads_leave();
214
215 return TRUE;
216 }
217
218 #endif // wxUSE_THREADS
219
220 //-----------------------------------------------------------------------------
221 // wxApp
222 //-----------------------------------------------------------------------------
223
224 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
225
226 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
227 EVT_IDLE(wxApp::OnIdle)
228 END_EVENT_TABLE()
229
230 wxApp::wxApp()
231 {
232 wxTheApp = this;
233
234 m_topWindow = (wxWindow *) NULL;
235 m_exitOnFrameDelete = TRUE;
236
237 m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
238
239 #if wxUSE_THREADS
240 m_wakeUpTimerTag = 0;
241 wxapp_install_thread_wakeup();
242 #endif
243
244 m_colorCube = (unsigned char*) NULL;
245 }
246
247 wxApp::~wxApp()
248 {
249 if (m_idleTag) gtk_idle_remove( m_idleTag );
250
251 #if wxUSE_THREADS
252 wxapp_uninstall_thread_wakeup();
253 #endif
254
255 if (m_colorCube) free(m_colorCube);
256 }
257
258 bool wxApp::OnInitGui()
259 {
260 GdkVisual *visual = gdk_visual_get_system();
261
262 /* on some machines, the default visual is just 256 colours, so
263 we make sure we get the best. this can sometimes be wasteful,
264 of course, but what do these guys pay $30.000 for? */
265 /*
266 if (gdk_visual_get_best() != gdk_visual_get_system())
267 {
268 GdkVisual* vis = gdk_visual_get_best();
269 gtk_widget_set_default_visual( vis );
270
271 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
272 gtk_widget_set_default_colormap( colormap );
273
274 visual = vis;
275 }
276 */
277
278 /* Nothing to do for 15, 16, 24, 32 bit displays */
279 if (visual->depth > 8) return TRUE;
280
281 /* initialize color cube for 8-bit color reduction dithering */
282
283 GdkColormap *cmap = gtk_widget_get_default_colormap();
284
285 m_colorCube = (unsigned char*)malloc(32 * 32 * 32);
286
287 for (int r = 0; r < 32; r++)
288 {
289 for (int g = 0; g < 32; g++)
290 {
291 for (int b = 0; b < 32; b++)
292 {
293 int rr = (r << 3) | (r >> 2);
294 int gg = (g << 3) | (g >> 2);
295 int bb = (b << 3) | (b >> 2);
296
297 int index = -1;
298
299 GdkColor *colors = cmap->colors;
300 if (colors)
301 {
302 int max = 3 * 65536;
303
304 for (int i = 0; i < cmap->size; i++)
305 {
306 int rdiff = ((rr << 8) - colors[i].red);
307 int gdiff = ((gg << 8) - colors[i].green);
308 int bdiff = ((bb << 8) - colors[i].blue);
309 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
310 if (sum < max)
311 {
312 index = i; max = sum;
313 }
314 }
315 }
316 else
317 {
318 #if (GTK_MINOR_VERSION > 0)
319 /* assume 8-bit true or static colors. this really
320 exists. */
321 GdkVisual* vis = gdk_colormap_get_visual( cmap );
322 index = (r >> (5 - vis->red_prec)) << vis->red_shift;
323 index |= (g >> (5 - vis->green_prec)) << vis->green_shift;
324 index |= (b >> (5 - vis->blue_prec)) << vis->blue_shift;
325 #else
326 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
327 #endif
328 }
329 m_colorCube[ (r*1024) + (g*32) + b ] = index;
330 }
331 }
332 }
333
334 return TRUE;
335 }
336
337 bool wxApp::ProcessIdle()
338 {
339 wxIdleEvent event;
340 event.SetEventObject( this );
341 ProcessEvent( event );
342
343 return event.MoreRequested();
344 }
345
346 void wxApp::OnIdle( wxIdleEvent &event )
347 {
348 static bool s_inOnIdle = FALSE;
349
350 /* Avoid recursion (via ProcessEvent default case) */
351 if (s_inOnIdle)
352 return;
353
354 s_inOnIdle = TRUE;
355
356 /* Resend in the main thread events which have been prepared in other
357 threads */
358 ProcessPendingEvents();
359
360 /* 'Garbage' collection of windows deleted with Close(). */
361 DeletePendingObjects();
362
363 /* flush the logged messages if any */
364 #if wxUSE_LOG
365 wxLog *log = wxLog::GetActiveTarget();
366 if (log != NULL && log->HasPendingMessages())
367 log->Flush();
368 #endif // wxUSE_LOG
369
370 /* Send OnIdle events to all windows */
371 bool needMore = SendIdleEvents();
372
373 if (needMore)
374 event.RequestMore(TRUE);
375
376 s_inOnIdle = FALSE;
377 }
378
379 bool wxApp::SendIdleEvents()
380 {
381 bool needMore = FALSE;
382
383 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
384 while (node)
385 {
386 wxWindow* win = node->GetData();
387 if (SendIdleEvents(win))
388 needMore = TRUE;
389 node = node->GetNext();
390 }
391
392 return needMore;
393 }
394
395 bool wxApp::SendIdleEvents( wxWindow* win )
396 {
397 bool needMore = FALSE;
398
399 wxIdleEvent event;
400 event.SetEventObject(win);
401
402 win->ProcessEvent(event);
403
404 win->OnInternalIdle();
405
406 if (event.MoreRequested())
407 needMore = TRUE;
408
409 wxNode* node = win->GetChildren().First();
410 while (node)
411 {
412 wxWindow* win = (wxWindow*) node->Data();
413 if (SendIdleEvents(win))
414 needMore = TRUE;
415
416 node = node->Next();
417 }
418 return needMore ;
419 }
420
421 int wxApp::MainLoop()
422 {
423 gtk_main();
424 return 0;
425 }
426
427 void wxApp::ExitMainLoop()
428 {
429 if (gtk_main_level() > 0)
430 gtk_main_quit();
431 }
432
433 bool wxApp::Initialized()
434 {
435 return m_initialized;
436 }
437
438 bool wxApp::Pending()
439 {
440 return (gtk_events_pending() > 0);
441 }
442
443 void wxApp::Dispatch()
444 {
445 gtk_main_iteration();
446 }
447
448 void wxApp::DeletePendingObjects()
449 {
450 wxNode *node = wxPendingDelete.First();
451 while (node)
452 {
453 wxObject *obj = (wxObject *)node->Data();
454
455 delete obj;
456
457 if (wxPendingDelete.Find(obj))
458 delete node;
459
460 node = wxPendingDelete.First();
461 }
462 }
463
464 bool wxApp::Initialize()
465 {
466 wxBuffer = new wxChar[BUFSIZ + 512];
467
468 wxClassInfo::InitializeClasses();
469
470 wxSystemSettings::Init();
471
472 // GL: I'm annoyed ... I don't know where to put this and I don't want to
473 // create a module for that as it's part of the core.
474 #if wxUSE_THREADS
475 wxPendingEvents = new wxList();
476 wxPendingEventsLocker = new wxCriticalSection();
477 #endif
478
479 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
480 wxTheColourDatabase->Initialize();
481
482 wxInitializeStockLists();
483 wxInitializeStockObjects();
484
485 #if wxUSE_WX_RESOURCES
486 wxInitializeResourceSystem();
487 #endif
488
489 wxModule::RegisterModules();
490 if (!wxModule::InitializeModules()) return FALSE;
491
492 return TRUE;
493 }
494
495 void wxApp::CleanUp()
496 {
497 wxModule::CleanUpModules();
498
499 #if wxUSE_WX_RESOURCES
500 wxCleanUpResourceSystem();
501 #endif
502
503 if (wxTheColourDatabase)
504 delete wxTheColourDatabase;
505
506 wxTheColourDatabase = (wxColourDatabase*) NULL;
507
508 wxDeleteStockObjects();
509
510 wxDeleteStockLists();
511
512 delete wxTheApp;
513 wxTheApp = (wxApp*) NULL;
514
515 // GL: I'm annoyed ... I don't know where to put this and I don't want to
516 // create a module for that as it's part of the core.
517 #if wxUSE_THREADS
518 delete wxPendingEvents;
519 delete wxPendingEventsLocker;
520 #endif
521
522 wxSystemSettings::Done();
523
524 delete[] wxBuffer;
525
526 wxClassInfo::CleanUpClasses();
527
528 // check for memory leaks
529 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
530 if (wxDebugContext::CountObjectsLeft() > 0)
531 {
532 wxLogDebug(wxT("There were memory leaks.\n"));
533 wxDebugContext::Dump();
534 wxDebugContext::PrintStatistics();
535 }
536 #endif // Debug
537
538 #if wxUSE_LOG
539 // do this as the very last thing because everything else can log messages
540 wxLog::DontCreateOnDemand();
541
542 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
543 if (oldLog)
544 delete oldLog;
545 #endif // wxUSE_LOG
546 }
547
548 //-----------------------------------------------------------------------------
549 // wxEntry
550 //-----------------------------------------------------------------------------
551
552 int wxEntry( int argc, char *argv[] )
553 {
554 #if wxUSE_THREADS
555 g_thread_init(NULL);
556 #endif
557
558 gtk_set_locale();
559
560 #if wxUSE_WCHAR_T
561 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
562 #else
563 if (!wxOKlibc()) wxConvCurrent = (wxMBConv*) NULL;
564 #endif
565
566 gdk_threads_enter();
567
568 gtk_init( &argc, &argv );
569
570 wxSetDetectableAutoRepeat( TRUE );
571
572 if (!wxApp::Initialize())
573 {
574 gdk_threads_leave();
575 return -1;
576 }
577
578 if (!wxTheApp)
579 {
580 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
581 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
582
583 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
584
585 wxObject *test_app = app_ini();
586
587 wxTheApp = (wxApp*) test_app;
588 }
589
590 wxCHECK_MSG( wxTheApp, -1, wxT("wxWindows error: no application object") );
591
592 wxTheApp->argc = argc;
593 #if wxUSE_UNICODE
594 wxTheApp->argv = new wxChar*[argc+1];
595 int mb_argc = 0;
596 while (mb_argc < argc)
597 {
598 wxTheApp->argv[mb_argc] = wxStrdup(wxConvLibc.cMB2WX(argv[mb_argc]));
599 mb_argc++;
600 }
601 wxTheApp->argv[mb_argc] = (wxChar *)NULL;
602 #else
603 wxTheApp->argv = argv;
604 #endif
605
606 wxString name(wxFileNameFromPath(argv[0]));
607 wxStripExtension( name );
608 wxTheApp->SetAppName( name );
609
610 int retValue = 0;
611
612 if ( !wxTheApp->OnInitGui() )
613 retValue = -1;
614
615 // Here frames insert themselves automatically into wxTopLevelWindows by
616 // getting created in OnInit().
617 if ( retValue == 0 )
618 {
619 if ( !wxTheApp->OnInit() )
620 retValue = -1;
621 }
622
623 if ( retValue == 0 )
624 {
625 /* delete pending toplevel windows (typically a single
626 dialog) so that, if there isn't any left, we don't
627 call OnRun() */
628 wxTheApp->DeletePendingObjects();
629
630 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
631
632 if (wxTheApp->Initialized())
633 {
634 retValue = wxTheApp->OnRun();
635
636 wxWindow *topWindow = wxTheApp->GetTopWindow();
637 if (topWindow)
638 {
639 /* Forcibly delete the window. */
640 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
641 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
642 {
643 topWindow->Close( TRUE );
644 wxTheApp->DeletePendingObjects();
645 }
646 else
647 {
648 delete topWindow;
649 wxTheApp->SetTopWindow( (wxWindow*) NULL );
650 }
651 }
652 wxTheApp->OnExit();
653 }
654 }
655
656 #if wxUSE_LOG
657 // flush the logged messages if any
658 wxLog *log = wxLog::GetActiveTarget();
659 if (log != NULL && log->HasPendingMessages())
660 log->Flush();
661
662 // continuing to use user defined log target is unsafe from now on because
663 // some resources may be already unavailable, so replace it by something
664 // more safe
665 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
666 if ( oldlog )
667 delete oldlog;
668 #endif // wxUSE_LOG
669
670 wxApp::CleanUp();
671
672 gdk_threads_leave();
673
674 return retValue;
675 }
676
677 #include "wx/gtk/info.xpm"
678 #include "wx/gtk/error.xpm"
679 #include "wx/gtk/question.xpm"
680 #include "wx/gtk/warning.xpm"
681
682 wxIcon
683 wxApp::GetStdIcon(int which) const
684 {
685 switch(which)
686 {
687 case wxICON_INFORMATION:
688 return wxIcon(info_xpm);
689
690 case wxICON_QUESTION:
691 return wxIcon(question_xpm);
692
693 case wxICON_EXCLAMATION:
694 return wxIcon(warning_xpm);
695
696 default:
697 wxFAIL_MSG(wxT("requested non existent standard icon"));
698 // still fall through
699
700 case wxICON_HAND:
701 return wxIcon(error_xpm);
702 }
703 }