]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/app.cpp
added wxAppTraits::GetStandardCmdLineOptions() allowing to add the description of...
[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/thread.h"
30
31 #ifdef __WXGPE__
32 #include <gpe/init.h>
33 #endif
34
35 #include "wx/gtk/win_gtk.h"
36 #include "wx/gtk/private.h"
37
38 #include <gdk/gdkx.h>
39
40 //-----------------------------------------------------------------------------
41 // link GnomeVFS
42 //-----------------------------------------------------------------------------
43
44 #if wxUSE_MIMETYPE && wxUSE_LIBGNOMEVFS
45 #include "wx/link.h"
46 wxFORCE_LINK_MODULE(gnome_vfs)
47 #endif
48
49 //-----------------------------------------------------------------------------
50 // global data
51 //-----------------------------------------------------------------------------
52
53 bool g_mainThreadLocked = false;
54
55 static GtkWidget *gs_RootWindow = (GtkWidget*) NULL;
56
57 //-----------------------------------------------------------------------------
58 // wxYield
59 //-----------------------------------------------------------------------------
60
61 // not static because used by textctrl.cpp
62 //
63 // MT-FIXME
64 bool wxIsInsideYield = false;
65
66 bool wxApp::Yield(bool onlyIfNeeded)
67 {
68 if ( wxIsInsideYield )
69 {
70 if ( !onlyIfNeeded )
71 {
72 wxFAIL_MSG( wxT("wxYield called recursively" ) );
73 }
74
75 return false;
76 }
77
78 #if wxUSE_THREADS
79 if ( !wxThread::IsMain() )
80 {
81 // can't call gtk_main_iteration() from other threads like this
82 return true;
83 }
84 #endif // wxUSE_THREADS
85
86 wxIsInsideYield = true;
87
88 #if wxUSE_LOG
89 // disable log flushing from here because a call to wxYield() shouldn't
90 // normally result in message boxes popping up &c
91 wxLog::Suspend();
92 #endif
93
94 while (EventsPending())
95 gtk_main_iteration();
96
97 // It's necessary to call ProcessIdle() to update the frames sizes which
98 // might have been changed (it also will update other things set from
99 // OnUpdateUI() which is a nice (and desired) side effect). But we
100 // call ProcessIdle() only once since this is not meant for longish
101 // background jobs (controlled by wxIdleEvent::RequestMore() and the
102 // return value of Processidle().
103 ProcessIdle();
104
105 #if wxUSE_LOG
106 // let the logs be flashed again
107 wxLog::Resume();
108 #endif
109
110 wxIsInsideYield = false;
111
112 return true;
113 }
114
115 //-----------------------------------------------------------------------------
116 // local functions
117 //-----------------------------------------------------------------------------
118
119 // One-shot signal emission hook, to install idle handler.
120 extern "C" {
121 static gboolean
122 wx_emission_hook(GSignalInvocationHint*, guint, const GValue*, gpointer data)
123 {
124 wxApp* app = wxTheApp;
125 if (app != NULL)
126 app->WakeUpIdle();
127 gulong* hook_id = (gulong*)data;
128 // record that hook is not installed
129 *hook_id = 0;
130 // remove hook
131 return false;
132 }
133 }
134
135 // Add signal emission hooks, to re-install idle handler when needed.
136 static void wx_add_idle_hooks()
137 {
138 // "event" hook
139 {
140 static gulong hook_id = 0;
141 if (hook_id == 0)
142 {
143 static guint sig_id = 0;
144 if (sig_id == 0)
145 sig_id = g_signal_lookup("event", GTK_TYPE_WIDGET);
146 hook_id = g_signal_add_emission_hook(
147 sig_id, 0, wx_emission_hook, &hook_id, NULL);
148 }
149 }
150 // "size_allocate" hook
151 // Needed to match the behavior of the old idle system,
152 // but probably not necessary.
153 {
154 static gulong hook_id = 0;
155 if (hook_id == 0)
156 {
157 static guint sig_id = 0;
158 if (sig_id == 0)
159 sig_id = g_signal_lookup("size_allocate", GTK_TYPE_WIDGET);
160 hook_id = g_signal_add_emission_hook(
161 sig_id, 0, wx_emission_hook, &hook_id, NULL);
162 }
163 }
164 }
165
166 extern "C" {
167 static gboolean wxapp_idle_callback(gpointer)
168 {
169 return wxTheApp->DoIdle();
170 }
171 }
172
173 bool wxApp::DoIdle()
174 {
175 guint id_save;
176 {
177 // Allow another idle source to be added while this one is busy.
178 // Needed if an idle event handler runs a new event loop,
179 // for example by showing a dialog.
180 #if wxUSE_THREADS
181 wxMutexLocker lock(*m_idleMutex);
182 #endif
183 id_save = m_idleSourceId;
184 m_idleSourceId = 0;
185 wx_add_idle_hooks();
186 #ifdef __WXDEBUG__
187 // don't generate the idle events while the assert modal dialog is shown,
188 // this matches the behavior of wxMSW
189 if (m_isInAssert)
190 return false;
191 #endif
192 }
193
194 gdk_threads_enter();
195 bool needMore;
196 do {
197 needMore = ProcessIdle();
198 } while (needMore && gtk_events_pending() == 0);
199 gdk_threads_leave();
200
201 #if wxUSE_THREADS
202 wxMutexLocker lock(*m_idleMutex);
203 #endif
204 // if a new idle source was added during ProcessIdle
205 if (m_idleSourceId != 0)
206 {
207 // remove it
208 g_source_remove(m_idleSourceId);
209 m_idleSourceId = 0;
210 }
211 // if more idle processing requested
212 if (needMore)
213 {
214 // keep this source installed
215 m_idleSourceId = id_save;
216 return true;
217 }
218 // add hooks and remove this source
219 wx_add_idle_hooks();
220 return false;
221 }
222
223 #if wxUSE_THREADS
224
225 static GPollFunc wxgs_poll_func;
226
227 extern "C" {
228 static gint wxapp_poll_func( GPollFD *ufds, guint nfds, gint timeout )
229 {
230 gdk_threads_enter();
231
232 wxMutexGuiLeave();
233 g_mainThreadLocked = true;
234
235 gint res = (*wxgs_poll_func)(ufds, nfds, timeout);
236
237 wxMutexGuiEnter();
238 g_mainThreadLocked = false;
239
240 gdk_threads_leave();
241
242 return res;
243 }
244 }
245
246 #endif // wxUSE_THREADS
247
248 //-----------------------------------------------------------------------------
249 // Access to the root window global
250 //-----------------------------------------------------------------------------
251
252 GtkWidget* wxGetRootWindow()
253 {
254 if (gs_RootWindow == NULL)
255 {
256 gs_RootWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
257 gtk_widget_realize( gs_RootWindow );
258 }
259 return gs_RootWindow;
260 }
261
262 //-----------------------------------------------------------------------------
263 // wxApp
264 //-----------------------------------------------------------------------------
265
266 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
267
268 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
269 EVT_IDLE(wxAppBase::OnIdle)
270 END_EVENT_TABLE()
271
272 wxApp::wxApp()
273 {
274 #ifdef __WXDEBUG__
275 m_isInAssert = false;
276 #endif // __WXDEBUG__
277 #if wxUSE_THREADS
278 m_idleMutex = NULL;
279 #endif
280 m_idleSourceId = 0;
281 }
282
283 wxApp::~wxApp()
284 {
285 }
286
287 bool wxApp::OnInitGui()
288 {
289 if ( !wxAppBase::OnInitGui() )
290 return false;
291
292 // if this is a wxGLApp (derived from wxApp), and we've already
293 // chosen a specific visual, then derive the GdkVisual from that
294 if ( GetXVisualInfo() )
295 {
296 GdkVisual* vis = gtk_widget_get_default_visual();
297
298 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
299 gtk_widget_set_default_colormap( colormap );
300 }
301 else
302 {
303 // On some machines, the default visual is just 256 colours, so
304 // we make sure we get the best. This can sometimes be wasteful.
305 if (m_useBestVisual)
306 {
307 if (m_forceTrueColour)
308 {
309 GdkVisual* visual = gdk_visual_get_best_with_both( 24, GDK_VISUAL_TRUE_COLOR );
310 if (!visual)
311 {
312 wxLogError(wxT("Unable to initialize TrueColor visual."));
313 return false;
314 }
315 GdkColormap *colormap = gdk_colormap_new( visual, FALSE );
316 gtk_widget_set_default_colormap( colormap );
317 }
318 else
319 {
320 if (gdk_visual_get_best() != gdk_visual_get_system())
321 {
322 GdkVisual* visual = gdk_visual_get_best();
323 GdkColormap *colormap = gdk_colormap_new( visual, FALSE );
324 gtk_widget_set_default_colormap( colormap );
325 }
326 }
327 }
328 }
329
330 return true;
331 }
332
333 GdkVisual *wxApp::GetGdkVisual()
334 {
335 GdkVisual *visual = NULL;
336
337 XVisualInfo *xvi = (XVisualInfo *)GetXVisualInfo();
338 if ( xvi )
339 visual = gdkx_visual_get( xvi->visualid );
340 else
341 visual = gdk_drawable_get_visual( wxGetRootWindow()->window );
342
343 wxASSERT( visual );
344
345 return visual;
346 }
347
348 bool wxApp::Initialize(int& argc, wxChar **argv)
349 {
350 if ( !wxAppBase::Initialize(argc, argv) )
351 return false;
352
353 #if wxUSE_THREADS
354 if (!g_thread_supported())
355 g_thread_init(NULL);
356
357 wxgs_poll_func = g_main_context_get_poll_func(NULL);
358 g_main_context_set_poll_func(NULL, wxapp_poll_func);
359 #endif // wxUSE_THREADS
360
361 // We should have the wxUSE_WCHAR_T test on the _outside_
362 #if wxUSE_WCHAR_T
363 // gtk+ 2.0 supports Unicode through UTF-8 strings
364 wxConvCurrent = &wxConvUTF8;
365 #else // !wxUSE_WCHAR_T
366 if (!wxOKlibc())
367 wxConvCurrent = (wxMBConv*) NULL;
368 #endif // wxUSE_WCHAR_T/!wxUSE_WCHAR_T
369
370 // decide which conversion to use for the file names
371
372 // (1) this variable exists for the sole purpose of specifying the encoding
373 // of the filenames for GTK+ programs, so use it if it is set
374 wxString encName(wxGetenv(_T("G_FILENAME_ENCODING")));
375 encName = encName.BeforeFirst(_T(','));
376 if (encName.CmpNoCase(_T("@locale")) == 0)
377 encName.clear();
378 encName.MakeUpper();
379 #if wxUSE_INTL
380 if (encName.empty())
381 {
382 // (2) if a non default locale is set, assume that the user wants his
383 // filenames in this locale too
384 encName = wxLocale::GetSystemEncodingName().Upper();
385 // (3) finally use UTF-8 by default
386 if (encName.empty() || encName == _T("US-ASCII"))
387 encName = _T("UTF-8");
388 wxSetEnv(_T("G_FILENAME_ENCODING"), encName);
389 }
390 #else
391 if (encName.empty())
392 encName = _T("UTF-8");
393
394 // if wxUSE_INTL==0 it probably indicates that only "C" locale is supported
395 // by the program anyhow so prevent GTK+ from calling setlocale(LC_ALL, "")
396 // from gtk_init_check() as it does by default
397 gtk_disable_setlocale();
398
399 #endif // wxUSE_INTL
400 static wxConvBrokenFileNames fileconv(encName);
401 wxConvFileName = &fileconv;
402
403
404 bool init_result;
405
406 #if wxUSE_UNICODE
407 // gtk_init() wants UTF-8, not wchar_t, so convert
408 int i;
409 char **argvGTK = new char *[argc + 1];
410 for ( i = 0; i < argc; i++ )
411 {
412 argvGTK[i] = wxStrdupA(wxConvUTF8.cWX2MB(argv[i]));
413 }
414
415 argvGTK[argc] = NULL;
416
417 int argcGTK = argc;
418
419 #ifdef __WXGPE__
420 init_result = true; // is there a _check() version of this?
421 gpe_application_init( &argcGTK, &argvGTK );
422 #else
423 init_result = gtk_init_check( &argcGTK, &argvGTK );
424 #endif
425
426 if ( argcGTK != argc )
427 {
428 // we have to drop the parameters which were consumed by GTK+
429 for ( i = 0; i < argcGTK; i++ )
430 {
431 while ( strcmp(wxConvUTF8.cWX2MB(argv[i]), argvGTK[i]) != 0 )
432 {
433 memmove(argv + i, argv + i + 1, (argc - i)*sizeof(*argv));
434 }
435 }
436
437 argc = argcGTK;
438 }
439 //else: gtk_init() didn't modify our parameters
440
441 // free our copy
442 for ( i = 0; i < argcGTK; i++ )
443 {
444 free(argvGTK[i]);
445 }
446
447 delete [] argvGTK;
448 #else // !wxUSE_UNICODE
449 // gtk_init() shouldn't actually change argv itself (just its contents) so
450 // it's ok to pass pointer to it
451 init_result = gtk_init_check( &argc, &argv );
452 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
453
454 if (!init_result) {
455 wxLogError(wxT("Unable to initialize gtk, is DISPLAY set properly?"));
456 return false;
457 }
458
459 // we can not enter threads before gtk_init is done
460 gdk_threads_enter();
461
462 wxSetDetectableAutoRepeat( true );
463
464 #if wxUSE_INTL
465 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
466 #endif
467
468 #if wxUSE_THREADS
469 m_idleMutex = new wxMutex;
470 #endif
471 // make sure GtkWidget type is loaded, idle hooks need it
472 g_type_class_ref(GTK_TYPE_WIDGET);
473 WakeUpIdle();
474
475 return true;
476 }
477
478 void wxApp::CleanUp()
479 {
480 if (m_idleSourceId != 0)
481 g_source_remove(m_idleSourceId);
482 #if wxUSE_THREADS
483 delete m_idleMutex;
484 m_idleMutex = NULL;
485 #endif
486 // release reference acquired by Initialize()
487 g_type_class_unref(g_type_class_peek(GTK_TYPE_WIDGET));
488
489 gdk_threads_leave();
490
491 wxAppBase::CleanUp();
492 }
493
494 void wxApp::WakeUpIdle()
495 {
496 #if wxUSE_THREADS
497 wxMutexLocker lock(*m_idleMutex);
498 #endif
499 if (m_idleSourceId == 0)
500 m_idleSourceId = g_idle_add_full(G_PRIORITY_LOW, wxapp_idle_callback, NULL, NULL);
501 }
502
503 // Checking for pending events requires first removing our idle source,
504 // otherwise it will cause the check to always return true.
505 bool wxApp::EventsPending()
506 {
507 #if wxUSE_THREADS
508 wxMutexLocker lock(*m_idleMutex);
509 #endif
510 if (m_idleSourceId != 0)
511 {
512 g_source_remove(m_idleSourceId);
513 m_idleSourceId = 0;
514 wx_add_idle_hooks();
515 }
516 return gtk_events_pending() != 0;
517 }
518
519 #ifdef __WXDEBUG__
520
521 void wxApp::OnAssertFailure(const wxChar *file,
522 int line,
523 const wxChar* func,
524 const wxChar* cond,
525 const wxChar *msg)
526 {
527
528 // block wx idle events while assert dialog is showing
529 m_isInAssert = true;
530
531 wxAppBase::OnAssertFailure(file, line, func, cond, msg);
532
533 m_isInAssert = false;
534 }
535
536 #endif // __WXDEBUG__