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