diff --git a/src/servers/app/ViewLayer.cpp b/src/servers/app/ViewLayer.cpp index 09479298a1..11e96a64f5 100644 --- a/src/servers/app/ViewLayer.cpp +++ b/src/servers/app/ViewLayer.cpp @@ -22,6 +22,8 @@ #include "ServerWindow.h" #include "WindowLayer.h" +#include "drawing_support.h" + #include #include #include @@ -809,14 +811,16 @@ ViewLayer::ScrollBy(int32 x, int32 y, BRegion* dirtyRegion) // do the blit, this will make sure // that other more complex dirty regions // are taken care of - BRegion copyRegion(stillVisibleBounds); - fWindow->CopyContents(©Region, -x, -y); + BRegion* copyRegion = fWindow->GetRegion(); + if (!copyRegion) + return; + copyRegion->Set(stillVisibleBounds); + fWindow->CopyContents(copyRegion, -x, -y); // find the dirty region as far as we are // concerned - BRegion* dirty = fWindow->GetRegion(); - if (!dirty) - return; + BRegion* dirty = copyRegion; + // reuse copyRegion and call it dirty dirty->Set(oldBounds); stillVisibleBounds.OffsetBy(-x, -y); @@ -860,17 +864,20 @@ ViewLayer::CopyBits(BRect src, BRect dst, BRegion& windowContentClipping) // do the blit, this will make sure // that other more complex dirty regions // are taken care of - BRegion copyRegion(visibleSrc); - copyRegion.IntersectWith(&ScreenClipping(&windowContentClipping)); - fWindow->CopyContents(©Region, xOffset, yOffset); + BRegion* copyRegion = fWindow->GetRegion(); + if (!copyRegion) + return; + + copyRegion->Set(visibleSrc); + copyRegion->IntersectWith(&ScreenClipping(&windowContentClipping)); + fWindow->CopyContents(copyRegion, xOffset, yOffset); // find the dirty region as far as we are concerned BRect dirtyDst(dst); ConvertToVisibleInTopView(&dirtyDst); - BRegion* dirty = fWindow->GetRegion(); - if (!dirty) - return; + BRegion* dirty = copyRegion; + // reuse copyRegion and call it "dirty" dirty->Set(dirtyDst); // exclude the part that we could copy @@ -965,9 +972,11 @@ ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, if (fViewBitmap != NULL || !fViewColor.IsTransparentMagic()) { // we can only draw within our own area - BRegion redraw(ScreenClipping(windowContentClipping)); + BRegion* redraw = fWindow->GetRegion(ScreenClipping(windowContentClipping)); + if (!redraw) + return; // add the current clipping - redraw.IntersectWith(effectiveClipping); + redraw->IntersectWith(effectiveClipping); if (fViewBitmap != NULL) { // draw view bitmap @@ -975,20 +984,79 @@ ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, BRect rect = fBitmapDestination; ConvertToScreenForDrawing(&rect); - // lock the drawing engine for as long as we need the clipping - // to be valid - if (drawingEngine->Lock()) { - drawingEngine->ConstrainClippingRegion(&redraw); + align_rect_to_pixels(&rect); - DrawState defaultDrawState; - drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, - rect, &defaultDrawState); - // NOTE: It is ok not to reset the clipping, that - // would only waste time - drawingEngine->Unlock(); + if (fBitmapOptions & B_TILE_BITMAP_Y) { + // move rect up as much as needed + while (rect.top > redraw->Frame().top) + rect.OffsetBy(0.0, -(rect.Height() + 1)); + } + if (fBitmapOptions & B_TILE_BITMAP_X) { + // move rect left as much as needed + while (rect.left > redraw->Frame().left) + rect.OffsetBy(-(rect.Width() + 1), 0.0); + } + +// XXX: locking removed because the WindowLayer keeps the engine locked +// because it keeps track of syncing right now + + // lock the drawing engine for as long as we need the clipping + // to be valid + if (rect.IsValid()/* && drawingEngine->Lock()*/) { + drawingEngine->ConstrainClippingRegion(redraw); + + DrawState defaultDrawState; + + if (fBitmapOptions & B_TILE_BITMAP) { + // tile across entire view + float start = rect.left; + while (rect.top < redraw->Frame().bottom) { + while (rect.left < redraw->Frame().right) { + drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, + rect, &defaultDrawState); + rect.OffsetBy(rect.Width() + 1, 0.0); + } + rect.OffsetBy(start - rect.left, rect.Height() + 1); + } + // nothing left to be drawn + redraw->MakeEmpty(); + + } else if (fBitmapOptions & B_TILE_BITMAP_X) { + // tile in x direction + while (rect.left < redraw->Frame().right) { + drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, + rect, &defaultDrawState); + rect.OffsetBy(rect.Width() + 1, 0.0); + } + // remove horizontal stripe from clipping + rect.left = redraw->Frame().left; + rect.right = redraw->Frame().right; + redraw->Exclude(rect); + + } else if (fBitmapOptions & B_TILE_BITMAP_Y) { + // tile in y direction + while (rect.top < redraw->Frame().bottom) { + drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, + rect, &defaultDrawState); + rect.OffsetBy(0.0, rect.Height() + 1); + } + // remove vertical stripe from clipping + rect.top = redraw->Frame().top; + rect.bottom = redraw->Frame().bottom; + redraw->Exclude(rect); + + } else { + // no tiling at all + drawingEngine->DrawBitmap(fViewBitmap, fBitmapSource, + rect, &defaultDrawState); + redraw->Exclude(rect); + } + + // NOTE: It is ok not to reset the clipping, that + // would only waste time +// drawingEngine->Unlock(); } - redraw.Exclude(rect); } if (!fViewColor.IsTransparentMagic()) { @@ -996,8 +1064,10 @@ ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, // this version of FillRegion ignores any // clipping, that's why "redraw" needs to // be correct - drawingEngine->FillRegion(redraw, fViewColor); + drawingEngine->FillRegion(*redraw, fViewColor); } + + fWindow->RecycleRegion(redraw); } fBackgroundDirty = false; @@ -1176,9 +1246,12 @@ ViewLayer::RebuildClipping(bool deep) // hand, views for which this feature is actually used will // probably not have any children, so it is not that expensive // after all - BRegion screenUserClipping(*userClipping); - fDrawState->Transform(&screenUserClipping); - fLocalClipping.IntersectWith(&screenUserClipping); + BRegion* screenUserClipping = fWindow->GetRegion(*userClipping); + if (!screenUserClipping) + return; + fDrawState->Transform(screenUserClipping); + fLocalClipping.IntersectWith(screenUserClipping); + fWindow->RecycleRegion(screenUserClipping); } fScreenClippingValid = false; @@ -1198,8 +1271,12 @@ ViewLayer::ScreenClipping(BRegion* windowContentClipping, bool force) const ConvertToVisibleInTopView(&clippedBounds); if (clippedBounds.Width() < fScreenClipping.Frame().Width() || clippedBounds.Height() < fScreenClipping.Frame().Height()) { - BRegion temp(clippedBounds); - fScreenClipping.IntersectWith(&temp); + BRegion* temp = fWindow->GetRegion(); + if (temp) { + temp->Set(clippedBounds); + fScreenClipping.IntersectWith(temp); + fWindow->RecycleRegion(temp); + } } fScreenClipping.IntersectWith(windowContentClipping); diff --git a/src/servers/app/WindowLayer.cpp b/src/servers/app/WindowLayer.cpp index 10b7eb077c..7be1ea018d 100644 --- a/src/servers/app/WindowLayer.cpp +++ b/src/servers/app/WindowLayer.cpp @@ -65,7 +65,6 @@ using std::nothrow; // its previous position though if the exposed parts are not // cleared right away. maybe there ought to be a flag in // the update session, which tells us the cause of the update -#define DELAYED_BACKGROUND_CLEARING 1 WindowLayer::WindowLayer(const BRect& frame, const char *name, @@ -81,6 +80,7 @@ WindowLayer::WindowLayer(const BRect& frame, const char *name, fVisibleContentRegion(), fVisibleContentRegionValid(false), fDirtyRegion(), + fDirtyCause(0), fBorderRegion(), fBorderRegionValid(false), @@ -609,8 +609,8 @@ WindowLayer::ProcessDirtyRegion(BRegion& region) ServerWindow()->RequestRedraw(); } - // this is executed from the desktop thread fDirtyRegion.Include(®ion); + fDirtyCause |= UPDATE_EXPOSE; } @@ -639,6 +639,7 @@ WindowLayer::RedrawDirtyRegion() // get write access, since we're holding // the read lock for the whole time. fDirtyRegion.MakeEmpty(); + fDirtyCause = 0; } @@ -665,6 +666,7 @@ WindowLayer::MarkContentDirty(BRegion& regionOnScreen) return; regionOnScreen.IntersectWith(&VisibleContentRegion()); + fDirtyCause |= UPDATE_REQUEST; _TriggerContentRedraw(regionOnScreen); } @@ -684,7 +686,7 @@ WindowLayer::InvalidateView(ViewLayer* layer, BRegion& layerRegion) //fDrawingEngine->FillRegion(layerRegion, RGBColor(0, 255, 0, 255)); //snooze(10000); - + fDirtyCause |= UPDATE_REQUEST; _TriggerContentRedraw(layerRegion); } } @@ -1621,25 +1623,32 @@ WindowLayer::_TriggerContentRedraw(BRegion& dirtyContentRegion) if (IsVisible() && dirtyContentRegion.CountRects() > 0) { // put this into the pending dirty region // to eventually trigger a client redraw + bool wasExpose = fPendingUpdateSession.IsExpose(); + BRegion* backgroundClearingRegion = &dirtyContentRegion; + _TransferToUpdateSession(&dirtyContentRegion); -#if DELAYED_BACKGROUND_CLEARING -// NOTE: currently not used, might come in handy later though -// if (!fTopLayer->IsBackgroundDirty()) -// fTopLayer->MarkBackgroundDirty(); -#else -// NOTE: turning off DELAYED_BACKGROUND_CLEARING will -// need investigation if it even still works... - if (!fContentRegionValid) - _UpdateContentRegion(); + if (fPendingUpdateSession.IsExpose()) { + if (!fContentRegionValid) + _UpdateContentRegion(); - if (fDrawingEngine->Lock()) { - fDrawingEngine->ConstrainClippingRegion(&dirtyContentRegion); - fTopLayer->Draw(fDrawingEngine, &dirtyContentRegion, - &fContentRegion, true); - fDrawingEngine->Unlock(); + if (!wasExpose) { + // there was suddenly added a dirty region + // caused by exposing content, we need to clear + // the entire background + backgroundClearingRegion = &fPendingUpdateSession.DirtyRegion(); + } + + if (fDrawingEngine->Lock()) { + fDrawingEngine->SuspendAutoSync(); + + fTopLayer->Draw(fDrawingEngine, backgroundClearingRegion, + &fContentRegion, true); + + fDrawingEngine->Sync(); + fDrawingEngine->Unlock(); + } } -#endif } } @@ -1691,6 +1700,8 @@ WindowLayer::_TransferToUpdateSession(BRegion* contentDirtyRegion) // add to pending fPendingUpdateSession.SetUsed(true); +// if (!fPendingUpdateSession.IsExpose()) + fPendingUpdateSession.AddCause(fDirtyCause); fPendingUpdateSession.Include(contentDirtyRegion); // clip pending update session from current @@ -1699,14 +1710,10 @@ WindowLayer::_TransferToUpdateSession(BRegion* contentDirtyRegion) // this could be done smarter (clip layers from pending // that have not yet been redrawn in the current update // session) -#if !DELAYED_BACKGROUND_CLEARING -// NOTE: turning off DELAYED_BACKGROUND_CLEARING will -// need investigation if it even still works... - if (fCurrentUpdateSession.IsUsed()) { + if (fCurrentUpdateSession.IsUsed() && fCurrentUpdateSession.IsExpose()) { fCurrentUpdateSession.Exclude(contentDirtyRegion); fEffectiveDrawingRegionValid = false; } -#endif if (!fUpdateRequested) { // send this to client @@ -1768,8 +1775,6 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link) dirty->IntersectWith(&VisibleContentRegion()); -//fDrawingEngine->FillRegion(dirty, RGBColor(255, 0, 0, 255)); - link.StartMessage(B_OK); // append the current window geometry to the // message, the client will need it @@ -1785,14 +1790,19 @@ WindowLayer::BeginUpdate(BPrivate::PortLink& link) link.Attach(B_NULL_TOKEN); link.Flush(); -#if DELAYED_BACKGROUND_CLEARING -// NOTE: turning off DELAYED_BACKGROUND_CLEARING will -// need investigation if it even still works... - fTopLayer->Draw(fDrawingEngine, dirty, - &fContentRegion, true); + if (!fCurrentUpdateSession.IsExpose() && fDrawingEngine->Lock()) { +//fDrawingEngine->FillRegion(dirty, RGBColor(255, 0, 0, 255)); + fDrawingEngine->SuspendAutoSync(); + + fTopLayer->Draw(fDrawingEngine, dirty, + &fContentRegion, true); + + fDrawingEngine->Sync(); + fDrawingEngine->Unlock(); + } // else the background was cleared already fRegionPool.Recycle(dirty); -#endif + } else { printf("BeginUpdate() but no update requested!!\n"); link.StartMessage(B_ERROR); @@ -1908,7 +1918,8 @@ WindowLayer::_ObeySizeLimits() // constructor WindowLayer::UpdateSession::UpdateSession() : fDirtyRegion(), - fInUse(false) + fInUse(false), + fCause(0) { } @@ -1943,8 +1954,17 @@ void WindowLayer::UpdateSession::SetUsed(bool used) { fInUse = used; - if (!fInUse) + if (!fInUse) { fDirtyRegion.MakeEmpty(); + fCause = 0; + } +} + + +void +WindowLayer::UpdateSession::AddCause(uint8 cause) +{ + fCause |= cause; } @@ -1953,6 +1973,7 @@ WindowLayer::UpdateSession::operator=(const WindowLayer::UpdateSession& other) { fDirtyRegion = other.fDirtyRegion; fInUse = other.fInUse; + fCause = other.fCause; return *this; } diff --git a/src/servers/app/WindowLayer.h b/src/servers/app/WindowLayer.h index 99eb5936bb..6db8efeca7 100644 --- a/src/servers/app/WindowLayer.h +++ b/src/servers/app/WindowLayer.h @@ -35,6 +35,11 @@ class WindowLayer; // TODO: move this into a proper place #define AS_REDRAW 'rdrw' +enum { + UPDATE_REQUEST = 0x01, + UPDATE_EXPOSE = 0x02, +}; + class WindowLayer { public: WindowLayer(const BRect& frame, @@ -248,6 +253,7 @@ class WindowLayer { // the clipping, since it is local and the desktop // thread is blocked BRegion fDirtyRegion; + uint32 fDirtyCause; // caching local regions BRegion fBorderRegion; @@ -301,12 +307,19 @@ class WindowLayer { void SetUsed(bool used); inline bool IsUsed() const { return fInUse; } + + void AddCause(uint8 cause); + inline bool IsExpose() const + { return fCause & UPDATE_EXPOSE; } + inline bool IsRequest() const + { return fCause & UPDATE_REQUEST; } UpdateSession& operator=(const UpdateSession& other); private: BRegion fDirtyRegion; bool fInUse; + uint8 fCause; }; BRegion fDecoratorRegion; diff --git a/src/servers/app/drawing/AccelerantHWInterface.cpp b/src/servers/app/drawing/AccelerantHWInterface.cpp index 0434e058c6..5db2de2ab1 100644 --- a/src/servers/app/drawing/AccelerantHWInterface.cpp +++ b/src/servers/app/drawing/AccelerantHWInterface.cpp @@ -733,7 +733,8 @@ AccelerantHWInterface::CopyRegion(const clipping_rect* sortedRectList, // FillRegion void -AccelerantHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& color) +AccelerantHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& color, + bool autoSync) { if (fAccFillRect && fAccAcquireEngine) { if (fAccAcquireEngine(B_2D_ACCELERATION, 0xff, &fSyncToken, &fEngineToken) >= B_OK) { @@ -750,7 +751,7 @@ AccelerantHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& col fAccReleaseEngine(fEngineToken, &fSyncToken); // sync - if (fAccSyncToToken) + if (autoSync && fAccSyncToToken) fAccSyncToToken(&fSyncToken); } } @@ -781,6 +782,14 @@ AccelerantHWInterface::InvertRegion(/*const*/ BRegion& region) } } +// Sync +void +AccelerantHWInterface::Sync() +{ + if (fAccSyncToToken) + fAccSyncToToken(&fSyncToken); +} + // SetCursor void AccelerantHWInterface::SetCursor(ServerCursor* cursor) diff --git a/src/servers/app/drawing/AccelerantHWInterface.h b/src/servers/app/drawing/AccelerantHWInterface.h index 29d9f6bdf5..5a03903f92 100644 --- a/src/servers/app/drawing/AccelerantHWInterface.h +++ b/src/servers/app/drawing/AccelerantHWInterface.h @@ -59,9 +59,12 @@ public: uint32 count, int32 xOffset, int32 yOffset); virtual void FillRegion(/*const*/ BRegion& region, - const RGBColor& color); + const RGBColor& color, + bool autoSync); virtual void InvertRegion(/*const*/ BRegion& region); + virtual void Sync(); + // cursor handling virtual void SetCursor(ServerCursor* cursor); virtual void SetCursorVisible(bool visible); diff --git a/src/servers/app/drawing/DWindowHWInterface.cpp b/src/servers/app/drawing/DWindowHWInterface.cpp index e3527b32fb..15cb85fc7d 100644 --- a/src/servers/app/drawing/DWindowHWInterface.cpp +++ b/src/servers/app/drawing/DWindowHWInterface.cpp @@ -944,7 +944,7 @@ DWindowHWInterface::CopyRegion(const clipping_rect* sortedRectList, // FillRegion void -DWindowHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& color) +DWindowHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& color, bool autoSync) { if (fAccFillRect && fAccAcquireEngine) { if (fAccAcquireEngine(B_2D_ACCELERATION, 0xff, &fSyncToken, &fEngineToken) >= B_OK) { @@ -961,7 +961,7 @@ DWindowHWInterface::FillRegion(/*const*/ BRegion& region, const RGBColor& color) fAccReleaseEngine(fEngineToken, &fSyncToken); // sync - if (fAccSyncToToken) + if (autoSync && fAccSyncToToken) fAccSyncToToken(&fSyncToken); } } @@ -997,6 +997,14 @@ DWindowHWInterface::InvertRegion(/*const*/ BRegion& region) } } +// Sync +void +DWindowHWInterface::Sync() +{ + if (fAccSyncToToken) + fAccSyncToToken(&fSyncToken); +} + // FrontBuffer RenderingBuffer* DWindowHWInterface::FrontBuffer() const diff --git a/src/servers/app/drawing/DWindowHWInterface.h b/src/servers/app/drawing/DWindowHWInterface.h index 0304e64155..4b2007a70e 100644 --- a/src/servers/app/drawing/DWindowHWInterface.h +++ b/src/servers/app/drawing/DWindowHWInterface.h @@ -59,9 +59,12 @@ class DWindowHWInterface : public HWInterface { uint32 count, int32 xOffset, int32 yOffset); virtual void FillRegion(/*const*/ BRegion& region, - const RGBColor& color); + const RGBColor& color, + bool autoSync); virtual void InvertRegion(/*const*/ BRegion& region); + virtual void Sync(); + // frame buffer access virtual RenderingBuffer* FrontBuffer() const; virtual RenderingBuffer* BackBuffer() const; diff --git a/src/servers/app/drawing/DrawingEngine.cpp b/src/servers/app/drawing/DrawingEngine.cpp index 272b4b9cc5..fa616d3e7b 100644 --- a/src/servers/app/drawing/DrawingEngine.cpp +++ b/src/servers/app/drawing/DrawingEngine.cpp @@ -21,7 +21,7 @@ #include "ServerCursor.h" #include "RenderingBuffer.h" -#include "frame_buffer_support.h" +#include "drawing_support.h" // make_rect_valid static inline void @@ -82,7 +82,8 @@ class FontLocker { DrawingEngine::DrawingEngine(HWInterface* interface) : fPainter(new Painter()), fGraphicsCard(interface), - fAvailableHWAccleration(0) + fAvailableHWAccleration(0), + fSuspendSyncLevel(0) { } @@ -152,6 +153,24 @@ DrawingEngine::ConstrainClippingRegion(const BRegion* region) } } +// SuspendAutoSync +void +DrawingEngine::SuspendAutoSync() +{ + fSuspendSyncLevel++; +} + +// Sync +void +DrawingEngine::Sync() +{ + fSuspendSyncLevel--; + if (fSuspendSyncLevel == 0) + fGraphicsCard->Sync(); +} + +// #pragma mark - + // CopyRegion() does a topological sort of the rects in the // region. The algorithm was suggested by Ingo Weinhold. // It compares each rect with each rect and builds a tree @@ -596,20 +615,23 @@ DrawingEngine::FillRect(BRect r, const RGBColor& color) make_rect_valid(r); r = fPainter->ClipRect(r); if (r.IsValid()) { - fGraphicsCard->HideSoftwareCursor(r); + bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r); // try hardware optimized version first if (fAvailableHWAccleration & HW_ACC_FILL_REGION) { BRegion region(r); region.IntersectWith(fPainter->ClippingRegion()); - fGraphicsCard->FillRegion(region, color); + fGraphicsCard->FillRegion(region, color, + fSuspendSyncLevel == 0 + || cursorTouched); } else { fPainter->FillRect(r, color.GetColor32()); fGraphicsCard->Invalidate(r); } - - fGraphicsCard->ShowSoftwareCursor(); + + if (cursorTouched) + fGraphicsCard->ShowSoftwareCursor(); } WriteUnlock(); @@ -626,13 +648,14 @@ DrawingEngine::FillRegion(BRegion& r, const RGBColor& color) // NOTE: region expected to be already clipped correctly!! if (WriteLock()) { BRect frame = r.Frame(); - fGraphicsCard->HideSoftwareCursor(frame); + bool cursorTouched = fGraphicsCard->HideSoftwareCursor(frame); bool doInSoftware = true; // try hardware optimized version first if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0 && frame.Width() * frame.Height() > 100) { - fGraphicsCard->FillRegion(r, color); + fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0 + || cursorTouched); doInSoftware = false; } @@ -646,7 +669,8 @@ DrawingEngine::FillRegion(BRegion& r, const RGBColor& color) fGraphicsCard->Invalidate(r.Frame()); } - fGraphicsCard->ShowSoftwareCursor(); + if (cursorTouched) + fGraphicsCard->ShowSoftwareCursor(); WriteUnlock(); } @@ -689,7 +713,7 @@ DrawingEngine::FillRect(BRect r, const DrawState *d) make_rect_valid(r); r = fPainter->ClipRect(r); if (r.IsValid()) { - fGraphicsCard->HideSoftwareCursor(r); + bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r); bool doInSoftware = true; if ((r.Width() + 1) * (r.Height() + 1) > 100.0) { @@ -701,13 +725,17 @@ DrawingEngine::FillRect(BRect r, const DrawState *d) || d->GetDrawingMode() == B_OP_OVER)) { BRegion region(r); region.IntersectWith(fPainter->ClippingRegion()); - fGraphicsCard->FillRegion(region, d->HighColor()); + fGraphicsCard->FillRegion(region, d->HighColor(), + fSuspendSyncLevel == 0 + || cursorTouched); doInSoftware = false; } else if (d->GetPattern() == B_SOLID_LOW && d->GetDrawingMode() == B_OP_COPY) { BRegion region(r); region.IntersectWith(fPainter->ClippingRegion()); - fGraphicsCard->FillRegion(region, d->LowColor()); + fGraphicsCard->FillRegion(region, d->LowColor(), + fSuspendSyncLevel == 0 + || cursorTouched); doInSoftware = false; } } @@ -719,7 +747,8 @@ DrawingEngine::FillRect(BRect r, const DrawState *d) fGraphicsCard->Invalidate(r); } - fGraphicsCard->ShowSoftwareCursor(); + if (cursorTouched) + fGraphicsCard->ShowSoftwareCursor(); } WriteUnlock(); @@ -736,7 +765,7 @@ DrawingEngine::FillRegion(BRegion& r, const DrawState *d) if (WriteLock()) { BRect clipped = fPainter->ClipRect(r.Frame()); if (clipped.IsValid()) { - fGraphicsCard->HideSoftwareCursor(clipped); + bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped); bool doInSoftware = true; // try hardware optimized version first @@ -745,12 +774,16 @@ DrawingEngine::FillRegion(BRegion& r, const DrawState *d) && (d->GetDrawingMode() == B_OP_COPY || d->GetDrawingMode() == B_OP_OVER)) { r.IntersectWith(fPainter->ClippingRegion()); - fGraphicsCard->FillRegion(r, d->HighColor()); + fGraphicsCard->FillRegion(r, d->HighColor(), + fSuspendSyncLevel == 0 + || cursorTouched); doInSoftware = false; } else if (d->GetPattern() == B_SOLID_LOW && d->GetDrawingMode() == B_OP_COPY) { r.IntersectWith(fPainter->ClippingRegion()); - fGraphicsCard->FillRegion(r, d->LowColor()); + fGraphicsCard->FillRegion(r, d->LowColor(), + fSuspendSyncLevel == 0 + || cursorTouched); doInSoftware = false; } } @@ -768,7 +801,8 @@ DrawingEngine::FillRegion(BRegion& r, const DrawState *d) fGraphicsCard->Invalidate(touched); } - fGraphicsCard->ShowSoftwareCursor(); + if (cursorTouched) + fGraphicsCard->ShowSoftwareCursor(); } WriteUnlock(); @@ -1270,3 +1304,4 @@ DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height, } } + diff --git a/src/servers/app/DrawingEngine.h b/src/servers/app/drawing/DrawingEngine.h similarity index 98% rename from src/servers/app/DrawingEngine.h rename to src/servers/app/drawing/DrawingEngine.h index 9c0ee15051..9dfec9b6d6 100644 --- a/src/servers/app/DrawingEngine.h +++ b/src/servers/app/drawing/DrawingEngine.h @@ -66,6 +66,9 @@ public: // will remove any clipping (drawing allowed everywhere) void ConstrainClippingRegion(const BRegion* region); + void SuspendAutoSync(); + void Sync(); + // drawing functions void CopyRegion(/*const*/ BRegion* region, int32 xOffset, int32 yOffset); @@ -156,6 +159,7 @@ public: Painter* fPainter; HWInterface* fGraphicsCard; uint32 fAvailableHWAccleration; + int32 fSuspendSyncLevel; }; #endif // DRAWING_ENGINE_H_ diff --git a/src/servers/app/drawing/HWInterface.cpp b/src/servers/app/drawing/HWInterface.cpp index 55f2befe7e..e158145c4e 100644 --- a/src/servers/app/drawing/HWInterface.cpp +++ b/src/servers/app/drawing/HWInterface.cpp @@ -10,7 +10,7 @@ #include #include -#include "frame_buffer_support.h" +#include "drawing_support.h" #include "RenderingBuffer.h" #include "ServerCursor.h" @@ -302,7 +302,7 @@ HWInterface::CopyBackToFront(const BRect& frame) } // HideSoftwareCursor -void +bool HWInterface::HideSoftwareCursor(const BRect& area) { if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) { @@ -312,8 +312,10 @@ HWInterface::HideSoftwareCursor(const BRect& area) fCursorAreaBackup->bottom); if (area.Intersects(backupArea)) { _RestoreCursorArea(); + return true; } } + return false; } // HideSoftwareCursor @@ -404,10 +406,11 @@ HWInterface::_DrawCursor(BRect area) const *(uint32*)b = *(uint32*)s; // assumes backbuffer alpha = 255 // assuming pre-multiplied cursor bitmap - uint8 a = 255 - c[3]; - d[0] = ((b[0] * a) >> 8) + c[0]; - d[1] = ((b[1] * a) >> 8) + c[1]; - d[2] = ((b[2] * a) >> 8) + c[2]; + int a = 255 - c[3]; + d[0] = ((int)(b[0] * a + 255) >> 8) + c[0]; + d[1] = ((int)(b[1] * a + 255) >> 8) + c[1]; + d[2] = ((int)(b[2] * a + 255) >> 8) + c[2]; + s += 4; c += 4; d += 4; @@ -428,9 +431,10 @@ HWInterface::_DrawCursor(BRect area) const // assumes backbuffer alpha = 255 // assuming pre-multiplied cursor bitmap uint8 a = 255 - c[3]; - d[0] = ((s[0] * a) >> 8) + c[0]; - d[1] = ((s[1] * a) >> 8) + c[1]; - d[2] = ((s[2] * a) >> 8) + c[2]; + d[0] = ((s[0] * a + 255) >> 8) + c[0]; + d[1] = ((s[1] * a + 255) >> 8) + c[1]; + d[2] = ((s[2] * a + 255) >> 8) + c[2]; + s += 4; c += 4; d += 4; diff --git a/src/servers/app/drawing/HWInterface.h b/src/servers/app/drawing/HWInterface.h index 7e1eac6e16..3dfd818d64 100644 --- a/src/servers/app/drawing/HWInterface.h +++ b/src/servers/app/drawing/HWInterface.h @@ -73,9 +73,12 @@ class HWInterface : public MultiLocker { uint32 count, int32 xOffset, int32 yOffset) {} virtual void FillRegion(/*const*/ BRegion& region, - const RGBColor& color) {} + const RGBColor& color, + bool autoSync) {} virtual void InvertRegion(/*const*/ BRegion& region) {} + virtual void Sync() {} + // cursor handling (these do their own Read/Write locking) ServerCursor* Cursor() const { return fCursor; } virtual void SetCursor(ServerCursor* cursor); @@ -115,7 +118,7 @@ class HWInterface : public MultiLocker { // --- // NOTE: Investigate locking for these! The client code should already hold a // ReadLock, but maybe these functions should acquire a WriteLock! - void HideSoftwareCursor(const BRect& area); + bool HideSoftwareCursor(const BRect& area); void HideSoftwareCursor(); void ShowSoftwareCursor(); diff --git a/src/servers/app/drawing/Jamfile b/src/servers/app/drawing/Jamfile index f1053e5d3b..212bffafae 100644 --- a/src/servers/app/drawing/Jamfile +++ b/src/servers/app/drawing/Jamfile @@ -13,6 +13,7 @@ StaticLibrary libasdrawing.a : AccelerantBuffer.cpp AccelerantHWInterface.cpp BitmapBuffer.cpp + drawing_support.cpp DrawingEngine.cpp MallocBuffer.cpp UpdateQueue.cpp diff --git a/src/servers/app/drawing/Painter/Painter.cpp b/src/servers/app/drawing/Painter/Painter.cpp index 888a3e0990..45961edffe 100644 --- a/src/servers/app/drawing/Painter/Painter.cpp +++ b/src/servers/app/drawing/Painter/Painter.cpp @@ -26,7 +26,7 @@ #include #include -#include "frame_buffer_support.h" +#include "drawing_support.h" #include "DrawState.h" @@ -210,6 +210,8 @@ Painter::SetDrawState(const DrawState* data, bool updateFont) fLineJoinMode = data->LineJoinMode(); fMiterLimit = data->MiterLimit(); + // adopt the color *after* the pattern is set + // to set the renderers to the correct color SetHighColor(data->HighColor().GetColor32()); SetLowColor(data->LowColor().GetColor32()); @@ -240,7 +242,7 @@ Painter::SetHighColor(const rgb_color& color) void Painter::SetLowColor(const rgb_color& color) { - fPatternHandler->SetLowColor(color);; + fPatternHandler->SetLowColor(color); if (*(fPatternHandler->GetR5Pattern()) == B_SOLID_LOW) _SetRendererColor(color); } @@ -1356,12 +1358,8 @@ Painter::_DrawBitmap(const agg::rendering_buffer& srcBuffer, color_space format, return; } - if (!fSubpixelPrecise) { - // round off viewRect (in a way avoiding too much distortion) - viewRect.OffsetTo(roundf(viewRect.left), roundf(viewRect.top)); - viewRect.right = roundf(viewRect.right); - viewRect.bottom = roundf(viewRect.bottom); - } + if (!fSubpixelPrecise) + align_rect_to_pixels(&viewRect); double xScale = (viewRect.Width() + 1) / (bitmapRect.Width() + 1); double yScale = (viewRect.Height() + 1) / (bitmapRect.Height() + 1); diff --git a/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h b/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h index 68792b1100..7330934f36 100644 --- a/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h +++ b/src/servers/app/drawing/Painter/drawing_modes/DrawingMode.h @@ -9,7 +9,7 @@ #ifndef DRAWING_MODE_H #define DRAWING_MODE_H -#include "frame_buffer_support.h" +#include "drawing_support.h" #include "PatternHandler.h" #include "PixelFormat.h" diff --git a/src/servers/app/drawing/drawing_support.cpp b/src/servers/app/drawing/drawing_support.cpp new file mode 100644 index 0000000000..f69878b8f4 --- /dev/null +++ b/src/servers/app/drawing/drawing_support.cpp @@ -0,0 +1,13 @@ +#include "drawing_support.h" + +#include + +void +align_rect_to_pixels(BRect* rect) +{ + // round the rect with the least ammount of distortion + rect->OffsetTo(roundf(rect->left), roundf(rect->top)); + rect->right = roundf(rect->right); + rect->bottom = roundf(rect->bottom); +} + diff --git a/src/servers/app/drawing/frame_buffer_support.h b/src/servers/app/drawing/drawing_support.h similarity index 98% rename from src/servers/app/drawing/frame_buffer_support.h rename to src/servers/app/drawing/drawing_support.h index 37bae1a8a3..ca0764097d 100644 --- a/src/servers/app/drawing/frame_buffer_support.h +++ b/src/servers/app/drawing/drawing_support.h @@ -138,6 +138,9 @@ blend_line32(uint8* buffer, int32 pixels, uint8 r, uint8 g, uint8 b, uint8 a) gfxcpy32(buffer, tempBuffer, pixels * 4); } +void +align_rect_to_pixels(BRect* rect); + #endif // SUPPORT_H diff --git a/src/tests/servers/app/Jamfile b/src/tests/servers/app/Jamfile index bc443bd5db..727b859f75 100644 --- a/src/tests/servers/app/Jamfile +++ b/src/tests/servers/app/Jamfile @@ -103,6 +103,7 @@ Server haiku_app_server : AccelerantBuffer.cpp AccelerantHWInterface.cpp BitmapBuffer.cpp + drawing_support.cpp DrawingEngine.cpp MallocBuffer.cpp UpdateQueue.cpp @@ -148,6 +149,7 @@ SubInclude HAIKU_TOP src tests servers app copy_bits ; SubInclude HAIKU_TOP src tests servers app cursor_test ; SubInclude HAIKU_TOP src tests servers app desktop_window ; SubInclude HAIKU_TOP src tests servers app event_mask ; +SubInclude HAIKU_TOP src tests servers app following ; SubInclude HAIKU_TOP src tests servers app look_and_feel ; SubInclude HAIKU_TOP src tests servers app painter ; SubInclude HAIKU_TOP src tests servers app playground ; diff --git a/src/tests/servers/app/following/Jamfile b/src/tests/servers/app/following/Jamfile new file mode 100644 index 0000000000..3c01c443fd --- /dev/null +++ b/src/tests/servers/app/following/Jamfile @@ -0,0 +1,17 @@ +SubDir HAIKU_TOP src tests servers app following ; + +SetSubDirSupportedPlatformsBeOSCompatible ; +AddSubDirSupportedPlatforms libbe_test ; + +UseHeaders [ FDirName os app ] ; +UseHeaders [ FDirName os interface ] ; + +SimpleTest Following : + main.cpp + : be ; + +if ( $(TARGET_PLATFORM) = libbe_test ) { + HaikuInstall install-test-apps : $(HAIKU_APP_TEST_DIR) : Following + : tests!apps ; +} + diff --git a/src/tests/servers/app/following/main.cpp b/src/tests/servers/app/following/main.cpp new file mode 100644 index 0000000000..955a8a1895 --- /dev/null +++ b/src/tests/servers/app/following/main.cpp @@ -0,0 +1,164 @@ +// main.cpp + +#include +#include + +#include +#include +#include +#include +#include + +enum { + TRACKING_NONE = 0, + TRACKING_ALL, + TRACKING_RIGHT, + TRACKING_BOTTOM, + TRACKING_RIGHT_BOTTOM, +}; + +class TestView : public BView { + + public: + TestView(BRect frame, const char* name, + uint32 resizeFlags, uint32 flags) + : BView(frame, name, resizeFlags, flags), + fTracking(TRACKING_NONE), + fLastMousePos(0.0, 0.0) + { + rgb_color color; + color.red = rand() / 256; + color.green = rand() / 256; + color.blue = rand() / 256; + color.alpha = 255; + SetViewColor(color); + SetLowColor(color); + } + + virtual void Draw(BRect updateRect); + + virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage); + + private: + uint32 fTracking; + + BPoint fLastMousePos; +}; + +// Draw +void +TestView::Draw(BRect updateRect) +{ + // text + SetHighColor(0, 0, 0, 255); + const char* message = "Click and drag to move this view!"; + DrawString(message, BPoint(20.0, 30.0)); + + BRect r(Bounds()); + r.right -= 15.0; + r.bottom -= 15.0; + StrokeLine(r.RightTop(), BPoint(r.right, Bounds().bottom)); + StrokeLine(r.LeftBottom(), BPoint(Bounds().right, r.bottom)); +} + +// MouseDown +void +TestView::MouseDown(BPoint where) +{ + BRect r(Bounds()); + r.right -= 15.0; + r.bottom -= 15.0; + if (r.Contains(where)) + fTracking = TRACKING_ALL; + else if (r.bottom < where.y && r.right < where.x) + fTracking = TRACKING_RIGHT_BOTTOM; + else if (r.bottom < where.y) + fTracking = TRACKING_BOTTOM; + else if (r.right < where.x) + fTracking = TRACKING_RIGHT; + + fLastMousePos = where; + SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); +} + +// MouseUp +void +TestView::MouseUp(BPoint where) +{ + fTracking = TRACKING_NONE; +} + +// MouseMoved +void +TestView::MouseMoved(BPoint where, uint32 transit, + const BMessage* dragMessage) +{ + BPoint offset = where - fLastMousePos; + switch (fTracking) { + case TRACKING_ALL: + MoveBy(offset.x, offset.y); + // fLastMousePos stays fixed + break; + case TRACKING_RIGHT: + ResizeBy(offset.x, 0.0); + fLastMousePos = where; + break; + case TRACKING_BOTTOM: + ResizeBy(0.0, offset.y); + fLastMousePos = where; + break; + case TRACKING_RIGHT_BOTTOM: + ResizeBy(offset.x, offset.y); + fLastMousePos = where; + break; + } +} + + + +// show_window +void +show_window(BRect frame, const char* name) +{ + BWindow* window = new BWindow(frame, name, + B_TITLED_WINDOW, + B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE); + + BView* view = new TestView(window->Bounds(), "test 1", B_FOLLOW_ALL, + B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + + window->AddChild(view); + + BRect bounds = view->Bounds(); + bounds.InsetBy(20, 20); + BView* view1 = new TestView(bounds, "test 2", B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM, + B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + view->AddChild(view1); + + bounds = view1->Bounds(); + bounds.InsetBy(20, 20); + BView* view2 = new TestView(bounds, "test 3", B_FOLLOW_NONE, + B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); + view1->AddChild(view2); + + + window->Show(); +} + +// main +int +main(int argc, char** argv) +{ + BApplication* app = new BApplication("application/x.vnd-Haiku.Following"); + + BRect frame(50.0, 50.0, 300.0, 250.0); + show_window(frame, "Following Test"); + + app->Run(); + + delete app; + return 0; +} diff --git a/src/tests/servers/app/following/run b/src/tests/servers/app/following/run new file mode 100755 index 0000000000..fcce0ef409 --- /dev/null +++ b/src/tests/servers/app/following/run @@ -0,0 +1,18 @@ +#!/bin/sh + +../../../../../generated/tests/apps/run_haiku_registrar || exit + +if test -f ../../../../../generated/tests/apps/haiku_app_server; then + ../../../../../generated/tests/apps/haiku_app_server & +else + echo "You need to \"TARGET_PLATFORM=r5 jam install-test-apps\" first." +fi + +sleep 1s + +if test -f ../../../../../generated/tests/apps/Following; then + ../../../../../generated/tests/apps/Following +else + echo "You need to \"TARGET_PLATFORM=r5 jam install-test-apps\" first." +fi +