1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/app.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling, Julian Smart
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12 #include "wx/app.h"
13
14 #ifndef WX_PRECOMP
15 #include "wx/intl.h"
16 #include "wx/log.h"
17 #include "wx/utils.h"
18 #include "wx/memory.h"
19 #include "wx/font.h"
20 #endif
21
22 #include "wx/thread.h"
23
24 #ifdef __WXGPE__
25 #include <gpe/init.h>
26 #endif
27
28 #include "wx/apptrait.h"
29 #include "wx/fontmap.h"
30 #include "wx/msgout.h"
31
32 #include "wx/gtk/private.h"
33
34 #include "wx/gtk/mimetype.h"
35 //-----------------------------------------------------------------------------
36 // link GnomeVFS
37 //-----------------------------------------------------------------------------
38
39 #if wxUSE_MIMETYPE && wxUSE_LIBGNOMEVFS
40 #include "wx/link.h"
wxFORCE_LINK_MODULE(gnome_vfs)41 wxFORCE_LINK_MODULE(gnome_vfs)
42 #endif
43
44 //-----------------------------------------------------------------------------
45 // local functions
46 //-----------------------------------------------------------------------------
47
48 // One-shot signal emission hook, to install idle handler.
49 extern "C" {
50 static gboolean
51 wx_emission_hook(GSignalInvocationHint*, guint, const GValue*, gpointer data)
52 {
53 wxApp* app = wxTheApp;
54 if (app != NULL)
55 app->WakeUpIdle();
56 bool* hook_installed = (bool*)data;
57 // record that hook is not installed
58 *hook_installed = false;
59 // remove hook
60 return false;
61 }
62 }
63
64 // Add signal emission hooks, to re-install idle handler when needed.
wx_add_idle_hooks()65 static void wx_add_idle_hooks()
66 {
67 // "event" hook
68 {
69 static bool hook_installed;
70 if (!hook_installed)
71 {
72 static guint sig_id;
73 if (sig_id == 0)
74 sig_id = g_signal_lookup("event", GTK_TYPE_WIDGET);
75 hook_installed = true;
76 g_signal_add_emission_hook(
77 sig_id, 0, wx_emission_hook, &hook_installed, NULL);
78 }
79 }
80 // "size_allocate" hook
81 // Needed to match the behaviour of the old idle system,
82 // but probably not necessary.
83 {
84 static bool hook_installed;
85 if (!hook_installed)
86 {
87 static guint sig_id;
88 if (sig_id == 0)
89 sig_id = g_signal_lookup("size_allocate", GTK_TYPE_WIDGET);
90 hook_installed = true;
91 g_signal_add_emission_hook(
92 sig_id, 0, wx_emission_hook, &hook_installed, NULL);
93 }
94 }
95 }
96
97 extern "C" {
wxapp_idle_callback(gpointer)98 static gboolean wxapp_idle_callback(gpointer)
99 {
100 return wxTheApp->DoIdle();
101 }
102 }
103
104 // 0: no change, 1: focus in, 2: focus out
105 static wxUIntPtr gs_focusChange;
106
107 extern "C" {
108 static gboolean
wx_focus_event_hook(GSignalInvocationHint *,unsigned,const GValue * param_values,void * data)109 wx_focus_event_hook(GSignalInvocationHint*, unsigned, const GValue* param_values, void* data)
110 {
111 // If focus change on TLW
112 if (GTK_IS_WINDOW(g_value_peek_pointer(param_values)))
113 gs_focusChange = wxUIntPtr(data);
114
115 return true;
116 }
117 }
118
DoIdle()119 bool wxApp::DoIdle()
120 {
121 guint id_save;
122 {
123 // Allow another idle source to be added while this one is busy.
124 // Needed if an idle event handler runs a new event loop,
125 // for example by showing a dialog.
126 #if wxUSE_THREADS
127 wxMutexLocker lock(m_idleMutex);
128 #endif
129 id_save = m_idleSourceId;
130 m_idleSourceId = 0;
131 wx_add_idle_hooks();
132
133 #if wxDEBUG_LEVEL
134 // don't generate the idle events while the assert modal dialog is shown,
135 // this matches the behaviour of wxMSW
136 if (m_isInAssert)
137 return false;
138 #endif
139 }
140
141 gdk_threads_enter();
142
143 if (gs_focusChange) {
144 SetActive(gs_focusChange == 1, NULL);
145 gs_focusChange = 0;
146 }
147
148 bool needMore;
149 do {
150 ProcessPendingEvents();
151
152 needMore = ProcessIdle();
153 } while (needMore && gtk_events_pending() == 0);
154 gdk_threads_leave();
155
156 #if wxUSE_THREADS
157 wxMutexLocker lock(m_idleMutex);
158 #endif
159
160 bool keepSource = false;
161 // if a new idle source has not been added, either as a result of idle
162 // processing above or by another thread calling WakeUpIdle()
163 if (m_idleSourceId == 0)
164 {
165 // if more idle processing was requested or pending events have appeared
166 if (needMore || HasPendingEvents())
167 {
168 // keep this source installed
169 m_idleSourceId = id_save;
170 keepSource = true;
171 }
172 else // add hooks and remove this source
173 wx_add_idle_hooks();
174 }
175 // else remove this source, leave new one installed
176 // we must keep an idle source, otherwise a wakeup could be lost
177
178 return keepSource;
179 }
180
181 //-----------------------------------------------------------------------------
182 // wxApp
183 //-----------------------------------------------------------------------------
184
185 wxIMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler);
186
wxApp()187 wxApp::wxApp()
188 {
189 m_isInAssert = false;
190 m_idleSourceId = 0;
191 }
192
~wxApp()193 wxApp::~wxApp()
194 {
195 }
196
SetNativeTheme(const wxString & theme)197 bool wxApp::SetNativeTheme(const wxString& theme)
198 {
199 #ifdef __WXGTK3__
200 wxUnusedVar(theme);
201 return false;
202 #else
203 wxString path;
204 path = gtk_rc_get_theme_dir();
205 path += "/";
206 path += theme.utf8_str();
207 path += "/gtk-2.0/gtkrc";
208
209 if ( wxFileExists(path.utf8_str()) )
210 gtk_rc_add_default_file(path.utf8_str());
211 else if ( wxFileExists(theme.utf8_str()) )
212 gtk_rc_add_default_file(theme.utf8_str());
213 else
214 {
215 wxLogWarning("Theme \"%s\" not available.", theme);
216
217 return false;
218 }
219
220 gtk_rc_reparse_all_for_settings(gtk_settings_get_default(), TRUE);
221
222 return true;
223 #endif
224 }
225
OnInitGui()226 bool wxApp::OnInitGui()
227 {
228 if ( !wxAppBase::OnInitGui() )
229 return false;
230
231 #ifndef __WXGTK3__
232 // if this is a wxGLApp (derived from wxApp), and we've already
233 // chosen a specific visual, then derive the GdkVisual from that
234 if ( GetXVisualInfo() )
235 {
236 GdkVisual* vis = gtk_widget_get_default_visual();
237
238 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
239 gtk_widget_set_default_colormap( colormap );
240 }
241 else
242 {
243 // On some machines, the default visual is just 256 colours, so
244 // we make sure we get the best. This can sometimes be wasteful.
245 if (m_useBestVisual)
246 {
247 if (m_forceTrueColour)
248 {
249 GdkVisual* visual = gdk_visual_get_best_with_both( 24, GDK_VISUAL_TRUE_COLOR );
250 if (!visual)
251 {
252 wxLogError(wxT("Unable to initialize TrueColor visual."));
253 return false;
254 }
255 GdkColormap *colormap = gdk_colormap_new( visual, FALSE );
256 gtk_widget_set_default_colormap( colormap );
257 }
258 else
259 {
260 if (gdk_visual_get_best() != gdk_visual_get_system())
261 {
262 GdkVisual* visual = gdk_visual_get_best();
263 GdkColormap *colormap = gdk_colormap_new( visual, FALSE );
264 gtk_widget_set_default_colormap( colormap );
265 }
266 }
267 }
268 }
269 #endif
270
271 return true;
272 }
273
274 // use unusual names for the parameters to avoid conflict with wxApp::arg[cv]
Initialize(int & argc_,wxChar ** argv_)275 bool wxApp::Initialize(int& argc_, wxChar **argv_)
276 {
277 if ( !wxAppBase::Initialize(argc_, argv_) )
278 return false;
279
280 // Thread support is always on since glib 2.31.
281 #if !GLIB_CHECK_VERSION(2, 31, 0)
282 #if wxUSE_THREADS
283 if (!g_thread_supported())
284 {
285 // g_thread_init() does nothing and is deprecated in recent glib but
286 // might still be needed in the older versions, which are the only ones
287 // for which this code is going to be executed (as g_thread_supported()
288 // is always TRUE in these recent glib versions anyhow).
289 g_thread_init(NULL);
290 gdk_threads_init();
291 }
292 #endif // wxUSE_THREADS
293 #endif // glib < 2.31
294
295 // gtk+ 2.0 supports Unicode through UTF-8 strings
296 wxConvCurrent = &wxConvUTF8;
297
298 #ifdef __UNIX__
299 // decide which conversion to use for the file names
300
301 // (1) this variable exists for the sole purpose of specifying the encoding
302 // of the filenames for GTK+ programs, so use it if it is set
303 wxString encName(wxGetenv(wxT("G_FILENAME_ENCODING")));
304 encName = encName.BeforeFirst(wxT(','));
305 if (encName.CmpNoCase(wxT("@locale")) == 0)
306 encName.clear();
307 encName.MakeUpper();
308 if (encName.empty())
309 {
310 #if wxUSE_INTL
311 // (2) if a non default locale is set, assume that the user wants his
312 // filenames in this locale too
313 encName = wxLocale::GetSystemEncodingName().Upper();
314
315 // But don't consider ASCII in this case.
316 if ( !encName.empty() )
317 {
318 #if wxUSE_FONTMAP
319 wxFontEncoding enc = wxFontMapperBase::GetEncodingFromName(encName);
320 if ( enc == wxFONTENCODING_DEFAULT )
321 #else // !wxUSE_FONTMAP
322 if ( encName == wxT("US-ASCII") )
323 #endif // wxUSE_FONTMAP/!wxUSE_FONTMAP
324 {
325 // This means US-ASCII when returned from GetEncodingFromName().
326 encName.clear();
327 }
328 }
329 #endif // wxUSE_INTL
330
331 // (3) finally use UTF-8 by default
332 if ( encName.empty() )
333 encName = wxT("UTF-8");
334 wxSetEnv(wxT("G_FILENAME_ENCODING"), encName);
335 }
336
337 static wxConvBrokenFileNames fileconv(encName);
338 wxConvFileName = &fileconv;
339 #endif // __UNIX__
340
341
342 // Using XIM results in many problems, so try to warn people about it.
343 wxString inputMethod;
344 if ( wxGetEnv("GTK_IM_MODULE", &inputMethod) && inputMethod == "xim" )
345 {
346 wxMessageOutputStderr().Output
347 (
348 _("WARNING: using XIM input method is unsupported and may result "
349 "in problems with input handling and flickering. Consider "
350 "unsetting GTK_IM_MODULE or setting to \"ibus\".")
351 );
352 }
353
354 bool init_result;
355
356 #if wxUSE_UNICODE
357 int i;
358
359 // gtk_init() wants UTF-8, not wchar_t, so convert
360 char **argvGTK = new char *[argc_ + 1];
361 for ( i = 0; i < argc_; i++ )
362 {
363 argvGTK[i] = wxStrdupA(wxConvUTF8.cWX2MB(argv_[i]));
364 }
365
366 argvGTK[argc_] = NULL;
367
368 int argcGTK = argc_;
369
370 // Prevent gtk_init_check() from changing the locale automatically for
371 // consistency with the other ports that don't do it. If necessary,
372 // wxApp::SetCLocale() may be explicitly called.
373 //
374 // Note that this function generates a warning if it's called more than
375 // once, so avoid them.
376 static bool s_gtkLocalDisabled = false;
377 if ( !s_gtkLocalDisabled )
378 {
379 s_gtkLocalDisabled = true;
380 gtk_disable_setlocale();
381 }
382
383 #ifdef __WXGPE__
384 init_result = true; // is there a _check() version of this?
385 gpe_application_init( &argcGTK, &argvGTK );
386 #elif defined(__WXGTK4__)
387 init_result = gtk_init_check() != 0;
388 #else
389 init_result = gtk_init_check( &argcGTK, &argvGTK ) != 0;
390 #endif
391
392 if ( argcGTK != argc_ )
393 {
394 // we have to drop the parameters which were consumed by GTK+
395 for ( i = 0; i < argcGTK; i++ )
396 {
397 while ( strcmp(wxConvUTF8.cWX2MB(argv_[i]), argvGTK[i]) != 0 )
398 {
399 memmove(argv_ + i, argv_ + i + 1, (argc_ - i)*sizeof(*argv_));
400 }
401 }
402
403 argc_ = argcGTK;
404 argv_[argc_] = NULL;
405 }
406 //else: gtk_init() didn't modify our parameters
407
408 // free our copy
409 for ( i = 0; i < argcGTK; i++ )
410 {
411 free(argvGTK[i]);
412 }
413
414 delete [] argvGTK;
415 #else // !wxUSE_UNICODE
416 // gtk_init() shouldn't actually change argv_ itself (just its contents) so
417 // it's ok to pass pointer to it
418 init_result = gtk_init_check( &argc_, &argv_ );
419 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
420
421 // update internal arg[cv] as GTK+ may have removed processed options:
422 this->argc = argc_;
423 #if wxUSE_UNICODE
424 this->argv.Init(argc_, argv_);
425 #else
426 this->argv = argv_;
427 #endif
428
429 if ( !init_result )
430 {
431 wxLogError(_("Unable to initialize GTK+, is DISPLAY set properly?"));
432 return false;
433 }
434
435 #if wxUSE_MIMETYPE
436 wxMimeTypesManagerFactory::Set(new wxGTKMimeTypesManagerFactory());
437 #endif
438
439 // we cannot enter threads before gtk_init is done
440 gdk_threads_enter();
441
442 #if wxUSE_INTL
443 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
444 #endif
445
446 // make sure GtkWidget type is loaded, signal emission hooks need it
447 const GType widgetType = GTK_TYPE_WIDGET;
448 g_type_class_ref(widgetType);
449
450 // focus in/out hooks used for generating wxEVT_ACTIVATE_APP
451 g_signal_add_emission_hook(
452 g_signal_lookup("focus_in_event", widgetType),
453 0, wx_focus_event_hook, GINT_TO_POINTER(1), NULL);
454 g_signal_add_emission_hook(
455 g_signal_lookup("focus_out_event", widgetType),
456 0, wx_focus_event_hook, GINT_TO_POINTER(2), NULL);
457
458 WakeUpIdle();
459
460 return true;
461 }
462
CleanUp()463 void wxApp::CleanUp()
464 {
465 if (m_idleSourceId != 0)
466 g_source_remove(m_idleSourceId);
467
468 // release reference acquired by Initialize()
469 gpointer gt = g_type_class_peek(GTK_TYPE_WIDGET);
470 if (gt != NULL)
471 g_type_class_unref(gt);
472
473 gdk_threads_leave();
474
475 wxAppBase::CleanUp();
476 }
477
WakeUpIdle()478 void wxApp::WakeUpIdle()
479 {
480 #if wxUSE_THREADS
481 wxMutexLocker lock(m_idleMutex);
482 #endif
483 if (m_idleSourceId == 0)
484 m_idleSourceId = g_idle_add_full(G_PRIORITY_LOW, wxapp_idle_callback, NULL, NULL);
485 }
486
487 // Checking for pending events requires first removing our idle source,
488 // otherwise it will cause the check to always return true.
EventsPending()489 bool wxApp::EventsPending()
490 {
491 #if wxUSE_THREADS
492 wxMutexLocker lock(m_idleMutex);
493 #endif
494 if (m_idleSourceId != 0)
495 {
496 g_source_remove(m_idleSourceId);
497 m_idleSourceId = 0;
498 wx_add_idle_hooks();
499 }
500 return gtk_events_pending() != 0;
501 }
502
OnAssertFailure(const wxChar * file,int line,const wxChar * func,const wxChar * cond,const wxChar * msg)503 void wxApp::OnAssertFailure(const wxChar *file,
504 int line,
505 const wxChar* func,
506 const wxChar* cond,
507 const wxChar *msg)
508 {
509 // there is no need to do anything if asserts are disabled in this build
510 // anyhow
511 #if wxDEBUG_LEVEL
512 // block wx idle events while assert dialog is showing
513 m_isInAssert = true;
514
515 wxAppBase::OnAssertFailure(file, line, func, cond, msg);
516
517 m_isInAssert = false;
518 #else // !wxDEBUG_LEVEL
519 wxUnusedVar(file);
520 wxUnusedVar(line);
521 wxUnusedVar(func);
522 wxUnusedVar(cond);
523 wxUnusedVar(msg);
524 #endif // wxDEBUG_LEVEL/!wxDEBUG_LEVEL
525 }
526
527 #if wxUSE_THREADS
MutexGuiEnter()528 void wxGUIAppTraits::MutexGuiEnter()
529 {
530 gdk_threads_enter();
531 }
532
MutexGuiLeave()533 void wxGUIAppTraits::MutexGuiLeave()
534 {
535 gdk_threads_leave();
536 }
537 #endif // wxUSE_THREADS
538
539 /* static */
GTKIsUsingGlobalMenu()540 bool wxApp::GTKIsUsingGlobalMenu()
541 {
542 static int s_isUsingGlobalMenu = -1;
543 if ( s_isUsingGlobalMenu == -1 )
544 {
545 // Currently we just check for this environment variable because this
546 // is how support for the global menu is implemented under Ubuntu.
547 //
548 // If we ever get false positives, we could also check for
549 // XDG_CURRENT_DESKTOP env var being set to "Unity".
550 wxString proxy;
551 s_isUsingGlobalMenu = wxGetEnv("UBUNTU_MENUPROXY", &proxy) &&
552 !proxy.empty() && proxy != "0";
553 }
554
555 return s_isUsingGlobalMenu == 1;
556 }
557