[qt] Use Qt for double buffering; let Qt handle events that Gecko doesn't want
authorVladimir Vukicevic <vladimir@pobox.com>
Sun, 20 Apr 2008 01:37:07 -0700
changeset 16888 2d2c8992759b2d29bda259f6e5abbec929597500
parent 16887 f41143848d2b43bb3d61d70f6558af6c7680f95c
child 16889 d1e6a4e26aaf72bfb970ff001dc46e2992825587
push idunknown
push userunknown
push dateunknown
milestone2.0a1pre
[qt] Use Qt for double buffering; let Qt handle events that Gecko doesn't want In particular, the Update event needs to be passed to QWidget::event so that a Paint event is generated and we can actually do the painting.
widget/src/qt/mozqwidget.cpp
widget/src/qt/nsWindow.cpp
widget/src/qt/nsWindow.h
--- a/widget/src/qt/mozqwidget.cpp
+++ b/widget/src/qt/mozqwidget.cpp
@@ -8,16 +8,17 @@ MozQWidget::MozQWidget(nsWindow *receive
       mReceiver(receiver)
 {
     setAttribute(Qt::WA_QuitOnClose, false);
 }
 
 bool MozQWidget::event(QEvent *e)
 {
     nsEventStatus status = nsEventStatus_eIgnore;
+    bool handled = true;
 
     if (!mReceiver)
         return false;
 
     switch(e->type()) {
 /*
     case QEvent::Accessibility:
     {
@@ -171,32 +172,46 @@ bool MozQWidget::event(QEvent *e)
         status = mReceiver->OnDragLeaveEvent(dev);
     }
     break;
     case QEvent::Drop:
     {
         QDropEvent *dev = (QDropEvent*)(e);
         status = mReceiver->OnDragDropEvent(dev);
     }
+    case QEvent::GrabMouse:
+        fprintf (stderr, "[%p] %p GrabMouse\n", this, mReceiver);
+        break;
+    case QEvent::UngrabMouse:
+        fprintf (stderr, "[%p] %p GrabMouse\n", this, mReceiver);
+        break;
     break;
     default:
+        handled = false;
         break;
     }
 
+    if (handled)
+        return true;
+
+    return QWidget::event(e);
+
+#if 0
     // If we were going to ignore this event, pass it up to the parent
     // and return its value
     if (status == nsEventStatus_eIgnore)
         return QWidget::event(e);
 
     // Otherwise, we know we already consumed it -- we just need to know
     // whether we want default handling or not
     if (status == nsEventStatus_eConsumeDoDefault)
         QWidget::event(e);
 
     return true;
+#endif
 }
 
 bool
 MozQWidget::SetCursor(nsCursor aCursor)
 {
     Qt::CursorShape cursor = Qt::ArrowCursor;
     switch(aCursor) {
     case eCursor_standard:
--- a/widget/src/qt/nsWindow.cpp
+++ b/widget/src/qt/nsWindow.cpp
@@ -170,18 +170,16 @@ keyEventToContextMenuEvent(const nsKeyEv
     aCMEvent->clickCount = 0;
     aCMEvent->acceptActivation = PR_FALSE;
 }
 
 nsWindow::nsWindow()
 {
     mDrawingarea         = nsnull;
     mIsVisible           = PR_FALSE;
-    mRetryPointerGrab    = PR_FALSE;
-    mRetryKeyboardGrab   = PR_FALSE;
     mActivatePending     = PR_FALSE;
     mWindowType          = eWindowType_child;
     mSizeState           = nsSizeMode_Normal;
     mPluginType          = PluginType_NONE;
     mQCursor             = Qt::ArrowCursor;
 
     if (!gGlobalsInitialized) {
         gGlobalsInitialized = PR_TRUE;
@@ -685,17 +683,17 @@ nsWindow::Validate()
 NS_IMETHODIMP
 nsWindow::Invalidate(PRBool aIsSynchronous)
 {
     LOGDRAW(("Invalidate (all) [%p]: \n", (void *)this));
 
     if (!mDrawingarea)
         return NS_OK;
 
-    if (aIsSynchronous)
+    if (aIsSynchronous && !mDrawingarea->paintingActive())
         mDrawingarea->repaint();
     else
         mDrawingarea->update();
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -729,17 +727,17 @@ nsWindow::InvalidateRegion(const nsIRegi
 
     if (region && mDrawingarea) {
         QRect rect = region->boundingRect();
 
 //        LOGDRAW(("Invalidate (region) [%p]: %d %d %d %d (sync: %d)\n",
 //                 (void *)this,
 //                 rect.x, rect.y, rect.width, rect.height, aIsSynchronous));
 
-        if (aIsSynchronous)
+        if (aIsSynchronous && !mDrawingarea->paintingActive())
             mDrawingarea->repaint(*region);
         else
             mDrawingarea->update(*region);
     }
     else {
         qDebug("FIXME:>>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__);
         LOGDRAW(("Invalidate (region) [%p] with empty region\n",
                  (void *)this));
@@ -918,36 +916,32 @@ nsWindow::WidgetToScreen(const nsRect& a
     QPoint origin(aOldRect.x, aOldRect.y);
     origin = mDrawingarea->mapToGlobal(origin);
 
     aNewRect.x = origin.x();
     aNewRect.y = origin.y();
     aNewRect.width = aOldRect.width;
     aNewRect.height = aOldRect.height;
 
-    LOG(("WidgetToScreen %d %d -> %d %d\n", aOldRect.x, aOldRect.y, aNewRect.x, aNewRect.y));
-
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::ScreenToWidget(const nsRect& aOldRect, nsRect& aNewRect)
 {
     NS_ENSURE_TRUE(mDrawingarea, NS_OK);
 
     QPoint origin(aOldRect.x, aOldRect.y);
     origin = mDrawingarea->mapFromGlobal(origin);
 
     aNewRect.x = origin.x();
     aNewRect.y = origin.y();
     aNewRect.width = aOldRect.width;
     aNewRect.height = aOldRect.height;
 
-    LOG(("ScreenToWidget %d %d -> %d %d\n", aOldRect.x, aOldRect.y, aNewRect.x, aNewRect.y));
-
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::BeginResizingChildren(void)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
@@ -985,24 +979,20 @@ nsWindow::PreCreateWidget(nsWidgetInitDa
 NS_IMETHODIMP
 nsWindow::CaptureMouse(PRBool aCapture)
 {
     LOG(("CaptureMouse %p\n", (void *)this));
 
     if (!mDrawingarea)
         return NS_OK;
 
-/*
-    if (aCapture) {
-        GrabPointer();
-    }
-    else {
-        ReleaseGrabs();
-    }
-*/
+    if (aCapture)
+        mDrawingarea->grabMouse();
+    else
+        mDrawingarea->releaseMouse();
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::CaptureRollupEvents(nsIRollupListener *aListener,
                               PRBool             aDoCapture,
                               PRBool             aConsumeRollupEvent)
@@ -1073,79 +1063,48 @@ nsWindow::OnExposeEvent(QPaintEvent *aEv
     nsCOMPtr<nsIRegion> updateRegion = do_CreateInstance(kRegionCID);
     if (!updateRegion)
         return nsEventStatus_eIgnore;
 
     updateRegion->Init();
 
     QVector<QRect>  rects = aEvent->region().rects();
 
-    LOGDRAW(("sending expose event [%p] %p 0x%lx (rects follow):\n",
+    LOGDRAW(("[%p] sending expose event %p 0x%lx (rects follow):\n",
              (void *)this, (void *)aEvent, 0));
 
     for (int i = 0; i < rects.size(); ++i) {
        QRect r = rects.at(i);
        updateRegion->Union(r.x(), r.y(), r.width(), r.height());
        LOGDRAW(("\t%d %d %d %d\n", r.x(), r.y(), r.width(), r.height()));
     }
 
 #ifndef QT_XLIB_SURFACE
-    QPainter painter(mDrawingarea);
+    QPainter painter;
+
+    if (!painter.begin(mDrawingarea)) {
+        fprintf (stderr, "*********** Failed to begin painting!\n");
+        return nsEventStatus_eConsumeNoDefault;
+    }
 
     nsRefPtr<gfxQPainterSurface> targetSurface = new gfxQPainterSurface(&painter);
     nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
 
     nsCOMPtr<nsIRenderingContext> rc;
     GetDeviceContext()->CreateRenderingContextInstance(*getter_AddRefs(rc));
     if (NS_UNLIKELY(!rc))
         return nsEventStatus_eIgnore;
 
     rc->Init(GetDeviceContext(), ctx);
 
-    PRBool translucent;
-    GetHasTransparentBackground(translucent);
     nsIntRect boundsRect;
 
     updateRegion->GetBoundingBox(&boundsRect.x, &boundsRect.y,
                                  &boundsRect.width, &boundsRect.height);
 
-    // do double-buffering and clipping here
-    ctx->Save();
-    ctx->NewPath();
-    if (translucent) {
-        // Collapse update area to the bounding box. This is so we only have to
-        // call UpdateTranslucentWindowAlpha once. After we have dropped
-        // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
-        // our private interface so we can rework things to avoid this.
-        ctx->Rectangle(gfxRect(boundsRect.x, boundsRect.y,
-                               boundsRect.width, boundsRect.height));
-    } else {
-        for (int i = 0; i < rects.size(); ++i) {
-           QRect r = rects.at(i);
-           ctx->Rectangle(gfxRect(r.x(), r.y(), r.width(), r.height()));
-        }
-    }
-    ctx->Clip();
-
-    // double buffer
-    if (translucent) {
-        ctx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
-    } else if (gDoubleBuffering) {
-        ctx->PushGroup(gfxASurface::CONTENT_COLOR);
-    }
-
-#if 0
-    // NOTE: Paint flashing region would be wrong for cairo, since
-    // cairo inflates the update region, etc.  So don't paint flash
-    // for cairo.
-#ifdef DEBUG
-    if (WANT_PAINT_FLASHING && aEvent->window)
-        gdk_window_flash(aEvent->window, 1, 100, aEvent->region);
-#endif
-#endif
 #else /* QT_XLIB_SURFACE */
     nsCOMPtr<nsIRenderingContext> rc = getter_AddRefs(GetRenderingContext());
     if (NS_UNLIKELY(!rc)) {
         return FALSE;
     }
 
     PRBool translucent;
     GetHasTransparentBackground(translucent);
@@ -1153,17 +1112,17 @@ nsWindow::OnExposeEvent(QPaintEvent *aEv
     QPixmap* bufferPixmap = nsnull;
     nsRefPtr<gfxXlibSurface> bufferPixmapSurface;
 
     updateRegion->GetBoundingBox(&boundsRect.x, &boundsRect.y,
                                  &boundsRect.width, &boundsRect.height);
 
     // do double-buffering and clipping here
     nsRefPtr<gfxContext> ctx = rc->ThebesContext();
-    ctx->Save();
+
     ctx->NewPath();
     if (translucent) {
         // Collapse update area to the bounding box. This is so we only have to
         // call UpdateTranslucentWindowAlpha once. After we have dropped
         // support for non-Thebes graphics, UpdateTranslucentWindowAlpha will be
         // our private interface so we can rework things to avoid this.
         ctx->Rectangle(gfxRect(boundsRect.x, boundsRect.y,
                                boundsRect.width, boundsRect.height));
@@ -1218,27 +1177,27 @@ nsWindow::OnExposeEvent(QPaintEvent *aEv
     nsRect rect(r.x(), r.y(), r.width(), r.height());
     event.refPoint.x = aEvent->rect().x();
     event.refPoint.y = aEvent->rect().y();
     event.rect = &rect; // was null FIXME
     event.region = updateRegion;
     event.renderingContext = rc;
 
     nsEventStatus status = DispatchEvent(&event);
+    //nsEventStatus status = nsEventStatus_eConsumeNoDefault;
 
     // DispatchEvent can Destroy us (bug 378273), avoid doing any paint
     // operations below if that happened - it will lead to XError and exit().
     if (NS_UNLIKELY(mIsDestroyed))
         return status;
 
-    if (status == nsEventStatus_eIgnore) {
-        ctx->Restore();
+    if (status == nsEventStatus_eIgnore)
         return status;
-    }
-
+
+#ifdef QT_XLIB_SURFACE
     if (translucent) {
         nsRefPtr<gfxPattern> pattern = ctx->PopGroup();
         ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
         ctx->SetPattern(pattern);
         ctx->Paint();
 
         nsRefPtr<gfxImageSurface> img =
             new gfxImageSurface(gfxIntSize(boundsRect.width, boundsRect.height),
@@ -1253,28 +1212,27 @@ nsWindow::OnExposeEvent(QPaintEvent *aEv
                 imgCtx->Paint();
             }
 
             UpdateTranslucentWindowAlphaInternal(nsRect(boundsRect.x, boundsRect.y,
                                                         boundsRect.width, boundsRect.height),
                                                  img->Data(), img->Stride());
         }
     } else if (gDoubleBuffering) {
-#ifndef QT_XLIB_SURFACE
-        ctx->PopGroupToSource();
-        ctx->Paint();
-#else
         if (bufferPixmapSurface) {
             ctx->SetSource(bufferPixmapSurface);
             ctx->Paint();
         }
+    }
 #endif
-    }
-
-    ctx->Restore();
+
+    LOGDRAW(("[%p] draw done\n", this));
+
+    ctx = nsnull;
+    targetSurface = nsnull;
 
     // check the return value!
     return status;
 }
 
 nsEventStatus
 nsWindow::OnConfigureEvent(QMoveEvent *aEvent)
 {
@@ -1403,17 +1361,21 @@ nsWindow::OnMotionNotifyEvent(QMouseEven
     event.refPoint.y = nscoord(aEvent->y());
 
     event.isShift         = aEvent->modifiers() & Qt::ShiftModifier;
     event.isControl       = aEvent->modifiers() & Qt::ControlModifier;
     event.isAlt           = aEvent->modifiers() & Qt::AltModifier;
     event.isMeta          = aEvent->modifiers() & Qt::MetaModifier;
     event.clickCount      = 0;
 
-    return DispatchEvent(&event);
+    nsEventStatus status = DispatchEvent(&event);
+
+    //fprintf (stderr, "[%p] %p MotionNotify -> %d\n", this, mDrawingarea, status);
+
+    return status;
 }
 
 void
 nsWindow::InitButtonEvent(nsMouseEvent &event,
                           QMouseEvent *aEvent, int aClickCount)
 {
     event.refPoint.x = nscoord(aEvent->x());
     event.refPoint.y = nscoord(aEvent->y());
@@ -1440,27 +1402,31 @@ nsWindow::OnButtonPressEvent(QMouseEvent
         domButton = nsMouseEvent::eLeftButton;
         break;
     }
 
     nsMouseEvent event(PR_TRUE, NS_MOUSE_BUTTON_DOWN, this, nsMouseEvent::eReal);
     event.button = domButton;
     InitButtonEvent(event, aEvent, 1);
 
+    LOG(("%s [%p] button: %d\n", __PRETTY_FUNCTION__, (void*)this, domButton));
+
     nsEventStatus status = DispatchEvent(&event);
 
     // right menu click on linux should also pop up a context menu
     if (domButton == nsMouseEvent::eRightButton &&
         NS_LIKELY(!mIsDestroyed)) {
         nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, this,
                                       nsMouseEvent::eReal);
         InitButtonEvent(contextMenuEvent, aEvent, 1);
         DispatchEvent(&contextMenuEvent, status);
     }
 
+    //fprintf (stderr, "[%p] %p ButtonPress -> %d\n", this, mDrawingarea, status);
+
     return status;
 }
 
 nsEventStatus
 nsWindow::OnButtonReleaseEvent(QMouseEvent *aEvent)
 {
     PRUint16 domButton;
 //    mLastButtonReleaseTime = aEvent->time;
@@ -1472,21 +1438,27 @@ nsWindow::OnButtonReleaseEvent(QMouseEve
     case Qt::RightButton:
         domButton = nsMouseEvent::eRightButton;
         break;
     default:
         domButton = nsMouseEvent::eLeftButton;
         break;
     }
 
+    LOG(("%s [%p] button: %d\n", __PRETTY_FUNCTION__, (void*)this, domButton));
+
     nsMouseEvent event(PR_TRUE, NS_MOUSE_BUTTON_UP, this, nsMouseEvent::eReal);
     event.button = domButton;
     InitButtonEvent(event, aEvent, 1);
 
-    return DispatchEvent(&event);
+    nsEventStatus status = DispatchEvent(&event);
+
+    //fprintf (stderr, "[%p] %p ButtonRelease -> %d\n", this, mDrawingarea, status);
+
+    return status;
 }
 
 nsEventStatus
 nsWindow::mouseDoubleClickEvent(QMouseEvent *e)
 {
     PRUint32      eventType;
 
     switch (e->button()) {
@@ -1954,20 +1926,18 @@ nsWindow::NativeResize(PRInt32 aWidth, P
 
     ResizeTransparencyBitmap(aWidth, aHeight);
 
     // clear our resize flag
     mNeedsResize = PR_FALSE;
 
     mDrawingarea->resize( aWidth, aHeight);
 
-    if (aRepaint) {
-        if (mDrawingarea->isVisible())
-            mDrawingarea->repaint();
-    }
+    if (aRepaint)
+        mDrawingarea->update();
 }
 
 void
 nsWindow::NativeResize(PRInt32 aX, PRInt32 aY,
                        PRInt32 aWidth, PRInt32 aHeight,
                        PRBool  aRepaint)
 {
     mNeedsResize = PR_FALSE;
@@ -1996,20 +1966,18 @@ nsWindow::NativeResize(PRInt32 aX, PRInt
 #ifdef DEBUG_WIDGETS
             qDebug("Widget with original position? (%p)", mDrawingarea);
 #endif
         }
     }
 
     mDrawingarea->setGeometry(pos.x(), pos.y(), aWidth, aHeight);
 
-    if (aRepaint) {
-        if (mDrawingarea->isVisible())
-            mDrawingarea->repaint();
-    }
+    if (aRepaint)
+        mDrawingarea->update();
 }
 
 void
 nsWindow::NativeShow (PRBool  aAction)
 {
     if (aAction) {
         // GTK wants us to set the window mask before we show the window
         // for the first time, or setting the mask later won't work.
@@ -2030,25 +1998,16 @@ nsWindow::NativeShow (PRBool  aAction)
         //XXX: apperently can be null during the printing, check whether
         //     that's true
         qDebug("nsCommon::Show : widget empty");
         return;
     }
     mDrawingarea->setShown(aAction);
 }
 
-void
-nsWindow::EnsureGrabs(void)
-{
-    if (mRetryPointerGrab)
-        GrabPointer();
-    if (mRetryKeyboardGrab)
-        GrabKeyboard();
-}
-
 NS_IMETHODIMP
 nsWindow::SetHasTransparentBackground(PRBool aTransparent)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 
 //    if (!mDrawingarea) {
         // Pass the request to the toplevel window
 //        return topWindow->SetHasTransparentBackground(aTransparent);
@@ -2242,76 +2201,16 @@ nsWindow::UpdateTranslucentWindowAlphaIn
 
     if (!mNeedsShow) {
         ApplyTransparencyBitmap();
     }
     return NS_OK;
 }
 
 void
-nsWindow::GrabPointer(void)
-{
-    LOG(("GrabPointer %d\n", mRetryPointerGrab));
-
-    mRetryPointerGrab = PR_FALSE;
-
-    // If the window isn't visible, just set the flag to retry the
-    // grab.  When this window becomes visible, the grab will be
-    // retried.
-    PRBool visibility = PR_TRUE;
-    IsVisible(visibility);
-    if (!visibility) {
-        LOG(("GrabPointer: window not visible\n"));
-        mRetryPointerGrab = PR_TRUE;
-        return;
-    }
-
-    if (!mDrawingarea)
-        return;
-
-    mDrawingarea->grabMouse();
-}
-
-void
-nsWindow::GrabKeyboard(void)
-{
-    LOG(("GrabKeyboard %d\n", mRetryKeyboardGrab));
-
-    mRetryKeyboardGrab = PR_FALSE;
-
-    // If the window isn't visible, just set the flag to retry the
-    // grab.  When this window becomes visible, the grab will be
-    // retried.
-    PRBool visibility = PR_TRUE;
-    IsVisible(visibility);
-    if (!visibility) {
-        LOG(("GrabKeyboard: window not visible\n"));
-        mRetryKeyboardGrab = PR_TRUE;
-        return;
-    }
-
-    if (!mDrawingarea)
-        return;
-
-    mDrawingarea->grabKeyboard();
-}
-
-void
-nsWindow::ReleaseGrabs(void)
-{
-    LOG(("ReleaseGrabs\n"));
-
-    mRetryPointerGrab = PR_FALSE;
-    mRetryKeyboardGrab = PR_FALSE;
-
-//    gdk_pointer_ungrab(Qt::Key_CURRENT_TIME);
-//    gdk_keyboard_ungrab(Qt::Key_CURRENT_TIME);
-}
-
-void
 nsWindow::GetToplevelWidget(QWidget **aWidget)
 {
     *aWidget = nsnull;
 
     if (mDrawingarea) {
         *aWidget = mDrawingarea;
         return;
     }
@@ -2729,22 +2628,21 @@ nsWindow::createQWidget(QWidget *parent,
         // windows, which are what gtk uses for popups.
         SetCursor(eCursor_standard);
     } else if (mIsTopLevel) {
         SetDefaultIcon();
     }
  
     mMozQWidget->setAttribute(Qt::WA_StaticContents);
     mMozQWidget->setAttribute(Qt::WA_OpaquePaintEvent); // Transparent Widget Background
+    mMozQWidget->setAttribute(Qt::WA_NoSystemBackground);
   
-    // Disable the double buffer because it will make the caret crazy
-    // For bug#153805 (Gtk2 double buffer makes carets misbehave)
-    mMozQWidget->setAttribute(Qt::WA_NoSystemBackground);
-    mMozQWidget->setAttribute(Qt::WA_PaintOnScreen);
-  
+    if (!gDoubleBuffering)
+        mMozQWidget->setAttribute(Qt::WA_PaintOnScreen);
+
     return mDrawingarea;
 }
 
 // return the gfxASurface for rendering to this widget
 gfxASurface*
 nsWindow::GetThebesSurface()
 {
     // XXXvlad always create a new thebes surface for now,
--- a/widget/src/qt/nsWindow.h
+++ b/widget/src/qt/nsWindow.h
@@ -206,21 +206,16 @@ protected:
     void               NativeResize(PRInt32 aX,
                                     PRInt32 aY,
                                     PRInt32 aWidth,
                                     PRInt32 aHeight,
                                     PRBool  aRepaint);
 
     void               NativeShow  (PRBool  aAction);
 
-    void               EnsureGrabs  (void);
-    void               GrabKeyboard (void);
-    void               ReleaseGrabs (void);
-    void               GrabPointer(void);
-
     enum PluginType {
         PluginType_NONE = 0,   /* do not have any plugin */
         PluginType_XEMBED,     /* the plugin support xembed */
         PluginType_NONXEMBED   /* the plugin does not support xembed */
     };
 
     void               SetPluginType(PluginType aPluginType);
     void               SetNonXEmbedPluginFocus(void);
@@ -248,19 +243,17 @@ private:
     void               InitButtonEvent(nsMouseEvent &aEvent, QMouseEvent *aEvent, int aClickCount = 1);
     PRBool             DispatchCommandEvent(nsIAtom* aCommand);
     QWidget           *createQWidget(QWidget *parent, nsWidgetInitData *aInitData);
 
     QWidget            *mDrawingarea;
     MozQWidget *mMozQWidget;
 
     PRUint32            mIsVisible : 1,
-                        mRetryPointerGrab : 1,
-                        mActivatePending : 1,
-                        mRetryKeyboardGrab : 1;
+                        mActivatePending : 1;
     PRInt32             mSizeState;
     PluginType          mPluginType;
 
     PRInt32             mTransparencyBitmapWidth;
     PRInt32             mTransparencyBitmapHeight;
 
     nsRefPtr<gfxASurface> mThebesSurface;