Adi and I have had long talks about better approaches to clipping and we are convinced that a different design can significantly speed up the clipping processing in the root layer thread. This is a first prototype implementing the new ideas. Lots of features are missing yet, but Adi asked me to commit it now, so that we can both continue to work on it. The purpose of the new design is to significantly reduce the computations during an atomic clipping update, and also to scale much better with many more open windows.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15101 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,573 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Bitmap.h>
|
||||
#include <Message.h>
|
||||
#include <MessageQueue.h>
|
||||
#include <Messenger.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "DrawingEngine.h"
|
||||
#include "WindowLayer.h"
|
||||
|
||||
#include "Desktop.h"
|
||||
|
||||
// constructor
|
||||
Desktop::Desktop(DrawView* drawView)
|
||||
: BLooper("desktop"),
|
||||
fTracking(false),
|
||||
fLastMousePos(-1.0, -1.0),
|
||||
fClickedWindow(NULL),
|
||||
fResizing(false),
|
||||
fIs2ndButton(false),
|
||||
|
||||
fClippingLock("clipping lock"),
|
||||
fDirtyRegion(),
|
||||
fBackgroundRegion(),
|
||||
|
||||
fDrawView(drawView),
|
||||
fDrawingEngine(fDrawView->GetDrawingEngine()),
|
||||
|
||||
fWindows(64)
|
||||
{
|
||||
fDrawView->SetDesktop(this);
|
||||
|
||||
BRegion stillAvailableOnScreen;
|
||||
_RebuildClippingForAllWindows(&stillAvailableOnScreen);
|
||||
_SetBackground(&stillAvailableOnScreen);
|
||||
}
|
||||
|
||||
// destructor
|
||||
Desktop::~Desktop()
|
||||
{
|
||||
int32 count = CountWindows();
|
||||
for (int32 i = count - 1; i >= 0; i--) {
|
||||
WindowLayer* window = WindowAtFast(i);
|
||||
window->Lock();
|
||||
window->Quit();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw
|
||||
void
|
||||
Desktop::Draw(BRect updateRect)
|
||||
{
|
||||
// since parts of the view might have been exposed,
|
||||
// we need a clipping rebuild
|
||||
if (LockClipping()) {
|
||||
BRegion background;
|
||||
_RebuildClippingForAllWindows(&background);
|
||||
_SetBackground(&background);
|
||||
|
||||
UnlockClipping();
|
||||
}
|
||||
if (fDrawingEngine->Lock()) {
|
||||
fDrawingEngine->SetHighColor(51, 102, 152);
|
||||
fDrawingEngine->FillRegion(&fBackgroundRegion);
|
||||
fDrawingEngine->Unlock();
|
||||
}
|
||||
|
||||
// trigger redrawing windows
|
||||
BRegion update(updateRect);
|
||||
update.Exclude(&fBackgroundRegion);
|
||||
MarkDirty(&update);
|
||||
}
|
||||
|
||||
// MouseDown
|
||||
void
|
||||
Desktop::MouseDown(BPoint where, uint32 buttons)
|
||||
{
|
||||
fLastMousePos = where;
|
||||
fClickedWindow = WindowAt(where);
|
||||
fClickTime = system_time();
|
||||
if (buttons == B_PRIMARY_MOUSE_BUTTON) {
|
||||
fTracking = true;
|
||||
if (fClickedWindow) {
|
||||
BRect frame(fClickedWindow->Frame());
|
||||
BRect resizeRect(frame.right - 10, frame.bottom - 10,
|
||||
frame.right + 4, frame.bottom + 4);
|
||||
fResizing = resizeRect.Contains(where);
|
||||
}
|
||||
} else if (buttons == B_SECONDARY_MOUSE_BUTTON) {
|
||||
if (fClickedWindow)
|
||||
SendToBack(fClickedWindow);
|
||||
|
||||
fIs2ndButton = true;
|
||||
} else if (buttons == B_TERTIARY_MOUSE_BUTTON) {
|
||||
if (modifiers() & B_SHIFT_KEY) {
|
||||
// render global dirty region
|
||||
if (fDrawingEngine->Lock()) {
|
||||
fDrawingEngine->SetHighColor(255, 0, 0);
|
||||
fDrawingEngine->FillRegion(&fDirtyRegion);
|
||||
fDrawingEngine->MarkDirty(&fDirtyRegion);
|
||||
fDrawingEngine->Unlock();
|
||||
}
|
||||
} else {
|
||||
// complete redraw
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
// TODO: broken
|
||||
BRegion region(Bounds());
|
||||
MarkDirty(®ion);
|
||||
region = fBackgroundRegion;
|
||||
fBackgroundRegion.MakeEmpty();
|
||||
_SetBackground(®ion);
|
||||
#else
|
||||
fDrawingEngine->MarkDirty();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MouseUp
|
||||
void
|
||||
Desktop::MouseUp(BPoint where)
|
||||
{
|
||||
if (!fIs2ndButton && system_time() - fClickTime < 250000L && fClickedWindow) {
|
||||
BringToFront(fClickedWindow);
|
||||
}
|
||||
fTracking = false;
|
||||
fIs2ndButton = false;
|
||||
fClickedWindow = NULL;
|
||||
}
|
||||
|
||||
// MouseMoved
|
||||
void
|
||||
Desktop::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
|
||||
{
|
||||
if (fTracking) {
|
||||
int32 dx = (int32)(where.x - fLastMousePos.x);
|
||||
int32 dy = (int32)(where.y - fLastMousePos.y);
|
||||
fLastMousePos = where;
|
||||
|
||||
if (dx != 0 || dy != 0) {
|
||||
if (fClickedWindow) {
|
||||
if (fResizing) {
|
||||
ResizeWindowBy(fClickedWindow, dx, dy);
|
||||
} else {
|
||||
MoveWindowBy(fClickedWindow, dx, dy);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (fIs2ndButton) {
|
||||
if (fDrawingEngine->Lock()) {
|
||||
fDrawingEngine->SetHighColor(0, 0, 0);
|
||||
fDrawingEngine->StrokeLine(fLastMousePos, where);
|
||||
|
||||
BRect dirty(fLastMousePos, where);
|
||||
if (dirty.left > dirty.right) {
|
||||
dirty.left = where.x;
|
||||
dirty.right = fLastMousePos.x;
|
||||
}
|
||||
if (dirty.top > dirty.bottom) {
|
||||
dirty.top = where.y;
|
||||
dirty.bottom = fLastMousePos.y;
|
||||
}
|
||||
fDrawingEngine->MarkDirty(dirty);
|
||||
|
||||
fDrawingEngine->Unlock();
|
||||
}
|
||||
fLastMousePos = where;
|
||||
}
|
||||
}
|
||||
|
||||
// MessageReceived
|
||||
void
|
||||
Desktop::MessageReceived(BMessage* message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case B_MOUSE_DOWN: {
|
||||
BPoint where;
|
||||
uint32 buttons;
|
||||
if (message->FindPoint("where", &where) >= B_OK &&
|
||||
message->FindInt32("buttons", (int32*)&buttons) >= B_OK) {
|
||||
|
||||
MouseDown(where, buttons);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case B_MOUSE_UP: {
|
||||
BPoint where;
|
||||
if (message->FindPoint("where", &where) >= B_OK) {
|
||||
|
||||
MouseUp(where);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case B_MOUSE_MOVED: {
|
||||
if (!MessageQueue()->FindMessage(B_MOUSE_MOVED, 0)) {
|
||||
BPoint where;
|
||||
uint32 transit;
|
||||
if (message->FindPoint("where", &where) >= B_OK &&
|
||||
message->FindInt32("be:transit", (int32*)&transit) >= B_OK) {
|
||||
|
||||
MouseMoved(where, transit, NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MSG_DRAW: {
|
||||
BRect area;
|
||||
if (message->FindRect("area", &area) >= B_OK)
|
||||
Draw(area);
|
||||
}
|
||||
case MSG_ADD_WINDOW: {
|
||||
WindowLayer* window;
|
||||
if (message->FindPointer("window", (void**)&window) >= B_OK)
|
||||
AddWindow(window);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLooper::MessageReceived(message);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
// AddWindow
|
||||
bool
|
||||
Desktop::AddWindow(WindowLayer* window)
|
||||
{
|
||||
bool success = false;
|
||||
if (fWindows.AddItem((void*)window)) {
|
||||
// rebuild the entire screen clipping and draw the new window
|
||||
if (LockClipping()) {
|
||||
BRegion background;
|
||||
_RebuildClippingForAllWindows(&background);
|
||||
fBackgroundRegion.Exclude(&window->VisibleRegion());
|
||||
MarkDirty(&window->VisibleRegion());
|
||||
_SetBackground(&background);
|
||||
|
||||
UnlockClipping();
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// RemoveWindow
|
||||
bool
|
||||
Desktop::RemoveWindow(WindowLayer* window)
|
||||
{
|
||||
bool success = false;
|
||||
if (fWindows.RemoveItem((void*)window)) {
|
||||
// rebuild the entire screen clipping and redraw the exposed windows
|
||||
if (LockClipping()) {
|
||||
BRegion dirty = window->VisibleRegion();
|
||||
BRegion background;
|
||||
_RebuildClippingForAllWindows(&background);
|
||||
MarkDirty(&dirty);
|
||||
_SetBackground(&background);
|
||||
|
||||
UnlockClipping();
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// IndexOf
|
||||
int32
|
||||
Desktop::IndexOf(WindowLayer* window) const
|
||||
{
|
||||
return fWindows.IndexOf((void*)window);
|
||||
}
|
||||
|
||||
// CountWindows
|
||||
int32
|
||||
Desktop::CountWindows() const
|
||||
{
|
||||
return fWindows.CountItems();
|
||||
}
|
||||
|
||||
// HasWindow
|
||||
bool
|
||||
Desktop::HasWindow(WindowLayer* window) const
|
||||
{
|
||||
return fWindows.HasItem((void*)window);
|
||||
}
|
||||
|
||||
// WindowAt
|
||||
WindowLayer*
|
||||
Desktop::WindowAt(int32 index) const
|
||||
{
|
||||
return (WindowLayer*)fWindows.ItemAt(index);
|
||||
}
|
||||
|
||||
// WindowAtFast
|
||||
WindowLayer*
|
||||
Desktop::WindowAtFast(int32 index) const
|
||||
{
|
||||
return (WindowLayer*)fWindows.ItemAtFast(index);
|
||||
}
|
||||
|
||||
// WindowAt
|
||||
WindowLayer*
|
||||
Desktop::WindowAt(const BPoint& where) const
|
||||
{
|
||||
// NOTE, since the clipping is only changed from this thread,
|
||||
// it is save to use it without locking
|
||||
int32 count = CountWindows();
|
||||
for (int32 i = count - 1; i >= 0; i--) {
|
||||
WindowLayer* window = WindowAtFast(i);
|
||||
if (window->VisibleRegion().Contains(where))
|
||||
return window;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TopWindow
|
||||
WindowLayer*
|
||||
Desktop::TopWindow() const
|
||||
{
|
||||
return (WindowLayer*)fWindows.LastItem();
|
||||
}
|
||||
|
||||
// BottomWindow
|
||||
WindowLayer*
|
||||
Desktop::BottomWindow() const
|
||||
{
|
||||
return (WindowLayer*)fWindows.FirstItem();
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
// MoveWindowBy
|
||||
void
|
||||
Desktop::MoveWindowBy(WindowLayer* window, int32 x, int32 y)
|
||||
{
|
||||
if (LockClipping()) {
|
||||
// the dirty region starts with the visible area of the window being moved
|
||||
BRegion newDirtyRegion(window->VisibleRegion());
|
||||
BRegion alreadyDirtyRegion(fDirtyRegion);
|
||||
// we have to move along the part of the current dirty region
|
||||
// that intersects with the window being moved
|
||||
alreadyDirtyRegion.IntersectWith(&window->VisibleRegion());
|
||||
|
||||
window->MoveBy(x, y);
|
||||
|
||||
BRegion background;
|
||||
_RebuildClippingForAllWindows(&background);
|
||||
|
||||
// construct the region that is possible to be blitted
|
||||
// to move the contents of the window
|
||||
BRegion copyRegion(window->VisibleRegion());
|
||||
copyRegion.OffsetBy(-x, -y);
|
||||
copyRegion.IntersectWith(&newDirtyRegion);
|
||||
|
||||
// include the the new visible region of the window being
|
||||
// moved into the dirty region (for now)
|
||||
newDirtyRegion.Include(&window->VisibleRegion());
|
||||
|
||||
if (fDrawingEngine->Lock()) {
|
||||
fDrawingEngine->CopyRegion(©Region, x, y);
|
||||
|
||||
// in the dirty region, exclude the parts that we
|
||||
// could move by blitting
|
||||
copyRegion.OffsetBy(x, y);
|
||||
newDirtyRegion.Exclude(©Region);
|
||||
fDrawingEngine->MarkDirty(©Region);
|
||||
|
||||
fDrawingEngine->Unlock();
|
||||
}
|
||||
// include the moved peviously dirty region
|
||||
// TODO: redesign dirty regions to be located in
|
||||
// each window -> less intersecting
|
||||
alreadyDirtyRegion.OffsetBy(x, y);
|
||||
newDirtyRegion.Include(&alreadyDirtyRegion);
|
||||
|
||||
MarkDirty(&newDirtyRegion);
|
||||
_SetBackground(&background);
|
||||
|
||||
UnlockClipping();
|
||||
}
|
||||
}
|
||||
|
||||
// ResizeWindowBy
|
||||
void
|
||||
Desktop::ResizeWindowBy(WindowLayer* window, int32 x, int32 y)
|
||||
{
|
||||
if (LockClipping()) {
|
||||
BRegion newDirtyRegion;
|
||||
BRegion previouslyOccupiedRegion(window->VisibleRegion());
|
||||
|
||||
window->ResizeBy(x, y, &newDirtyRegion);
|
||||
|
||||
BRegion background;
|
||||
_RebuildClippingForAllWindows(&background);
|
||||
|
||||
previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
|
||||
|
||||
newDirtyRegion.IntersectWith(&window->VisibleRegion());
|
||||
newDirtyRegion.Include(&previouslyOccupiedRegion);
|
||||
|
||||
MarkDirty(&newDirtyRegion);
|
||||
_SetBackground(&background);
|
||||
|
||||
UnlockClipping();
|
||||
}
|
||||
}
|
||||
|
||||
// BringToFront
|
||||
void
|
||||
Desktop::BringToFront(WindowLayer* window)
|
||||
{
|
||||
if (window == TopWindow())
|
||||
return;
|
||||
|
||||
if (LockClipping()) {
|
||||
|
||||
// we don't need to redraw what is currently
|
||||
// visible of the window
|
||||
BRegion clean(window->VisibleRegion());
|
||||
|
||||
// detach window and re-atach at last position
|
||||
if (fWindows.RemoveItem((void*)window) &&
|
||||
fWindows.AddItem((void*)window)) {
|
||||
|
||||
BRegion dummy;
|
||||
_RebuildClippingForAllWindows(&dummy);
|
||||
|
||||
// redraw what became visible of the window
|
||||
BRegion dirty(window->VisibleRegion());
|
||||
dirty.Exclude(&clean);
|
||||
|
||||
MarkDirty(&dirty);
|
||||
}
|
||||
|
||||
UnlockClipping();
|
||||
}
|
||||
}
|
||||
|
||||
// SendToBack
|
||||
void
|
||||
Desktop::SendToBack(WindowLayer* window)
|
||||
{
|
||||
if (window == BottomWindow())
|
||||
return;
|
||||
|
||||
if (LockClipping()) {
|
||||
|
||||
// what is currently visible of the window
|
||||
// might be dirty after the window is send to back
|
||||
BRegion dirty(window->VisibleRegion());
|
||||
|
||||
// detach window and re-atach at last position
|
||||
if (fWindows.RemoveItem((void*)window) &&
|
||||
fWindows.AddItem((void*)window, 0)) {
|
||||
|
||||
BRegion dummy;
|
||||
_RebuildClippingForAllWindows(&dummy);
|
||||
|
||||
// redraw what was previously visible of the window
|
||||
BRegion clean(window->VisibleRegion());
|
||||
dirty.Exclude(&clean);
|
||||
|
||||
MarkDirty(&dirty);
|
||||
}
|
||||
|
||||
UnlockClipping();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
// MarkDirty
|
||||
void
|
||||
Desktop::MarkDirty(BRegion* region)
|
||||
{
|
||||
if (region->CountRects() == 0)
|
||||
return;
|
||||
|
||||
// NOTE: the idea is that for all dirty areas ever included
|
||||
// in the culmulative dirty region, redraw messages have been
|
||||
// sent to the windows affected by just the newly included
|
||||
// area. Therefor, _TriggerWindowRedrawing() is not called
|
||||
// with "fDirtyRegion", but with just the new "region" instead.
|
||||
// Whenever a window is actually carrying out a redraw request,
|
||||
// it is expected to remove the redrawn area from the dirty region.
|
||||
|
||||
if (LockClipping()) {
|
||||
// add the new dirty region to the culmulative dirty region
|
||||
fDirtyRegion.Include(region);
|
||||
// send redraw messages to all windows intersecting the dirty region
|
||||
_TriggerWindowRedrawing(region);
|
||||
|
||||
UnlockClipping();
|
||||
}
|
||||
}
|
||||
|
||||
// MarkClean
|
||||
void
|
||||
Desktop::MarkClean(BRegion* region)
|
||||
{
|
||||
if (LockClipping()) {
|
||||
// remove the clean region from the culmulative dirty region
|
||||
fDirtyRegion.Exclude(region);
|
||||
|
||||
UnlockClipping();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
// _RebuildClippingForAllWindows
|
||||
void
|
||||
Desktop::_RebuildClippingForAllWindows(BRegion* stillAvailableOnScreen)
|
||||
{
|
||||
// the available region on screen starts with the entire screen area
|
||||
// each window on the screen will take a portion from that area
|
||||
|
||||
// figure out what the entire screen area is
|
||||
if (!fDrawView->Window())
|
||||
stillAvailableOnScreen->Set(fDrawView->Bounds());
|
||||
else {
|
||||
if (fDrawView->Window()->Lock()) {
|
||||
fDrawView->GetClippingRegion(stillAvailableOnScreen);
|
||||
fDrawView->Window()->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// set clipping of each window
|
||||
int32 count = CountWindows();
|
||||
for (int32 i = count - 1; i >= 0; i--) {
|
||||
WindowLayer* window = WindowAtFast(i);
|
||||
window->SetClipping(stillAvailableOnScreen);
|
||||
// that windows region is not available on screen anymore
|
||||
stillAvailableOnScreen->Exclude(&window->VisibleRegion());
|
||||
}
|
||||
}
|
||||
|
||||
// _TriggerWindowRedrawing
|
||||
void
|
||||
Desktop::_TriggerWindowRedrawing(BRegion* newDirtyRegion)
|
||||
{
|
||||
// send redraw messages to all windows intersecting the dirty region
|
||||
int32 count = CountWindows();
|
||||
for (int32 i = count - 1; i >= 0; i--) {
|
||||
WindowLayer* window = WindowAtFast(i);
|
||||
if (newDirtyRegion->Intersects(window->VisibleRegion().Frame()))
|
||||
window->PostMessage(MSG_REDRAW);
|
||||
}
|
||||
}
|
||||
|
||||
// _SetBackground
|
||||
void
|
||||
Desktop::_SetBackground(BRegion* background)
|
||||
{
|
||||
// remember the region not covered by any windows
|
||||
// and redraw the dirty background
|
||||
BRegion dirtyBackground(*background);
|
||||
dirtyBackground.Exclude(&fBackgroundRegion);
|
||||
dirtyBackground.IntersectWith(background);
|
||||
fBackgroundRegion = *background;
|
||||
if (dirtyBackground.Frame().IsValid()) {
|
||||
if (fDrawingEngine->Lock()) {
|
||||
fDrawingEngine->SetHighColor(51, 102, 152);
|
||||
fDrawingEngine->FillRegion(&dirtyBackground);
|
||||
fDrawingEngine->MarkDirty(&dirtyBackground);
|
||||
|
||||
fDrawingEngine->Unlock();
|
||||
}
|
||||
MarkClean(&dirtyBackground);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
|
||||
#ifndef DESKTOP_H
|
||||
#define DESKTOP_H
|
||||
|
||||
#include <List.h>
|
||||
#include <Locker.h>
|
||||
#include <Region.h>
|
||||
#include <View.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "DrawingEngine.h"
|
||||
#include "MultiLocker.h"
|
||||
|
||||
class WindowLayer;
|
||||
|
||||
enum {
|
||||
MSG_ADD_WINDOW = 'addw',
|
||||
MSG_DRAW = 'draw',
|
||||
};
|
||||
|
||||
#define MULTI_LOCKER 0
|
||||
|
||||
class Desktop : public BLooper {
|
||||
public:
|
||||
Desktop(DrawView* drawView);
|
||||
virtual ~Desktop();
|
||||
|
||||
// functions for the DrawView
|
||||
void Draw(BRect updateRect);
|
||||
|
||||
void MouseDown(BPoint where, uint32 buttons);
|
||||
void MouseUp(BPoint where);
|
||||
void MouseMoved(BPoint where, uint32 code,
|
||||
const BMessage* dragMessage);
|
||||
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
bool AddWindow(WindowLayer* window);
|
||||
bool RemoveWindow(WindowLayer* window);
|
||||
int32 IndexOf(WindowLayer* window) const;
|
||||
int32 CountWindows() const;
|
||||
bool HasWindow(WindowLayer* window) const;
|
||||
|
||||
WindowLayer* WindowAt(int32 index) const;
|
||||
WindowLayer* WindowAtFast(int32 index) const;
|
||||
WindowLayer* WindowAt(const BPoint& where) const;
|
||||
WindowLayer* TopWindow() const;
|
||||
WindowLayer* BottomWindow() const;
|
||||
|
||||
// doing something with the windows
|
||||
void MoveWindowBy(WindowLayer* window, int32 x, int32 y);
|
||||
void ResizeWindowBy(WindowLayer* window, int32 x, int32 y);
|
||||
|
||||
void BringToFront(WindowLayer* window);
|
||||
void SendToBack(WindowLayer* window);
|
||||
|
||||
#if MULTI_LOCKER
|
||||
# if 0
|
||||
bool ReadLockClipping() { return fClippingLock.ReadLock(); }
|
||||
void ReadUnlockClipping() { fClippingLock.ReadUnlock(); }
|
||||
# else
|
||||
bool ReadLockClipping() { return fClippingLock.WriteLock(); }
|
||||
void ReadUnlockClipping() { fClippingLock.WriteUnlock(); }
|
||||
# endif
|
||||
#else
|
||||
bool ReadLockClipping() { return fClippingLock.LockWithTimeout(10000) >= B_OK; }
|
||||
void ReadUnlockClipping() { fClippingLock.Unlock(); }
|
||||
#endif
|
||||
|
||||
bool LockClipping() { return fClippingLock.Lock(); }
|
||||
void UnlockClipping() { fClippingLock.Unlock(); }
|
||||
|
||||
void MarkDirty(BRegion* region);
|
||||
void MarkClean(BRegion* region);
|
||||
BRegion* DirtyRegion()
|
||||
{ return &fDirtyRegion; }
|
||||
|
||||
DrawingEngine* GetDrawingEngine() const
|
||||
{ return fDrawingEngine; }
|
||||
|
||||
BRegion& BackgroundRegion()
|
||||
{ return fBackgroundRegion; }
|
||||
|
||||
private:
|
||||
void _RebuildClippingForAllWindows(BRegion* stillAvailableOnScreen);
|
||||
void _TriggerWindowRedrawing(BRegion* newDirtyRegion);
|
||||
void _SetBackground(BRegion* background);
|
||||
|
||||
bool fTracking;
|
||||
BPoint fLastMousePos;
|
||||
WindowLayer* fClickedWindow;
|
||||
bool fResizing;
|
||||
bigtime_t fClickTime;
|
||||
bool fIs2ndButton;
|
||||
|
||||
#if MULTI_LOCKER
|
||||
MultiLocker fClippingLock;
|
||||
#else
|
||||
BLocker fClippingLock;
|
||||
#endif
|
||||
BRegion fDirtyRegion;
|
||||
BRegion fBackgroundRegion;
|
||||
|
||||
DrawView* fDrawView;
|
||||
DrawingEngine* fDrawingEngine;
|
||||
|
||||
BList fWindows;
|
||||
};
|
||||
|
||||
#endif // DESKTOP_H
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stack.h>
|
||||
|
||||
#include <Message.h>
|
||||
#include <Region.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "Desktop.h"
|
||||
|
||||
#include "DrawingEngine.h"
|
||||
|
||||
// constructor
|
||||
DrawingEngine::DrawingEngine(BRect frame, DrawView* drawView)
|
||||
: BView(frame, "drawing engine", B_FOLLOW_ALL, B_WILL_DRAW),
|
||||
fDrawView(drawView)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
DrawingEngine::~DrawingEngine()
|
||||
{
|
||||
}
|
||||
|
||||
// Lock
|
||||
bool
|
||||
DrawingEngine::Lock()
|
||||
{
|
||||
return Window()->Lock();
|
||||
}
|
||||
|
||||
// Unlock
|
||||
void
|
||||
DrawingEngine::Unlock()
|
||||
{
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
Sync();
|
||||
#else
|
||||
Flush();
|
||||
#endif
|
||||
Window()->Unlock();
|
||||
}
|
||||
|
||||
// MarkDirty
|
||||
void
|
||||
DrawingEngine::MarkDirty(BRegion* region)
|
||||
{
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
BRect frame = region->Frame();
|
||||
if (frame.IsValid()) {
|
||||
BMessage message(MSG_INVALIDATE);
|
||||
message.AddRect("area", frame);
|
||||
fDrawView->Looper()->PostMessage(&message, fDrawView);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// MarkDirty
|
||||
void
|
||||
DrawingEngine::MarkDirty(BRect rect)
|
||||
{
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
if (rect.IsValid()) {
|
||||
BMessage message(MSG_INVALIDATE);
|
||||
message.AddRect("area", rect);
|
||||
fDrawView->Looper()->PostMessage(&message, fDrawView);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// MarkDirty
|
||||
void
|
||||
DrawingEngine::MarkDirty()
|
||||
{
|
||||
if (Lock()) {
|
||||
Invalidate();
|
||||
Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
struct node {
|
||||
node()
|
||||
{
|
||||
pointers = NULL;
|
||||
}
|
||||
node(const BRect& r, int32 maxPointers)
|
||||
{
|
||||
init(r, maxPointers);
|
||||
}
|
||||
~node()
|
||||
{
|
||||
delete [] pointers;
|
||||
}
|
||||
|
||||
void init(const BRect& r, int32 maxPointers)
|
||||
{
|
||||
rect = r;
|
||||
pointers = new node*[maxPointers];
|
||||
in_degree = 0;
|
||||
next_pointer = 0;
|
||||
}
|
||||
|
||||
void push(node* node)
|
||||
{
|
||||
pointers[next_pointer] = node;
|
||||
next_pointer++;
|
||||
}
|
||||
node* top()
|
||||
{
|
||||
return pointers[next_pointer];
|
||||
}
|
||||
node* pop()
|
||||
{
|
||||
node* ret = top();
|
||||
next_pointer--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
BRect rect;
|
||||
int32 in_degree;
|
||||
node** pointers;
|
||||
int32 next_pointer;
|
||||
};
|
||||
|
||||
bool
|
||||
is_left_of(const BRect& a, const BRect& b)
|
||||
{
|
||||
return (a.right < b.left);
|
||||
}
|
||||
bool
|
||||
is_above(const BRect& a, const BRect& b)
|
||||
{
|
||||
return (a.bottom < b.top);
|
||||
}
|
||||
|
||||
void
|
||||
DrawingEngine::CopyRegion(BRegion* region, int32 xOffset, int32 yOffset)
|
||||
{
|
||||
int32 count = region->CountRects();
|
||||
|
||||
// TODO: make this step unnecessary
|
||||
// (by using different stack impl inside node)
|
||||
node nodes[count];
|
||||
for (int32 i= 0; i < count; i++) {
|
||||
nodes[i].init(region->RectAt(i), count);
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
BRect a = region->RectAt(i);
|
||||
for (int32 k = i + 1; k < count; k++) {
|
||||
BRect b = region->RectAt(k);
|
||||
int cmp = 0;
|
||||
// compare horizontally
|
||||
if (xOffset > 0) {
|
||||
if (is_left_of(a, b)) {
|
||||
cmp -= 1;
|
||||
} else if (is_left_of(b, a)) {
|
||||
cmp += 1;
|
||||
}
|
||||
} else if (xOffset < 0) {
|
||||
if (is_left_of(a, b)) {
|
||||
cmp += 1;
|
||||
} else if (is_left_of(b, a)) {
|
||||
cmp -= 1;
|
||||
}
|
||||
}
|
||||
// compare vertically
|
||||
if (yOffset > 0) {
|
||||
if (is_above(a, b)) {
|
||||
cmp -= 1;
|
||||
} else if (is_above(b, a)) {
|
||||
cmp += 1;
|
||||
}
|
||||
} else if (yOffset < 0) {
|
||||
if (is_above(a, b)) {
|
||||
cmp += 1;
|
||||
} else if (is_above(b, a)) {
|
||||
cmp -= 1;
|
||||
}
|
||||
}
|
||||
// add appropriate node as successor
|
||||
if (cmp > 0) {
|
||||
nodes[i].push(&nodes[k]);
|
||||
nodes[k].in_degree++;
|
||||
} else if (cmp < 0) {
|
||||
nodes[k].push(&nodes[i]);
|
||||
nodes[i].in_degree++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// put all nodes onto a stack that have an "indegree" count of zero
|
||||
stack<node*> inDegreeZeroNodes;
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
if (nodes[i].in_degree == 0) {
|
||||
inDegreeZeroNodes.push(&nodes[i]);
|
||||
}
|
||||
}
|
||||
// pop the rects from the stack, do the actual copy operation
|
||||
// and decrease the "indegree" count of the other rects not
|
||||
// currently on the stack and to which the current rect pointed
|
||||
// to. If their "indegree" count reaches zero, put them onto the
|
||||
// stack as well.
|
||||
|
||||
while (!inDegreeZeroNodes.empty()) {
|
||||
node* n = inDegreeZeroNodes.top();
|
||||
inDegreeZeroNodes.pop();
|
||||
|
||||
CopyBits(n->rect, BRect(n->rect).OffsetByCopy(xOffset, yOffset));
|
||||
|
||||
for (int32 k = 0; k < n->next_pointer; k++) {
|
||||
n->pointers[k]->in_degree--;
|
||||
if (n->pointers[k]->in_degree == 0)
|
||||
inDegreeZeroNodes.push(n->pointers[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// constructor
|
||||
DrawView::DrawView(BRect frame)
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
: BView(frame, "desktop", B_FOLLOW_ALL, B_WILL_DRAW),
|
||||
#else
|
||||
: DrawingEngine(frame, NULL),
|
||||
#endif
|
||||
fDesktop(NULL),
|
||||
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
fFrameBuffer(new BBitmap(Bounds(), B_RGB32, true)),
|
||||
fDrawingEngine(new DrawingEngine(Bounds(), this)),
|
||||
#else
|
||||
fDrawingEngine(this)
|
||||
#endif
|
||||
{
|
||||
SetViewColor(B_TRANSPARENT_COLOR);
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
if (fFrameBuffer->Lock()) {
|
||||
fFrameBuffer->AddChild(fDrawingEngine);
|
||||
fFrameBuffer->Unlock();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// destructor
|
||||
DrawView::~DrawView()
|
||||
{
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
delete fFrameBuffer;
|
||||
#endif
|
||||
}
|
||||
|
||||
// MessageReceived
|
||||
void
|
||||
DrawView::MessageReceived(BMessage* message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case MSG_INVALIDATE: {
|
||||
BRect area;
|
||||
if (message->FindRect("area", &area) == B_OK) {
|
||||
Invalidate(area);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BView::MessageReceived(message);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw
|
||||
void
|
||||
DrawView::Draw(BRect updateRect)
|
||||
{
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
DrawBitmap(fFrameBuffer, updateRect, updateRect);
|
||||
#else
|
||||
BMessage message(MSG_DRAW);
|
||||
message.AddRect("area", updateRect);
|
||||
fDesktop->PostMessage(&message);
|
||||
#endif
|
||||
}
|
||||
|
||||
// MouseDown
|
||||
void
|
||||
DrawView::MouseDown(BPoint where)
|
||||
{
|
||||
SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
|
||||
|
||||
fDesktop->PostMessage(Window()->CurrentMessage());
|
||||
}
|
||||
|
||||
// MouseUp
|
||||
void
|
||||
DrawView::MouseUp(BPoint where)
|
||||
{
|
||||
fDesktop->PostMessage(Window()->CurrentMessage());
|
||||
}
|
||||
|
||||
// MouseMoved
|
||||
void
|
||||
DrawView::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
|
||||
{
|
||||
fDesktop->PostMessage(Window()->CurrentMessage());
|
||||
}
|
||||
|
||||
// SetDesktop
|
||||
void
|
||||
DrawView::SetDesktop(Desktop* desktop)
|
||||
{
|
||||
fDesktop = desktop;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
|
||||
#ifndef DRAWING_ENGINE_H
|
||||
#define DRAWING_ENGINE_H
|
||||
|
||||
#include <View.h>
|
||||
|
||||
class Desktop;
|
||||
class DrawView;
|
||||
|
||||
enum {
|
||||
MSG_INVALIDATE = 'invl',
|
||||
};
|
||||
|
||||
class DrawingEngine : public BView {
|
||||
public:
|
||||
DrawingEngine(BRect frame, DrawView* drawView);
|
||||
virtual ~DrawingEngine();
|
||||
|
||||
bool Lock();
|
||||
void Unlock();
|
||||
|
||||
void CopyRegion(BRegion *region, int32 xOffset, int32 yOffset);
|
||||
|
||||
void MarkDirty(BRegion* region);
|
||||
void MarkDirty(BRect rect);
|
||||
void MarkDirty();
|
||||
|
||||
private:
|
||||
DrawView* fDrawView;
|
||||
};
|
||||
|
||||
#define RUN_WITH_FRAME_BUFFER 0
|
||||
|
||||
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
class DrawView : public BView {
|
||||
#else
|
||||
class DrawView : public DrawingEngine {
|
||||
#endif
|
||||
|
||||
public:
|
||||
DrawView(BRect frame);
|
||||
virtual ~DrawView();
|
||||
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
virtual void Draw(BRect updateRect);
|
||||
|
||||
virtual void MouseDown(BPoint where);
|
||||
virtual void MouseUp(BPoint where);
|
||||
virtual void MouseMoved(BPoint where, uint32 code,
|
||||
const BMessage* dragMessage);
|
||||
|
||||
void SetDesktop(Desktop* desktop);
|
||||
DrawingEngine* GetDrawingEngine() const
|
||||
{ return fDrawingEngine; }
|
||||
|
||||
private:
|
||||
Desktop* fDesktop;
|
||||
|
||||
#if RUN_WITH_FRAME_BUFFER
|
||||
BBitmap* fFrameBuffer;
|
||||
#endif
|
||||
DrawingEngine* fDrawingEngine;
|
||||
};
|
||||
|
||||
#endif // DRAWING_ENGINE_H
|
||||
|
||||
@@ -0,0 +1,464 @@
|
||||
/* MultiLocker.cpp */
|
||||
/*
|
||||
Copyright 1999, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
|
||||
#include "MultiLocker.h"
|
||||
|
||||
#include <Debug.h>
|
||||
#include <Errors.h>
|
||||
#include <OS.h>
|
||||
|
||||
//#define TIMING 1
|
||||
#define DEBUG 1
|
||||
|
||||
|
||||
MultiLocker::MultiLocker(const char* semaphoreBaseName)
|
||||
: fInit(B_NO_INIT),
|
||||
fReadCount(0),
|
||||
fReadSem(-1),
|
||||
fWriteCount(0),
|
||||
fWriteSem(-1),
|
||||
fLockCount(0),
|
||||
fWriterLock(-1),
|
||||
fWriterNest(0),
|
||||
fWriterThread(-1),
|
||||
fWriterStackBase(0),
|
||||
fDebugArray(NULL),
|
||||
fMaxThreads(0)
|
||||
{
|
||||
//build the semaphores
|
||||
if (semaphoreBaseName) {
|
||||
char name[128];
|
||||
sprintf(name, "%s-%s", semaphoreBaseName, "ReadSem");
|
||||
fReadSem = create_sem(0, name);
|
||||
sprintf(name, "%s-%s", semaphoreBaseName, "WriteSem");
|
||||
fWriteSem = create_sem(0, name);
|
||||
sprintf(name, "%s-%s", semaphoreBaseName, "WriterLock");
|
||||
fWriterLock = create_sem(0, name);
|
||||
} else {
|
||||
fReadSem = create_sem(0, "MultiLocker_ReadSem");
|
||||
fWriteSem = create_sem(0, "MultiLocker_WriteSem");
|
||||
fWriterLock = create_sem(0, "MultiLocker_WriterLock");
|
||||
}
|
||||
|
||||
if (fReadSem >= 0 && fWriteSem >=0 && fWriterLock >= 0)
|
||||
fInit = B_OK;
|
||||
|
||||
#if DEBUG
|
||||
//we are in debug mode!
|
||||
//create the reader tracking list
|
||||
//the array needs to be large enough to hold all possible threads
|
||||
system_info sys;
|
||||
get_system_info(&sys);
|
||||
fMaxThreads = sys.max_threads;
|
||||
fDebugArray = (int32 *) malloc(fMaxThreads * sizeof(int32));
|
||||
for (int32 i = 0; i < fMaxThreads; i++) {
|
||||
fDebugArray[i] = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#if TIMING
|
||||
//initialize the counter variables
|
||||
rl_count = ru_count = wl_count = wu_count = islock_count = 0;
|
||||
rl_time = ru_time = wl_time = wu_time = islock_time = 0;
|
||||
#if DEBUG
|
||||
reg_count = unreg_count = 0;
|
||||
reg_time = unreg_time = 0;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
MultiLocker::~MultiLocker()
|
||||
{
|
||||
//become the writer
|
||||
if (!IsWriteLocked()) WriteLock();
|
||||
|
||||
//set locker to be uninitialized
|
||||
fInit = B_NO_INIT;
|
||||
|
||||
//delete the semaphores
|
||||
delete_sem(fReadSem);
|
||||
delete_sem(fWriteSem);
|
||||
delete_sem(fWriterLock);
|
||||
|
||||
#if DEBUG
|
||||
//we are in debug mode!
|
||||
//clear and delete the reader tracking list
|
||||
free(fDebugArray);
|
||||
#endif
|
||||
#if TIMING
|
||||
//let's produce some performance numbers
|
||||
printf("MultiLocker Statistics:\n"
|
||||
"Avg ReadLock: %lld\n"
|
||||
"Avg ReadUnlock: %lld\n"
|
||||
"Avg WriteLock: %lld\n"
|
||||
"Avg WriteUnlock: %lld\n"
|
||||
"Avg IsWriteLocked: %lld\n",
|
||||
rl_count > 0 ? rl_time / rl_count : 0,
|
||||
ru_count > 0 ? ru_time / ru_count : 0,
|
||||
wl_count > 0 ? wl_time / wl_count : 0,
|
||||
wu_count > 0 ? wu_time / wu_count : 0,
|
||||
islock_count > 0 ? islock_time / islock_count : 0
|
||||
);
|
||||
#if DEBUG
|
||||
printf( "Avg register_thread: %lld\n"
|
||||
"Avg unregister_thread: %lld\n",
|
||||
reg_count > 0 ? reg_time / reg_count : 0,
|
||||
unreg_count > 0 ? unreg_time / unreg_count : 0
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
status_t
|
||||
MultiLocker::InitCheck()
|
||||
{
|
||||
return fInit;
|
||||
}
|
||||
|
||||
bool
|
||||
MultiLocker::ReadLock()
|
||||
{
|
||||
#if TIMING
|
||||
bigtime_t start = system_time();
|
||||
#endif
|
||||
|
||||
bool locked = false;
|
||||
|
||||
//the lock must be initialized
|
||||
if (fInit == B_OK) {
|
||||
if (IsWriteLocked()) {
|
||||
//the writer simply increments the nesting
|
||||
fWriterNest++;
|
||||
locked = true;
|
||||
} else {
|
||||
//increment and retrieve the current count of readers
|
||||
int32 current_count = atomic_add(&fReadCount, 1);
|
||||
if (current_count < 0) {
|
||||
//a writer holds the lock so wait for fReadSem to be released
|
||||
locked = (acquire_sem_etc(fReadSem, 1, B_DO_NOT_RESCHEDULE,
|
||||
B_INFINITE_TIMEOUT) == B_OK);
|
||||
} else locked = true;
|
||||
#if DEBUG
|
||||
//register if we acquired the lock
|
||||
if (locked) register_thread();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if TIMING
|
||||
bigtime_t end = system_time();
|
||||
rl_time += (end - start);
|
||||
rl_count++;
|
||||
#endif
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
bool
|
||||
MultiLocker::WriteLock()
|
||||
{
|
||||
#if TIMING
|
||||
bigtime_t start = system_time();
|
||||
#endif
|
||||
|
||||
bool locked = false;
|
||||
|
||||
if (fInit == B_OK) {
|
||||
uint32 stack_base = 0;
|
||||
thread_id thread = -1;
|
||||
|
||||
if (IsWriteLocked(&stack_base, &thread)) {
|
||||
//already the writer - increment the nesting count
|
||||
fWriterNest++;
|
||||
locked = true;
|
||||
} else {
|
||||
//new writer acquiring the lock
|
||||
if (atomic_add(&fLockCount, 1) >= 1) {
|
||||
//another writer in the lock - acquire the semaphore
|
||||
locked = (acquire_sem_etc(fWriterLock, 1, B_DO_NOT_RESCHEDULE,
|
||||
B_INFINITE_TIMEOUT) == B_OK);
|
||||
} else locked = true;
|
||||
|
||||
if (locked) {
|
||||
//new holder of the lock
|
||||
|
||||
//decrement fReadCount by a very large number
|
||||
//this will cause new readers to block on fReadSem
|
||||
int32 readers = atomic_add(&fReadCount, -LARGE_NUMBER);
|
||||
|
||||
if (readers > 0) {
|
||||
//readers hold the lock - acquire fWriteSem
|
||||
locked = (acquire_sem_etc(fWriteSem, readers, B_DO_NOT_RESCHEDULE,
|
||||
B_INFINITE_TIMEOUT) == B_OK);
|
||||
}
|
||||
if (locked) {
|
||||
ASSERT(fWriterThread == -1);
|
||||
//record thread information
|
||||
fWriterThread = thread;
|
||||
fWriterStackBase = stack_base;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if TIMING
|
||||
bigtime_t end = system_time();
|
||||
wl_time += (end - start);
|
||||
wl_count++;
|
||||
#endif
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
bool
|
||||
MultiLocker::ReadUnlock()
|
||||
{
|
||||
#if TIMING
|
||||
bigtime_t start = system_time();
|
||||
#endif
|
||||
|
||||
bool unlocked = false;
|
||||
|
||||
if (IsWriteLocked()) {
|
||||
//writers simply decrement the nesting count
|
||||
fWriterNest--;
|
||||
unlocked = true;
|
||||
} else {
|
||||
//decrement and retrieve the read counter
|
||||
int32 current_count = atomic_add(&fReadCount, -1);
|
||||
if (current_count < 0) {
|
||||
//a writer is waiting for the lock so release fWriteSem
|
||||
unlocked = (release_sem_etc(fWriteSem, 1,
|
||||
B_DO_NOT_RESCHEDULE) == B_OK);
|
||||
} else unlocked = true;
|
||||
|
||||
#ifdef DEBUG
|
||||
//unregister if we released the lock
|
||||
if (unlocked) unregister_thread();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TIMING
|
||||
bigtime_t end = system_time();
|
||||
ru_time += (end - start);
|
||||
ru_count++;
|
||||
#endif
|
||||
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
bool
|
||||
MultiLocker::WriteUnlock()
|
||||
{
|
||||
#if TIMING
|
||||
bigtime_t start = system_time();
|
||||
#endif
|
||||
|
||||
bool unlocked = false;
|
||||
|
||||
if (IsWriteLocked()) {
|
||||
//if this is a nested lock simply decrement the nest count
|
||||
if (fWriterNest > 0) {
|
||||
fWriterNest--;
|
||||
unlocked = true;
|
||||
} else {
|
||||
//writer finally unlocking
|
||||
|
||||
//increment fReadCount by a large number
|
||||
//this will let new readers acquire the read lock
|
||||
//retrieve the number of current waiters
|
||||
int32 readersWaiting = atomic_add(&fReadCount, LARGE_NUMBER) + LARGE_NUMBER;
|
||||
|
||||
if (readersWaiting > 0) {
|
||||
//readers are waiting to acquire the lock
|
||||
unlocked = (release_sem_etc(fReadSem, readersWaiting,
|
||||
B_DO_NOT_RESCHEDULE) == B_OK);
|
||||
} else unlocked = true;
|
||||
|
||||
if (unlocked) {
|
||||
//clear the information
|
||||
fWriterThread = -1;
|
||||
fWriterStackBase = 0;
|
||||
|
||||
//decrement and retrieve the lock count
|
||||
if (atomic_add(&fLockCount, -1) > 1) {
|
||||
//other writers are waiting so release fWriterLock
|
||||
unlocked = (release_sem_etc(fWriterLock, 1,
|
||||
B_DO_NOT_RESCHEDULE) == B_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else debugger("Non-writer attempting to WriteUnlock()\n");
|
||||
|
||||
#if TIMING
|
||||
bigtime_t end = system_time();
|
||||
wu_time += (end - start);
|
||||
wu_count++;
|
||||
#endif
|
||||
|
||||
return unlocked;
|
||||
}
|
||||
|
||||
/* this function demonstrates a nice method of determining if the current thread */
|
||||
/* is the writer or not. The method involves caching the index of the page in memory */
|
||||
/* where the thread's stack is located. Each time a new writer acquires the lock, */
|
||||
/* its thread_id and stack_page are recorded. IsWriteLocked gets the stack_page of the */
|
||||
/* current thread and sees if it is a match. If the stack_page matches you are guaranteed */
|
||||
/* to have the matching thread. If the stack page doesn't match the more traditional */
|
||||
/* find_thread(NULL) method of matching the thread_ids is used. */
|
||||
|
||||
/* This technique is very useful when dealing with a lock that is acquired in a nested fashion. */
|
||||
/* It could be expanded to cache the information of the last thread in the lock, and then if */
|
||||
/* the same thread returns while there is no one in the lock, it could save some time, if the */
|
||||
/* same thread is likely to acquire the lock again and again. */
|
||||
/* I should note another shortcut that could be implemented here */
|
||||
/* If fWriterThread is set to -1 then there is no writer in the lock, and we could */
|
||||
/* return from this function much faster. However the function is currently set up */
|
||||
/* so all of the stack_base and thread_id info is determined here. WriteLock passes */
|
||||
/* in some variables so that if the lock is not held it does not have to get the thread_id */
|
||||
/* and stack base again. Instead this function returns that information. So this shortcut */
|
||||
/* would only move this information gathering outside of this function, and I like it all */
|
||||
/* contained. */
|
||||
|
||||
bool
|
||||
MultiLocker::IsWriteLocked(uint32 *the_stack_base, thread_id *the_thread)
|
||||
{
|
||||
#if TIMING
|
||||
bigtime_t start = system_time();
|
||||
#endif
|
||||
|
||||
//get a variable on the stack
|
||||
bool write_lock_holder = false;
|
||||
|
||||
if (fInit == B_OK) {
|
||||
uint32 stack_base;
|
||||
thread_id thread = 0;
|
||||
|
||||
//determine which page in memory this stack represents
|
||||
//this is managed by taking the address of the item on the
|
||||
//stack and dividing it by the size of the memory pages
|
||||
//if it is the same as the cached stack_page, there is a match
|
||||
stack_base = (uint32) &write_lock_holder/B_PAGE_SIZE;
|
||||
if (fWriterStackBase == stack_base) {
|
||||
write_lock_holder = true;
|
||||
} else {
|
||||
//as there was no stack_page match we resort to the
|
||||
//tried and true methods
|
||||
thread = find_thread(NULL);
|
||||
if (fWriterThread == thread) {
|
||||
write_lock_holder = true;
|
||||
}
|
||||
}
|
||||
|
||||
//if someone wants this information, give it to them
|
||||
if (the_stack_base != NULL) {
|
||||
*the_stack_base = stack_base;
|
||||
}
|
||||
if (the_thread != NULL) {
|
||||
*the_thread = thread;
|
||||
}
|
||||
}
|
||||
|
||||
#if TIMING
|
||||
bigtime_t end = system_time();
|
||||
islock_time += (end - start);
|
||||
islock_count++;
|
||||
#endif
|
||||
|
||||
return write_lock_holder;
|
||||
}
|
||||
|
||||
bool
|
||||
MultiLocker::IsReadLocked()
|
||||
{
|
||||
//a properly initialized MultiLocker in non-debug always returns true
|
||||
bool locked = true;
|
||||
if (fInit == B_NO_INIT) locked = false;
|
||||
|
||||
#if DEBUG
|
||||
//determine if the lock is actually held
|
||||
thread_id thread = find_thread(NULL);
|
||||
if (fDebugArray[thread % fMaxThreads] > 0) locked = true;
|
||||
else locked = false;
|
||||
#endif
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
|
||||
/* these two functions manage the debug array for readers */
|
||||
/* an array is created in the constructor large enough to hold */
|
||||
/* an int32 for each of the maximum number of threads the system */
|
||||
/* can have at one time. */
|
||||
/* this array does not need to be locked because each running thread */
|
||||
/* can be uniquely mapped to a slot in the array by performing: */
|
||||
/* thread_id % max_threads */
|
||||
/* each time ReadLock is called while in debug mode the thread_id */
|
||||
/* is retrived in register_thread() and the count is adjusted in the */
|
||||
/* array. If register thread is ever called and the count is not 0 then */
|
||||
/* an illegal, potentially deadlocking nested ReadLock occured */
|
||||
/* unregister_thread clears the appropriate slot in the array */
|
||||
|
||||
/* this system could be expanded or retracted to include multiple arrays of information */
|
||||
/* in all fairness for it's current use, fDebugArray could be an array of bools */
|
||||
|
||||
/* The disadvantage of this system for maintaining state is that it sucks up a ton of */
|
||||
/* memory. The other method (which would be slower), would involve an additional lock and */
|
||||
/* traversing a list of cached information. As this is only for a debug mode, the extra memory */
|
||||
/* was not deemed to be a problem */
|
||||
|
||||
void
|
||||
MultiLocker::register_thread()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
#if TIMING
|
||||
bigtime_t start = system_time();
|
||||
#endif
|
||||
|
||||
thread_id thread = find_thread(NULL);
|
||||
|
||||
ASSERT_WITH_MESSAGE(fDebugArray[thread%fMaxThreads] == 0,"Nested ReadLock!\n");
|
||||
fDebugArray[thread%fMaxThreads]++;
|
||||
|
||||
#if TIMING
|
||||
bigtime_t end = system_time();
|
||||
reg_time += (end - start);
|
||||
reg_count++;
|
||||
#endif
|
||||
#else
|
||||
debugger("register_thread should never be called unless in DEBUG mode!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
MultiLocker::unregister_thread()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
#if TIMING
|
||||
bigtime_t start = system_time();
|
||||
#endif
|
||||
|
||||
thread_id thread = find_thread(NULL);
|
||||
|
||||
ASSERT(fDebugArray[thread%fMaxThreads] == 1);
|
||||
fDebugArray[thread%fMaxThreads]--;
|
||||
|
||||
#if TIMING
|
||||
bigtime_t end = system_time();
|
||||
unreg_time += (end - start);
|
||||
unreg_count++;
|
||||
#endif
|
||||
#else
|
||||
debugger("unregister_thread should never be called unless in DEBUG mode!\n");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/* MultiLocker.h */
|
||||
/*
|
||||
Copyright 1999, Be Incorporated. All Rights Reserved.
|
||||
This file may be used under the terms of the Be Sample Code License.
|
||||
*/
|
||||
|
||||
/* multiple-reader single-writer locking class */
|
||||
|
||||
#ifndef MULTI_LOCKER_H
|
||||
#define MULTI_LOCKER_H
|
||||
|
||||
//#define TIMING 1
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
const int32 LARGE_NUMBER = 100000;
|
||||
|
||||
class MultiLocker {
|
||||
public:
|
||||
MultiLocker(const char* semaphoreBaseName);
|
||||
virtual ~MultiLocker();
|
||||
|
||||
status_t InitCheck();
|
||||
|
||||
//locking for reading or writing
|
||||
bool ReadLock();
|
||||
bool WriteLock();
|
||||
|
||||
//unlocking after reading or writing
|
||||
bool ReadUnlock();
|
||||
bool WriteUnlock();
|
||||
|
||||
//does the current thread hold a write lock ?
|
||||
bool IsWriteLocked(uint32 *stack_base = NULL, thread_id *thread = NULL);
|
||||
//in DEBUG mode returns whether the lock is held
|
||||
//in non-debug mode returns true
|
||||
bool IsReadLocked();
|
||||
|
||||
private:
|
||||
//functions for managing the DEBUG reader array
|
||||
void register_thread();
|
||||
void unregister_thread();
|
||||
|
||||
status_t fInit;
|
||||
//readers adjust count and block on fReadSem when a writer
|
||||
//hold the lock
|
||||
int32 fReadCount;
|
||||
sem_id fReadSem;
|
||||
//writers adjust the count and block on fWriteSem
|
||||
//when readers hold the lock
|
||||
int32 fWriteCount;
|
||||
sem_id fWriteSem;
|
||||
//writers must acquire fWriterLock when acquiring a write lock
|
||||
int32 fLockCount;
|
||||
sem_id fWriterLock;
|
||||
int32 fWriterNest;
|
||||
|
||||
thread_id fWriterThread;
|
||||
uint32 fWriterStackBase;
|
||||
|
||||
int32 * fDebugArray;
|
||||
int32 fMaxThreads;
|
||||
|
||||
#if TIMING
|
||||
uint32 rl_count;
|
||||
bigtime_t rl_time;
|
||||
uint32 ru_count;
|
||||
bigtime_t ru_time;
|
||||
uint32 wl_count;
|
||||
bigtime_t wl_time;
|
||||
uint32 wu_count;
|
||||
bigtime_t wu_time;
|
||||
uint32 islock_count;
|
||||
bigtime_t islock_time;
|
||||
uint32 reg_count;
|
||||
bigtime_t reg_time;
|
||||
uint32 unreg_count;
|
||||
bigtime_t unreg_time;
|
||||
#endif
|
||||
};
|
||||
|
||||
class AutoWriteLocker {
|
||||
public:
|
||||
AutoWriteLocker(MultiLocker* lock)
|
||||
: fLock(lock)
|
||||
{
|
||||
fLock->WriteLock();
|
||||
}
|
||||
~AutoWriteLocker()
|
||||
{
|
||||
fLock->WriteUnlock();
|
||||
}
|
||||
private:
|
||||
MultiLocker* fLock;
|
||||
};
|
||||
|
||||
class AutoReadLocker {
|
||||
public:
|
||||
AutoReadLocker(MultiLocker* lock)
|
||||
: fLock(lock)
|
||||
{
|
||||
fLock->ReadLock();
|
||||
}
|
||||
~AutoReadLocker()
|
||||
{
|
||||
fLock->ReadUnlock();
|
||||
}
|
||||
private:
|
||||
MultiLocker* fLock;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,400 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <View.h> // for resize modes
|
||||
|
||||
#include "Desktop.h"
|
||||
#include "DrawingEngine.h"
|
||||
|
||||
#include "ViewLayer.h"
|
||||
|
||||
extern BWindow* wind;
|
||||
|
||||
// constructor
|
||||
ViewLayer::ViewLayer(BRect frame, const char* name,
|
||||
uint32 resizeMode, uint32 flags,
|
||||
rgb_color viewColor)
|
||||
: fName(name),
|
||||
|
||||
fFrame(frame),
|
||||
fScrollingOffset(0.0, 0.0),
|
||||
|
||||
fViewColor(viewColor),
|
||||
|
||||
fResizeMode(resizeMode),
|
||||
fFlags(flags),
|
||||
fShowLevel(1),
|
||||
|
||||
fWindow(NULL),
|
||||
fParent(NULL),
|
||||
|
||||
fFirstChild(NULL),
|
||||
fPreviousSibling(NULL),
|
||||
fNextSibling(NULL),
|
||||
fLastChild(NULL),
|
||||
|
||||
fCurrentChild(NULL),
|
||||
|
||||
fLocalClipping(Bounds()),
|
||||
fScreenClipping(),
|
||||
fScreenClippingValid(false)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
ViewLayer::~ViewLayer()
|
||||
{
|
||||
// iterate over children and delete each one
|
||||
ViewLayer* layer = fFirstChild;
|
||||
while (layer) {
|
||||
ViewLayer* toast = layer;
|
||||
layer = layer->fNextSibling;
|
||||
delete toast;
|
||||
}
|
||||
}
|
||||
|
||||
// Bounds
|
||||
BRect
|
||||
ViewLayer::Bounds() const
|
||||
{
|
||||
BRect bounds(fScrollingOffset.x, fScrollingOffset.y,
|
||||
fScrollingOffset.x + fFrame.Width(),
|
||||
fScrollingOffset.y + fFrame.Height());
|
||||
return bounds;
|
||||
}
|
||||
|
||||
// AttachedToWindow
|
||||
void
|
||||
ViewLayer::AttachedToWindow(WindowLayer* window)
|
||||
{
|
||||
fWindow = window;
|
||||
for (ViewLayer* child = FirstChild(); child; child = NextChild())
|
||||
child->AttachedToWindow(window);
|
||||
}
|
||||
|
||||
|
||||
// DetachedFromWindow
|
||||
void
|
||||
ViewLayer::DetachedFromWindow()
|
||||
{
|
||||
fWindow = NULL;
|
||||
for (ViewLayer* child = FirstChild(); child; child = NextChild())
|
||||
child->DetachedFromWindow();
|
||||
}
|
||||
|
||||
// AddChild
|
||||
void
|
||||
ViewLayer::AddChild(ViewLayer* layer)
|
||||
{
|
||||
if (layer->fParent) {
|
||||
printf("ViewLayer::AddChild() - ViewLayer already has a parent\n");
|
||||
return;
|
||||
}
|
||||
|
||||
layer->fParent = this;
|
||||
|
||||
if (!fLastChild) {
|
||||
// no children yet
|
||||
fFirstChild = layer;
|
||||
} else {
|
||||
// append layer to formerly last child
|
||||
fLastChild->fNextSibling = layer;
|
||||
layer->fPreviousSibling = fLastChild;
|
||||
}
|
||||
fLastChild = layer;
|
||||
|
||||
if (fParent) {
|
||||
RebuildClipping(false);
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveChild
|
||||
bool
|
||||
ViewLayer::RemoveChild(ViewLayer* layer)
|
||||
{
|
||||
if (layer->fParent != this) {
|
||||
printf("ViewLayer::RemoveChild(%p - %s) - ViewLayer is not child of this (%p) layer!\n", layer, layer ? layer->Name() : NULL, this);
|
||||
return false;
|
||||
}
|
||||
|
||||
layer->fParent = NULL;
|
||||
|
||||
if (fLastChild == layer)
|
||||
fLastChild = layer->fPreviousSibling;
|
||||
// layer->fNextSibling would be NULL
|
||||
|
||||
if (fFirstChild == layer )
|
||||
fFirstChild = layer->fNextSibling;
|
||||
// layer->fPreviousSibling would be NULL
|
||||
|
||||
// connect child before and after layer
|
||||
if (layer->fPreviousSibling)
|
||||
layer->fPreviousSibling->fNextSibling = layer->fNextSibling;
|
||||
|
||||
if (layer->fNextSibling)
|
||||
layer->fNextSibling->fPreviousSibling = layer->fPreviousSibling;
|
||||
|
||||
// layer has no siblings anymore
|
||||
layer->fPreviousSibling = NULL;
|
||||
layer->fNextSibling = NULL;
|
||||
|
||||
// TODO: track regions
|
||||
RebuildClipping(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// FirstChild
|
||||
ViewLayer*
|
||||
ViewLayer::FirstChild() const
|
||||
{
|
||||
fCurrentChild = fFirstChild;
|
||||
return fCurrentChild;
|
||||
}
|
||||
|
||||
// PreviousChild
|
||||
ViewLayer*
|
||||
ViewLayer::PreviousChild() const
|
||||
{
|
||||
fCurrentChild = fCurrentChild->fPreviousSibling;
|
||||
return fCurrentChild;
|
||||
}
|
||||
|
||||
// NextChild
|
||||
ViewLayer*
|
||||
ViewLayer::NextChild() const
|
||||
{
|
||||
fCurrentChild = fCurrentChild->fNextSibling;
|
||||
return fCurrentChild;
|
||||
}
|
||||
|
||||
// LastChild
|
||||
ViewLayer*
|
||||
ViewLayer::LastChild() const
|
||||
{
|
||||
fCurrentChild = fLastChild;
|
||||
return fCurrentChild;
|
||||
}
|
||||
|
||||
// TopLayer
|
||||
ViewLayer*
|
||||
ViewLayer::TopLayer()
|
||||
{
|
||||
if (fParent)
|
||||
return fParent->TopLayer();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// CountChildren
|
||||
uint32
|
||||
ViewLayer::CountChildren() const
|
||||
{
|
||||
uint32 count = 0;
|
||||
if (ViewLayer* layer = fFirstChild) {
|
||||
count++;
|
||||
while (layer->fNextSibling) {
|
||||
count++;
|
||||
layer = layer->fNextSibling;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// ConvertToTop
|
||||
void
|
||||
ViewLayer::ConvertToTop(BPoint* point) const
|
||||
{
|
||||
// remove scrolling offset and convert to parent coordinate space
|
||||
point->x += fFrame.left - fScrollingOffset.x;
|
||||
point->y += fFrame.top - fScrollingOffset.y;
|
||||
|
||||
if (fParent)
|
||||
fParent->ConvertToTop(point);
|
||||
}
|
||||
|
||||
// ConvertToTop
|
||||
void
|
||||
ViewLayer::ConvertToTop(BRect* rect) const
|
||||
{
|
||||
// remove scrolling offset and convert to parent coordinate space
|
||||
rect->OffsetBy(fFrame.left - fScrollingOffset.x,
|
||||
fFrame.top - fScrollingOffset.y);
|
||||
|
||||
if (fParent)
|
||||
fParent->ConvertToTop(rect);
|
||||
}
|
||||
|
||||
// ConvertToTop
|
||||
void
|
||||
ViewLayer::ConvertToTop(BRegion* region) const
|
||||
{
|
||||
// remove scrolling offset and convert to parent coordinate space
|
||||
region->OffsetBy(fFrame.left - fScrollingOffset.x,
|
||||
fFrame.top - fScrollingOffset.y);
|
||||
|
||||
if (fParent)
|
||||
fParent->ConvertToTop(region);
|
||||
}
|
||||
|
||||
// SetName
|
||||
void
|
||||
ViewLayer::SetName(const char* string)
|
||||
{
|
||||
fName.SetTo(string);
|
||||
}
|
||||
|
||||
// MoveBy
|
||||
void
|
||||
ViewLayer::MoveBy(int32 x, int32 y)
|
||||
{
|
||||
fFrame.OffsetBy(x, y);
|
||||
|
||||
_InvalidateScreenClipping(true);
|
||||
// TODO: ...
|
||||
}
|
||||
|
||||
// ResizeBy
|
||||
void
|
||||
ViewLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
|
||||
{
|
||||
BRect oldBounds(Bounds());
|
||||
|
||||
fFrame.right += x;
|
||||
fFrame.bottom += y;
|
||||
|
||||
// TODO: broken when getting smaller
|
||||
// ... either here or in WindowLayer::ResizeBy()
|
||||
|
||||
BRegion dirty(Bounds());
|
||||
dirty.Exclude(oldBounds);
|
||||
if (dirty.CountRects() > 0) {
|
||||
ConvertToTop(&dirty);
|
||||
dirtyRegion->Include(&dirty);
|
||||
}
|
||||
|
||||
RebuildClipping(false);
|
||||
_InvalidateScreenClipping(false);
|
||||
// TODO: layout children
|
||||
// TODO: ...
|
||||
}
|
||||
|
||||
// ScrollBy
|
||||
void
|
||||
ViewLayer::ScrollBy(int32 x, int32 y)
|
||||
{
|
||||
fScrollingOffset.x += x;
|
||||
fScrollingOffset.y += y;
|
||||
// TODO: CopyRegion...
|
||||
// TODO: ...
|
||||
}
|
||||
|
||||
// Draw
|
||||
void
|
||||
ViewLayer::Draw(DrawingEngine* drawingEngine, BRegion* effectiveClipping, bool deep)
|
||||
{
|
||||
if (drawingEngine->Lock()) {
|
||||
// TODO intersect with local dirtyRegion
|
||||
// fill visible region with view color
|
||||
drawingEngine->SetHighColor(fViewColor);
|
||||
drawingEngine->FillRegion(effectiveClipping);
|
||||
|
||||
drawingEngine->MarkDirty(effectiveClipping);
|
||||
drawingEngine->Unlock();
|
||||
|
||||
// let children draw
|
||||
if (deep) {
|
||||
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
|
||||
child->Draw(drawingEngine, effectiveClipping, deep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsHidden
|
||||
bool
|
||||
ViewLayer::IsHidden() const
|
||||
{
|
||||
// if we're explicitely hidden, then we're hidden...
|
||||
if (fShowLevel < 1)
|
||||
return true;
|
||||
|
||||
// ...but if we're not hidden, we might still be hidden if our parent is
|
||||
if (fParent)
|
||||
return fParent->IsHidden();
|
||||
|
||||
// nope, definitely not hidden
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hide
|
||||
void
|
||||
ViewLayer::Hide()
|
||||
{
|
||||
fShowLevel--;
|
||||
|
||||
// TODO: track regions
|
||||
}
|
||||
|
||||
// Show
|
||||
void
|
||||
ViewLayer::Show()
|
||||
{
|
||||
fShowLevel++;
|
||||
|
||||
// TODO: track regions
|
||||
}
|
||||
|
||||
// PrintToStream
|
||||
void
|
||||
ViewLayer::PrintToStream() const
|
||||
{
|
||||
}
|
||||
|
||||
// RebuildClipping
|
||||
void
|
||||
ViewLayer::RebuildClipping(bool deep)
|
||||
{
|
||||
// remember current local clipping in dirty region
|
||||
BRegion oldLocalClipping(fLocalClipping);
|
||||
|
||||
// the clipping spans over the bounds area
|
||||
fLocalClipping.Set(Bounds());
|
||||
|
||||
// exclude all childs from the clipping
|
||||
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
|
||||
fLocalClipping.Exclude(child->Frame());
|
||||
|
||||
if (deep)
|
||||
child->RebuildClipping(deep);
|
||||
}
|
||||
|
||||
fScreenClippingValid = false;
|
||||
}
|
||||
|
||||
// ScreenClipping
|
||||
BRegion&
|
||||
ViewLayer::ScreenClipping() const
|
||||
{
|
||||
if (!fScreenClippingValid) {
|
||||
fScreenClipping = fLocalClipping;
|
||||
ConvertToTop(&fScreenClipping);
|
||||
fScreenClippingValid = true;
|
||||
}
|
||||
return fScreenClipping;
|
||||
}
|
||||
|
||||
// _InvalidateScreenClipping
|
||||
void
|
||||
ViewLayer::_InvalidateScreenClipping(bool deep)
|
||||
{
|
||||
fScreenClippingValid = false;
|
||||
if (deep) {
|
||||
// invalidate the childrens screen clipping as well
|
||||
for (ViewLayer* child = FirstChild(); child; child = NextChild()) {
|
||||
child->_InvalidateScreenClipping(deep);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
|
||||
#ifndef VIEW_LAYER_H
|
||||
#define VIEW_LAYER_H
|
||||
|
||||
#include <Region.h>
|
||||
#include <String.h>
|
||||
|
||||
|
||||
class DrawingEngine;
|
||||
class WindowLayer;
|
||||
|
||||
class ViewLayer {
|
||||
public:
|
||||
ViewLayer(BRect frame,
|
||||
const char* name,
|
||||
uint32 reizeMode,
|
||||
uint32 flags,
|
||||
rgb_color viewColor);
|
||||
|
||||
virtual ~ViewLayer();
|
||||
|
||||
inline BRect Frame() const
|
||||
{ return fFrame; }
|
||||
BRect Bounds() const;
|
||||
|
||||
inline rgb_color ViewColor() const
|
||||
{ return fViewColor; }
|
||||
|
||||
void AttachedToWindow(WindowLayer* window);
|
||||
void DetachedFromWindow();
|
||||
|
||||
// tree stuff
|
||||
void AddChild(ViewLayer* layer);
|
||||
bool RemoveChild(ViewLayer* layer);
|
||||
|
||||
inline ViewLayer* Parent() const
|
||||
{ return fParent; }
|
||||
|
||||
ViewLayer* FirstChild() const;
|
||||
ViewLayer* PreviousChild() const;
|
||||
ViewLayer* NextChild() const;
|
||||
ViewLayer* LastChild() const;
|
||||
|
||||
ViewLayer* TopLayer();
|
||||
|
||||
uint32 CountChildren() const;
|
||||
|
||||
// coordinate conversion
|
||||
void ConvertToTop(BPoint* point) const;
|
||||
void ConvertToTop(BRect* rect) const;
|
||||
void ConvertToTop(BRegion* region) const;
|
||||
|
||||
// settings
|
||||
void SetName(const char* string);
|
||||
inline const char* Name() const
|
||||
{ return fName.String(); }
|
||||
|
||||
void MoveBy(int32 dx, int32 dy);
|
||||
void ResizeBy(int32 dx, int32 dy, BRegion* dirtyRegion);
|
||||
void ScrollBy(int32 dx, int32 dy);
|
||||
|
||||
void Draw(DrawingEngine* drawingEngine,
|
||||
BRegion* effectiveClipping,
|
||||
bool deep = false);
|
||||
|
||||
bool IsHidden() const;
|
||||
void Hide();
|
||||
void Show();
|
||||
|
||||
// clipping
|
||||
void RebuildClipping(bool deep = false);
|
||||
BRegion& ScreenClipping() const;
|
||||
|
||||
// debugging
|
||||
void PrintToStream() const;
|
||||
|
||||
private:
|
||||
void _InvalidateScreenClipping(bool deep = false);
|
||||
|
||||
BString fName;
|
||||
// area within parent coordinate space
|
||||
BRect fFrame;
|
||||
// scrolling offset
|
||||
BPoint fScrollingOffset;
|
||||
|
||||
rgb_color fViewColor;
|
||||
|
||||
uint32 fResizeMode;
|
||||
uint32 fFlags;
|
||||
int32 fShowLevel;
|
||||
|
||||
WindowLayer* fWindow;
|
||||
ViewLayer* fParent;
|
||||
|
||||
ViewLayer* fFirstChild;
|
||||
ViewLayer* fPreviousSibling;
|
||||
ViewLayer* fNextSibling;
|
||||
ViewLayer* fLastChild;
|
||||
|
||||
// used for traversing the childs
|
||||
mutable ViewLayer* fCurrentChild;
|
||||
|
||||
// clipping
|
||||
BRegion fLocalClipping;
|
||||
|
||||
mutable BRegion fScreenClipping;
|
||||
mutable bool fScreenClippingValid;
|
||||
|
||||
};
|
||||
|
||||
#endif // LAYER_H
|
||||
@@ -0,0 +1,219 @@
|
||||
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Message.h>
|
||||
#include <MessageQueue.h>
|
||||
|
||||
#include "Desktop.h"
|
||||
#include "DrawingEngine.h"
|
||||
|
||||
#include "WindowLayer.h"
|
||||
|
||||
#define SLOW_DRAWING 0
|
||||
|
||||
// constructor
|
||||
WindowLayer::WindowLayer(BRect frame, const char* name,
|
||||
DrawingEngine* drawingEngine, Desktop* desktop)
|
||||
: BLooper(name),
|
||||
fFrame(frame),
|
||||
fVisibleRegion(),
|
||||
|
||||
fBorderColor((rgb_color){ 255, 203, 0, 255 }),
|
||||
|
||||
fTopLayer(NULL),
|
||||
|
||||
fDrawingEngine(drawingEngine),
|
||||
fDesktop(desktop)
|
||||
{
|
||||
// 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 = new(nothrow) ViewLayer(fFrame, "top view", B_FOLLOW_ALL, 0,
|
||||
(rgb_color){ 255, 255, 255, 255 });
|
||||
}
|
||||
|
||||
// destructor
|
||||
WindowLayer::~WindowLayer()
|
||||
{
|
||||
delete fTopLayer;
|
||||
}
|
||||
|
||||
// MessageReceived
|
||||
void
|
||||
WindowLayer::MessageReceived(BMessage* message)
|
||||
{
|
||||
switch (message->what) {
|
||||
case MSG_REDRAW: {
|
||||
if (!MessageQueue()->FindMessage(MSG_REDRAW, 0)) {
|
||||
while (!fDesktop->ReadLockClipping()) {
|
||||
//printf("%s MSG_REDRAW -> timeout\n", Name());
|
||||
if (MessageQueue()->FindMessage(MSG_REDRAW, 0)) {
|
||||
//printf("%s MSG_REDRAW -> timeout - leaving because there are pending redraws\n", Name());
|
||||
return;
|
||||
}
|
||||
}
|
||||
_DrawContents(fTopLayer);
|
||||
_DrawBorder();
|
||||
fDesktop->ReadUnlockClipping();
|
||||
} else {
|
||||
//printf("%s MSG_REDRAW -> pending redraws\n", Name());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BLooper::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SetClipping
|
||||
void
|
||||
WindowLayer::SetClipping(BRegion* stillAvailableOnScreen)
|
||||
{
|
||||
// start from full region (as if the window was fully visible)
|
||||
GetFullRegion(&fVisibleRegion);
|
||||
// clip to region still available on screen
|
||||
fVisibleRegion.IntersectWith(stillAvailableOnScreen);
|
||||
}
|
||||
|
||||
// GetFullRegion
|
||||
void
|
||||
WindowLayer::GetFullRegion(BRegion* region) const
|
||||
{
|
||||
// start from the frame, extend to include decorator border
|
||||
region->Set(BRect(fFrame.left - 4, fFrame.top - 4,
|
||||
fFrame.right + 4, fFrame.bottom + 4));
|
||||
// add the title tab
|
||||
region->Include(BRect(fFrame.left - 4, fFrame.top - 20,
|
||||
(fFrame.left + fFrame.right) / 2, fFrame.top - 5));
|
||||
}
|
||||
|
||||
// GetBorderRegion
|
||||
void
|
||||
WindowLayer::GetBorderRegion(BRegion* region) const
|
||||
{
|
||||
// TODO: speed up by avoiding "Exclude()"
|
||||
// start from the frame, extend to include decorator border
|
||||
region->Set(BRect(fFrame.left - 4, fFrame.top - 4,
|
||||
fFrame.right + 4, fFrame.bottom + 4));
|
||||
|
||||
region->Exclude(fFrame);
|
||||
|
||||
// add the title tab
|
||||
region->Include(BRect(fFrame.left - 4, fFrame.top - 20,
|
||||
(fFrame.left + fFrame.right) / 2, fFrame.top - 5));
|
||||
|
||||
// resize handle
|
||||
// if (B_DOCUMENT_WINDOW_LOOK)
|
||||
region->Include(BRect(fFrame.right - 10, fFrame.bottom - 10,
|
||||
fFrame.right, fFrame.bottom));
|
||||
}
|
||||
|
||||
// MoveBy
|
||||
void
|
||||
WindowLayer::MoveBy(int32 x, int32 y)
|
||||
{
|
||||
if (x == 0 && y == 0)
|
||||
return;
|
||||
|
||||
fFrame.OffsetBy(x, y);
|
||||
|
||||
fTopLayer->MoveBy(x, y);
|
||||
|
||||
// TODO: move a local dirty region!
|
||||
}
|
||||
|
||||
// ResizeBy
|
||||
void
|
||||
WindowLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
|
||||
{
|
||||
if (x == 0 && y == 0)
|
||||
return;
|
||||
|
||||
// BRegion previousBorder();
|
||||
//
|
||||
fFrame.right += x;
|
||||
fFrame.bottom += y;
|
||||
|
||||
// the border is dirty, put it into
|
||||
// dirtyRegion for a start
|
||||
GetBorderRegion(dirtyRegion);
|
||||
|
||||
fTopLayer->ResizeBy(x, y, dirtyRegion);
|
||||
}
|
||||
|
||||
// AddChild
|
||||
void
|
||||
WindowLayer::AddChild(ViewLayer* layer)
|
||||
{
|
||||
fTopLayer->AddChild(layer);
|
||||
|
||||
// TODO: trigger redraw for dirty regions
|
||||
}
|
||||
|
||||
// MarkDirty
|
||||
void
|
||||
WindowLayer::MarkDirty(BRegion* regionOnScreen)
|
||||
{
|
||||
fDesktop->MarkDirty(regionOnScreen);
|
||||
}
|
||||
|
||||
# pragma mark -
|
||||
|
||||
// _DrawContents
|
||||
void
|
||||
WindowLayer::_DrawContents(ViewLayer* layer)
|
||||
{
|
||||
//printf("%s - DrawContents()\n", Name());
|
||||
#if SLOW_DRAWING
|
||||
snooze(10000);
|
||||
#endif
|
||||
|
||||
if (!layer)
|
||||
layer = fTopLayer;
|
||||
|
||||
BRegion effectiveLayerClipping(layer->ScreenClipping());
|
||||
effectiveLayerClipping.IntersectWith(&fVisibleRegion);
|
||||
effectiveLayerClipping.IntersectWith(fDesktop->DirtyRegion());
|
||||
if (effectiveLayerClipping.Frame().IsValid()) {
|
||||
layer->Draw(fDrawingEngine, &effectiveLayerClipping, true);
|
||||
fDesktop->MarkClean(&effectiveLayerClipping);
|
||||
}
|
||||
//else {
|
||||
//printf(" nothing to do\n");
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
// _DrawBorder
|
||||
void
|
||||
WindowLayer::_DrawBorder()
|
||||
{
|
||||
//printf("%s - DrawBorder()\n", Name());
|
||||
#if SLOW_DRAWING
|
||||
snooze(10000);
|
||||
#endif
|
||||
|
||||
// construct the region containing just the border
|
||||
BRegion borderRegion(fVisibleRegion);
|
||||
borderRegion.Exclude(fFrame);
|
||||
// intersect with the Desktop's dirty region
|
||||
borderRegion.IntersectWith(fDesktop->DirtyRegion());
|
||||
|
||||
if (borderRegion.Frame().IsValid()) {
|
||||
if (fDrawingEngine->Lock()) {
|
||||
fDrawingEngine->SetHighColor(fBorderColor);
|
||||
fDrawingEngine->FillRegion(&borderRegion);
|
||||
fDrawingEngine->MarkDirty(&borderRegion);
|
||||
fDrawingEngine->Unlock();
|
||||
}
|
||||
fDesktop->MarkClean(&borderRegion);
|
||||
}
|
||||
//else {
|
||||
//printf(" nothing to do\n");
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
#ifndef WINDOW_LAYER_H
|
||||
#define WINDOW_LAYER_H
|
||||
|
||||
#include <Looper.h>
|
||||
#include <Region.h>
|
||||
#include <String.h>
|
||||
|
||||
#include "ViewLayer.h"
|
||||
|
||||
class Desktop;
|
||||
class DrawingEngine;
|
||||
|
||||
enum {
|
||||
MSG_REDRAW = 'rdrw',
|
||||
};
|
||||
|
||||
class WindowLayer : public BLooper {
|
||||
public:
|
||||
WindowLayer(BRect frame, const char* name,
|
||||
DrawingEngine* drawingEngine,
|
||||
Desktop* desktop);
|
||||
virtual ~WindowLayer();
|
||||
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
inline BRect Frame() const
|
||||
{ return fFrame; }
|
||||
void SetClipping(BRegion* stillAvailableOnScreen);
|
||||
inline BRegion& VisibleRegion()
|
||||
{ return fVisibleRegion; }
|
||||
void GetFullRegion(BRegion* region) const;
|
||||
void GetBorderRegion(BRegion* region) const;
|
||||
|
||||
void MoveBy(int32 x, int32 y);
|
||||
void ResizeBy(int32 x, int32 y, BRegion* dirtyRegion);
|
||||
|
||||
void AddChild(ViewLayer* layer);
|
||||
|
||||
void MarkDirty(BRegion* regionOnScreen);
|
||||
|
||||
private:
|
||||
void _DrawContents(ViewLayer* layer = NULL);
|
||||
void _DrawBorder();
|
||||
|
||||
|
||||
BRect fFrame;
|
||||
// the visible region is only recalculated from the
|
||||
// Desktop thread, when using it, Desktop::LockClipping()
|
||||
// has to be called
|
||||
BRegion fVisibleRegion;
|
||||
|
||||
rgb_color fBorderColor;
|
||||
|
||||
ViewLayer* fTopLayer;
|
||||
|
||||
DrawingEngine* fDrawingEngine;
|
||||
Desktop* fDesktop;
|
||||
};
|
||||
|
||||
#endif // WINDOW_LAYER_H
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
#include <Application.h>
|
||||
#include <Window.h>
|
||||
#include <View.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Desktop.h"
|
||||
#include "DrawingEngine.h"
|
||||
#include "ViewLayer.h"
|
||||
#include "WindowLayer.h"
|
||||
|
||||
class App : public BApplication {
|
||||
public:
|
||||
App();
|
||||
~App();
|
||||
|
||||
virtual void ReadyToRun();
|
||||
};
|
||||
|
||||
class Window : public BWindow {
|
||||
public:
|
||||
Window(const char* title);
|
||||
~Window();
|
||||
|
||||
void AddWindow(BRect frame, const char* name);
|
||||
void Test();
|
||||
private:
|
||||
DrawView* fView;
|
||||
Desktop* fDesktop;
|
||||
};
|
||||
|
||||
App::App()
|
||||
: BApplication("application/x-vnd.stippi.ClippingTest")
|
||||
{
|
||||
srand(real_time_clock_usecs());
|
||||
}
|
||||
|
||||
App::~App()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
App::ReadyToRun()
|
||||
{
|
||||
Window* win = new Window("clipping");
|
||||
win->Show();
|
||||
|
||||
win->Test();
|
||||
}
|
||||
|
||||
Window::Window(const char* title)
|
||||
: BWindow(BRect(50, 50, 800, 650), title,
|
||||
B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
|
||||
B_QUIT_ON_WINDOW_CLOSE | B_ASYNCHRONOUS_CONTROLS)
|
||||
{
|
||||
fView = new DrawView(Bounds());
|
||||
fDesktop = new Desktop(fView);
|
||||
fDesktop->Run();
|
||||
AddChild(fView);
|
||||
fView->MakeFocus(true);
|
||||
}
|
||||
|
||||
Window::~Window()
|
||||
{
|
||||
fDesktop->Lock();
|
||||
fDesktop->Quit();
|
||||
}
|
||||
|
||||
// AddWindow
|
||||
void
|
||||
Window::AddWindow(BRect frame, const char* name)
|
||||
{
|
||||
WindowLayer* window = new WindowLayer(frame, name,
|
||||
fDesktop->GetDrawingEngine(),
|
||||
fDesktop);
|
||||
window->Run();
|
||||
|
||||
BMessage message(MSG_ADD_WINDOW);
|
||||
message.AddPointer("window", (void*)window);
|
||||
fDesktop->PostMessage(&message);
|
||||
}
|
||||
|
||||
// Test
|
||||
void
|
||||
Window::Test()
|
||||
{
|
||||
AddWindow(BRect(20, 20, 80, 80), "Window 1");
|
||||
AddWindow(BRect(60, 60, 220, 180), "Window 2");
|
||||
AddWindow(BRect(120, 160, 500, 380), "Window 3");
|
||||
AddWindow(BRect(40, 210, 400, 280), "Window 4");
|
||||
AddWindow(BRect(180, 410, 400, 680), "Window 5");
|
||||
AddWindow(BRect(30, 350, 100, 440), "Window 6");
|
||||
AddWindow(BRect(80, 10, 200, 120), "Window 7");
|
||||
AddWindow(BRect(480, 40, 670, 320), "Window 8");
|
||||
AddWindow(BRect(250, 500, 310, 600), "Window 9");
|
||||
AddWindow(BRect(130, 450, 230, 500), "Window 10");
|
||||
}
|
||||
|
||||
// main
|
||||
int
|
||||
main(int argc, const char* argv[])
|
||||
{
|
||||
App app;
|
||||
app.Run();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
## BeOS Generic Makefile v2.2 ##
|
||||
|
||||
## Fill in this file to specify the project being created, and the referenced
|
||||
## makefile-engine will do all of the hard work for you. This handles both
|
||||
## Intel and PowerPC builds of the BeOS.
|
||||
|
||||
## Application Specific Settings ---------------------------------------------
|
||||
|
||||
# specify the name of the binary
|
||||
NAME= ../ClippingTest
|
||||
|
||||
# specify the type of binary
|
||||
# APP: Application
|
||||
# SHARED: Shared library or add-on
|
||||
# STATIC: Static library archive
|
||||
# DRIVER: Kernel Driver
|
||||
TYPE= APP
|
||||
|
||||
# add support for new Pe and Eddie features
|
||||
# to fill in generic makefile
|
||||
|
||||
#%{
|
||||
# @src->@
|
||||
|
||||
# specify the source files to use
|
||||
# full paths or paths relative to the makefile can be included
|
||||
# all files, regardless of directory, will have their object
|
||||
# files created in the common object directory.
|
||||
# Note that this means this makefile will not work correctly
|
||||
# if two source files with the same name (source.c or source.cpp)
|
||||
# are included from different directories. Also note that spaces
|
||||
# in folder names do not work well with this makefile.
|
||||
SRCS= Desktop.cpp \
|
||||
DrawingEngine.cpp \
|
||||
main.cpp \
|
||||
MultiLocker.cpp \
|
||||
ViewLayer.cpp \
|
||||
WindowLayer.cpp
|
||||
|
||||
# specify the resource files to use
|
||||
# full path or a relative path to the resource file can be used.
|
||||
RSRCS=
|
||||
|
||||
# @<-src@
|
||||
#%}
|
||||
|
||||
# end support for Pe and Eddie
|
||||
|
||||
# specify additional libraries to link against
|
||||
# there are two acceptable forms of library specifications
|
||||
# - if your library follows the naming pattern of:
|
||||
# libXXX.so or libXXX.a you can simply specify XXX
|
||||
# library: libbe.so entry: be
|
||||
#
|
||||
# - if your library does not follow the standard library
|
||||
# naming scheme you need to specify the path to the library
|
||||
# and it's name
|
||||
# library: my_lib.a entry: my_lib.a or path/my_lib.a
|
||||
LIBS= be
|
||||
|
||||
# specify additional paths to directories following the standard
|
||||
# libXXX.so or libXXX.a naming scheme. You can specify full paths
|
||||
# or paths relative to the makefile. The paths included may not
|
||||
# be recursive, so include all of the paths where libraries can
|
||||
# be found. Directories where source files are found are
|
||||
# automatically included.
|
||||
LIBPATHS=
|
||||
|
||||
# additional paths to look for system headers
|
||||
# thes use the form: #include <header>
|
||||
# source file directories are NOT auto-included here
|
||||
SYSTEM_INCLUDE_PATHS =
|
||||
|
||||
# additional paths to look for local headers
|
||||
# thes use the form: #include "header"
|
||||
# source file directories are automatically included
|
||||
LOCAL_INCLUDE_PATHS =
|
||||
|
||||
# specify the level of optimization that you desire
|
||||
# NONE, SOME, FULL
|
||||
OPTIMIZE= SOME
|
||||
|
||||
# specify any preprocessor symbols to be defined. The symbols will not
|
||||
# have their values set automatically; you must supply the value (if any)
|
||||
# to use. For example, setting DEFINES to "DEBUG=1" will cause the
|
||||
# compiler option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG"
|
||||
# would pass "-DDEBUG" on the compiler's command line.
|
||||
DEFINES=
|
||||
|
||||
# specify special warning levels
|
||||
# if unspecified default warnings will be used
|
||||
# NONE = supress all warnings
|
||||
# ALL = enable all warnings
|
||||
WARNINGS =
|
||||
|
||||
# specify whether image symbols will be created
|
||||
# so that stack crawls in the debugger are meaningful
|
||||
# if TRUE symbols will be created
|
||||
SYMBOLS =
|
||||
|
||||
# specify debug settings
|
||||
# if TRUE will allow application to be run from a source-level
|
||||
# debugger. Note that this will disable all optimzation.
|
||||
DEBUGGER =
|
||||
|
||||
# specify additional compiler flags for all files
|
||||
COMPILER_FLAGS =
|
||||
|
||||
# specify additional linker flags
|
||||
LINKER_FLAGS =
|
||||
|
||||
# specify the version of this particular item
|
||||
# (for example, -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL")
|
||||
# This may also be specified in a resource.
|
||||
APP_VERSION =
|
||||
|
||||
# (for TYPE == DRIVER only) Specify desired location of driver in the /dev
|
||||
# hierarchy. Used by the driverinstall rule. E.g., DRIVER_PATH = video/usb will
|
||||
# instruct the driverinstall rule to place a symlink to your driver's binary in
|
||||
# ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will appear at
|
||||
# /dev/video/usb when loaded. Default is "misc".
|
||||
DRIVER_PATH =
|
||||
|
||||
## include the makefile-engine
|
||||
include $(BUILDHOME)/etc/makefile-engine
|
||||
Reference in New Issue
Block a user