[OS/2] Bug 371505: finally fix repaint issues with Thebes
authormozilla@weilbacher.org
Mon, 22 Oct 2007 10:59:24 -0700
changeset 7090 b0a871695a9b2689778393c4064410d5caf665e6
parent 7089 66cb6d813eb05fbe5b75bfe280c5555b429d9e8f
child 7091 174217bf7334c09c70c8a29e4f877683d059d5c4
push idunknown
push userunknown
push dateunknown
bugs371505
milestone1.9a9pre
[OS/2] Bug 371505: finally fix repaint issues with Thebes
gfx/thebes/public/gfxOS2Surface.h
gfx/thebes/src/gfxOS2Surface.cpp
widget/src/os2/nsWindow.cpp
--- a/gfx/thebes/public/gfxOS2Surface.h
+++ b/gfx/thebes/public/gfxOS2Surface.h
@@ -42,30 +42,37 @@
 
 #define INCL_GPIBITMAPS
 #include <os2.h>
 #include <cairo-os2.h>
 
 class THEBES_API gfxOS2Surface : public gfxASurface {
 
 public:
-    // constructor to create a cairo surface using an existing PS
-    gfxOS2Surface(HPS aPS, const gfxIntSize& aSize);
     // constructor used to create a memory surface of given size
     gfxOS2Surface(const gfxIntSize& aSize,
                   gfxASurface::gfxImageFormat aImageFormat);
     // constructor for surface connected to an onscreen window
     gfxOS2Surface(HWND aWnd);
     virtual ~gfxOS2Surface();
 
+    // Special functions that only make sense for the OS/2 port of cairo:
+
+    // Update the cairo surface.
+    // While gfxOS2Surface keeps track of the presentation handle itself,
+    // use the one from WinBeginPaint() here.
+    void Refresh(RECTL *aRect, HPS aPS);
+
+    // Reset the cairo surface to the given size.
+    int Resize(const gfxIntSize& aSize);
+
     HPS GetPS() { return mPS; }
     gfxIntSize GetSize() { return mSize; }
 
 private:
-    PRBool mOwnsPS;
     PRBool mHasWnd; // indicates if created through the HWND constructor
     HDC mDC; // memory device context
     HPS mPS; // presentation space connected to window or memory device
     HBITMAP mBitmap; // bitmap for initialization of memory surface
     gfxIntSize mSize; // current size of the surface
 };
 
 #endif /* GFX_OS2_SURFACE_H */
--- a/gfx/thebes/src/gfxOS2Surface.cpp
+++ b/gfx/thebes/src/gfxOS2Surface.cpp
@@ -38,38 +38,19 @@
 #include "gfxOS2Surface.h"
 
 #include <stdio.h>
 
 /**********************************************************************
  * class gfxOS2Surface
  **********************************************************************/
 
-gfxOS2Surface::gfxOS2Surface(HPS aPS, const gfxIntSize& aSize)
-    : mOwnsPS(PR_FALSE), mHasWnd(PR_FALSE), mDC(nsnull), mPS(aPS), mBitmap(nsnull), mSize(aSize)
-{
-#ifdef DEBUG_thebes_2
-    printf("gfxOS2Surface[%#x]::gfxOS2Surface(HPS=%#x, Size=%dx%d)\n", (unsigned int)this,
-           (unsigned int)mPS, aSize.width, aSize.height);
-#endif
-
-    // create the cairo surface on the passed PS
-    cairo_surface_t *surf = cairo_os2_surface_create(mPS, mSize.width, mSize.height);
-#ifdef DEBUG_thebes_2
-    printf("  type(%#x)=%d (ID=%#x, h/w=%d/%d)\n", (unsigned int)surf,
-           cairo_surface_get_type(surf), (unsigned int)mPS, mSize.width, mSize.height);
-#endif
-    // XXX for now uncomment the mark_dirty function, see bug 371505
-    //cairo_surface_mark_dirty(surf);
-    Init(surf);
-}
-
 gfxOS2Surface::gfxOS2Surface(const gfxIntSize& aSize,
                              gfxASurface::gfxImageFormat aImageFormat)
-    : mOwnsPS(PR_TRUE), mHasWnd(PR_FALSE), mSize(aSize)
+    : mHasWnd(PR_FALSE), mSize(aSize)
 {
 #ifdef DEBUG_thebes_2
     printf("gfxOS2Surface[%#x]::gfxOS2Surface(Size=%dx%d, %d)\n", (unsigned int)this,
            aSize.width, aSize.height, aImageFormat);
 #endif
     // in this case we don't have a window, so we create a memory presentation
     // space to construct the cairo surface on
 
@@ -100,23 +81,28 @@ gfxOS2Surface::gfxOS2Surface(const gfxIn
     GpiSetBitmap(mPS, mBitmap);
 
     // now we can finally create the cairo surface on the in-memory PS
     cairo_surface_t *surf = cairo_os2_surface_create(mPS, mSize.width, mSize.height);
 #ifdef DEBUG_thebes_2
     printf("  type(%#x)=%d (ID=%#x, h/w=%d/%d)\n", (unsigned int)surf,
            cairo_surface_get_type(surf), (unsigned int)mPS, mSize.width, mSize.height);
 #endif
-    // XXX for now uncomment the mark_dirty function, see bug 371505
-    //cairo_surface_mark_dirty(surf);
+    // Normally, OS/2 cairo surfaces have to be forced to redraw completely
+    // by calling cairo_surface_mark_dirty(surf), but Mozilla paints them in
+    // full, so that is not necessary here.
+
+    // manual refresh is done from nsWindow::OnPaint
+    cairo_os2_surface_set_manual_window_refresh(surf, 1);
+
     Init(surf);
 }
 
 gfxOS2Surface::gfxOS2Surface(HWND aWnd)
-    : mOwnsPS(PR_TRUE), mHasWnd(PR_TRUE), mDC(nsnull), mBitmap(nsnull)
+    : mHasWnd(PR_TRUE), mDC(nsnull), mBitmap(nsnull)
 {
 #ifdef DEBUG_thebes_2
     printf("gfxOS2Surface[%#x]::gfxOS2Surface(HWND=%#x)\n", (unsigned int)this,
            (unsigned int)aWnd);
 #endif
 
     mPS = WinGetPS(aWnd);
 
@@ -126,42 +112,69 @@ gfxOS2Surface::gfxOS2Surface(HWND aWnd)
     mSize.height = rectl.yTop - rectl.yBottom;
     if (mSize.width == 0) mSize.width = 1;   // fake a minimal surface area to let
     if (mSize.height == 0) mSize.height = 1; // cairo_os2_surface_create() return something
     cairo_surface_t *surf = cairo_os2_surface_create(mPS, mSize.width, mSize.height);
 #ifdef DEBUG_thebes_2
     printf("  type(%#x)=%d (ID=%#x, h/w=%d/%d)\n", (unsigned int)surf,
            cairo_surface_get_type(surf), (unsigned int)mPS, mSize.width, mSize.height);
 #endif
+    // Normally, OS/2 cairo surfaces have to be forced to redraw completely
+    // by calling cairo_surface_mark_dirty(surf), but Mozilla paints them in
+    // full, so that is not necessary here.
+
     // record the window handle in the cairo surface, so that refresh works
     cairo_os2_surface_set_hwnd(surf, aWnd);
-    // XXX for now uncomment the mark_dirty function, see bug 371505
-    //cairo_surface_mark_dirty(surf);
+    // manual refresh is done from nsWindow::OnPaint
+    cairo_os2_surface_set_manual_window_refresh(surf, 1);
+
     Init(surf);
 }
 
 gfxOS2Surface::~gfxOS2Surface()
 {
 #ifdef DEBUG_thebes_2
     printf("gfxOS2Surface[%#x]::~gfxOS2Surface()\n", (unsigned int)this);
 #endif
 
     // Surfaces connected to a window were created using WinGetPS so we should
     // release it again with WinReleasePS. Memory surfaces on the other
     // hand were created on memory device contexts with the GPI functions, so
     // use those to clean up stuff.
     if (mHasWnd) {
-        if (mOwnsPS && mPS) {
+        if (mPS) {
             WinReleasePS(mPS);
         }
     } else {
         if (mBitmap) {
             GpiSetBitmap(mPS, NULL);
             GpiDeleteBitmap(mBitmap);
         }
-        if (mOwnsPS && mPS) {
+        if (mPS) {
             GpiDestroyPS(mPS);
         }
         if (mDC) {
             DevCloseDC(mDC);
         }
     }
 }
+
+void gfxOS2Surface::Refresh(RECTL *aRect, HPS aPS)
+{
+#ifdef DEBUG_thebes_2
+    printf("gfxOS2Surface[%#x]::Refresh(x=%ld,%ld/y=%ld,%ld, HPS=%#x), mPS=%#x\n",
+           (unsigned int)this,
+           aRect->xLeft, aRect->xRight, aRect->yBottom, aRect->yTop,
+           (unsigned int)aPS, (unsigned int)mPS);
+#endif
+    cairo_os2_surface_refresh_window(CairoSurface(), aPS, aRect);
+}
+
+int gfxOS2Surface::Resize(const gfxIntSize& aSize)
+{
+#ifdef DEBUG_thebes_2
+    printf("gfxOS2Surface[%#x]::Resize(%dx%d)\n", (unsigned int)this,
+           aSize.width, aSize.height);
+#endif
+    mSize = aSize; // record the new size
+    // hardcode mutex timeout to 50ms for now
+    return cairo_os2_surface_set_size(CairoSurface(), mSize.width, mSize.height, 50);
+}
--- a/widget/src/os2/nsWindow.cpp
+++ b/widget/src/os2/nsWindow.cpp
@@ -1120,19 +1120,16 @@ NS_METHOD nsWindow::Destroy()
     // the rollup widget, rollup and turn off capture.
     if (this == gRollupWidget) {
       if (gRollupListener) {
         gRollupListener->Rollup();
       }
       CaptureRollupEvents(nsnull, PR_FALSE, PR_TRUE);
     }
 
-    // Destroy thebes surface now, XXX do we need this at all??
-    mThebesSurface = nsnull;
-
     if (mWnd) {
       HWND hwndBeingDestroyed = mFrameWnd ? mFrameWnd : mWnd;
       DEBUGFOCUS(Destroy);
       if (hwndBeingDestroyed == WinQueryFocus(HWND_DESKTOP)) {
         WinSetFocus(HWND_DESKTOP, WinQueryWindow(hwndBeingDestroyed, QW_PARENT));
       }
       WinDestroyWindow(hwndBeingDestroyed);
     }
@@ -3160,52 +3157,53 @@ PRBool nsWindow::OnPaint()
         event.rect = &rect;
         event.region = nsnull;
 
 #ifdef NS_DEBUG
         debug_DumpPaintEvent(stdout, this, &event, nsCAutoString("noname"),
                              (PRInt32)mWnd);
 #endif // NS_DEBUG
 
-        // Thebes code version, adapted from windows/nsWindow.cpp
-        // XXX as a preliminary solution for repaint problems of cairo-os2
-        //     builds, repaint the whole window for each paint event
-        //     see Bug 371505
-        SWP swp;
-        WinQueryWindowPos(mWnd, &swp);
-        nsRefPtr<gfxASurface> targetSurface =
-          new gfxOS2Surface(hPS, gfxIntSize(swp.cx, swp.cy));
-        //new gfxOS2Surface(hPS, gfxIntSize(rect.width, rect.height));
-        nsRefPtr<gfxContext> thebesContext = new gfxContext(targetSurface);
+        nsRefPtr<gfxContext> thebesContext = new gfxContext(mThebesSurface);
 
         nsCOMPtr<nsIRenderingContext> context;
         nsresult rv = mContext->CreateRenderingContextInstance(*getter_AddRefs(context));
         if (NS_FAILED(rv)) {
           NS_WARNING("CreateRenderingContextInstance failed");
           return PR_FALSE;
         }
 
         rv = context->Init(mContext, thebesContext);
         if (NS_FAILED(rv)) {
           NS_WARNING("context::Init failed");
           return PR_FALSE;
         }
 
         event.renderingContext = context;
-        rc = DispatchWindowEvent(&event, eventStatus);
+        // try to dispatch a few times, 10 should be more than enough, in tests
+        // we get something at the second try at the latest
+        for (int i = 0; i < 10; i++) {
+          rc = DispatchWindowEvent(&event, eventStatus);
+          if (rc) {
+            // this was handled, so we can stop trying
+            break;
+          }
+        }
         event.renderingContext = nsnull;
 
         if (rc) {
           // Only update if DispatchWindowEvent returned TRUE; otherwise, nothing handled
           // this, and we'll just end up painting with black.
           thebesContext->PopGroupToSource();
           thebesContext->SetOperator(gfxContext::OPERATOR_SOURCE);
           thebesContext->Paint();
         }
+        NS_RELEASE(event.widget);
       } // if (mEventCallback)
+      mThebesSurface->Refresh(&rcl, hPS);
     } // if (!WinIsRectEmpty(0, &rcl))
 
     WinEndPaint(hPS);
     if (hpsDrag) {
       ReleaseIfDragHPS(hpsDrag);
     }
   } // if (mContext && (mEventCallback || mEventListener))
 
@@ -3238,16 +3236,26 @@ PRBool nsWindow::OnPaint()
 //
 // Send a resize message to the listener
 //
 //-------------------------------------------------------------------------
 PRBool nsWindow::OnResize(PRInt32 aX, PRInt32 aY)
 {
    mBounds.width = aX;
    mBounds.height = aY;
+
+   // resize the thebes surface to the new size
+   if (!mThebesSurface) {
+     // So we need to create a thebes surface for this window.
+     // (This is necessary for the first resize of a window.)
+     mThebesSurface = new gfxOS2Surface(mWnd);
+   }
+
+   mThebesSurface->Resize(gfxIntSize(aX, aY));
+
    return DispatchResizeEvent( aX, aY);
 }
 
 PRBool nsWindow::DispatchResizeEvent( PRInt32 aX, PRInt32 aY)
 {
    PRBool result;
    // call the event callback 
    nsSizeEvent event(PR_TRUE, NS_SIZE, this);