]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/window.cpp
Fix type mismatch in wxLogTrace() call in wxSocket.
[wxWidgets.git] / src / gtk / window.cpp
index e45dd06302285d288de1f4f645915420730eee1f..ac10b6fc7dbd3eb182cc072f78dbe06e9250d55d 100644 (file)
 #include "wx/gtk/private/event.h"
 using namespace wxGTKImpl;
 
+#ifdef GDK_WINDOWING_X11
 #include <gdk/gdkx.h>
+#else
+typedef guint KeySym;
+#endif
 
 #include <gdk/gdkkeysyms.h>
 #if GTK_CHECK_VERSION(3,0,0)
 #include <gdk/gdkkeysyms-compat.h>
 #endif
 
+// gdk_window_set_composited() is only supported since 2.12
+#define wxGTK_VERSION_REQUIRED_FOR_COMPOSITING 2,12,0
+#define wxGTK_HAS_COMPOSITING_SUPPORT GTK_CHECK_VERSION(2,12,0)
+
 //-----------------------------------------------------------------------------
 // documentation on internals
 //-----------------------------------------------------------------------------
@@ -724,6 +732,7 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
                 keysym = (KeySym)gdk_event->string[0];
             }
 
+#ifdef GDK_WINDOWING_X11
             // we want to always get the same key code when the same key is
             // pressed regardless of the state of the modifiers, i.e. on a
             // standard US keyboard pressing '5' or '%' ('5' key with
@@ -742,6 +751,9 @@ wxTranslateGTKKeyEventToWx(wxKeyEvent& event,
             // use the normalized, i.e. lower register, keysym if we've
             // got one
             key_code = keysymNormalized ? keysymNormalized : keysym;
+#else
+            key_code = keysym;
+#endif
 
             // as explained above, we want to have lower register key codes
             // normally but for the letter keys we want to have the upper ones
@@ -875,6 +887,17 @@ void AdjustCharEventKeyCodes(wxKeyEvent& event)
 
 } // anonymous namespace
 
+// If a widget does not handle a key or mouse event, GTK+ sends it up the
+// parent chain until it is handled. These events are not supposed to propagate
+// in wxWidgets, so this code avoids handling them in any parent wxWindow,
+// while still allowing the event to propagate so things like native keyboard
+// navigation will work.
+#define wxPROCESS_EVENT_ONCE(EventType, event) \
+    static EventType eventPrev; \
+    if (memcmp(&eventPrev, event, sizeof(EventType)) == 0) \
+        return false; \
+    eventPrev = *event
+
 extern "C" {
 static gboolean
 gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
@@ -886,6 +909,8 @@ gtk_window_key_press_callback( GtkWidget *WXUNUSED(widget),
     if (g_blockEventsOnDrag)
         return FALSE;
 
+    wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
+
     wxKeyEvent event( wxEVT_KEY_DOWN );
     bool ret = false;
     bool return_after_IM = false;
@@ -1057,6 +1082,8 @@ gtk_window_key_release_callback( GtkWidget * WXUNUSED(widget),
     if (g_blockEventsOnDrag)
         return FALSE;
 
+    wxPROCESS_EVENT_ONCE(GdkEventKey, gdk_event);
+
     wxKeyEvent event( wxEVT_KEY_UP );
     if ( !wxTranslateGTKKeyEventToWx(event, win, gdk_event) )
     {
@@ -1068,21 +1095,6 @@ gtk_window_key_release_callback( GtkWidget * WXUNUSED(widget),
 }
 }
 
-//-----------------------------------------------------------------------------
-// key and mouse events, after, from m_widget
-//-----------------------------------------------------------------------------
-
-extern "C" {
-static gboolean key_and_mouse_event_after(GtkWidget* widget, GdkEventKey*, wxWindow*)
-{
-    // If a widget does not handle a key or mouse event, GTK+ sends it up the
-    // parent chain until it is handled. These events are not supposed to
-    // propagate in wxWidgets, so prevent it unless widget is in a native
-    // container.
-    return WX_IS_PIZZA(gtk_widget_get_parent(widget));
-}
-}
-
 // ============================================================================
 // the mouse events
 // ============================================================================
@@ -1137,7 +1149,7 @@ static void AdjustEventButtonState(wxMouseEvent& event)
     }
 }
 
-// find the window to send the mouse event too
+// find the window to send the mouse event to
 static
 wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
 {
@@ -1187,6 +1199,7 @@ wxWindowGTK *FindWindowForMouseEvent(wxWindowGTK *win, wxCoord& x, wxCoord& y)
         else
         {
             if ((child->m_wxwindow == NULL) &&
+                win->IsClientAreaChild(child) &&
                 (child->m_x <= xx) &&
                 (child->m_y <= yy) &&
                 (child->m_x+child->m_width  >= xx) &&
@@ -1266,6 +1279,8 @@ gtk_window_button_press_callback( GtkWidget *widget,
                                   GdkEventButton *gdk_event,
                                   wxWindowGTK *win )
 {
+    wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
+
     wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
 
     g_lastButtonNumber = gdk_event->button;
@@ -1463,6 +1478,8 @@ gtk_window_button_release_callback( GtkWidget *WXUNUSED(widget),
                                     GdkEventButton *gdk_event,
                                     wxWindowGTK *win )
 {
+    wxPROCESS_EVENT_ONCE(GdkEventButton, gdk_event);
+
     wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
 
     g_lastButtonNumber = 0;
@@ -1526,6 +1543,8 @@ gtk_window_motion_notify_callback( GtkWidget * WXUNUSED(widget),
                                    GdkEventMotion *gdk_event,
                                    wxWindowGTK *win )
 {
+    wxPROCESS_EVENT_ONCE(GdkEventMotion, gdk_event);
+
     wxCOMMON_CALLBACK_PROLOGUE(gdk_event, win);
 
     if (gdk_event->is_hint)
@@ -1622,22 +1641,43 @@ window_scroll_event_hscrollbar(GtkWidget*, GdkEventScroll* gdk_event, wxWindow*
 static gboolean
 window_scroll_event(GtkWidget*, GdkEventScroll* gdk_event, wxWindow* win)
 {
-    if (gdk_event->direction != GDK_SCROLL_UP &&
-        gdk_event->direction != GDK_SCROLL_DOWN)
-    {
-        return false;
-    }
-
     wxMouseEvent event(wxEVT_MOUSEWHEEL);
     InitMouseEvent(win, event, gdk_event);
 
     // FIXME: Get these values from GTK or GDK
     event.m_linesPerAction = 3;
     event.m_wheelDelta = 120;
-    if (gdk_event->direction == GDK_SCROLL_UP)
-        event.m_wheelRotation = 120;
-    else
-        event.m_wheelRotation = -120;
+
+    // Determine the scroll direction.
+    switch (gdk_event->direction)
+    {
+        case GDK_SCROLL_UP:
+        case GDK_SCROLL_RIGHT:
+            event.m_wheelRotation = 120;
+            break;
+
+        case GDK_SCROLL_DOWN:
+        case GDK_SCROLL_LEFT:
+            event.m_wheelRotation = -120;
+            break;
+
+        default:
+            return false;  // Unknown/unhandled direction
+    }
+
+    // And the scroll axis.
+    switch (gdk_event->direction)
+    {
+        case GDK_SCROLL_UP:
+        case GDK_SCROLL_DOWN:
+            event.m_wheelAxis = wxMOUSE_WHEEL_VERTICAL;
+            break;
+
+        case GDK_SCROLL_LEFT:
+        case GDK_SCROLL_RIGHT:
+            event.m_wheelAxis = wxMOUSE_WHEEL_HORIZONTAL;
+            break;
+    }
 
     if (win->GTKProcessEvent(event))
       return TRUE;
@@ -1979,6 +2019,25 @@ void wxWindowGTK::GTKHandleRealized()
         );
     }
 
+    // Use composited window if background is transparent, if supported.
+    if (m_backgroundStyle == wxBG_STYLE_TRANSPARENT)
+    {
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+        if (IsTransparentBackgroundSupported())
+        {
+            GdkWindow* const window = GTKGetDrawingWindow();
+            if (window)
+                gdk_window_set_composited(window, true);
+        }
+        else
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT
+        {
+            // We revert to erase mode if transparency is not supported
+            m_backgroundStyle = wxBG_STYLE_ERASE;
+        }
+    }
+
+
     // We cannot set colours and fonts before the widget
     // been realized, so we do this directly after realization
     // or otherwise in idle time
@@ -2309,8 +2368,11 @@ bool wxWindowGTK::PreCreation( wxWindowGTK *parent, const wxPoint &pos,  const w
     m_width = WidthDefault(size.x) ;
     m_height = HeightDefault(size.y);
 
-    m_x = (int)pos.x;
-    m_y = (int)pos.y;
+    if (pos != wxDefaultPosition)
+    {
+        m_x = pos.x;
+        m_y = pos.y;
+    }
 
     return true;
 }
@@ -2319,6 +2381,21 @@ void wxWindowGTK::PostCreation()
 {
     wxASSERT_MSG( (m_widget != NULL), wxT("invalid window") );
 
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+    // Set RGBA visual as soon as possible to minimize the possibility that
+    // somebody uses the wrong one.
+    if ( m_backgroundStyle == wxBG_STYLE_TRANSPARENT &&
+            IsTransparentBackgroundSupported() )
+    {
+        GdkScreen *screen = gtk_widget_get_screen (m_widget);
+
+        GdkColormap *rgba_colormap = gdk_screen_get_rgba_colormap (screen);
+
+        if (rgba_colormap)
+            gtk_widget_set_colormap(m_widget, rgba_colormap);
+    }
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT
+
     if (m_wxwindow)
     {
         if (!m_noExpose)
@@ -2380,18 +2457,6 @@ void wxWindowGTK::PostCreation()
 
     ConnectWidget( connect_widget );
 
-    // connect handler to prevent events from propagating up parent chain
-    g_signal_connect_after(m_widget,
-        "key_press_event", G_CALLBACK(key_and_mouse_event_after), this);
-    g_signal_connect_after(m_widget,
-        "key_release_event", G_CALLBACK(key_and_mouse_event_after), this);
-    g_signal_connect_after(m_widget,
-        "button_press_event", G_CALLBACK(key_and_mouse_event_after), this);
-    g_signal_connect_after(m_widget,
-        "button_release_event", G_CALLBACK(key_and_mouse_event_after), this);
-    g_signal_connect_after(m_widget,
-        "motion_notify_event", G_CALLBACK(key_and_mouse_event_after), this);
-
     // We cannot set colours, fonts and cursors before the widget has been
     // realized, so we do this directly after realization -- unless the widget
     // was in fact realized already.
@@ -2653,8 +2718,6 @@ void wxWindowGTK::OnInternalIdle()
 
 void wxWindowGTK::DoGetSize( int *width, int *height ) const
 {
-    wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
-
     if (width) (*width) = m_width;
     if (height) (*height) = m_height;
 }
@@ -2749,39 +2812,17 @@ wxSize wxWindowGTK::DoGetBorderSize() const
 
 void wxWindowGTK::DoGetPosition( int *x, int *y ) const
 {
-    wxCHECK_RET( (m_widget != NULL), wxT("invalid window") );
-
     int dx = 0;
     int dy = 0;
-    if (!IsTopLevel() && m_parent && m_parent->m_wxwindow)
+    GtkWidget* parent = NULL;
+    if (m_widget)
+        parent = gtk_widget_get_parent(m_widget);
+    if (WX_IS_PIZZA(parent))
     {
-        wxPizza* pizza = WX_PIZZA(m_parent->m_wxwindow);
+        wxPizza* pizza = WX_PIZZA(parent);
         dx = pizza->m_scroll_x;
         dy = pizza->m_scroll_y;
     }
-
-    if (m_x == -1 && m_y == -1)
-    {
-        GdkWindow *source = NULL;
-        if (m_wxwindow)
-            source = gtk_widget_get_window(m_wxwindow);
-        else
-            source = gtk_widget_get_window(m_widget);
-
-        if (source)
-        {
-            int org_x = 0;
-            int org_y = 0;
-            gdk_window_get_origin( source, &org_x, &org_y );
-
-            if (m_parent)
-                m_parent->ScreenToClient(&org_x, &org_y);
-
-            const_cast<wxWindowGTK*>(this)->m_x = org_x;
-            const_cast<wxWindowGTK*>(this)->m_y = org_y;
-        }
-    }
-
     if (x) (*x) = m_x - dx;
     if (y) (*y) = m_y - dy;
 }
@@ -3540,11 +3581,13 @@ void wxWindowGTK::WarpPointer( int x, int y )
     GdkDeviceManager* manager = gdk_display_get_device_manager(display);
     gdk_device_warp(gdk_device_manager_get_client_pointer(manager), screen, x, y);
 #else
+#ifdef GDK_WINDOWING_X11
     XWarpPointer(GDK_DISPLAY_XDISPLAY(display),
         None,
         GDK_WINDOW_XID(gdk_screen_get_root_window(screen)),
         0, 0, 0, 0, x, y);
 #endif
+#endif
 }
 
 wxWindowGTK::ScrollDir wxWindowGTK::ScrollDirFromRange(GtkRange *range) const
@@ -3660,7 +3703,9 @@ void wxWindowGTK::GtkSendPaintEvents()
         m_updateRegion.Clear();
         return;
     }
-
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+    cairo_t* cr = NULL;
+#endif
     // Clip to paint region in wxClientDC
     m_clipPaintRegion = true;
 
@@ -3692,6 +3737,27 @@ void wxWindowGTK::GtkSendPaintEvents()
 
     switch ( GetBackgroundStyle() )
     {
+        case wxBG_STYLE_TRANSPARENT:
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+            if (IsTransparentBackgroundSupported())
+            {
+                // Set a transparent background, so that overlaying in parent
+                // might indeed let see through where this child did not
+                // explicitly paint.
+                // NB: it works also for top level windows (but this is the
+                // windows manager which then does the compositing job)
+                cr = gdk_cairo_create(m_wxwindow->window);
+                gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
+                cairo_clip(cr);
+
+                cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
+                cairo_paint(cr);
+                cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+                cairo_surface_flush(cairo_get_target(cr));
+            }
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT
+            break;
+
         case wxBG_STYLE_ERASE:
             {
                 wxWindowDC dc( (wxWindow*)this );
@@ -3768,6 +3834,39 @@ void wxWindowGTK::GtkSendPaintEvents()
     paint_event.SetEventObject( this );
     HandleWindowEvent( paint_event );
 
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+    if (IsTransparentBackgroundSupported())
+    { // now composite children which need it
+        // Overlay all our composite children on top of the painted area
+        wxWindowList::compatibility_iterator node;
+        for ( node = m_children.GetFirst(); node ; node = node->GetNext() )
+        {
+            wxWindow *compositeChild = node->GetData();
+            if (compositeChild->GetBackgroundStyle() == wxBG_STYLE_TRANSPARENT)
+            {
+                if (cr == NULL)
+                {
+                    cr = gdk_cairo_create(m_wxwindow->window);
+                    gdk_cairo_region(cr, m_nativeUpdateRegion.GetRegion());
+                    cairo_clip(cr);
+                }
+
+                GtkWidget *child = compositeChild->m_wxwindow;
+                GtkAllocation alloc;
+                gtk_widget_get_allocation(child, &alloc);
+
+                // The source data is the (composited) child
+                gdk_cairo_set_source_window(
+                    cr, gtk_widget_get_window(child), alloc.x, alloc.y);
+
+                cairo_paint(cr);
+            }
+        }
+        if (cr)
+            cairo_destroy(cr);
+    }
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT
+
     m_clipPaintRegion = false;
 
     m_updateRegion.Clear();
@@ -3971,30 +4070,29 @@ void wxWindowGTK::DoApplyWidgetStyle(GtkRcStyle *style)
 
 bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
 {
-    wxWindowBase::SetBackgroundStyle(style);
+    if (!wxWindowBase::SetBackgroundStyle(style))
+        return false;
 
-    if ( style == wxBG_STYLE_PAINT )
+    GdkWindow *window;
+    if ( m_wxwindow )
     {
-        GdkWindow *window;
-        if ( m_wxwindow )
-        {
-            window = GTKGetDrawingWindow();
-        }
-        else
-        {
-            GtkWidget * const w = GetConnectWidget();
-            window = w ? gtk_widget_get_window(w) : NULL;
-        }
+        window = GTKGetDrawingWindow();
+    }
+    else
+    {
+        GtkWidget * const w = GetConnectWidget();
+        window = w ? gtk_widget_get_window(w) : NULL;
+    }
 
+    bool wantNoBackPixmap = style == wxBG_STYLE_PAINT || style == wxBG_STYLE_TRANSPARENT;
+
+    if ( wantNoBackPixmap )
+    {
         if (window)
         {
             // Make sure GDK/X11 doesn't refresh the window
             // automatically.
-            gdk_window_set_back_pixmap( window, None, False );
-#ifdef __X__
-            Display* display = GDK_WINDOW_DISPLAY(window);
-            XFlush(display);
-#endif
+            gdk_window_set_back_pixmap( window, NULL, FALSE );
             m_needsStyleChange = false;
         }
         else // window not realized yet
@@ -4015,6 +4113,49 @@ bool wxWindowGTK::SetBackgroundStyle(wxBackgroundStyle style)
     return true;
 }
 
+bool wxWindowGTK::IsTransparentBackgroundSupported(wxString* reason) const
+{
+#if wxGTK_HAS_COMPOSITING_SUPPORT
+    if (gtk_check_version(wxGTK_VERSION_REQUIRED_FOR_COMPOSITING) != NULL)
+    {
+        if (reason)
+        {
+            *reason = _("GTK+ installed on this machine is too old to "
+                        "support screen compositing, please install "
+                        "GTK+ 2.12 or later.");
+        }
+
+        return false;
+    }
+
+    // NB: We don't check here if the particular kind of widget supports
+    // transparency, we check only if it would be possible for a generic window
+
+    wxCHECK_MSG ( m_widget, false, "Window must be created first" );
+
+    if (!gdk_screen_is_composited(gtk_widget_get_screen(m_widget)))
+    {
+        if (reason)
+        {
+            *reason = _("Compositing not supported by this system, "
+                        "please enable it in your Window Manager.");
+        }
+
+        return false;
+    }
+
+    return true;
+#else
+    if (reason)
+    {
+        *reason = _("This program was compiled with a too old version of GTK+, "
+                    "please rebuild with GTK+ 2.12 or newer.");
+    }
+#endif // wxGTK_HAS_COMPOSITING_SUPPORT/!wxGTK_HAS_COMPOSITING_SUPPORT
+
+    return false;
+}
+
 // ----------------------------------------------------------------------------
 // Pop-up menu stuff
 // ----------------------------------------------------------------------------