use "event" signal emission hook to install idle handler for many events
[wxWidgets.git] / src / gtk / app.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/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 __VMS
11 // vms_jackets.h should for proper working be included before anything else
12 # include <vms_jackets.h>
13 #undef ConnectionNumber
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #include "wx/app.h"
20
21 #ifndef WX_PRECOMP
22 #include "wx/intl.h"
23 #include "wx/log.h"
24 #include "wx/utils.h"
25 #include "wx/memory.h"
26 #include "wx/font.h"
27 #endif
28
29 #include "wx/file.h"
30 #include "wx/filename.h"
31 #include "wx/thread.h"
32
33 #ifdef __WXGPE__
34 #include <gpe/init.h>
35 #endif
36
37 #ifdef __WXUNIVERSAL__
38 #include "wx/univ/theme.h"
39 #include "wx/univ/renderer.h"
40 #endif
41
42 #include <unistd.h>
43
44 #ifdef HAVE_POLL
45 #if defined(__VMS)
46 #include <poll.h>
47 #else
48 // bug in the OpenBSD headers: at least in 3.1 there is no extern "C"
49 // in neither poll.h nor sys/poll.h which results in link errors later
50 #ifdef __OPENBSD__
51 extern "C"
52 {
53 #endif
54
55 #include <sys/poll.h>
56
57 #ifdef __OPENBSD__
58 };
59 #endif
60 #endif // platform
61 #else // !HAVE_POLL
62 // we implement poll() ourselves using select() which is supposed exist in
63 // all modern Unices
64 #include <sys/types.h>
65 #include <sys/time.h>
66 #include <unistd.h>
67 #ifdef HAVE_SYS_SELECT_H
68 #include <sys/select.h>
69 #endif
70 #endif // HAVE_POLL/!HAVE_POLL
71
72 #include "wx/unix/private.h"
73 #include "wx/gtk/win_gtk.h"
74 #include "wx/gtk/private.h"
75
76 #include <gdk/gdkx.h>
77
78 //-----------------------------------------------------------------------------
79 // link GnomeVFS
80 //-----------------------------------------------------------------------------
81
82 #if wxUSE_LIBGNOMEVFS
83 #include "wx/html/forcelnk.h"
84 FORCE_LINK(gnome_vfs)
85 #endif
86
87 //-----------------------------------------------------------------------------
88 // global data
89 //-----------------------------------------------------------------------------
90
91 bool g_mainThreadLocked = false;
92
93 static GtkWidget *gs_RootWindow = (GtkWidget*) NULL;
94
95 //-----------------------------------------------------------------------------
96 // idle system
97 //-----------------------------------------------------------------------------
98
99 void wxapp_install_idle_handler();
100
101 #if wxUSE_THREADS
102 static wxMutex gs_idleTagsMutex;
103 #endif
104
105 //-----------------------------------------------------------------------------
106 // wxYield
107 //-----------------------------------------------------------------------------
108
109 // not static because used by textctrl.cpp
110 //
111 // MT-FIXME
112 bool wxIsInsideYield = false;
113
114 bool wxApp::Yield(bool onlyIfNeeded)
115 {
116 if ( wxIsInsideYield )
117 {
118 if ( !onlyIfNeeded )
119 {
120 wxFAIL_MSG( wxT("wxYield called recursively" ) );
121 }
122
123 return false;
124 }
125
126 #if wxUSE_THREADS
127 if ( !wxThread::IsMain() )
128 {
129 // can't call gtk_main_iteration() from other threads like this
130 return true;
131 }
132 #endif // wxUSE_THREADS
133
134 wxIsInsideYield = true;
135
136 // We need to remove idle callbacks or the loop will
137 // never finish.
138 wxTheApp->RemoveIdleTag();
139
140 #if wxUSE_LOG
141 // disable log flushing from here because a call to wxYield() shouldn't
142 // normally result in message boxes popping up &c
143 wxLog::Suspend();
144 #endif
145
146 while (gtk_events_pending())
147 gtk_main_iteration();
148
149 // It's necessary to call ProcessIdle() to update the frames sizes which
150 // might have been changed (it also will update other things set from
151 // OnUpdateUI() which is a nice (and desired) side effect). But we
152 // call ProcessIdle() only once since this is not meant for longish
153 // background jobs (controlled by wxIdleEvent::RequestMore() and the
154 // return value of Processidle().
155 ProcessIdle();
156
157 #if wxUSE_LOG
158 // let the logs be flashed again
159 wxLog::Resume();
160 #endif
161
162 wxIsInsideYield = false;
163
164 return true;
165 }
166
167 //-----------------------------------------------------------------------------
168 // wxWakeUpIdle
169 //-----------------------------------------------------------------------------
170
171 // RR/KH: No wxMutexGui calls are needed here according to the GTK faq,
172 // http://www.gtk.org/faq/#AEN500 - this caused problems for wxPostEvent.
173
174 void wxApp::WakeUpIdle()
175 {
176 wxapp_install_idle_handler();
177 }
178
179 //-----------------------------------------------------------------------------
180 // local functions
181 //-----------------------------------------------------------------------------
182
183 // the callback functions must be extern "C" to comply with GTK+ declarations
184 extern "C"
185 {
186
187 // One-shot emission hook for "event" signal, to install idle handler.
188 // This will be called when the "event" signal is issued on any GtkWidget object.
189 static gboolean
190 event_emission_hook(GSignalInvocationHint*, guint, const GValue*, gpointer)
191 {
192 wxapp_install_idle_handler();
193 // remove hook
194 return false;
195 }
196
197 static gint wxapp_idle_callback( gpointer WXUNUSED(data) )
198 {
199 if (!wxTheApp)
200 return false;
201
202 #ifdef __WXDEBUG__
203 // don't generate the idle events while the assert modal dialog is shown,
204 // this completely confuses the apps which don't expect to be reentered
205 // from some safely-looking functions
206 if ( wxTheApp->IsInAssert() )
207 return false;
208 #endif // __WXDEBUG__
209
210 // When getting called from GDK's time-out handler
211 // we are no longer within GDK's grab on the GUI
212 // thread so we must lock it here ourselves.
213 gdk_threads_enter();
214
215 // Indicate that we are now in idle mode and event handlers
216 // will have to reinstall the idle handler again.
217 {
218 #if wxUSE_THREADS
219 wxMutexLocker lock(gs_idleTagsMutex);
220 #endif
221 g_isIdle = true;
222 wxTheApp->m_idleTag = 0;
223 }
224
225 bool moreIdles;
226
227 // Send idle event to all who request them as long as
228 // no events have popped up in the event queue.
229 while ( (moreIdles = wxTheApp->ProcessIdle()) && gtk_events_pending() == 0)
230 ;
231
232 // Release lock again
233 gdk_threads_leave();
234
235 if (!moreIdles)
236 {
237 // add emission hook for "event" signal, to re-install idle handler when needed
238 guint sig_id = g_signal_lookup("event", GTK_TYPE_WIDGET);
239 g_signal_add_emission_hook(sig_id, 0, event_emission_hook, NULL, NULL);
240 }
241
242 // Return FALSE if no more idle events are to be sent
243 return moreIdles;
244 }
245
246 #if wxUSE_THREADS
247
248 #ifdef HAVE_POLL
249 #define wxPoll poll
250 #define wxPollFd pollfd
251 #else // !HAVE_POLL
252
253 typedef GPollFD wxPollFd;
254
255 int wxPoll(wxPollFd *ufds, unsigned int nfds, int timeout)
256 {
257 // convert timeout from ms to struct timeval (s/us)
258 timeval tv_timeout;
259 tv_timeout.tv_sec = timeout/1000;
260 tv_timeout.tv_usec = (timeout%1000)*1000;
261
262 // remember the highest fd used here
263 int fdMax = -1;
264
265 // and fill the sets for select()
266 fd_set readfds;
267 fd_set writefds;
268 fd_set exceptfds;
269 wxFD_ZERO(&readfds);
270 wxFD_ZERO(&writefds);
271 wxFD_ZERO(&exceptfds);
272
273 unsigned int i;
274 for ( i = 0; i < nfds; i++ )
275 {
276 wxASSERT_MSG( ufds[i].fd < wxFD_SETSIZE, _T("fd out of range") );
277
278 if ( ufds[i].events & G_IO_IN )
279 wxFD_SET(ufds[i].fd, &readfds);
280
281 if ( ufds[i].events & G_IO_PRI )
282 wxFD_SET(ufds[i].fd, &exceptfds);
283
284 if ( ufds[i].events & G_IO_OUT )
285 wxFD_SET(ufds[i].fd, &writefds);
286
287 if ( ufds[i].fd > fdMax )
288 fdMax = ufds[i].fd;
289 }
290
291 fdMax++;
292 int res = select(fdMax, &readfds, &writefds, &exceptfds, &tv_timeout);
293
294 // translate the results back
295 for ( i = 0; i < nfds; i++ )
296 {
297 ufds[i].revents = 0;
298
299 if ( wxFD_ISSET(ufds[i].fd, &readfds ) )
300 ufds[i].revents |= G_IO_IN;
301
302 if ( wxFD_ISSET(ufds[i].fd, &exceptfds ) )
303 ufds[i].revents |= G_IO_PRI;
304
305 if ( wxFD_ISSET(ufds[i].fd, &writefds ) )
306 ufds[i].revents |= G_IO_OUT;
307 }
308
309 return res;
310 }
311
312 #endif // HAVE_POLL/!HAVE_POLL
313
314 static gint wxapp_poll_func( GPollFD *ufds, guint nfds, gint timeout )
315 {
316 gdk_threads_enter();
317
318 wxMutexGuiLeave();
319 g_mainThreadLocked = true;
320
321 // we rely on the fact that glib GPollFD struct is really just pollfd but
322 // I wonder how wise is this in the long term (VZ)
323 gint res = wxPoll( (wxPollFd *) ufds, nfds, timeout );
324
325 wxMutexGuiEnter();
326 g_mainThreadLocked = false;
327
328 gdk_threads_leave();
329
330 return res;
331 }
332
333 #endif // wxUSE_THREADS
334
335 } // extern "C"
336
337 void wxapp_install_idle_handler()
338 {
339 #if wxUSE_THREADS
340 wxMutexLocker lock(gs_idleTagsMutex);
341 #endif
342
343 // Don't install the handler if it's already installed. This test *MUST*
344 // be done when gs_idleTagsMutex is locked!
345 if (!g_isIdle)
346 return;
347
348 // GD: this assert is raised when using the thread sample (which works)
349 // so the test is probably not so easy. Can widget callbacks be
350 // triggered from child threads and, if so, for which widgets?
351 // wxASSERT_MSG( wxThread::IsMain() || gs_WakeUpIdle, wxT("attempt to install idle handler from widget callback in child thread (should be exclusively from wxWakeUpIdle)") );
352
353 wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
354
355 g_isIdle = false;
356
357 // This routine gets called by all event handlers
358 // indicating that the idle is over. It may also
359 // get called from other thread for sending events
360 // to the main thread (and processing these in
361 // idle time). Very low priority.
362 wxTheApp->m_idleTag = g_idle_add_full(G_PRIORITY_LOW, wxapp_idle_callback, NULL, NULL);
363 }
364
365 //-----------------------------------------------------------------------------
366 // Access to the root window global
367 //-----------------------------------------------------------------------------
368
369 GtkWidget* wxGetRootWindow()
370 {
371 if (gs_RootWindow == NULL)
372 {
373 gs_RootWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
374 gtk_widget_realize( gs_RootWindow );
375 }
376 return gs_RootWindow;
377 }
378
379 //-----------------------------------------------------------------------------
380 // wxApp
381 //-----------------------------------------------------------------------------
382
383 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
384
385 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
386 EVT_IDLE(wxAppBase::OnIdle)
387 END_EVENT_TABLE()
388
389 wxApp::wxApp()
390 {
391 #ifdef __WXDEBUG__
392 m_isInAssert = false;
393 #endif // __WXDEBUG__
394
395 m_idleTag = 0;
396 g_isIdle = true;
397 wxapp_install_idle_handler();
398
399 #if wxUSE_THREADS
400 g_main_context_set_poll_func( NULL, wxapp_poll_func );
401 #endif
402
403 // this is NULL for a "regular" wxApp, but is set (and freed) by a wxGLApp
404 m_glVisualInfo = (void *) NULL;
405 m_glFBCInfo = (void *) NULL;
406 }
407
408 wxApp::~wxApp()
409 {
410 if (m_idleTag)
411 g_source_remove( m_idleTag );
412 }
413
414 bool wxApp::OnInitGui()
415 {
416 if ( !wxAppBase::OnInitGui() )
417 return false;
418
419 // if this is a wxGLApp (derived from wxApp), and we've already
420 // chosen a specific visual, then derive the GdkVisual from that
421 if (m_glVisualInfo != NULL)
422 {
423 // seems gtk_widget_set_default_visual no longer exists?
424 GdkVisual* vis = gtk_widget_get_default_visual();
425
426 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
427 gtk_widget_set_default_colormap( colormap );
428 }
429
430 // On some machines, the default visual is just 256 colours, so
431 // we make sure we get the best. This can sometimes be wasteful.
432
433 else
434 if ((gdk_visual_get_best() != gdk_visual_get_system()) && (m_useBestVisual))
435 {
436 /* seems gtk_widget_set_default_visual no longer exists? */
437 GdkVisual* vis = gtk_widget_get_default_visual();
438
439 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
440 gtk_widget_set_default_colormap( colormap );
441 }
442
443 return true;
444 }
445
446 GdkVisual *wxApp::GetGdkVisual()
447 {
448 GdkVisual *visual = NULL;
449
450 if (m_glVisualInfo)
451 visual = gdkx_visual_get( ((XVisualInfo *) m_glVisualInfo)->visualid );
452 else
453 visual = gdk_drawable_get_visual( wxGetRootWindow()->window );
454
455 wxASSERT( visual );
456
457 return visual;
458 }
459
460 bool wxApp::Initialize(int& argc, wxChar **argv)
461 {
462 bool init_result;
463
464 #if wxUSE_THREADS
465 if (!g_thread_supported())
466 g_thread_init(NULL);
467 #endif // wxUSE_THREADS
468
469 gtk_set_locale();
470
471 // We should have the wxUSE_WCHAR_T test on the _outside_
472 #if wxUSE_WCHAR_T
473 // gtk+ 2.0 supports Unicode through UTF-8 strings
474 wxConvCurrent = &wxConvUTF8;
475 #else // !wxUSE_WCHAR_T
476 if (!wxOKlibc())
477 wxConvCurrent = (wxMBConv*) NULL;
478 #endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T
479
480 // decide which conversion to use for the file names
481
482 // (1) this variable exists for the sole purpose of specifying the encoding
483 // of the filenames for GTK+ programs, so use it if it is set
484 wxString encName(wxGetenv(_T("G_FILENAME_ENCODING")));
485 encName = encName.BeforeFirst(_T(','));
486 if (encName == _T("@locale"))
487 encName.clear();
488 encName.MakeUpper();
489 #if wxUSE_INTL
490 if (encName.empty())
491 {
492 // (2) if a non default locale is set, assume that the user wants his
493 // filenames in this locale too
494 encName = wxLocale::GetSystemEncodingName().Upper();
495 // (3) finally use UTF-8 by default
496 if (encName.empty() || encName == _T("US-ASCII"))
497 encName = _T("UTF-8");
498 wxSetEnv(_T("G_FILENAME_ENCODING"), encName);
499 }
500 #else
501 if (encName.empty())
502 encName = _T("UTF-8");
503 #endif // wxUSE_INTL
504 static wxConvBrokenFileNames fileconv(encName);
505 wxConvFileName = &fileconv;
506
507 #if wxUSE_UNICODE
508 // gtk_init() wants UTF-8, not wchar_t, so convert
509 int i;
510 char **argvGTK = new char *[argc + 1];
511 for ( i = 0; i < argc; i++ )
512 {
513 argvGTK[i] = wxStrdupA(wxConvUTF8.cWX2MB(argv[i]));
514 }
515
516 argvGTK[argc] = NULL;
517
518 int argcGTK = argc;
519
520 #ifdef __WXGPE__
521 init_result = true; // is there a _check() version of this?
522 gpe_application_init( &argcGTK, &argvGTK );
523 #else
524 init_result = gtk_init_check( &argcGTK, &argvGTK );
525 #endif
526
527 if ( argcGTK != argc )
528 {
529 // we have to drop the parameters which were consumed by GTK+
530 for ( i = 0; i < argcGTK; i++ )
531 {
532 while ( strcmp(wxConvUTF8.cWX2MB(argv[i]), argvGTK[i]) != 0 )
533 {
534 memmove(argv + i, argv + i + 1, argc - i);
535 }
536 }
537
538 argc = argcGTK;
539 }
540 //else: gtk_init() didn't modify our parameters
541
542 // free our copy
543 for ( i = 0; i < argcGTK; i++ )
544 {
545 free(argvGTK[i]);
546 }
547
548 delete [] argvGTK;
549 #else // !wxUSE_UNICODE
550 // gtk_init() shouldn't actually change argv itself (just its contents) so
551 // it's ok to pass pointer to it
552 init_result = gtk_init_check( &argc, &argv );
553 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
554
555 if (!init_result) {
556 wxLogError(wxT("Unable to initialize gtk, is DISPLAY set properly?"));
557 return false;
558 }
559
560 // we can not enter threads before gtk_init is done
561 gdk_threads_enter();
562
563 if ( !wxAppBase::Initialize(argc, argv) )
564 {
565 gdk_threads_leave();
566
567 return false;
568 }
569
570 wxSetDetectableAutoRepeat( true );
571
572 #if wxUSE_INTL
573 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
574 #endif
575
576 return true;
577 }
578
579 void wxApp::CleanUp()
580 {
581 gdk_threads_leave();
582
583 wxAppBase::CleanUp();
584 }
585
586 #ifdef __WXDEBUG__
587
588 void wxApp::OnAssertFailure(const wxChar *file,
589 int line,
590 const wxChar* func,
591 const wxChar* cond,
592 const wxChar *msg)
593 {
594 m_isInAssert = true;
595
596 wxAppBase::OnAssertFailure(file, line, func, cond, msg);
597
598 m_isInAssert = false;
599 }
600
601 #endif // __WXDEBUG__
602
603 void wxApp::RemoveIdleTag()
604 {
605 #if wxUSE_THREADS
606 wxMutexLocker lock(gs_idleTagsMutex);
607 #endif
608 if (!g_isIdle)
609 {
610 g_source_remove( wxTheApp->m_idleTag );
611 wxTheApp->m_idleTag = 0;
612 g_isIdle = true;
613 }
614 }