+#ifdef __UNIX__
+
+extern "C" {
+static gboolean EndProcessDetector(GIOChannel* source, GIOCondition, void* data)
+{
+ wxEndProcessData * const
+ proc_data = static_cast<wxEndProcessData *>(data);
+
+ // child exited, end waiting
+ close(g_io_channel_unix_get_fd(source));
+
+ wxHandleProcessTermination(proc_data);
+
+ // don't call us again!
+ return false;
+}
+}
+
+int wxGUIAppTraits::AddProcessCallback(wxEndProcessData *proc_data, int fd)
+{
+ GIOChannel* channel = g_io_channel_unix_new(fd);
+ GIOCondition cond = GIOCondition(G_IO_IN | G_IO_HUP | G_IO_ERR);
+ unsigned id = g_io_add_watch(channel, cond, EndProcessDetector, proc_data);
+ g_io_channel_unref(channel);
+ return int(id);
+}
+
+#endif // __UNIX__
+
+// ----------------------------------------------------------------------------
+// wxPlatformInfo-related
+// ----------------------------------------------------------------------------
+
+wxPortId wxGUIAppTraits::GetToolkitVersion(int *verMaj, int *verMin) const
+{
+ if ( verMaj )
+ *verMaj = gtk_major_version;
+ if ( verMin )
+ *verMin = gtk_minor_version;
+
+ return wxPORT_GTK;
+}
+
+#if wxUSE_TIMER
+
+wxTimerImpl *wxGUIAppTraits::CreateTimerImpl(wxTimer *timer)
+{
+ return new wxGTKTimerImpl(timer);
+}
+
+#endif // wxUSE_TIMER
+
+#if wxUSE_DETECT_SM
+static wxString GetSM()
+{
+ wxX11Display dpy;
+ if ( !dpy )
+ return wxEmptyString;
+
+ char smerr[256];
+ char *client_id;
+ SmcConn smc_conn = SmcOpenConnection(NULL, NULL,
+ 999, 999,
+ 0 /* mask */, NULL /* callbacks */,
+ NULL, &client_id,
+ WXSIZEOF(smerr), smerr);
+
+ if ( !smc_conn )
+ {
+ wxLogDebug("Failed to connect to session manager: %s", smerr);
+ return wxEmptyString;
+ }
+
+ char *vendor = SmcVendor(smc_conn);
+ wxString ret = wxString::FromAscii( vendor );
+ free(vendor);
+
+ SmcCloseConnection(smc_conn, 0, NULL);
+ free(client_id);
+
+ return ret;
+}
+#endif // wxUSE_DETECT_SM
+
+
+//-----------------------------------------------------------------------------
+// wxGUIAppTraits
+//-----------------------------------------------------------------------------
+
+wxEventLoopBase *wxGUIAppTraits::CreateEventLoop()
+{
+ return new wxEventLoop();
+}
+
+
+#if wxUSE_INTL && defined(__UNIX__)
+void wxGUIAppTraits::SetLocale()
+{
+#ifdef __WXGTK3__
+ setlocale(LC_ALL, "");
+#else
+ gtk_set_locale();
+#endif
+ wxUpdateLocaleIsUtf8();
+}
+#endif
+
+#ifdef __UNIX__
+
+#if wxDEBUG_LEVEL && wxUSE_STACKWALKER
+
+// private helper class
+class StackDump : public wxStackWalker
+{
+public:
+ StackDump(GtkAssertDialog *dlg) { m_dlg=dlg; }
+
+protected:
+ virtual void OnStackFrame(const wxStackFrame& frame)
+ {
+ wxString fncname = frame.GetName();
+
+ // append this stack frame's info in the dialog
+ if (!frame.GetFileName().empty() || !fncname.empty())
+ gtk_assert_dialog_append_stack_frame(m_dlg,
+ fncname.mb_str(),
+ frame.GetFileName().mb_str(),
+ frame.GetLine());
+ }
+
+private:
+ GtkAssertDialog *m_dlg;
+};
+
+static void get_stackframe_callback(void* p)
+{
+ StackDump* dump = static_cast<StackDump*>(p);
+ // skip over frames up to including wxOnAssert()
+ dump->ProcessFrames(3);
+}
+
+#endif // wxDEBUG_LEVEL && wxUSE_STACKWALKER
+
+bool wxGUIAppTraits::ShowAssertDialog(const wxString& msg)
+{
+#if wxDEBUG_LEVEL
+ // we can't show the dialog from another thread
+ if ( wxIsMainThread() )
+ {
+ // under GTK2 we prefer to use a dialog widget written using directly
+ // in GTK+ as use a dialog written using wxWidgets would need the
+ // wxWidgets idle processing to work correctly which might not be the
+ // case when assert happens
+ GtkWidget *dialog = gtk_assert_dialog_new();
+ gtk_assert_dialog_set_message(GTK_ASSERT_DIALOG(dialog), msg.mb_str());
+
+#if wxUSE_STACKWALKER
+ // save the current stack ow...
+ StackDump dump(GTK_ASSERT_DIALOG(dialog));
+ dump.SaveStack(100); // showing more than 100 frames is not very useful
+
+ // ...but process it only if the user needs it
+ gtk_assert_dialog_set_backtrace_callback
+ (
+ GTK_ASSERT_DIALOG(dialog),
+ get_stackframe_callback,
+ &dump
+ );
+#endif // wxUSE_STACKWALKER
+
+ gint result = gtk_dialog_run(GTK_DIALOG (dialog));
+ bool returnCode = false;
+ switch (result)
+ {
+ case GTK_ASSERT_DIALOG_STOP:
+ wxTrap();
+ break;
+ case GTK_ASSERT_DIALOG_CONTINUE:
+ // nothing to do
+ break;
+ case GTK_ASSERT_DIALOG_CONTINUE_SUPPRESSING:
+ // no more asserts
+ returnCode = true;
+ break;
+
+ default:
+ wxFAIL_MSG( wxT("unexpected return code from GtkAssertDialog") );
+ }
+
+ gtk_widget_destroy(dialog);
+ return returnCode;
+ }
+#endif // wxDEBUG_LEVEL
+
+ return wxAppTraitsBase::ShowAssertDialog(msg);
+}
+
+#endif // __UNIX__
+
+#if defined(__UNIX__) || defined(__OS2__)
+
+wxString wxGUIAppTraits::GetDesktopEnvironment() const
+{
+ wxString de = wxSystemOptions::GetOption(wxT("gtk.desktop"));
+#if wxUSE_DETECT_SM
+ if ( de.empty() )
+ {
+ static const wxString s_SM = GetSM();
+
+ if (s_SM == wxT("GnomeSM"))
+ de = wxT("GNOME");
+ else if (s_SM == wxT("KDE"))
+ de = wxT("KDE");
+ }
+#endif // wxUSE_DETECT_SM
+
+ return de;
+}
+
+#endif // __UNIX__ || __OS2__
+
+#ifdef __WXGTK26__
+
+// see the hack below in wxCmdLineParser::GetUsageString().
+// TODO: replace this hack with a g_option_group_get_entries()
+// call as soon as such function exists;
+// see http://bugzilla.gnome.org/show_bug.cgi?id=431021 for the relative
+// feature request
+struct _GOptionGroup
+{
+ gchar *name;
+ gchar *description;
+ gchar *help_description;
+
+ GDestroyNotify destroy_notify;
+ gpointer user_data;
+
+ GTranslateFunc translate_func;
+ GDestroyNotify translate_notify;
+ gpointer translate_data;
+
+ GOptionEntry *entries;
+ gint n_entries;
+
+ GOptionParseFunc pre_parse_func;
+ GOptionParseFunc post_parse_func;
+ GOptionErrorFunc error_func;
+};
+
+wxString wxGetNameFromGtkOptionEntry(const GOptionEntry *opt)