1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/gtk/utilsgtk.cpp
3// Purpose:
4// Author:      Robert Roebling
5// Id:          $Id: utilsgtk.cpp 49838 2007-11-11 23:52:54Z VZ $
6// Copyright:   (c) 1998 Robert Roebling
7// Licence:     wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#include "wx/utils.h"
14
15#ifndef WX_PRECOMP
16    #include "wx/string.h"
17    #include "wx/intl.h"
18    #include "wx/log.h"
19#endif
20
21#include "wx/sysopt.h"
22#include "wx/apptrait.h"
23#include "wx/process.h"
24#include "wx/unix/execute.h"
25
26#ifdef __WXDEBUG__
27    #include "wx/gtk/assertdlg_gtk.h"
28    #if wxUSE_STACKWALKER
29        #include "wx/stackwalk.h"
30    #endif // wxUSE_STACKWALKER
31#endif // __WXDEBUG__
32
33#include <stdarg.h>
34#include <string.h>
35#include <sys/stat.h>
36#include <sys/types.h>
37#include <sys/wait.h>   // for WNOHANG
38#include <unistd.h>
39
40#include "glib.h"
41#include "gdk/gdk.h"
42#include "gtk/gtk.h"
43#include "gdk/gdkx.h"
44
45#ifdef HAVE_X11_XKBLIB_H
46    /* under HP-UX and Solaris 2.6, at least, XKBlib.h defines structures with
47     * field named "explicit" - which is, of course, an error for a C++
48     * compiler. To be on the safe side, just redefine it everywhere. */
49    #define explicit __wx_explicit
50
51    #include "X11/XKBlib.h"
52
53    #undef explicit
54#endif // HAVE_X11_XKBLIB_H
55
56
57#if wxUSE_DETECT_SM
58    #include "X11/Xlib.h"
59    #include "X11/SM/SMlib.h"
60#endif
61
62//-----------------------------------------------------------------------------
63// data
64//-----------------------------------------------------------------------------
65
66extern GtkWidget *wxGetRootWindow();
67
68//----------------------------------------------------------------------------
69// misc.
70//----------------------------------------------------------------------------
71#ifndef __EMX__
72// on OS/2, we use the wxBell from wxBase library
73
74void wxBell()
75{
76    gdk_beep();
77}
78#endif
79
80/* Don't synthesize KeyUp events holding down a key and producing
81   KeyDown events with autorepeat. */
82#ifdef HAVE_X11_XKBLIB_H
83bool wxSetDetectableAutoRepeat( bool flag )
84{
85    Bool result;
86    XkbSetDetectableAutoRepeat( GDK_DISPLAY(), flag, &result );
87    return result;       /* true if keyboard hardware supports this mode */
88}
89#else
90bool wxSetDetectableAutoRepeat( bool WXUNUSED(flag) )
91{
92    return false;
93}
94#endif
95
96// Escapes string so that it is valid Pango markup XML string:
97wxString wxEscapeStringForPangoMarkup(const wxString& str)
98{
99    size_t len = str.length();
100    wxString out;
101    out.Alloc(len);
102    for (size_t i = 0; i < len; i++)
103    {
104        wxChar c = str[i];
105        switch (c)
106        {
107            case _T('&'):
108                out << _T("&amp;");
109                break;
110            case _T('<'):
111                out << _T("&lt;");
112                break;
113            case _T('>'):
114                out << _T("&gt;");
115                break;
116            case _T('\''):
117                out << _T("&apos;");
118                break;
119            case _T('"'):
120                out << _T("&quot;");
121                break;
122            default:
123                out << c;
124                break;
125        }
126    }
127    return out;
128}
129
130
131// ----------------------------------------------------------------------------
132// display characterstics
133// ----------------------------------------------------------------------------
134
135void *wxGetDisplay()
136{
137    return GDK_DISPLAY();
138}
139
140void wxDisplaySize( int *width, int *height )
141{
142    if (width) *width = gdk_screen_width();
143    if (height) *height = gdk_screen_height();
144}
145
146void wxDisplaySizeMM( int *width, int *height )
147{
148    if (width) *width = gdk_screen_width_mm();
149    if (height) *height = gdk_screen_height_mm();
150}
151
152void wxGetMousePosition( int* x, int* y )
153{
154    gdk_window_get_pointer( (GdkWindow*) NULL, x, y, (GdkModifierType*) NULL );
155}
156
157bool wxColourDisplay()
158{
159    return true;
160}
161
162int wxDisplayDepth()
163{
164    return gdk_drawable_get_visual( wxGetRootWindow()->window )->depth;
165}
166
167wxWindow* wxFindWindowAtPoint(const wxPoint& pt)
168{
169    return wxGenericFindWindowAtPoint(pt);
170}
171
172#if !wxUSE_UNICODE
173
174WXDLLIMPEXP_CORE
175wxCharBuffer wxConvertToGTK(const wxString& s, wxFontEncoding enc)
176{
177    wxWCharBuffer wbuf;
178    if ( enc == wxFONTENCODING_SYSTEM || enc == wxFONTENCODING_DEFAULT )
179    {
180        wbuf = wxConvUI->cMB2WC(s);
181    }
182    else // another encoding, use generic conversion class
183    {
184        wbuf = wxCSConv(enc).cMB2WC(s);
185    }
186
187    if ( !wbuf && !s.empty() )
188    {
189        // conversion failed, but we still want to show something to the user
190        // even if it's going to be wrong it is better than nothing
191        //
192        // we choose ISO8859-1 here arbitrarily, it's just the most common
193        // encoding probably and, also importantly here, conversion from it
194        // never fails as it's done internally by wxCSConv
195        wbuf = wxCSConv(wxFONTENCODING_ISO8859_1).cMB2WC(s);
196    }
197
198    return wxConvUTF8.cWC2MB(wbuf);
199}
200
201#endif // !wxUSE_UNICODE
202
203// ----------------------------------------------------------------------------
204// subprocess routines
205// ----------------------------------------------------------------------------
206
207extern "C" {
208static
209void GTK_EndProcessDetector(gpointer data, gint source,
210                            GdkInputCondition WXUNUSED(condition) )
211{
212   wxEndProcessData *proc_data = (wxEndProcessData *)data;
213
214   // has the process really terminated? unfortunately GDK (or GLib) seem to
215   // generate G_IO_HUP notification even when it simply tries to read from a
216   // closed fd and hasn't terminated at all
217   int pid = (proc_data->pid > 0) ? proc_data->pid : -(proc_data->pid);
218   int status = 0;
219   int rc = waitpid(pid, &status, WNOHANG);
220
221   if ( rc == 0 )
222   {
223       // no, it didn't exit yet, continue waiting
224       return;
225   }
226
227   // set exit code to -1 if something bad happened
228   proc_data->exitcode = rc != -1 && WIFEXITED(status) ? WEXITSTATUS(status)
229                                                      : -1;
230
231   // child exited, end waiting
232   close(source);
233
234   // don't call us again!
235   gdk_input_remove(proc_data->tag);
236
237   wxHandleProcessTermination(proc_data);
238}
239}
240
241int wxAddProcessCallback(wxEndProcessData *proc_data, int fd)
242{
243    int tag = gdk_input_add(fd,
244                            GDK_INPUT_READ,
245                            GTK_EndProcessDetector,
246                            (gpointer)proc_data);
247
248    return tag;
249}
250
251
252
253// ----------------------------------------------------------------------------
254// wxPlatformInfo-related
255// ----------------------------------------------------------------------------
256
257wxPortId wxGUIAppTraits::GetToolkitVersion(int *verMaj, int *verMin) const
258{
259    if ( verMaj )
260        *verMaj = gtk_major_version;
261    if ( verMin )
262        *verMin = gtk_minor_version;
263
264    return wxPORT_GTK;
265}
266
267#if wxUSE_DETECT_SM
268static wxString GetSM()
269{
270    class Dpy
271    {
272    public:
273        Dpy() { m_dpy = XOpenDisplay(NULL); }
274        ~Dpy() { if ( m_dpy ) XCloseDisplay(m_dpy); }
275
276        operator Display *() const { return m_dpy; }
277    private:
278        Display *m_dpy;
279    } dpy;
280
281    if ( !dpy )
282        return wxEmptyString;
283
284    char *client_id;
285    SmcConn smc_conn = SmcOpenConnection(NULL, NULL,
286                                         999, 999,
287                                         0 /* mask */, NULL /* callbacks */,
288                                         NULL, &client_id,
289                                         0, NULL);
290
291    if ( !smc_conn )
292        return wxEmptyString;
293
294    char *vendor = SmcVendor(smc_conn);
295    wxString ret = wxString::FromAscii( vendor );
296    free(vendor);
297
298    SmcCloseConnection(smc_conn, 0, NULL);
299    free(client_id);
300
301    return ret;
302}
303#endif // wxUSE_DETECT_SM
304
305
306//-----------------------------------------------------------------------------
307// wxGUIAppTraits
308//-----------------------------------------------------------------------------
309
310#ifdef __WXDEBUG__
311
312#if wxUSE_STACKWALKER
313
314// private helper class
315class StackDump : public wxStackWalker
316{
317public:
318    StackDump(GtkAssertDialog *dlg) { m_dlg=dlg; }
319
320protected:
321    virtual void OnStackFrame(const wxStackFrame& frame)
322    {
323        wxString fncname = frame.GetName();
324        wxString fncargs = fncname;
325
326        size_t n = fncname.find(wxT('('));
327        if (n != wxString::npos)
328        {
329            // remove arguments from function name
330            fncname.erase(n);
331
332            // remove function name and brackets from arguments
333            fncargs = fncargs.substr(n+1, fncargs.length()-n-2);
334        }
335        else
336            fncargs = wxEmptyString;
337
338        // append this stack frame's info in the dialog
339        if (!frame.GetFileName().empty() || !fncname.empty())
340            gtk_assert_dialog_append_stack_frame(m_dlg,
341                                                fncname.mb_str(),
342                                                fncargs.mb_str(),
343                                                frame.GetFileName().mb_str(),
344                                                frame.GetLine());
345    }
346
347private:
348    GtkAssertDialog *m_dlg;
349};
350
351// the callback functions must be extern "C" to comply with GTK+ declarations
352extern "C"
353{
354    void get_stackframe_callback(StackDump *dump)
355    {
356        // skip over frames up to including wxOnAssert()
357        dump->ProcessFrames(3);
358    }
359}
360
361#endif      // wxUSE_STACKWALKER
362
363bool wxGUIAppTraits::ShowAssertDialog(const wxString& msg)
364{
365    // under GTK2 we prefer to use a dialog widget written using directly GTK+;
366    // in fact we cannot use a dialog written using wxWidgets: it would need
367    // the wxWidgets idle processing to work correctly!
368    GtkWidget *dialog = gtk_assert_dialog_new();
369    gtk_assert_dialog_set_message(GTK_ASSERT_DIALOG(dialog), msg.mb_str());
370
371#if wxUSE_STACKWALKER
372    // don't show more than maxLines or we could get a dialog too tall to be
373    // shown on screen: 20 should be ok everywhere as even with 15 pixel high
374    // characters it is still only 300 pixels...
375    static const int maxLines = 20;
376
377    // save current stack frame...
378    StackDump dump(GTK_ASSERT_DIALOG(dialog));
379    dump.SaveStack(maxLines);
380
381    // ...but process it only if the user needs it
382    gtk_assert_dialog_set_backtrace_callback(GTK_ASSERT_DIALOG(dialog),
383                                             (GtkAssertDialogStackFrameCallback)get_stackframe_callback,
384                                             &dump);
385#endif      // wxUSE_STACKWALKER
386
387    gint result = gtk_dialog_run(GTK_DIALOG (dialog));
388    bool returnCode = false;
389    switch (result)
390    {
391    case GTK_ASSERT_DIALOG_STOP:
392        wxTrap();
393        break;
394    case GTK_ASSERT_DIALOG_CONTINUE:
395        // nothing to do
396        break;
397    case GTK_ASSERT_DIALOG_CONTINUE_SUPPRESSING:
398        // no more asserts
399        returnCode = true;
400        break;
401
402    default:
403        wxFAIL_MSG( _T("unexpected return code from GtkAssertDialog") );
404    }
405
406    gtk_widget_destroy(dialog);
407    return returnCode;
408}
409
410#endif  // __WXDEBUG__
411
412wxString wxGUIAppTraits::GetDesktopEnvironment() const
413{
414#if wxUSE_DETECT_SM
415    const wxString SM = GetSM();
416
417    if (SM == wxT("GnomeSM"))
418        return wxT("GNOME");
419
420    if (SM == wxT("KDE"))
421        return wxT("KDE");
422#endif // wxUSE_DETECT_SM
423
424    return wxEmptyString;
425}
426
427
428
429