]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/app.cpp
New idle handling. Only that.
[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 #include "wx/thread.h"
32
33 #include "unistd.h"
34
35 #include <glib.h>
36 #include <gdk/gdk.h>
37 #include <gtk/gtk.h>
38
39 #include "wx/gtk/win_gtk.h"
40
41 //-----------------------------------------------------------------------------
42 // global data
43 //-----------------------------------------------------------------------------
44
45 wxApp *wxTheApp = (wxApp *) NULL;
46 wxAppInitializerFunction wxApp::m_appInitFn = (wxAppInitializerFunction) NULL;
47
48 extern wxList wxPendingDelete;
49 #if wxUSE_THREADS
50 extern wxList *wxPendingEvents;
51 extern wxCriticalSection *wxPendingEventsLocker;
52 #endif
53 extern wxResourceCache *wxTheResourceCache;
54 extern bool g_isIdle;
55
56 unsigned char g_palette[64*3] =
57 {
58 0x0, 0x0, 0x0,
59 0xff, 0xff, 0xff,
60 0xff, 0x0, 0x0,
61 0xff, 0xff, 0x0,
62 0x0, 0xff, 0x0,
63 0x0, 0x0, 0xff,
64 0x0, 0xff, 0xff,
65 0x99, 0x99, 0x99,
66 0xff, 0x88, 0x0,
67 0x88, 0x0, 0x0,
68 0x0, 0x88, 0x88,
69 0x88, 0x88, 0x0,
70 0xff, 0xcc, 0x97,
71 0xbb, 0xbb, 0xbb,
72 0x9f, 0x6b, 0x42,
73 0x55, 0x55, 0x55,
74 0xdd, 0xdd, 0xdd,
75 0x77, 0x77, 0x77,
76 0x33, 0x33, 0x33,
77 0xcc, 0x0, 0x0,
78 0xff, 0x44, 0x0,
79 0xff, 0xcc, 0x0,
80 0xcc, 0xcc, 0x0,
81 0x60, 0x60, 0x0,
82 0x0, 0x43, 0x0,
83 0x0, 0x7f, 0x0,
84 0x0, 0xcc, 0x0,
85 0x0, 0x44, 0x44,
86 0x0, 0x0, 0x44,
87 0x0, 0x0, 0x88,
88 0xef, 0xb1, 0x7b,
89 0xdf, 0x98, 0x5f,
90 0xbf, 0x87, 0x56,
91 0x7f, 0x57, 0x26,
92 0x5f, 0x39, 0xc,
93 0x3f, 0x1c, 0x0,
94 0x21, 0x0, 0x0,
95 0x0, 0x43, 0x87,
96 0x2d, 0x70, 0xaf,
97 0x5a, 0x9e, 0xd7,
98 0x87, 0xcc, 0xff,
99 0xff, 0xe0, 0xba,
100 0x21, 0x43, 0xf,
101 0x3d, 0x5d, 0x25,
102 0x59, 0x78, 0x3a,
103 0x75, 0x93, 0x4f,
104 0x91, 0xae, 0x64,
105 0xad, 0xc8, 0x7a,
106 0xf0, 0xa8, 0xef,
107 0xd0, 0x88, 0xd0,
108 0xaf, 0x66, 0xaf,
109 0x8e, 0x44, 0x8e,
110 0x6d, 0x22, 0x6d,
111 0x4b, 0x0, 0x4b,
112 0xff, 0xc0, 0xbc,
113 0xff, 0x93, 0x91,
114 0xff, 0x66, 0x67,
115 0xd8, 0xf2, 0xbf,
116 0xff, 0xc9, 0x68,
117 0xff, 0x96, 0x67,
118 0xa5, 0x60, 0xff,
119 0x51, 0xff, 0x99,
120 0x3f, 0xa5, 0x63,
121 0x98, 0x90, 0x67
122 };
123
124 //-----------------------------------------------------------------------------
125 // local functions
126 //-----------------------------------------------------------------------------
127
128 extern void wxFlushResources(void);
129
130 //-----------------------------------------------------------------------------
131 // global functions
132 //-----------------------------------------------------------------------------
133
134 void wxExit()
135 {
136 gtk_main_quit();
137 }
138
139 /* forward declaration */
140 gint wxapp_idle_callback( gpointer WXUNUSED(data) );
141
142 bool wxYield()
143 {
144 // it's necessary to call ProcessIdle() to update the frames sizes which
145 // might have been changed (it also will update other things set from
146 // OnUpdateUI() which is a nice (and desired) side effect)
147 for ( wxWindowList::Node *node = wxTopLevelWindows.GetFirst();
148 node;
149 node = node->GetNext() )
150 {
151 wxWindow *win = node->GetData();
152 win->OnInternalIdle();
153 }
154
155 if (wxTheApp->m_idleTag)
156 {
157 /* We need to temporarily remove idle callbacks or the loop will
158 never finish. */
159 gtk_idle_remove( wxTheApp->m_idleTag );
160 wxTheApp->m_idleTag = 0;
161
162 while (gtk_events_pending())
163 gtk_main_iteration();
164
165 /* re-add idle handler */
166 wxTheApp->m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
167 }
168 else
169 {
170 while (gtk_events_pending())
171 gtk_main_iteration();
172 }
173
174 return TRUE;
175 }
176
177 gint wxapp_idle_callback( gpointer WXUNUSED(data) )
178 {
179 if (!wxTheApp) return TRUE;
180
181 /* sent idle event to all who request them */
182 while (wxTheApp->ProcessIdle()) { }
183
184 /* we don't want any more idle events until the next event is
185 sent to wxGTK */
186 gtk_idle_remove( wxTheApp->m_idleTag );
187 wxTheApp->m_idleTag = 0;
188
189 /* indicate that we are now in idle mode - even so deeply
190 in idle mode that we don't get any idle events anymore.
191 this is like wxMSW where an idle event is sent only
192 once each time after the event queue has been completely
193 emptied */
194 g_isIdle = TRUE;
195
196 /* wxMutexGuiLeave();
197 wxUsleep(10);
198 wxMutexGuiEnter(); */
199
200 return TRUE;
201 }
202
203 void wxapp_install_idle_handler()
204 {
205 /* this routine gets called by all event handlers
206 indicating that the idle is over. */
207
208 wxTheApp->m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
209
210 g_isIdle = FALSE;
211 }
212
213 //-----------------------------------------------------------------------------
214 // wxApp
215 //-----------------------------------------------------------------------------
216
217 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
218
219 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
220 EVT_IDLE(wxApp::OnIdle)
221 END_EVENT_TABLE()
222
223 wxApp::wxApp()
224 {
225 wxTheApp = this;
226
227 m_topWindow = (wxWindow *) NULL;
228 m_exitOnFrameDelete = TRUE;
229
230 m_idleTag = gtk_idle_add( wxapp_idle_callback, (gpointer) NULL );
231
232 m_colorCube = (unsigned char*) NULL;
233 }
234
235 wxApp::~wxApp()
236 {
237 if (m_idleTag) gtk_idle_remove( m_idleTag );
238
239 if (m_colorCube) free(m_colorCube);
240 }
241
242 bool wxApp::OnInitGui()
243 {
244 /* Nothing to do for 15, 16, 24, 32 bit displays */
245
246 GdkVisual *visual = gdk_visual_get_system();
247 if (visual->depth > 8) return TRUE;
248
249 /* this initiates the standard palette as defined by GdkImlib
250 in the GNOME libraries. it ensures that all GNOME applications
251 use the same 64 colormap entries on 8-bit displays so you
252 can use several rather graphics-heavy applications at the
253 same time.
254 NOTE: this doesn't really seem to work this way... */
255
256 /*
257 GdkColormap *cmap = gdk_colormap_new( gdk_visual_get_system(), TRUE );
258
259 for (int i = 0; i < 64; i++)
260 {
261 GdkColor col;
262 col.red = g_palette[i*3 + 0] << 8;
263 col.green = g_palette[i*3 + 1] << 8;
264 col.blue = g_palette[i*3 + 2] << 8;
265 col.pixel = 0;
266
267 gdk_color_alloc( cmap, &col );
268 }
269
270 gtk_widget_set_default_colormap( cmap );
271 */
272
273 /* initialize color cube for 8-bit color reduction dithering */
274
275 GdkColormap *cmap = gtk_widget_get_default_colormap();
276
277 m_colorCube = (unsigned char*)malloc(32 * 32 * 32);
278
279 for (int r = 0; r < 32; r++)
280 {
281 for (int g = 0; g < 32; g++)
282 {
283 for (int b = 0; b < 32; b++)
284 {
285 int rr = (r << 3) | (r >> 2);
286 int gg = (g << 3) | (g >> 2);
287 int bb = (b << 3) | (b >> 2);
288
289 GdkColor *colors = cmap->colors;
290 int max = 3 * 65536;
291 int index = -1;
292
293 for (int i = 0; i < cmap->size; i++)
294 {
295 int rdiff = ((rr << 8) - colors[i].red);
296 int gdiff = ((gg << 8) - colors[i].green);
297 int bdiff = ((bb << 8) - colors[i].blue);
298 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
299 if (sum < max) { index = i; max = sum; }
300 }
301
302 m_colorCube[ (r*1024) + (g*32) + b ] = index;
303 }
304 }
305 }
306
307 return TRUE;
308 }
309
310 bool wxApp::ProcessIdle()
311 {
312 wxIdleEvent event;
313 event.SetEventObject( this );
314 ProcessEvent( event );
315
316 return event.MoreRequested();
317 }
318
319 void wxApp::OnIdle( wxIdleEvent &event )
320 {
321 static bool inOnIdle = FALSE;
322
323 /* Avoid recursion (via ProcessEvent default case) */
324 if (inOnIdle)
325 return;
326
327 inOnIdle = TRUE;
328
329 #if wxUSE_THREADS
330 /* Resend in the main thread events which have been prepared in other
331 threads */
332 ProcessPendingEvents();
333 #endif
334
335 /* 'Garbage' collection of windows deleted with Close(). */
336 DeletePendingObjects();
337
338 /* flush the logged messages if any */
339 wxLog *log = wxLog::GetActiveTarget();
340 if (log != NULL && log->HasPendingMessages())
341 log->Flush();
342
343 /* Send OnIdle events to all windows */
344 bool needMore = SendIdleEvents();
345
346 if (needMore)
347 event.RequestMore(TRUE);
348
349 inOnIdle = FALSE;
350 }
351
352 bool wxApp::SendIdleEvents()
353 {
354 bool needMore = FALSE;
355
356 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
357 while (node)
358 {
359 wxWindow* win = node->GetData();
360 if (SendIdleEvents(win))
361 needMore = TRUE;
362 node = node->GetNext();
363 }
364
365 return needMore;
366 }
367
368 bool wxApp::SendIdleEvents( wxWindow* win )
369 {
370 bool needMore = FALSE;
371
372 wxIdleEvent event;
373 event.SetEventObject(win);
374
375 win->OnInternalIdle();
376
377 win->ProcessEvent(event);
378
379 if (event.MoreRequested())
380 needMore = TRUE;
381
382 wxNode* node = win->GetChildren().First();
383 while (node)
384 {
385 wxWindow* win = (wxWindow*) node->Data();
386 if (SendIdleEvents(win))
387 needMore = TRUE;
388
389 node = node->Next();
390 }
391 return needMore ;
392 }
393
394 int wxApp::MainLoop()
395 {
396 gtk_main();
397 return 0;
398 }
399
400 void wxApp::ExitMainLoop()
401 {
402 gtk_main_quit();
403 }
404
405 bool wxApp::Initialized()
406 {
407 return m_initialized;
408 }
409
410 bool wxApp::Pending()
411 {
412 return (gtk_events_pending() > 0);
413 }
414
415 void wxApp::Dispatch()
416 {
417 gtk_main_iteration();
418 }
419
420 #if wxUSE_THREADS
421 void wxApp::ProcessPendingEvents()
422 {
423 wxNode *node = wxPendingEvents->First();
424 wxCriticalSectionLocker locker(*wxPendingEventsLocker);
425
426 while (node)
427 {
428 wxEvtHandler *handler = (wxEvtHandler *)node->Data();
429
430 handler->ProcessPendingEvents();
431
432 delete node;
433
434 node = wxPendingEvents->First();
435 }
436 }
437 #endif // wxUSE_THREADS
438
439 void wxApp::DeletePendingObjects()
440 {
441 wxNode *node = wxPendingDelete.First();
442 while (node)
443 {
444 wxObject *obj = (wxObject *)node->Data();
445
446 delete obj;
447
448 if (wxPendingDelete.Member(obj))
449 delete node;
450
451 node = wxPendingDelete.First();
452 }
453 }
454
455 wxWindow *wxApp::GetTopWindow()
456 {
457 if (m_topWindow)
458 return m_topWindow;
459 else if (wxTopLevelWindows.GetCount() > 0)
460 return wxTopLevelWindows.GetFirst()->GetData();
461 else
462 return NULL;
463 }
464
465 void wxApp::SetTopWindow( wxWindow *win )
466 {
467 m_topWindow = win;
468 }
469
470 bool wxApp::Initialize()
471 {
472 wxBuffer = new wxChar[BUFSIZ + 512];
473
474 wxClassInfo::InitializeClasses();
475
476 wxSystemSettings::Init();
477
478 // GL: I'm annoyed ... I don't know where to put this and I don't want to
479 // create a module for that as it's part of the core.
480 #if wxUSE_THREADS
481 wxPendingEvents = new wxList();
482 wxPendingEventsLocker = new wxCriticalSection();
483 #endif
484
485 /*
486 wxTheFontNameDirectory = new wxFontNameDirectory;
487 wxTheFontNameDirectory->Initialize();
488 */
489
490 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
491 wxTheColourDatabase->Initialize();
492
493 wxInitializeStockLists();
494 wxInitializeStockObjects();
495
496 #if wxUSE_WX_RESOURCES
497 wxTheResourceCache = new wxResourceCache( wxKEY_STRING );
498
499 wxInitializeResourceSystem();
500 #endif
501
502 wxImage::InitStandardHandlers();
503
504 /* no global cursor under X
505 g_globalCursor = new wxCursor; */
506
507 wxModule::RegisterModules();
508 if (!wxModule::InitializeModules()) return FALSE;
509
510 return TRUE;
511 }
512
513 void wxApp::CleanUp()
514 {
515 wxModule::CleanUpModules();
516
517 #if wxUSE_WX_RESOURCES
518 wxFlushResources();
519
520 if (wxTheResourceCache)
521 delete wxTheResourceCache;
522 wxTheResourceCache = (wxResourceCache*) NULL;
523
524 wxCleanUpResourceSystem();
525 #endif
526
527 if (wxTheColourDatabase)
528 delete wxTheColourDatabase;
529 wxTheColourDatabase = (wxColourDatabase*) NULL;
530
531 /*
532 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
533 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
534 */
535
536 wxDeleteStockObjects();
537
538 wxDeleteStockLists();
539
540 wxImage::CleanUpHandlers();
541
542 delete wxTheApp;
543 wxTheApp = (wxApp*) NULL;
544
545 // GL: I'm annoyed ... I don't know where to put this and I don't want to
546 // create a module for that as it's part of the core.
547 #if wxUSE_THREADS
548 delete wxPendingEvents;
549 delete wxPendingEventsLocker;
550 #endif
551
552 wxSystemSettings::Done();
553
554 delete[] wxBuffer;
555
556 wxClassInfo::CleanUpClasses();
557
558 // check for memory leaks
559 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
560 if (wxDebugContext::CountObjectsLeft() > 0)
561 {
562 wxLogDebug(_T("There were memory leaks.\n"));
563 wxDebugContext::Dump();
564 wxDebugContext::PrintStatistics();
565 }
566 #endif // Debug
567
568 // do this as the very last thing because everything else can log messages
569 wxLog::DontCreateOnDemand();
570
571 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
572 if (oldLog)
573 delete oldLog;
574 }
575
576 wxLog *wxApp::CreateLogTarget()
577 {
578 return new wxLogGui;
579 }
580
581 //-----------------------------------------------------------------------------
582 // wxEntry
583 //-----------------------------------------------------------------------------
584
585 int wxEntry( int argc, char *argv[] )
586 {
587 gtk_set_locale();
588
589 gtk_init( &argc, &argv );
590
591 if (!wxApp::Initialize())
592 return -1;
593
594 if (!wxTheApp)
595 {
596 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
597 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
598
599 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
600
601 wxObject *test_app = app_ini();
602
603 wxTheApp = (wxApp*) test_app;
604 }
605
606 wxCHECK_MSG( wxTheApp, -1, _T("wxWindows error: no application object") );
607
608 wxTheApp->argc = argc;
609 wxTheApp->argv = argv;
610
611 wxString name(wxFileNameFromPath(argv[0]));
612 wxStripExtension( name );
613 wxTheApp->SetAppName( name );
614
615 int retValue = 0;
616
617 if ( !wxTheApp->OnInitGui() )
618 retValue = -1;
619
620 // Here frames insert themselves automatically into wxTopLevelWindows by
621 // getting created in OnInit().
622 if ( retValue == 0 )
623 {
624 if ( !wxTheApp->OnInit() )
625 retValue = -1;
626 }
627
628 if ( retValue == 0 )
629 {
630 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
631
632 if (wxTheApp->Initialized())
633 retValue = wxTheApp->OnRun();
634
635 wxWindow *topWindow = wxTheApp->GetTopWindow();
636 if (topWindow)
637 {
638 // Forcibly delete the window.
639 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
640 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
641 {
642 topWindow->Close( TRUE );
643 wxTheApp->DeletePendingObjects();
644 }
645 else
646 {
647 delete topWindow;
648 wxTheApp->SetTopWindow( (wxWindow*) NULL );
649 }
650 }
651
652 wxTheApp->OnExit();
653 }
654
655 // flush the logged messages if any
656 wxLog *log = wxLog::GetActiveTarget();
657 if (log != NULL && log->HasPendingMessages())
658 log->Flush();
659
660 // continuing to use user defined log target is unsafe from now on because
661 // some resources may be already unavailable, so replace it by something
662 // more safe
663 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
664 if ( oldlog )
665 delete oldlog;
666
667 wxApp::CleanUp();
668
669 return retValue;
670 }
671