haiku/src/servers/app/WindowLayer.cpp
Stephan Aßmus ce8ae4d8c2 _TriggerContentRedraw is now given the dirty region, so ProcessDirtyRegion does not have to be called for triggering window local redraws
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15593 a95241bf-73f2-0310-859d-f6bbb57e9c96
2005-12-19 21:16:23 +00:00

1842 lines
43 KiB
C++

/*
* Copyright (c) 2001-2005, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Author: DarkWyrm <bpmagic@columbus.rr.com>
* Adi Oanca <adioanca@gmail.com>
* Stephan Aßmus <superstippi@gmx.de>
* Axel Dörfler, axeld@pinc-software.de
*/
#include "WindowLayer.h"
#include "DebugInfoManager.h"
#include "Decorator.h"
#include "DecorManager.h"
#include "Desktop.h"
#include "DrawingEngine.h"
#include "MessagePrivate.h"
#include "PortLink.h"
#include "ServerApp.h"
#include "ServerWindow.h"
#include "Workspace.h"
#include "WorkspacesLayer.h"
#include <WindowPrivate.h>
#include <Debug.h>
#include <DirectWindow.h>
#include <View.h>
#include <new>
#include <stdio.h>
// Toggle debug output
//#define DEBUG_WINDOW_LAYER
//#define DEBUG_WINDOW_LAYER_CLICK
#ifdef DEBUG_WINDOW_LAYER
# define STRACE(x) printf x
#else
# define STRACE(x) ;
#endif
#ifdef DEBUG_WINDOW_LAYER_CLICK
# define STRACE_CLICK(x) printf x
#else
# define STRACE_CLICK(x) ;
#endif
// if the background clearing is delayed until
// the client draws the view, we have less flickering
// when contents have to be redrawn because of resizing
// a window or because the client invalidates parts.
// when redrawing something that has been exposed from underneath
// other windows, the other window will be seen longer at
// 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 0
// IMPORTANT: nested LockSingleWindow()s are not supported (by MultiLocker)
using std::nothrow;
WindowLayer::WindowLayer(const BRect& frame, const char *name,
window_look look, window_feel feel,
uint32 flags, uint32 workspaces,
::ServerWindow* window,
DrawingEngine* drawingEngine)
:
fTitle(name),
fFrame(frame),
fVisibleRegion(),
fVisibleContentRegion(),
fVisibleContentRegionValid(false),
fDirtyRegion(),
fBorderRegion(),
fBorderRegionValid(false),
fContentRegion(),
fContentRegionValid(false),
fEffectiveDrawingRegion(),
fEffectiveDrawingRegionValid(false),
fIsClosing(false),
fIsMinimizing(false),
fIsZooming(false),
fIsResizing(false),
fIsSlidingTab(false),
fIsDragging(false),
fDecorator(NULL),
fTopLayer(NULL),
fWindow(window),
fDrawingEngine(drawingEngine),
fDesktop(window->Desktop()),
fCurrentUpdateSession(),
fPendingUpdateSession(),
fUpdateRequested(false),
fInUpdate(false),
// windows start hidden
fHidden(true),
fIsFocus(false),
fLook(look),
fFeel(feel),
fWorkspaces(workspaces),
fCurrentWorkspace(-1),
fMinWidth(1),
fMaxWidth(32768),
fMinHeight(1),
fMaxHeight(32768),
fReadLocked(false)
{
// make sure our arguments are valid
if (!IsValidLook(fLook))
fLook = B_TITLED_WINDOW_LOOK;
if (!IsValidFeel(fFeel))
fFeel = B_NORMAL_WINDOW_FEEL;
SetFlags(flags, NULL);
if (fLook != B_NO_BORDER_WINDOW_LOOK) {
fDecorator = gDecorManager.AllocateDecorator(fDesktop, frame, name, fLook, fFlags);
if (fDecorator)
fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight);
}
// do we need to change our size to let the decorator fit?
// _ResizeBy() will adapt the frame for validity before resizing
if (feel == kDesktopWindowFeel) {
// the desktop window spans over the whole screen
// ToDo: this functionality should be moved somewhere else (so that it
// is always used when the workspace is changed)
uint16 width, height;
uint32 colorSpace;
float frequency;
if (fDesktop->ScreenAt(0)) {
fDesktop->ScreenAt(0)->GetMode(width, height, colorSpace, frequency);
// TODO: MOVE THIS AWAY!!! RemoveBy contains calls to virtual methods! Also, there is not TopLayer()!
fFrame.OffsetTo(B_ORIGIN);
// ResizeBy(width - frame.Width(), height - frame.Height(), NULL);
}
}
STRACE(("WindowLayer %p, %s:\n", this, Name()));
STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top,
fFrame.right, fFrame.bottom));
STRACE(("\tWindow %s\n", window ? window->Title() : "NULL"));
}
WindowLayer::~WindowLayer()
{
delete fTopLayer;
delete fDecorator;
}
bool
WindowLayer::ReadLockWindows()
{
if (fDesktop)
fReadLocked = fDesktop->LockSingleWindow();
else
fReadLocked = true;
return fReadLocked;
}
void
WindowLayer::ReadUnlockWindows()
{
if (fReadLocked) {
if (fDesktop)
fDesktop->UnlockSingleWindow();
fReadLocked = false;
}
}
void
WindowLayer::SetClipping(BRegion* stillAvailableOnScreen)
{
// this function is only called from the Desktop thread
// start from full region (as if the window was fully visible)
GetFullRegion(&fVisibleRegion);
// clip to region still available on screen
fVisibleRegion.IntersectWith(stillAvailableOnScreen);
fVisibleContentRegionValid = false;
fEffectiveDrawingRegionValid = false;
// TODO: review this!
fWindow->HandleDirectConnection(B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
}
void
WindowLayer::GetFullRegion(BRegion* region)
{
// TODO: if someone needs to call this from
// the outside, the clipping needs to be readlocked!
GetBorderRegion(region);
// start from the frame, extend to include decorator border
region->Include(fFrame);
}
// GetBorderRegion
void
WindowLayer::GetBorderRegion(BRegion* region)
{
// TODO: if someone needs to call this from
// the outside, the clipping needs to be readlocked!
if (!fBorderRegionValid) {
// TODO: checkup Decorator::GetFootPrint() to see if it is as fast as this:
/* fBorderRegion.Set(BRect(fFrame.left - 4, fFrame.top - 20,
(fFrame.left + fFrame.right) / 2, fFrame.top - 5));
fBorderRegion.Include(BRect(fFrame.left - 4, fFrame.top - 4,
fFrame.right + 4, fFrame.top - 1));
fBorderRegion.Include(BRect(fFrame.left - 4, fFrame.top,
fFrame.left - 1, fFrame.bottom));
fBorderRegion.Include(BRect(fFrame.right + 1, fFrame.top,
fFrame.right + 4, fFrame.bottom - 11));
fBorderRegion.Include(BRect(fFrame.left - 4, fFrame.bottom + 1,
fFrame.right - 11, fFrame.bottom + 4));
fBorderRegion.Include(BRect(fFrame.right - 10, fFrame.bottom - 10,
fFrame.right + 4, fFrame.bottom + 4));*/
// TODO: remove and use Decorator::GetFootPrint()
// start from the frame, extend to include decorator border
if (fDecorator) {
fDecorator->GetFootprint(&fBorderRegion);
}
fBorderRegionValid = true;
}
*region = fBorderRegion;
}
void
WindowLayer::GetContentRegion(BRegion* region)
{
// TODO: if someone needs to call this from
// the outside, the clipping needs to be readlocked!
if (!fContentRegionValid) {
_UpdateContentRegion();
}
*region = fContentRegion;
}
BRegion&
WindowLayer::VisibleContentRegion()
{
// TODO: if someone needs to call this from
// the outside, the clipping needs to be readlocked!
// regions expected to be locked
if (!fVisibleContentRegionValid) {
GetContentRegion(&fVisibleContentRegion);
fVisibleContentRegion.IntersectWith(&fVisibleRegion);
}
return fVisibleContentRegion;
}
// #pragma mark -
void
WindowLayer::_PropagatePosition()
{
if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) == 0)
return;
for (int32 i = 0; i < kListCount; i++) {
Anchor(i).position = fFrame.LeftTop();
}
}
void
WindowLayer::MoveBy(int32 x, int32 y)
{
// this function is only called from the desktop thread
if ((x == 0 && y == 0) || !ReadLockWindows())
return;
fWindow->HandleDirectConnection(B_DIRECT_STOP);
fFrame.OffsetBy(x, y);
_PropagatePosition();
fWindow->HandleDirectConnection(B_DIRECT_START | B_BUFFER_MOVED);
// take along the dirty region which have not
// processed yet
fDirtyRegion.OffsetBy(x, y);
if (fBorderRegionValid)
fBorderRegion.OffsetBy(x, y);
if (fContentRegionValid)
fContentRegion.OffsetBy(x, y);
if (fCurrentUpdateSession.IsUsed())
fCurrentUpdateSession.MoveBy(x, y);
if (fPendingUpdateSession.IsUsed())
fPendingUpdateSession.MoveBy(x, y);
fEffectiveDrawingRegionValid = false;
if (fDecorator)
fDecorator->MoveBy(x, y);
if (fTopLayer != NULL)
fTopLayer->MoveBy(x, y, NULL);
// the desktop will take care of dirty regions
// dispatch a message to the client informing about the changed size
BMessage msg(B_WINDOW_MOVED);
msg.AddInt64("when", system_time());
msg.AddPoint("where", fFrame.LeftTop());
fWindow->SendMessageToClient(&msg);
ReadUnlockWindows();
}
void
WindowLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
{
// this function is only called from the desktop thread
int32 wantWidth = fFrame.IntegerWidth() + x;
int32 wantHeight = fFrame.IntegerHeight() + y;
// enforce size limits
if (wantWidth < fMinWidth)
wantWidth = fMinWidth;
if (wantWidth > fMaxWidth)
wantWidth = fMaxWidth;
if (wantHeight < fMinHeight)
wantHeight = fMinHeight;
if (wantHeight > fMaxHeight)
wantHeight = fMaxHeight;
x = wantWidth - fFrame.IntegerWidth();
y = wantHeight - fFrame.IntegerHeight();
if ((x == 0 && y == 0) || !ReadLockWindows())
return;
fWindow->HandleDirectConnection(B_DIRECT_STOP);
fFrame.right += x;
fFrame.bottom += y;
fWindow->HandleDirectConnection(B_DIRECT_START | B_BUFFER_RESIZED);
// put the previous border region into the dirty region as well
// to handle the part that was overlapping a layer
if (dirtyRegion)
dirtyRegion->Include(&fBorderRegion);
fBorderRegionValid = false;
fContentRegionValid = false;
fEffectiveDrawingRegionValid = false;
if (fDecorator)
fDecorator->ResizeBy(x, y);
// the border is dirty, put it into
// dirtyRegion for a start
BRegion newBorderRegion;
GetBorderRegion(&newBorderRegion);
if (dirtyRegion) {
dirtyRegion->Include(&newBorderRegion);
}
if (fTopLayer != NULL)
fTopLayer->ResizeBy(x, y, dirtyRegion);
//if (dirtyRegion)
//fDrawingEngine->FillRegion(*dirtyRegion, RGBColor(0, 255, 255, 255));
// send a message to the client informing about the changed size
BRect frame(Frame());
BMessage msg(B_WINDOW_RESIZED);
msg.AddInt64("when", system_time());
msg.AddInt32("width", frame.IntegerWidth());
msg.AddInt32("height", frame.IntegerHeight());
fWindow->SendMessageToClient(&msg);
ReadUnlockWindows();
}
void
WindowLayer::ScrollViewBy(ViewLayer* view, int32 dx, int32 dy)
{
// this can be executed from any thread, but if the
// desktop thread is executing this, it should have
// the write lock, otherwise it is not prevented
// from executing this at the same time as the window
// is doing something else here!
if (!view || view == fTopLayer || (dx == 0 && dy == 0))
return;
if (fDesktop && fDesktop->LockSingleWindow()) {
BRegion dirty;
view->ScrollBy(dx, dy, &dirty);
//fDrawingEngine->FillRegion(dirty, RGBColor(255, 0, 255, 255));
//snooze(2000);
_TriggerContentRedraw(dirty);
fDesktop->UnlockSingleWindow();
}
}
//! Takes care of invalidating parts that could not be copied
void
WindowLayer::CopyContents(BRegion* region, int32 xOffset, int32 yOffset)
{
if (!fHidden && fDesktop && fDesktop->LockSingleWindow()) {
BRegion newDirty(*region);
// clip the region to the visible contents at the
// source and destination location (not that VisibleContentRegion()
// is used once to make sure it is valid, then fVisibleContentRegion
// is used directly)
region->IntersectWith(&VisibleContentRegion());
if (region->CountRects() > 0) {
region->OffsetBy(xOffset, yOffset);
region->IntersectWith(&fVisibleContentRegion);
if (region->CountRects() > 0) {
// if the region still contains any rects
// offset to source location again
region->OffsetBy(-xOffset, -yOffset);
// the part which we can copy is not dirty
newDirty.Exclude(region);
fDrawingEngine->CopyRegion(region, xOffset, yOffset);
// move along the already dirty regions that are common
// with the region that we could copy
_ShiftPartOfRegion(&fDirtyRegion, region, xOffset, yOffset);
if (fPendingUpdateSession.IsUsed())
_ShiftPartOfRegion(&fPendingUpdateSession.DirtyRegion(), region, xOffset, yOffset);
if (fCurrentUpdateSession.IsUsed()) {
// if there are parts in the current update session
// that intersect with the copied region, we cannot
// simply shift them as with the other dirty regions
// - we cannot change the update rect already told to the
// client, that's why we transfer those parts to the
// new dirty region instead
BRegion common(*region);
// see if there is a common part at all
common.IntersectWith(&fCurrentUpdateSession.DirtyRegion());
if (common.CountRects() > 0) {
// cut the common part from the region
fCurrentUpdateSession.DirtyRegion().Exclude(&common);
newDirty.Include(&common);
}
}
}
}
// what is left visible from the original region
// at the destination after the region which could be
// copied has been excluded, is considered dirty
// NOTE: it may look like dirty regions are not moved
// if no region could be copied, but that's alright,
// since these parts will now be in newDirty anyways
// (with the right offset)
newDirty.OffsetBy(xOffset, yOffset);
newDirty.IntersectWith(&fVisibleContentRegion);
if (newDirty.CountRects() > 0)
ProcessDirtyRegion(newDirty);
fDesktop->UnlockSingleWindow();
}
}
// #pragma mark -
void
WindowLayer::SetTopLayer(ViewLayer* topLayer)
{
// the top layer is special, it has a coordinate system
// as if it was attached directly to the desktop, therefor,
// the coordinate conversion through the layer tree works
// as expected, since the top layer has no "parent" but has
// fFrame as if it had
fTopLayer = topLayer;
// make sure the location of the top layer on screen matches ours
fTopLayer->MoveBy(fFrame.left - fTopLayer->Frame().left,
fFrame.top - fTopLayer->Frame().top, NULL);
// make sure the size of the top layer matches ours
fTopLayer->ResizeBy(fFrame.Width() - fTopLayer->Frame().Width(),
fFrame.Height() - fTopLayer->Frame().Height(), NULL);
fTopLayer->AttachedToWindow(this);
}
ViewLayer*
WindowLayer::ViewAt(const BPoint& where)
{
ViewLayer* view = NULL;
if (ReadLockWindows()) {
if (!fContentRegionValid)
_UpdateContentRegion();
view = fTopLayer->ViewAt(where, &fContentRegion);
ReadUnlockWindows();
}
return view;
}
window_anchor&
WindowLayer::Anchor(int32 index)
{
return fAnchor[index];
}
WindowLayer*
WindowLayer::NextWindow(int32 index)
{
return fAnchor[index].next;
}
WindowLayer*
WindowLayer::PreviousWindow(int32 index)
{
return fAnchor[index].previous;
}
// #pragma mark -
void
WindowLayer::GetEffectiveDrawingRegion(ViewLayer* layer, BRegion& region)
{
if (!fEffectiveDrawingRegionValid) {
fEffectiveDrawingRegion = VisibleContentRegion();
if (fInUpdate) {
// enforce the dirty region of the update session
fEffectiveDrawingRegion.IntersectWith(&fCurrentUpdateSession.DirtyRegion());
} else {
// not in update, the view can draw everywhere
//printf("WindowLayer(%s)::GetEffectiveDrawingRegion(for %s) - outside update\n", Title(), layer->Name());
}
fEffectiveDrawingRegionValid = true;
}
// TODO: this is a region that needs to be cached later in the server
// when the current layer in ServerWindow is set, and we are currently
// in an update (fInUpdate), than we can set this region and remember
// it for the comming drawing commands until the current layer changes
// again or fEffectiveDrawingRegionValid is suddenly false.
region = fEffectiveDrawingRegion;
if (!fContentRegionValid)
_UpdateContentRegion();
region.IntersectWith(&layer->ScreenClipping(&fContentRegion));
}
bool
WindowLayer::DrawingRegionChanged(ViewLayer* layer) const
{
return !fEffectiveDrawingRegionValid || !layer->IsScreenClippingValid();
}
void
WindowLayer::ProcessDirtyRegion(BRegion& region)
{
// if this is exectuted in the desktop thread,
// it means that the window thread currently
// blocks to get the read lock, if it is
// executed from the window thread, it should
// have the read lock and the desktop thread
// is blocking to get the write lock. IAW, this
// is only executed in one thread.
if (fDirtyRegion.CountRects() == 0) {
// the window needs to be informed
// when the dirty region was empty.
// NOTE: when the window thread has processed
// the dirty region in MessageReceived(),
// it will make the region empty again,
// when it is empty here, we need to send
// the message to initiate the next update round.
// Until the message is processed in the window
// thread, the desktop thread can add parts to
// the region as it likes.
ServerWindow()->RequestRedraw();
}
// this is executed from the desktop thread
fDirtyRegion.Include(&region);
}
void
WindowLayer::RedrawDirtyRegion()
{
if (!fDesktop || !fDesktop->LockSingleWindow())
return;
if (IsVisible()) {
_DrawBorder();
BRegion dirtyContentRegion(VisibleContentRegion());
dirtyContentRegion.IntersectWith(&fDirtyRegion);
_TriggerContentRedraw(dirtyContentRegion);
}
// reset the dirty region, since
// we're fully clean. If the desktop
// thread wanted to mark something
// dirty in the mean time, it was
// blocking on the global region lock to
// get write access, since we held the
// read lock for the whole time.
fDirtyRegion.MakeEmpty();
fDesktop->UnlockSingleWindow();
}
void
WindowLayer::MarkDirty(BRegion& regionOnScreen)
{
// for marking any part of the desktop dirty
// this will get write access to the global
// region lock, and result in ProcessDirtyRegion()
// to be called for any windows affected
if (fDesktop)
fDesktop->MarkDirty(regionOnScreen);
}
void
WindowLayer::MarkContentDirty(BRegion& regionOnScreen)
{
// for triggering AS_REDRAW
// since this won't affect other windows, read locking
// is sufficient. If there was no dirty region before,
// an update message is triggered
if (!fHidden && fDesktop && fDesktop->LockSingleWindow()) {
regionOnScreen.IntersectWith(&VisibleContentRegion());
_TriggerContentRedraw(regionOnScreen);
fDesktop->UnlockSingleWindow();
}
}
void
WindowLayer::InvalidateView(ViewLayer* layer, BRegion& layerRegion)
{
if (layer && !fHidden && fDesktop && fDesktop->LockSingleWindow()) {
if (!layer->IsVisible()) {
fDesktop->UnlockSingleWindow();
return;
}
if (!fContentRegionValid)
_UpdateContentRegion();
layer->ConvertToScreen(&layerRegion);
layerRegion.IntersectWith(&fVisibleRegion);
if (layerRegion.CountRects() > 0) {
layerRegion.IntersectWith(&layer->ScreenClipping(&fContentRegion));
//fDrawingEngine->FillRegion(layerRegion, RGBColor(255, 255, 0, 255));
//snooze(2000);
_TriggerContentRedraw(layerRegion);
}
fDesktop->UnlockSingleWindow();
}
}
// EnableUpdateRequests
void
WindowLayer::EnableUpdateRequests()
{
// fUpdateRequestsEnabled = true;
/* if (fCumulativeRegion.CountRects() > 0) {
GetRootLayer()->MarkForRedraw(fCumulativeRegion);
GetRootLayer()->TriggerRedraw();
}*/
}
// #pragma mark -
void
WindowLayer::MouseDown(BMessage* message, BPoint where, int32* _viewToken)
{
// TODO: move into Decorator
if (!fBorderRegionValid)
GetBorderRegion(&fBorderRegion);
// default action is to drag the WindowLayer
if (fBorderRegion.Contains(where)) {
// clicking WindowLayer visible area
click_type action = DEC_DRAG;
if (fDecorator)
action = _ActionFor(message);
// deactivate border buttons on first click (select)
if (!IsFocus() && action != DEC_MOVETOBACK
&& action != DEC_RESIZE && action != DEC_SLIDETAB)
action = DEC_DRAG;
// set decorator internals
switch (action) {
case DEC_CLOSE:
fIsClosing = true;
fDecorator->SetClose(true);
STRACE_CLICK(("===> DEC_CLOSE\n"));
break;
case DEC_ZOOM:
fIsZooming = true;
fDecorator->SetZoom(true);
STRACE_CLICK(("===> DEC_ZOOM\n"));
break;
case DEC_MINIMIZE:
fIsMinimizing = true;
fDecorator->SetMinimize(true);
STRACE_CLICK(("===> DEC_MINIMIZE\n"));
break;
case DEC_DRAG:
fIsDragging = true;
fLastMousePosition = where;
STRACE_CLICK(("===> DEC_DRAG\n"));
break;
case DEC_RESIZE:
fIsResizing = true;
fLastMousePosition = where;
STRACE_CLICK(("===> DEC_RESIZE\n"));
break;
case DEC_SLIDETAB:
fIsSlidingTab = true;
fLastMousePosition = where;
STRACE_CLICK(("===> DEC_SLIDETAB\n"));
break;
default:
break;
}
// redraw decoratpr
BRegion visibleBorder;
GetBorderRegion(&visibleBorder);
visibleBorder.IntersectWith(&VisibleRegion());
fDrawingEngine->Lock();
fDrawingEngine->ConstrainClippingRegion(&visibleBorder);
if (fIsZooming) {
fDecorator->SetZoom(true);
} else if (fIsClosing) {
fDecorator->SetClose(true);
} else if (fIsMinimizing) {
fDecorator->SetMinimize(true);
}
fDrawingEngine->Unlock();
// based on what the Decorator returned, properly place this window.
if (action == DEC_MOVETOBACK) {
fDesktop->SendWindowBehind(this);
} else {
fDesktop->SetMouseEventWindow(this);
fDesktop->ActivateWindow(this);
}
} else {
if (ViewLayer* view = ViewAt(where)) {
// clicking a simple ViewLayer
if (!IsFocus()) {
DesktopSettings desktopSettings(fDesktop);
// Activate window in case it doesn't accept first click, and
// we're not in FFM mode
if ((Flags() & B_WILL_ACCEPT_FIRST_CLICK) == 0
&& desktopSettings.MouseMode() == B_NORMAL_MOUSE)
fDesktop->ActivateWindow(this);
// eat the click if we don't accept first click
if ((Flags() & (B_WILL_ACCEPT_FIRST_CLICK | B_AVOID_FOCUS)) == 0)
return;
}
// fill out view token for the view under the mouse
*_viewToken = view->Token();
// TODO: that's not really a clean solution - maybe move that
// functionality back into the ViewLayer class
if (WorkspacesLayer* layer = dynamic_cast<WorkspacesLayer*>(view))
layer->MouseDown(message, where);
}
}
}
void
WindowLayer::MouseUp(BMessage* msg, BPoint where, int32* _viewToken)
{
bool invalidate = false;
if (fDecorator) {
click_type action = _ActionFor(msg);
// redraw decoratpr
BRegion visibleBorder;
GetBorderRegion(&visibleBorder);
visibleBorder.IntersectWith(&VisibleRegion());
fDrawingEngine->Lock();
fDrawingEngine->ConstrainClippingRegion(&visibleBorder);
if (fIsZooming) {
fDecorator->SetZoom(true);
} else if (fIsClosing) {
fDecorator->SetClose(true);
} else if (fIsMinimizing) {
fDecorator->SetMinimize(true);
}
if (fIsZooming) {
fIsZooming = false;
fDecorator->SetZoom(false);
if (action == DEC_ZOOM) {
invalidate = true;
fWindow->NotifyZoom();
}
}
if (fIsClosing) {
fIsClosing = false;
fDecorator->SetClose(false);
if (action == DEC_CLOSE) {
invalidate = true;
fWindow->NotifyQuitRequested();
}
}
if (fIsMinimizing) {
fIsMinimizing = false;
fDecorator->SetMinimize(false);
if (action == DEC_MINIMIZE) {
invalidate = true;
fWindow->NotifyMinimize(true);
}
}
fDrawingEngine->Unlock();
}
fIsDragging = false;
fIsResizing = false;
fIsSlidingTab = false;
if (ViewLayer* view = ViewAt(where))
*_viewToken = view->Token();
}
void
WindowLayer::MouseMoved(BMessage *msg, BPoint where, int32* _viewToken)
{
if (fDecorator) {
BRegion visibleBorder;
GetBorderRegion(&visibleBorder);
visibleBorder.IntersectWith(&VisibleRegion());
fDrawingEngine->Lock();
fDrawingEngine->ConstrainClippingRegion(&visibleBorder);
if (fIsZooming) {
fDecorator->SetZoom(_ActionFor(msg) == DEC_ZOOM);
} else if (fIsClosing) {
fDecorator->SetClose(_ActionFor(msg) == DEC_CLOSE);
} else if (fIsMinimizing) {
fDecorator->SetMinimize(_ActionFor(msg) == DEC_MINIMIZE);
}
fDrawingEngine->Unlock();
}
if (fIsDragging && !(Flags() & B_NOT_MOVABLE)) {
BPoint delta = where - fLastMousePosition;
fDesktop->MoveWindowBy(this, delta.x, delta.y);
}
if (fIsResizing && !(Flags() & B_NOT_RESIZABLE)) {
BPoint delta = where - fLastMousePosition;
if (Flags() & B_NOT_V_RESIZABLE)
delta.y = 0;
if (Flags() & B_NOT_H_RESIZABLE)
delta.x = 0;
fDesktop->ResizeWindowBy(this, delta.x, delta.y);
}
if (fIsSlidingTab) {
// TODO: implement
}
fLastMousePosition = where;
// change focus in FFM mode
DesktopSettings desktopSettings(fDesktop);
if (desktopSettings.MouseMode() != B_NORMAL_MOUSE && !IsFocus())
fDesktop->SetFocusWindow(this);
if (ViewLayer* view = ViewAt(where))
*_viewToken = view->Token();
}
// #pragma mark -
void
WindowLayer::WorkspaceActivated(int32 index, bool active)
{
BMessage activatedMsg(B_WORKSPACE_ACTIVATED);
activatedMsg.AddInt64("when", system_time());
activatedMsg.AddInt32("workspace", index);
activatedMsg.AddBool("active", active);
ServerWindow()->SendMessageToClient(&activatedMsg);
}
void
WindowLayer::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
{
fWorkspaces = newWorkspaces;
BMessage changedMsg(B_WORKSPACES_CHANGED);
changedMsg.AddInt64("when", system_time());
changedMsg.AddInt32("old", oldWorkspaces);
changedMsg.AddInt32("new", newWorkspaces);
ServerWindow()->SendMessageToClient(&changedMsg);
}
void
WindowLayer::Activated(bool active)
{
BMessage msg(B_WINDOW_ACTIVATED);
msg.AddBool("active", active);
ServerWindow()->SendMessageToClient(&msg);
}
//# pragma mark -
void
WindowLayer::SetTitle(const char* name, BRegion& dirty)
{
// rebuild the clipping for the title area
// and redraw it.
fTitle = name;
if (fDecorator) {
fDecorator->SetTitle(name, &dirty);
fBorderRegionValid = false;
// the border very likely changed
}
}
void
WindowLayer::SetFocus(bool focus)
{
// executed from Desktop thread
// it holds the clipping write lock,
// so the window thread cannot be
// accessing fIsFocus
BRegion dirty(fBorderRegion);
dirty.IntersectWith(&fVisibleRegion);
fDesktop->MarkDirty(dirty);
fIsFocus = focus;
if (fDecorator)
fDecorator->SetFocus(focus);
Activated(focus);
}
void
WindowLayer::SetHidden(bool hidden)
{
// the desktop takes care of dirty regions
if (fHidden != hidden) {
fHidden = hidden;
fTopLayer->SetHidden(hidden);
// TODO: anything else?
}
}
bool
WindowLayer::IsVisible() const
{
if (IsOffscreenWindow())
return true;
if (IsHidden())
return false;
/*
if (fVisibleRegion.CountRects() == 0)
return false;
*/
return fCurrentWorkspace >= 0 && fCurrentWorkspace < kWorkingList;
}
void
WindowLayer::SetSizeLimits(int32 minWidth, int32 maxWidth,
int32 minHeight, int32 maxHeight)
{
if (minWidth < 0)
minWidth = 0;
if (minHeight < 0)
minHeight = 0;
fMinWidth = minWidth;
fMaxWidth = maxWidth;
fMinHeight = minHeight;
fMaxHeight = maxHeight;
// give the Decorator a say in this too
if (fDecorator) {
fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight,
&fMaxWidth, &fMaxHeight);
}
_ObeySizeLimits();
}
void
WindowLayer::GetSizeLimits(int32* minWidth, int32* maxWidth,
int32* minHeight, int32* maxHeight) const
{
*minWidth = fMinWidth;
*maxWidth = fMaxWidth;
*minHeight = fMinHeight;
*maxHeight = fMaxHeight;
}
void
WindowLayer::SetTabLocation(float location)
{
if (fDecorator)
fDecorator->SetTabLocation(location);
}
float
WindowLayer::TabLocation() const
{
if (fDecorator)
return fDecorator->TabLocation();
return 0.0;
}
void
WindowLayer::SetLook(window_look look, BRegion* updateRegion)
{
if (fDecorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) {
// we need a new decorator
fDecorator = gDecorManager.AllocateDecorator(fDesktop, Frame(),
Title(), fLook, fFlags);
}
fLook = look;
fBorderRegionValid = false;
// the border very likely changed
fContentRegionValid = false;
// mabye a resize handle was added...
fEffectiveDrawingRegionValid = false;
// ...and therefor the drawing region is
// likely not valid anymore either
if (fDecorator != NULL) {
DesktopSettings settings(fDesktop);
fDecorator->SetLook(settings, look, updateRegion);
// we might need to resize the window!
fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight);
_ObeySizeLimits();
}
if (look == B_NO_BORDER_WINDOW_LOOK) {
// we don't need a decorator for this window
delete fDecorator;
fDecorator = NULL;
}
}
void
WindowLayer::SetFeel(window_feel feel)
{
// if the subset list is no longer needed, clear it
if ((fFeel == B_MODAL_SUBSET_WINDOW_FEEL || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL)
&& (feel != B_MODAL_SUBSET_WINDOW_FEEL && feel != B_FLOATING_SUBSET_WINDOW_FEEL))
fSubsets.MakeEmpty();
fFeel = feel;
// having modal windows with B_AVOID_FRONT or B_AVOID_FOCUS doesn't
// make that much sense, so we filter those flags out on demand
fFlags = fOriginalFlags;
fFlags &= ValidWindowFlags(fFeel);
if (!IsNormal()) {
fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
_PropagatePosition();
}
}
void
WindowLayer::SetFlags(uint32 flags, BRegion* updateRegion)
{
fOriginalFlags = flags;
fFlags = flags & ValidWindowFlags(fFeel);
if (!IsNormal())
fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0)
_PropagatePosition();
if (fDecorator == NULL)
return;
fDecorator->SetFlags(flags, updateRegion);
fBorderRegionValid = false;
// the border might have changed (smaller/larger tab)
// we might need to resize the window!
if (fDecorator) {
fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight);
_ObeySizeLimits();
}
}
/*!
\brief Returns wether or not the window is visible on the specified
workspace.
A modal or floating window may be visible on a workscreen if one
of its subset windows is visible there.
*/
bool
WindowLayer::InWorkspace(int32 index) const
{
if (IsNormal())
return (fWorkspaces & (1UL << index)) != 0;
if (fFeel == B_MODAL_ALL_WINDOW_FEEL
|| fFeel == B_FLOATING_ALL_WINDOW_FEEL)
return true;
if (fFeel == B_MODAL_APP_WINDOW_FEEL
|| fFeel == B_FLOATING_APP_WINDOW_FEEL)
return ServerWindow()->App()->InWorkspace(index);
if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL
|| fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) {
for (int32 i = 0; i < fSubsets.CountItems(); i++) {
WindowLayer* window = fSubsets.ItemAt(i);
if (window->InWorkspace(index))
return true;
}
}
return false;
}
bool
WindowLayer::SupportsFront()
{
if (fFeel == kDesktopWindowFeel)
return false;
return true;
}
bool
WindowLayer::IsModal() const
{
return IsModalFeel(fFeel);
}
bool
WindowLayer::IsFloating() const
{
return IsFloatingFeel(fFeel);
}
bool
WindowLayer::IsNormal() const
{
return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel);
}
/*!
\brief Returns the windows that's in behind of the backmost position
this window can get.
Returns NULL is this window can be the backmost window.
*/
WindowLayer*
WindowLayer::Backmost(WindowLayer* window, int32 workspace)
{
if (workspace == -1)
workspace = fCurrentWorkspace;
if (fFeel == kDesktopWindowFeel)
return NULL;
else if (fFeel == B_FLOATING_ALL_WINDOW_FEEL)
return window ? window : PreviousWindow(workspace);
if (window == NULL)
window = PreviousWindow(workspace);
for (; window != NULL; window = window->PreviousWindow(workspace)) {
if (window->IsHidden() || window == this)
continue;
if (HasInSubset(window))
return window;
}
return NULL;
}
/*!
\brief Returns the windows that's in front of the frontmost position
this window can get.
Returns NULL is this window can be the frontmost window.
*/
WindowLayer*
WindowLayer::Frontmost(WindowLayer* first, int32 workspace)
{
if (workspace == -1)
workspace = fCurrentWorkspace;
if (fFeel == kDesktopWindowFeel)
return first ? first : NextWindow(workspace);
if (fFeel == B_FLOATING_ALL_WINDOW_FEEL)
return NULL;
if (first == NULL)
first = NextWindow(workspace);
for (WindowLayer* window = first; window != NULL;
window = window->NextWindow(workspace)) {
if (window->IsHidden() || window == this)
continue;
// no one can be in front of a floating all window
if (window->Feel() == B_FLOATING_ALL_WINDOW_FEEL)
return window;
if (window->HasInSubset(this))
return window;
}
return NULL;
}
bool
WindowLayer::AddToSubset(WindowLayer* window)
{
return fSubsets.AddItem(window);
}
void
WindowLayer::RemoveFromSubset(WindowLayer* window)
{
fSubsets.RemoveItem(window);
}
bool
WindowLayer::HasInSubset(WindowLayer* window)
{
if (fFeel == B_MODAL_APP_WINDOW_FEEL && window->Feel() == B_MODAL_ALL_WINDOW_FEEL
|| fFeel == B_NORMAL_WINDOW_FEEL
|| fFeel == window->Feel())
return false;
if (fFeel == B_FLOATING_ALL_WINDOW_FEEL
|| fFeel == B_MODAL_ALL_WINDOW_FEEL)
return true;
if (fFeel == B_FLOATING_APP_WINDOW_FEEL
|| fFeel == B_MODAL_APP_WINDOW_FEEL)
return window->ServerWindow()->App() == ServerWindow()->App();
return fSubsets.HasItem(window);
}
bool
WindowLayer::SameSubset(WindowLayer* window)
{
// TODO: this is probably not needed at all, but it doesn't hurt to have it in svn
if (fFeel == B_MODAL_ALL_WINDOW_FEEL || window->Feel() == B_MODAL_ALL_WINDOW_FEEL)
return fFeel == window->Feel();
if (fFeel == B_MODAL_APP_WINDOW_FEEL || window->Feel() == B_MODAL_APP_WINDOW_FEEL)
return ServerWindow()->App() == window->ServerWindow()->App();
if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL) {
// we basically need to check if the subsets have anything in common
for (int32 i = fSubsets.CountItems(); i-- > 0;) {
if (window->HasInSubset(fSubsets.ItemAt(i)))
return true;
}
}
if (window->Feel() == B_MODAL_SUBSET_WINDOW_FEEL) {
for (int32 i = window->fSubsets.CountItems(); i-- > 0;) {
if (HasInSubset(window->fSubsets.ItemAt(i)))
return true;
}
}
return false;
}
uint32
WindowLayer::SubsetWorkspaces() const
{
if (fFeel == B_MODAL_ALL_WINDOW_FEEL
|| fFeel == B_FLOATING_ALL_WINDOW_FEEL)
return B_ALL_WORKSPACES;
if (fFeel == B_MODAL_APP_WINDOW_FEEL
|| fFeel == B_FLOATING_APP_WINDOW_FEEL)
return ServerWindow()->App()->Workspaces();
if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL
|| fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) {
uint32 workspaces = 0;
for (int32 i = 0; i < fSubsets.CountItems(); i++) {
WindowLayer* window = fSubsets.ItemAt(i);
if (!window->IsHidden())
workspaces |= window->Workspaces();
}
return workspaces;
}
return 0;
}
// #pragma mark - static
/*static*/
bool
WindowLayer::IsValidLook(window_look look)
{
return look == B_TITLED_WINDOW_LOOK
|| look == B_DOCUMENT_WINDOW_LOOK
|| look == B_MODAL_WINDOW_LOOK
|| look == B_FLOATING_WINDOW_LOOK
|| look == B_BORDERED_WINDOW_LOOK
|| look == B_NO_BORDER_WINDOW_LOOK
|| look == kDesktopWindowLook
|| look == kLeftTitledWindowLook;
}
/*static*/
bool
WindowLayer::IsValidFeel(window_feel feel)
{
return feel == B_NORMAL_WINDOW_FEEL
|| feel == B_MODAL_SUBSET_WINDOW_FEEL
|| feel == B_MODAL_APP_WINDOW_FEEL
|| feel == B_MODAL_ALL_WINDOW_FEEL
|| feel == B_FLOATING_SUBSET_WINDOW_FEEL
|| feel == B_FLOATING_APP_WINDOW_FEEL
|| feel == B_FLOATING_ALL_WINDOW_FEEL
|| feel == kDesktopWindowFeel
|| feel == kMenuWindowFeel
|| feel == kWindowScreenFeel;
}
/*static*/
bool
WindowLayer::IsModalFeel(window_feel feel)
{
return feel == B_MODAL_SUBSET_WINDOW_FEEL
|| feel == B_MODAL_APP_WINDOW_FEEL
|| feel == B_MODAL_ALL_WINDOW_FEEL;
}
/*static*/
bool
WindowLayer::IsFloatingFeel(window_feel feel)
{
return feel == B_FLOATING_SUBSET_WINDOW_FEEL
|| feel == B_FLOATING_APP_WINDOW_FEEL
|| feel == B_FLOATING_ALL_WINDOW_FEEL;
}
/*static*/
uint32
WindowLayer::ValidWindowFlags()
{
return B_NOT_MOVABLE | B_NOT_CLOSABLE | B_NOT_ZOOMABLE
| B_NOT_MINIMIZABLE | B_NOT_RESIZABLE
| B_NOT_H_RESIZABLE | B_NOT_V_RESIZABLE
| B_AVOID_FRONT | B_AVOID_FOCUS
| B_WILL_ACCEPT_FIRST_CLICK | B_OUTLINE_RESIZE
| B_NO_WORKSPACE_ACTIVATION
| B_NOT_ANCHORED_ON_ACTIVATE
| B_ASYNCHRONOUS_CONTROLS
| B_QUIT_ON_WINDOW_CLOSE
| B_SAME_POSITION_IN_ALL_WORKSPACES
| kWorkspacesWindowFlag
| kWindowScreenFlag;
}
/*static*/
uint32
WindowLayer::ValidWindowFlags(window_feel feel)
{
uint32 flags = ValidWindowFlags();
if (IsModalFeel(feel))
return flags & ~(B_AVOID_FOCUS | B_AVOID_FRONT);
return flags;
}
// #pragma mark - private
// _ShiftPartOfRegion
void
WindowLayer::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
int32 xOffset, int32 yOffset)
{
BRegion common(*regionToShift);
// see if there is a common part at all
common.IntersectWith(region);
if (common.CountRects() > 0) {
// cut the common part from the region,
// offset that to destination and include again
region->Exclude(&common);
common.OffsetBy(xOffset, yOffset);
region->Include(&common);
}
}
void
WindowLayer::_TriggerContentRedraw(BRegion& dirtyContentRegion)
{
if (dirtyContentRegion.CountRects() > 0) {
// send UPDATE message to the client
_TransferToUpdateSession(&dirtyContentRegion);
if (!fContentRegionValid)
_UpdateContentRegion();
if (fDrawingEngine->Lock()) {
fDrawingEngine->ConstrainClippingRegion(&dirtyContentRegion);
#if DELAYED_BACKGROUND_CLEARING
fTopLayer->Draw(fDrawingEngine, &dirtyContentRegion,
&fContentRegion, false);
#else
fTopLayer->Draw(fDrawingEngine, &dirtyContentRegion,
&fContentRegion, true);
#endif
fDrawingEngine->Unlock();
}
}
}
void
WindowLayer::_DrawBorder()
{
// this is executed in the window thread, but only
// in respond to a REDRAW message having been received, the
// clipping lock is held for reading
if (!fDecorator)
return;
// construct the region of the border that needs redrawing
BRegion dirtyBorderRegion;
GetBorderRegion(&dirtyBorderRegion);
// intersect with our visible region
dirtyBorderRegion.IntersectWith(&fVisibleRegion);
// intersect with the dirty region
dirtyBorderRegion.IntersectWith(&fDirtyRegion);
if (dirtyBorderRegion.CountRects() > 0) {
// TODO: decorator drawing with update region...
fDrawingEngine->ConstrainClippingRegion(&dirtyBorderRegion);
fDecorator->Draw(dirtyBorderRegion.Frame());
fDrawingEngine->ConstrainClippingRegion(NULL);
}
}
/*!
pre: the clipping is readlocked (this function is
only called from _TriggerContentRedraw()), which
in turn is only called from MessageReceived() with
the clipping lock held
*/
void
WindowLayer::_TransferToUpdateSession(BRegion* contentDirtyRegion)
{
if (contentDirtyRegion->CountRects() <= 0)
return;
// add to pending
fPendingUpdateSession.SetUsed(true);
fPendingUpdateSession.Include(contentDirtyRegion);
// clip pending update session from current
// update session, it makes no sense to draw stuff
// already needing a redraw anyways. Theoretically,
// this could be done smarter (clip layers from pending
// that have not yet been redrawn in the current update
// session)
if (fCurrentUpdateSession.IsUsed()) {
fCurrentUpdateSession.Exclude(contentDirtyRegion);
fEffectiveDrawingRegionValid = false;
}
if (!fUpdateRequested) {
// send this to client
_SendUpdateMessage();
// as long as we have not received
// the "begin update" command, the
// pending session does not become the
// current
// TODO: problem: since we sent the update regions frame,
// we should not add to the pending session after we
// sent the update message!!!
} else {
if (!fCurrentUpdateSession.IsUsed())
fprintf(stderr, "WindowLayer(%s)::_TransferToUpdateSession() - pending region changed before BeginUpdate()!\n", Title());
}
}
// _SendUpdateMessage
void
WindowLayer::_SendUpdateMessage()
{
BMessage message(_UPDATE_);
BRect updateRect = fPendingUpdateSession.DirtyRegion().Frame();
updateRect.OffsetBy(-fFrame.left, -fFrame.top);
message.AddRect("_rect", updateRect);
ServerWindow()->SendMessageToClient(&message);
fUpdateRequested = true;
// TODO: the toggling between the update sessions is too
// expensive, optimize with some pointer tricks
fCurrentUpdateSession = fPendingUpdateSession;
fPendingUpdateSession.SetUsed(false);
}
void
WindowLayer::BeginUpdate()
{
// NOTE: since we might "shift" parts of the
// internal dirty regions from the desktop thread
// in response to WindowLayer::ResizeBy(), which
// might move arround views, this function needs to block
// on the global clipping lock so that the internal
// dirty regions are not messed with from the Desktop thread
// and ServerWindow thread at the same time.
if (!fDesktop->LockSingleWindow())
return;
if (fUpdateRequested && fCurrentUpdateSession.IsUsed()) {
// all drawing command from the client
// will have the dirty region from the update
// session enforced
fInUpdate = true;
fEffectiveDrawingRegionValid = false;
}
fDesktop->UnlockSingleWindow();
}
void
WindowLayer::EndUpdate()
{
// NOTE: see comment in _BeginUpdate()
if (!fDesktop->LockSingleWindow())
return;
if (fInUpdate) {
fCurrentUpdateSession.SetUsed(false);
fInUpdate = false;
fEffectiveDrawingRegionValid = false;
}
if (fPendingUpdateSession.IsUsed()) {
// send this to client
_SendUpdateMessage();
} else {
fUpdateRequested = false;
}
fDesktop->UnlockSingleWindow();
}
void
WindowLayer::_UpdateContentRegion()
{
// TODO: speed up by avoiding "Exclude()"
// start from the frame, extend to include decorator border
fContentRegion.Set(fFrame);
// resize handle
if (fDecorator) {
if (!fBorderRegionValid)
GetBorderRegion(&fBorderRegion);
fContentRegion.Exclude(&fBorderRegion);
}
fContentRegionValid = true;
}
click_type
WindowLayer::_ActionFor(const BMessage* msg) const
{
BPoint where(0, 0);
int32 buttons = 0;
int32 modifiers = 0;
msg->FindPoint("where", &where);
msg->FindInt32("buttons", &buttons);
msg->FindInt32("modifiers", &modifiers);
if (fDecorator)
return fDecorator->Clicked(where, buttons, modifiers);
return DEC_NONE;
}
// _ObeySizeLimits
void
WindowLayer::_ObeySizeLimits()
{
// make sure we even have valid size limits
if (fMaxWidth < fMinWidth)
fMaxWidth = fMinWidth;
if (fMaxHeight < fMinHeight)
fMaxHeight = fMinHeight;
// Automatically resize the window to fit these new limits
// if it does not already.
// On R5, Windows don't automatically resize, but since
// BWindow::ResizeTo() even honors the limits, I would guess
// this is a bug that we don't have to adopt.
// Note that most current apps will do unnecessary resizing
// after having set the limits, but the overhead is neglible.
float minWidthDiff = fMinWidth - fFrame.Width();
float minHeightDiff = fMinHeight - fFrame.Height();
float maxWidthDiff = fMaxWidth - fFrame.Width();
float maxHeightDiff = fMaxHeight - fFrame.Height();
float xDiff = 0.0;
if (minWidthDiff > 0.0) // we're currently smaller than minWidth
xDiff = minWidthDiff;
else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth
xDiff = maxWidthDiff;
float yDiff = 0.0;
if (minHeightDiff > 0.0) // we're currently smaller than minHeight
yDiff = minHeightDiff;
else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight
yDiff = maxHeightDiff;
if (fDesktop)
fDesktop->ResizeWindowBy(this, xDiff, yDiff);
else
ResizeBy(xDiff, yDiff, NULL);
}
// #pragma mark - UpdateSession
// constructor
WindowLayer::UpdateSession::UpdateSession()
: fDirtyRegion(),
fInUse(false)
{
}
// destructor
WindowLayer::UpdateSession::~UpdateSession()
{
}
// Include
void
WindowLayer::UpdateSession::Include(BRegion* additionalDirty)
{
fDirtyRegion.Include(additionalDirty);
}
void
WindowLayer::UpdateSession::Exclude(BRegion* dirtyInNextSession)
{
fDirtyRegion.Exclude(dirtyInNextSession);
}
void
WindowLayer::UpdateSession::MoveBy(int32 x, int32 y)
{
fDirtyRegion.OffsetBy(x, y);
}
void
WindowLayer::UpdateSession::SetUsed(bool used)
{
fInUse = used;
if (!fInUse)
fDirtyRegion.MakeEmpty();
}
WindowLayer::UpdateSession&
WindowLayer::UpdateSession::operator=(const WindowLayer::UpdateSession& other)
{
fDirtyRegion = other.fDirtyRegion;
fInUse = other.fInUse;
return *this;
}