* adds drawing commands from clients

* adds concept of a current and a pending update session
* marks dirty views being resized or moved

Some aspects of the design are buggy, others are slow, but I'm
approaching a good overview of what's needed and what problems
lurk in the details. In the end I hope to make things work fast
and correctly at all times. Adi or anybody else, feel free to
join the efforts.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15158 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus 2005-11-26 00:13:34 +00:00
parent a40e885b9d
commit ff89d51e02
9 changed files with 963 additions and 514 deletions

View File

@ -0,0 +1,68 @@
#include <stdio.h>
#include <Message.h>
#include <MessageQueue.h>
#include <String.h>
#include "WindowLayer.h"
#include "ClientLooper.h"
#define SLOW_DRAWING 0
// constructor
ClientLooper::ClientLooper(const char* name, WindowLayer* serverWindow)
: BLooper(""),
fServerWindow(serverWindow),
fViewCount(0)
{
BString clientName(name);
clientName << " client";
SetName(clientName.String());
}
// destructor
ClientLooper::~ClientLooper()
{
}
// MessageReceived
void
ClientLooper::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_UPDATE:
fServerWindow->PostMessage(MSG_BEGIN_UPDATE);
for (int32 i = 0; i < fViewCount; i++) {
// the client is slow
snooze(1000L);
// send the command to redraw a view
BMessage command(MSG_DRAWING_COMMAND);
command.AddInt32("token", i);
fServerWindow->PostMessage(&command);
}
fServerWindow->PostMessage(MSG_END_UPDATE);
break;
case MSG_VIEWS_ADDED: {
int32 count;
if (message->FindInt32("count", &count) >= B_OK) {
fViewCount += count;
}
break;
}
case MSG_VIEWS_REMOVED: {
int32 count;
if (message->FindInt32("count", &count) >= B_OK)
fViewCount -= count;
break;
}
default:
BLooper::MessageReceived(message);
break;
}
}

View File

@ -0,0 +1,28 @@
#ifndef CLIENT_LOOPER_H
#define CLIENT_LOOPER_H
#include <Looper.h>
class WindowLayer;
enum {
MSG_UPDATE = 'updt',
MSG_VIEWS_ADDED = 'vwad',
MSG_VIEWS_REMOVED = 'vwrm',
};
class ClientLooper : public BLooper {
public:
ClientLooper(const char* name,
WindowLayer* serverWindow);
virtual ~ClientLooper();
virtual void MessageReceived(BMessage* message);
private:
WindowLayer* fServerWindow;
int32 fViewCount;
};
#endif // CLIENT_LOOPER_H

View File

@ -114,6 +114,8 @@ Desktop::MouseDown(BPoint where, uint32 buttons)
// complete redraw
#if RUN_WITH_FRAME_BUFFER
if (fDrawingEngine->Lock()) {
fDirtyRegion.MakeEmpty();
BRegion region(fDrawingEngine->Bounds());
fDrawingEngine->Unlock();
@ -123,6 +125,7 @@ Desktop::MouseDown(BPoint where, uint32 buttons)
_SetBackground(&region);
}
#else
fDirtyRegion.MakeEmpty();
fDrawingEngine->MarkDirty();
#endif
}
@ -156,10 +159,13 @@ Desktop::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
if (dx != 0 || dy != 0) {
if (fClickedWindow) {
//bigtime_t now = system_time();
if (fResizing) {
ResizeWindowBy(fClickedWindow, dx, dy);
//printf("resizing: %lld\n", system_time() - now);
} else {
MoveWindowBy(fClickedWindow, dx, dy);
//printf("moving: %lld\n", system_time() - now);
}
}
}
@ -355,9 +361,9 @@ 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
BRegion alreadyDirtyRegion(fDirtyRegion);
alreadyDirtyRegion.IntersectWith(&window->VisibleRegion());
window->MoveBy(x, y);

View File

@ -65,7 +65,8 @@ class Desktop : public BLooper {
void ReadUnlockClipping() { fClippingLock.WriteUnlock(); }
# endif
#else
bool ReadLockClipping() { return fClippingLock.LockWithTimeout(10000) >= B_OK; }
bool ReadLockClipping() { return fClippingLock.Lock(); }
bool ReadLockClippingWithTimeout() { return fClippingLock.LockWithTimeout(10000) >= B_OK; }
void ReadUnlockClipping() { fClippingLock.Unlock(); }
#endif

File diff suppressed because it is too large Load Diff

View File

@ -2,10 +2,11 @@
#ifndef VIEW_LAYER_H
#define VIEW_LAYER_H
#include <GraphicsDefs.h>
#include <Region.h>
#include <String.h>
class BList;
class DrawingEngine;
class WindowLayer;
@ -26,7 +27,8 @@ class ViewLayer {
inline rgb_color ViewColor() const
{ return fViewColor; }
void AttachedToWindow(WindowLayer* window);
void AttachedToWindow(WindowLayer* window,
bool topLayer = false);
void DetachedFromWindow();
// tree stuff
@ -43,7 +45,8 @@ class ViewLayer {
ViewLayer* TopLayer();
uint32 CountChildren() const;
uint32 CountChildren(bool deep = false) const;
void CollectTokensForChildren(BList* tokenMap) const;
// coordinate conversion
void ConvertToParent(BPoint* point) const;
@ -66,23 +69,30 @@ class ViewLayer {
void ParentResized(int32 dx, int32 dy,
BRegion* dirtyRegion);
// for background clearing
void Draw(DrawingEngine* drawingEngine,
BRegion* effectiveClipping,
bool deep = false);
// to simulate drawing done from client side
void ClientDraw(DrawingEngine* drawingEngine,
BRegion* effectiveClipping);
bool IsHidden() const;
void Hide();
void Show();
// clipping
void RebuildClipping(bool deep = false);
void RebuildClipping(bool deep);
BRegion& ScreenClipping() const;
// debugging
void PrintToStream() const;
private:
void _InvalidateScreenClipping(bool deep = false);
void _InvalidateScreenClipping(bool deep);
void _MoveScreenClipping(int32 x, int32 y,
bool deep);
BString fName;
// area within parent coordinate space
@ -98,6 +108,7 @@ private:
WindowLayer* fWindow;
ViewLayer* fParent;
bool fIsTopLayer;
ViewLayer* fFirstChild;
ViewLayer* fPreviousSibling;

View File

@ -5,6 +5,7 @@
#include <Message.h>
#include <MessageQueue.h>
#include "ClientLooper.h"
#include "Desktop.h"
#include "DrawingEngine.h"
@ -29,7 +30,15 @@ WindowLayer::WindowLayer(BRect frame, const char* name,
fTopLayer(NULL),
fDrawingEngine(drawingEngine),
fDesktop(desktop)
fDesktop(desktop),
fTokenViewMap(64),
fClient(new ClientLooper(name, this)),
fCurrentUpdateSession(NULL),
fPendingUpdateSession(NULL),
fUpdateRequested(false),
fInUpdate(false)
{
// the top layer is special, it has a coordinate system
// as if it was attached directly to the desktop, therefor,
@ -38,6 +47,9 @@ WindowLayer::WindowLayer(BRect frame, const char* name,
// fFrame as if it had
fTopLayer = new(nothrow) ViewLayer(fFrame, "top view", B_FOLLOW_ALL, 0,
(rgb_color){ 255, 255, 255, 255 });
fTopLayer->AttachedToWindow(this, true);
fClient->Run();
}
// destructor
@ -53,7 +65,7 @@ WindowLayer::MessageReceived(BMessage* message)
switch (message->what) {
case MSG_REDRAW: {
if (!MessageQueue()->FindMessage(MSG_REDRAW, 0)) {
while (!fDesktop->ReadLockClipping()) {
while (!fDesktop->ReadLockClippingWithTimeout()) {
//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());
@ -62,12 +74,25 @@ WindowLayer::MessageReceived(BMessage* message)
}
_DrawContents(fTopLayer);
_DrawBorder();
fDesktop->ReadUnlockClipping();
} else {
//printf("%s MSG_REDRAW -> pending redraws\n", Name());
}
break;
}
case MSG_BEGIN_UPDATE:
_BeginUpdate();
break;
case MSG_END_UPDATE:
_EndUpdate();
break;
case MSG_DRAWING_COMMAND: {
int32 token;
if (message->FindInt32("token", &token) >= B_OK)
_DrawClient(token);
break;
}
default:
BLooper::MessageReceived(message);
break;
@ -174,6 +199,11 @@ WindowLayer::MoveBy(int32 x, int32 y)
if (fContentRegionValid)
fContentRegion.OffsetBy(x, y);
if (fCurrentUpdateSession)
fCurrentUpdateSession->MoveBy(x, y);
if (fPendingUpdateSession)
fPendingUpdateSession->MoveBy(x, y);
fTopLayer->MoveBy(x, y);
// TODO: move a local dirty region!
@ -210,6 +240,14 @@ WindowLayer::AddChild(ViewLayer* layer)
{
fTopLayer->AddChild(layer);
// inform client about the view
// (just a part of the simulation)
fTokenViewMap.MakeEmpty();
fTopLayer->CollectTokensForChildren(&fTokenViewMap);
BMessage message(MSG_VIEWS_ADDED);
message.AddInt32("count", fTokenViewMap.CountItems());
fClient->PostMessage(&message);
// TODO: trigger redraw for dirty regions
}
@ -221,6 +259,18 @@ WindowLayer::MarkDirty(BRegion* regionOnScreen)
fDesktop->MarkDirty(regionOnScreen);
}
// DirtyRegion
BRegion
WindowLayer::DirtyRegion()
{
BRegion dirty;
if (fDesktop->ReadLockClipping()) {
dirty = *fDesktop->DirtyRegion();
fDesktop->ReadUnlockClipping();
}
return dirty;
}
# pragma mark -
// _DrawContents
@ -244,15 +294,16 @@ WindowLayer::_DrawContents(ViewLayer* layer)
// TODO: simplify
// ideally, there would only be a local fDirtyRegion,
// that we need to intersect with. fDirtyRegion would
// alread only include fVisibleRegion
// already only include fVisibleRegion
effectiveWindowClipping.IntersectWith(&fVisibleRegion);
effectiveWindowClipping.IntersectWith(fDesktop->DirtyRegion());
if (effectiveWindowClipping.Frame().IsValid()) {
// send UPDATE message to the client here
_MarkContentDirty(&effectiveWindowClipping);
layer->Draw(fDrawingEngine, &effectiveWindowClipping, true);
fDesktop->MarkClean(&fContentRegion);
// send UPDATE message to the client here
}
//else {
//printf(" nothing to do\n");
@ -260,6 +311,35 @@ WindowLayer::_DrawContents(ViewLayer* layer)
}
// _DrawClient
void
WindowLayer::_DrawClient(int32 token)
{
ViewLayer* layer = (ViewLayer*)fTokenViewMap.ItemAt(token);
if (!layer)
return;
BRegion effectiveClipping(layer->ScreenClipping());
if (fInUpdate) {
// enforce the dirty region of the update session
effectiveClipping.IntersectWith(&fCurrentUpdateSession->DirtyRegion());
} else {
printf("%s - _DrawClient(token: %ld) - not in update\n", Name(), token);
}
if (effectiveClipping.CountRects() > 0 && fDesktop->ReadLockClipping()) {
effectiveClipping.IntersectWith(&fVisibleRegion);
// TODO: This step seems too much
BRegion contentRegion;
GetContentRegion(&contentRegion);
effectiveClipping.IntersectWith(&contentRegion);
layer->ClientDraw(fDrawingEngine, &effectiveClipping);
fDesktop->ReadUnlockClipping();
}
}
// _DrawBorder
void
WindowLayer::_DrawBorder()
@ -298,3 +378,90 @@ WindowLayer::_DrawBorder()
//}
}
// _MarkContentDirty
void
WindowLayer::_MarkContentDirty(BRegion* localDirty)
{
if (localDirty->CountRects() <= 0)
return;
if (!fPendingUpdateSession) {
// create new pending
fPendingUpdateSession = new UpdateSession(*localDirty);
} else {
// add to pending
fPendingUpdateSession->Include(localDirty);
}
if (!fUpdateRequested) {
// send this to client
fClient->PostMessage(MSG_UPDATE);
fUpdateRequested = true;
}
}
// _BeginUpdate
void
WindowLayer::_BeginUpdate()
{
if (fUpdateRequested && !fCurrentUpdateSession) {
fCurrentUpdateSession = fPendingUpdateSession;
fPendingUpdateSession = NULL;
if (fCurrentUpdateSession) {
// all drawing command from the client
// will have the dirty region from the update
// session enforced
fInUpdate = true;
}
}
}
// _EndUpdate
void
WindowLayer::_EndUpdate()
{
if (fInUpdate) {
delete fCurrentUpdateSession;
fCurrentUpdateSession = NULL;
fInUpdate = false;
}
if (fPendingUpdateSession) {
// send this to client
fClient->PostMessage(MSG_UPDATE);
fUpdateRequested = true;
} else {
fUpdateRequested = false;
}
}
#pragma mark -
// constructor
UpdateSession::UpdateSession(const BRegion& dirtyRegion)
: fDirtyRegion(dirtyRegion)
{
}
// destructor
UpdateSession::~UpdateSession()
{
}
// Include
void
UpdateSession::Include(BRegion* additionalDirty)
{
fDirtyRegion.Include(additionalDirty);
}
// MoveBy
void
UpdateSession::MoveBy(int32 x, int32 y)
{
fDirtyRegion.OffsetBy(x, y);
}

View File

@ -2,17 +2,39 @@
#ifndef WINDOW_LAYER_H
#define WINDOW_LAYER_H
#include <List.h>
#include <Looper.h>
#include <Region.h>
#include <String.h>
#include "ViewLayer.h"
class ClientLooper;
class Desktop;
class DrawingEngine;
enum {
MSG_REDRAW = 'rdrw',
MSG_REDRAW = 'rdrw',
MSG_BEGIN_UPDATE = 'bgud',
MSG_END_UPDATE = 'edud',
MSG_DRAWING_COMMAND = 'draw',
};
class UpdateSession {
public:
UpdateSession(const BRegion& dirtyRegion);
virtual ~UpdateSession();
void Include(BRegion* additionalDirty);
inline BRegion& DirtyRegion()
{ return fDirtyRegion; }
void MoveBy(int32 x, int32 y);
private:
BRegion fDirtyRegion;
};
class WindowLayer : public BLooper {
@ -42,10 +64,20 @@ class WindowLayer : public BLooper {
void MarkDirty(BRegion* regionOnScreen);
DrawingEngine* GetDrawingEngine() const
{ return fDrawingEngine; }
BRegion DirtyRegion();
private:
void _DrawContents(ViewLayer* layer = NULL);
void _DrawClient(int32 token);
void _DrawBorder();
void _MarkContentDirty(BRegion* localDirty);
void _BeginUpdate();
void _EndUpdate();
BRect fFrame;
// the visible region is only recalculated from the
@ -65,6 +97,14 @@ class WindowLayer : public BLooper {
DrawingEngine* fDrawingEngine;
Desktop* fDesktop;
BList fTokenViewMap;
ClientLooper* fClient;
UpdateSession* fCurrentUpdateSession;
UpdateSession* fPendingUpdateSession;
bool fUpdateRequested;
bool fInUpdate;
};
#endif // WINDOW_LAYER_H

View File

@ -30,7 +30,8 @@ TYPE= APP
# 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 \
SRCS= ClientLooper.cpp \
Desktop.cpp \
DrawingEngine.cpp \
main.cpp \
MultiLocker.cpp \