* added a multi locker implementation from Ingo Weinhold,
which supports ReadLockWithTimeout() * commented the code in many more places * understood the problem of making the read/write locking work: While it would be possible for each window to remove the processed region from the global dirty region in read lock mode (since it is guaranteed not remove a region not intersecting with its own visible region), multiple window threads can still not do this at the same time, since BRegion itself is not threadsafe of course. * I need to figure out a way for the window threads to be able to access and modify all needed data in read only mode, I think this means to divide the global dirty region into each window again, so that each window thread can simply clear its own dirty region instead of excluding it from the global region. Yeah, that might work. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15230 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
a9b83b752c
commit
4dd89c6903
@ -13,7 +13,7 @@
|
||||
|
||||
// constructor
|
||||
ClientLooper::ClientLooper(const char* name, WindowLayer* serverWindow)
|
||||
: BLooper(""),
|
||||
: BLooper("", B_DISPLAY_PRIORITY),
|
||||
fServerWindow(serverWindow),
|
||||
fViewCount(0)
|
||||
{
|
||||
|
@ -231,12 +231,15 @@ Desktop::MessageReceived(BMessage* message)
|
||||
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);
|
||||
}
|
||||
@ -570,6 +573,16 @@ if (fDrawingEngine->Lock()) {
|
||||
void
|
||||
Desktop::MarkClean(BRegion* region)
|
||||
{
|
||||
// NOTE: when a window owns the read lock for
|
||||
// the clipping, it means the Desktop thread is currently
|
||||
// not messing with the regions. Since a window is expected
|
||||
// to only remove parts from the dirty region that intersect
|
||||
// it's own visible region, it cannot remove the wrong parts
|
||||
// of the dirty region (ie parts intersecting another window)
|
||||
// writelocking would therefor not be needed, but of course
|
||||
// BRegion is not threadsafe, and therefor, it would be cool
|
||||
// if the dirty region was moved into each window again.
|
||||
|
||||
if (LockClipping()) {
|
||||
// remove the clean region from the culmulative dirty region
|
||||
fDirtyRegion.Exclude(region);
|
||||
|
@ -3,24 +3,34 @@
|
||||
#define DESKTOP_H
|
||||
|
||||
#include <List.h>
|
||||
#include <Locker.h>
|
||||
#include <Region.h>
|
||||
#include <View.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include "DrawingEngine.h"
|
||||
#include "MultiLocker.h"
|
||||
|
||||
#define SHOW_GLOBAL_DIRTY_REGION 0
|
||||
#define SHOW_WINDOW_CONTENT_DIRTY_REGION 0
|
||||
|
||||
#define MULTI_LOCKER 0
|
||||
#define RW_LOCKER 0
|
||||
|
||||
#if MULTI_LOCKER
|
||||
# include "MultiLocker.h"
|
||||
#elif RW_LOCKER
|
||||
# include "RWLocker.h"
|
||||
#else
|
||||
# include <Locker.h>
|
||||
#endif
|
||||
|
||||
class WindowLayer;
|
||||
|
||||
enum {
|
||||
MSG_ADD_WINDOW = 'addw',
|
||||
MSG_DRAW = 'draw',
|
||||
};
|
||||
|
||||
#define MULTI_LOCKER 0
|
||||
#define SHOW_GLOBAL_DIRTY_REGION 0
|
||||
#define SHOW_WINDOW_CONTENT_DIRTY_REGION 0
|
||||
MSG_MARK_CLEAN = 'mcln',
|
||||
};
|
||||
|
||||
class Desktop : public BLooper {
|
||||
public:
|
||||
@ -58,22 +68,29 @@ class Desktop : public BLooper {
|
||||
|
||||
void SetFocusWindow(WindowLayer* window);
|
||||
|
||||
#if MULTI_LOCKER
|
||||
# if 0
|
||||
#if RW_LOCKER
|
||||
bool ReadLockClipping() { return fClippingLock.ReadLock(); }
|
||||
bool ReadLockClippingWithTimeout() { return fClippingLock.ReadLockWithTimeout(10000) >= B_OK; }
|
||||
void ReadUnlockClipping() { fClippingLock.ReadUnlock(); }
|
||||
# else
|
||||
bool ReadLockClipping() { return fClippingLock.WriteLock(); }
|
||||
void ReadUnlockClipping() { fClippingLock.WriteUnlock(); }
|
||||
# endif
|
||||
#else
|
||||
|
||||
bool LockClipping() { return fClippingLock.WriteLock(); }
|
||||
void UnlockClipping() { fClippingLock.WriteUnlock(); }
|
||||
#elif MULTI_LOCKER
|
||||
bool ReadLockClipping() { return fClippingLock.ReadLock(); }
|
||||
bool ReadLockClippingWithTimeout() { return fClippingLock.ReadLock(); }
|
||||
void ReadUnlockClipping() { fClippingLock.ReadUnlock(); }
|
||||
|
||||
bool LockClipping() { return fClippingLock.WriteLock(); }
|
||||
void UnlockClipping() { fClippingLock.WriteUnlock(); }
|
||||
#else // BLocker
|
||||
bool ReadLockClipping() { return fClippingLock.Lock(); }
|
||||
bool ReadLockClippingWithTimeout() { return fClippingLock.LockWithTimeout(10000) >= B_OK; }
|
||||
void ReadUnlockClipping() { fClippingLock.Unlock(); }
|
||||
#endif
|
||||
|
||||
bool LockClipping() { return fClippingLock.Lock(); }
|
||||
void UnlockClipping() { fClippingLock.Unlock(); }
|
||||
#endif
|
||||
|
||||
|
||||
void MarkDirty(BRegion* region);
|
||||
void MarkClean(BRegion* region);
|
||||
@ -100,6 +117,8 @@ private:
|
||||
|
||||
#if MULTI_LOCKER
|
||||
MultiLocker fClippingLock;
|
||||
#elif RW_LOCKER
|
||||
RWLocker fClippingLock;
|
||||
#else
|
||||
BLocker fClippingLock;
|
||||
#endif
|
||||
|
465
src/tests/servers/app/newerClipping/RWLocker.cpp
Normal file
465
src/tests/servers/app/newerClipping/RWLocker.cpp
Normal file
@ -0,0 +1,465 @@
|
||||
// RWLocker.cpp
|
||||
|
||||
#include <String.h>
|
||||
|
||||
#include "RWLocker.h"
|
||||
|
||||
// info about a read lock owner
|
||||
struct RWLocker::ReadLockInfo {
|
||||
thread_id reader;
|
||||
int32 count;
|
||||
};
|
||||
|
||||
|
||||
// constructor
|
||||
RWLocker::RWLocker()
|
||||
: fLock(),
|
||||
fMutex(),
|
||||
fQueue(),
|
||||
fReaderCount(0),
|
||||
fWriterCount(0),
|
||||
fReadLockInfos(8),
|
||||
fWriter(B_ERROR),
|
||||
fWriterWriterCount(0),
|
||||
fWriterReaderCount(0)
|
||||
{
|
||||
_Init(NULL);
|
||||
}
|
||||
|
||||
// constructor
|
||||
RWLocker::RWLocker(const char* name)
|
||||
: fLock(name),
|
||||
fMutex(),
|
||||
fQueue(),
|
||||
fReaderCount(0),
|
||||
fWriterCount(0),
|
||||
fReadLockInfos(8),
|
||||
fWriter(B_ERROR),
|
||||
fWriterWriterCount(0),
|
||||
fWriterReaderCount(0)
|
||||
{
|
||||
_Init(name);
|
||||
}
|
||||
|
||||
// destructor
|
||||
RWLocker::~RWLocker()
|
||||
{
|
||||
fLock.Lock();
|
||||
delete_sem(fMutex.semaphore);
|
||||
delete_sem(fQueue.semaphore);
|
||||
for (int32 i = 0; ReadLockInfo* info = _ReadLockInfoAt(i); i++)
|
||||
delete info;
|
||||
}
|
||||
|
||||
// ReadLock
|
||||
bool
|
||||
RWLocker::ReadLock()
|
||||
{
|
||||
status_t error = _ReadLock(B_INFINITE_TIMEOUT);
|
||||
return (error == B_OK);
|
||||
}
|
||||
|
||||
// ReadLockWithTimeout
|
||||
status_t
|
||||
RWLocker::ReadLockWithTimeout(bigtime_t timeout)
|
||||
{
|
||||
bigtime_t absoluteTimeout = system_time() + timeout;
|
||||
// take care of overflow
|
||||
if (timeout > 0 && absoluteTimeout < 0)
|
||||
absoluteTimeout = B_INFINITE_TIMEOUT;
|
||||
return _ReadLock(absoluteTimeout);
|
||||
}
|
||||
|
||||
// ReadUnlock
|
||||
void
|
||||
RWLocker::ReadUnlock()
|
||||
{
|
||||
if (fLock.Lock()) {
|
||||
thread_id thread = find_thread(NULL);
|
||||
if (thread == fWriter) {
|
||||
// We (also) have a write lock.
|
||||
if (fWriterReaderCount > 0)
|
||||
fWriterReaderCount--;
|
||||
// else: error: unmatched ReadUnlock()
|
||||
} else {
|
||||
int32 index = _IndexOf(thread);
|
||||
if (ReadLockInfo* info = _ReadLockInfoAt(index)) {
|
||||
fReaderCount--;
|
||||
if (--info->count == 0) {
|
||||
// The outer read lock bracket for the thread has been
|
||||
// reached. Dispose the info.
|
||||
_DeleteReadLockInfo(index);
|
||||
}
|
||||
if (fReaderCount == 0) {
|
||||
// The last reader needs to unlock the mutex.
|
||||
_ReleaseBenaphore(fMutex);
|
||||
}
|
||||
} // else: error: caller has no read lock
|
||||
}
|
||||
fLock.Unlock();
|
||||
} // else: we are probably going to be destroyed
|
||||
}
|
||||
|
||||
// IsReadLocked
|
||||
//
|
||||
// Returns whether or not the calling thread owns a read lock or even a
|
||||
// write lock.
|
||||
bool
|
||||
RWLocker::IsReadLocked() const
|
||||
{
|
||||
bool result = false;
|
||||
if (fLock.Lock()) {
|
||||
thread_id thread = find_thread(NULL);
|
||||
result = (thread == fWriter || _IndexOf(thread) >= 0);
|
||||
fLock.Unlock();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// WriteLock
|
||||
bool
|
||||
RWLocker::WriteLock()
|
||||
{
|
||||
status_t error = _WriteLock(B_INFINITE_TIMEOUT);
|
||||
return (error == B_OK);
|
||||
}
|
||||
|
||||
// WriteLockWithTimeout
|
||||
status_t
|
||||
RWLocker::WriteLockWithTimeout(bigtime_t timeout)
|
||||
{
|
||||
bigtime_t absoluteTimeout = system_time() + timeout;
|
||||
// take care of overflow
|
||||
if (timeout > 0 && absoluteTimeout < 0)
|
||||
absoluteTimeout = B_INFINITE_TIMEOUT;
|
||||
return _WriteLock(absoluteTimeout);
|
||||
}
|
||||
|
||||
// WriteUnlock
|
||||
void
|
||||
RWLocker::WriteUnlock()
|
||||
{
|
||||
if (fLock.Lock()) {
|
||||
thread_id thread = find_thread(NULL);
|
||||
if (thread == fWriter) {
|
||||
fWriterCount--;
|
||||
if (--fWriterWriterCount == 0) {
|
||||
// The outer write lock bracket for the thread has been
|
||||
// reached.
|
||||
fWriter = B_ERROR;
|
||||
if (fWriterReaderCount > 0) {
|
||||
// We still own read locks.
|
||||
_NewReadLockInfo(thread, fWriterReaderCount);
|
||||
// A reader that expects to be the first reader may wait
|
||||
// at the mutex semaphore. We need to wake it up.
|
||||
if (fReaderCount > 0)
|
||||
_ReleaseBenaphore(fMutex);
|
||||
fReaderCount += fWriterReaderCount;
|
||||
fWriterReaderCount = 0;
|
||||
} else {
|
||||
// We don't own any read locks. So we have to release the
|
||||
// mutex benaphore.
|
||||
_ReleaseBenaphore(fMutex);
|
||||
}
|
||||
}
|
||||
} // else: error: unmatched WriteUnlock()
|
||||
fLock.Unlock();
|
||||
} // else: We're probably going to die.
|
||||
}
|
||||
|
||||
// IsWriteLocked
|
||||
//
|
||||
// Returns whether or not the calling thread owns a write lock.
|
||||
bool
|
||||
RWLocker::IsWriteLocked() const
|
||||
{
|
||||
return (fWriter == find_thread(NULL));
|
||||
}
|
||||
|
||||
// _Init
|
||||
void
|
||||
RWLocker::_Init(const char* name)
|
||||
{
|
||||
// init the mutex benaphore
|
||||
BString mutexName(name);
|
||||
mutexName += "_RWLocker_mutex";
|
||||
fMutex.semaphore = create_sem(0, mutexName.String());
|
||||
fMutex.counter = 0;
|
||||
// init the queueing benaphore
|
||||
BString queueName(name);
|
||||
queueName += "_RWLocker_queue";
|
||||
fQueue.semaphore = create_sem(0, queueName.String());
|
||||
fQueue.counter = 0;
|
||||
}
|
||||
|
||||
// _ReadLock
|
||||
//
|
||||
// /timeout/ -- absolute timeout
|
||||
status_t
|
||||
RWLocker::_ReadLock(bigtime_t timeout)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
thread_id thread = find_thread(NULL);
|
||||
bool locked = false;
|
||||
if (fLock.Lock()) {
|
||||
// Check, if we already own a read (or write) lock. In this case we
|
||||
// can skip the usual locking procedure.
|
||||
if (thread == fWriter) {
|
||||
// We already own a write lock.
|
||||
fWriterReaderCount++;
|
||||
locked = true;
|
||||
} else if (ReadLockInfo* info = _ReadLockInfoAt(_IndexOf(thread))) {
|
||||
// We already own a read lock.
|
||||
info->count++;
|
||||
fReaderCount++;
|
||||
locked = true;
|
||||
}
|
||||
fLock.Unlock();
|
||||
} else // failed to lock the data
|
||||
error = B_ERROR;
|
||||
// Usual locking, i.e. we do not already own a read or write lock.
|
||||
if (error == B_OK && !locked) {
|
||||
error = _AcquireBenaphore(fQueue, timeout);
|
||||
if (error == B_OK) {
|
||||
if (fLock.Lock()) {
|
||||
bool firstReader = false;
|
||||
if (++fReaderCount == 1) {
|
||||
// We are the first reader.
|
||||
_NewReadLockInfo(thread);
|
||||
firstReader = true;
|
||||
} else
|
||||
_NewReadLockInfo(thread);
|
||||
fLock.Unlock();
|
||||
// The first reader needs to lock the mutex.
|
||||
if (firstReader) {
|
||||
error = _AcquireBenaphore(fMutex, timeout);
|
||||
switch (error) {
|
||||
case B_OK:
|
||||
// fine
|
||||
break;
|
||||
case B_TIMED_OUT: {
|
||||
// clean up
|
||||
if (fLock.Lock()) {
|
||||
_DeleteReadLockInfo(_IndexOf(thread));
|
||||
fReaderCount--;
|
||||
fLock.Unlock();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Probably we are going to be destroyed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Let the next candidate enter the game.
|
||||
_ReleaseBenaphore(fQueue);
|
||||
} else {
|
||||
// We couldn't lock the data, which can only happen, if
|
||||
// we're going to be destroyed.
|
||||
error = B_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// _WriteLock
|
||||
//
|
||||
// /timeout/ -- absolute timeout
|
||||
status_t
|
||||
RWLocker::_WriteLock(bigtime_t timeout)
|
||||
{
|
||||
status_t error = B_ERROR;
|
||||
if (fLock.Lock()) {
|
||||
bool infiniteTimeout = (timeout == B_INFINITE_TIMEOUT);
|
||||
bool locked = false;
|
||||
int32 readerCount = 0;
|
||||
thread_id thread = find_thread(NULL);
|
||||
int32 index = _IndexOf(thread);
|
||||
if (ReadLockInfo* info = _ReadLockInfoAt(index)) {
|
||||
// We already own a read lock.
|
||||
if (fWriterCount > 0) {
|
||||
// There are writers before us.
|
||||
if (infiniteTimeout) {
|
||||
// Timeout is infinite and there are writers before us.
|
||||
// Unregister the read locks and lock as usual.
|
||||
readerCount = info->count;
|
||||
fWriterCount++;
|
||||
fReaderCount -= readerCount;
|
||||
_DeleteReadLockInfo(index);
|
||||
error = B_OK;
|
||||
} else {
|
||||
// The timeout is finite and there are readers before us:
|
||||
// let the write lock request fail.
|
||||
error = B_WOULD_BLOCK;
|
||||
}
|
||||
} else if (info->count == fReaderCount) {
|
||||
// No writers before us.
|
||||
// We are the only read lock owners. Just move the read lock
|
||||
// info data to the special writer fields and then we are done.
|
||||
// Note: At this point we may overtake readers that already
|
||||
// have acquired the queueing benaphore, but have not yet
|
||||
// locked the data. But that doesn't harm.
|
||||
fWriter = thread;
|
||||
fWriterCount++;
|
||||
fWriterWriterCount = 1;
|
||||
fWriterReaderCount = info->count;
|
||||
fReaderCount -= fWriterReaderCount;
|
||||
_DeleteReadLockInfo(index);
|
||||
locked = true;
|
||||
error = B_OK;
|
||||
} else {
|
||||
// No writers before us, but other readers.
|
||||
// Note, we're quite restrictive here. If there are only
|
||||
// readers before us, we could reinstall our readers, if
|
||||
// our request times out. Unfortunately it is not easy
|
||||
// to ensure, that no writer overtakes us between unlocking
|
||||
// the data and acquiring the queuing benaphore.
|
||||
if (infiniteTimeout) {
|
||||
// Unregister the readers and lock as usual.
|
||||
readerCount = info->count;
|
||||
fWriterCount++;
|
||||
fReaderCount -= readerCount;
|
||||
_DeleteReadLockInfo(index);
|
||||
error = B_OK;
|
||||
} else
|
||||
error = B_WOULD_BLOCK;
|
||||
}
|
||||
} else {
|
||||
// We don't own a read lock.
|
||||
if (fWriter == thread) {
|
||||
// ... but a write lock.
|
||||
fWriterCount++;
|
||||
fWriterWriterCount++;
|
||||
locked = true;
|
||||
error = B_OK;
|
||||
} else {
|
||||
// We own neither read nor write locks.
|
||||
// Lock as usual.
|
||||
fWriterCount++;
|
||||
error = B_OK;
|
||||
}
|
||||
}
|
||||
fLock.Unlock();
|
||||
// Usual locking...
|
||||
// First step: acquire the queueing benaphore.
|
||||
if (!locked && error == B_OK) {
|
||||
error = _AcquireBenaphore(fQueue, timeout);
|
||||
switch (error) {
|
||||
case B_OK:
|
||||
break;
|
||||
case B_TIMED_OUT: {
|
||||
// clean up
|
||||
if (fLock.Lock()) {
|
||||
fWriterCount--;
|
||||
fLock.Unlock();
|
||||
} // else: failed to lock the data: we're probably going
|
||||
// to die.
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Probably we're going to die.
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Second step: acquire the mutex benaphore.
|
||||
if (!locked && error == B_OK) {
|
||||
error = _AcquireBenaphore(fMutex, timeout);
|
||||
switch (error) {
|
||||
case B_OK: {
|
||||
// Yeah, we made it. Set the special writer fields.
|
||||
fWriter = thread;
|
||||
fWriterWriterCount = 1;
|
||||
fWriterReaderCount = readerCount;
|
||||
break;
|
||||
}
|
||||
case B_TIMED_OUT: {
|
||||
// clean up
|
||||
if (fLock.Lock()) {
|
||||
fWriterCount--;
|
||||
fLock.Unlock();
|
||||
} // else: failed to lock the data: we're probably going
|
||||
// to die.
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Probably we're going to die.
|
||||
break;
|
||||
}
|
||||
// Whatever happened, we have to release the queueing benaphore.
|
||||
_ReleaseBenaphore(fQueue);
|
||||
}
|
||||
} else // failed to lock the data
|
||||
error = B_ERROR;
|
||||
return error;
|
||||
}
|
||||
|
||||
// _AddReadLockInfo
|
||||
int32
|
||||
RWLocker::_AddReadLockInfo(ReadLockInfo* info)
|
||||
{
|
||||
int32 index = fReadLockInfos.CountItems();
|
||||
fReadLockInfos.AddItem(info, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
// _NewReadLockInfo
|
||||
//
|
||||
// Create a new read lock info for the supplied thread and add it to the
|
||||
// list. Returns the index of the info.
|
||||
int32
|
||||
RWLocker::_NewReadLockInfo(thread_id thread, int32 count)
|
||||
{
|
||||
ReadLockInfo* info = new ReadLockInfo;
|
||||
info->reader = thread;
|
||||
info->count = count;
|
||||
return _AddReadLockInfo(info);
|
||||
}
|
||||
|
||||
// _DeleteReadLockInfo
|
||||
void
|
||||
RWLocker::_DeleteReadLockInfo(int32 index)
|
||||
{
|
||||
if (ReadLockInfo* info = (ReadLockInfo*)fReadLockInfos.RemoveItem(index))
|
||||
delete info;
|
||||
}
|
||||
|
||||
// _ReadLockInfoAt
|
||||
RWLocker::ReadLockInfo*
|
||||
RWLocker::_ReadLockInfoAt(int32 index) const
|
||||
{
|
||||
return (ReadLockInfo*)fReadLockInfos.ItemAt(index);
|
||||
}
|
||||
|
||||
// _IndexOf
|
||||
int32
|
||||
RWLocker::_IndexOf(thread_id thread) const
|
||||
{
|
||||
int32 count = fReadLockInfos.CountItems();
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
if (_ReadLockInfoAt(i)->reader == thread)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// _AcquireBenaphore
|
||||
status_t
|
||||
RWLocker::_AcquireBenaphore(Benaphore& benaphore, bigtime_t timeout)
|
||||
{
|
||||
status_t error = B_OK;
|
||||
if (atomic_add(&benaphore.counter, 1) > 0) {
|
||||
error = acquire_sem_etc(benaphore.semaphore, 1, B_ABSOLUTE_TIMEOUT,
|
||||
timeout);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
// _ReleaseBenaphore
|
||||
void
|
||||
RWLocker::_ReleaseBenaphore(Benaphore& benaphore)
|
||||
{
|
||||
if (atomic_add(&benaphore.counter, -1) > 1)
|
||||
release_sem(benaphore.semaphore);
|
||||
}
|
||||
|
126
src/tests/servers/app/newerClipping/RWLocker.h
Normal file
126
src/tests/servers/app/newerClipping/RWLocker.h
Normal file
@ -0,0 +1,126 @@
|
||||
// RWLocker.h
|
||||
//
|
||||
// This class provides a reader/writer locking mechanism:
|
||||
// * A writer needs an exclusive lock.
|
||||
// * For a reader a non-exclusive lock to be shared with other readers is
|
||||
// sufficient.
|
||||
// * The ownership of a lock is bound to the thread that requested the lock;
|
||||
// the same thread has to call Unlock() later.
|
||||
// * Nested locking is supported: a number of XXXLock() calls needs to be
|
||||
// bracketed by the same number of XXXUnlock() calls.
|
||||
// * The lock acquiration strategy is fair: a lock applicant needs to wait
|
||||
// only for those threads that already own a lock or requested one before
|
||||
// the current thread. No one can overtake. E.g. if a thread owns a read
|
||||
// lock, another one is waiting for a write lock, then a third one
|
||||
// requesting a read lock has to wait until the write locker is done.
|
||||
// This does not hold for threads that already own a lock (nested locking).
|
||||
// A read lock owner is immediately granted another read lock and a write
|
||||
// lock owner another write or a read lock.
|
||||
// * A write lock owner is allowed to request a read lock and a read lock
|
||||
// owner a write lock. While the first case is not problematic, the
|
||||
// second one needs some further explanation: A read lock owner requesting
|
||||
// a write lock temporarily looses its read lock(s) until the write lock
|
||||
// is granted. Otherwise two read lock owning threads trying to get
|
||||
// write locks at the same time would dead lock each other. The only
|
||||
// problem with this solution is, that the write lock acquiration must
|
||||
// not fail, because in that case the thread could not be given back
|
||||
// its read lock(s), since another thread may have been given a write lock
|
||||
// in the mean time. Fortunately locking can fail only either, if the
|
||||
// locker has been deleted, or, if a timeout occured. Therefore
|
||||
// WriteLockWithTimeout() immediatlely returns with a B_WOULD_BLOCK error
|
||||
// code, if the caller already owns a read lock (but no write lock) and
|
||||
// another thread already owns or has requested a read or write lock.
|
||||
// * Calls to read and write locking methods may interleave arbitrarily,
|
||||
// e.g.: ReadLock(); WriteLock(); ReadUnlock(); WriteUnlock();
|
||||
//
|
||||
// Important note: Read/WriteLock() can fail only, if the locker has been
|
||||
// deleted. However, it is NOT save to invoke any method on a deleted
|
||||
// locker object.
|
||||
//
|
||||
// Implementation details:
|
||||
// A locker needs three semaphores (a BLocker and two semaphores): one
|
||||
// to protect the lockers data, one as a reader/writer mutex (to be
|
||||
// acquired by each writer and the first reader) and one for queueing
|
||||
// waiting readers and writers. The simplified locking/unlocking
|
||||
// algorithm is the following:
|
||||
//
|
||||
// writer reader
|
||||
// queue.acquire() queue.acquire()
|
||||
// mutex.acquire() if (first reader) mutex.acquire()
|
||||
// queue.release() queue.release()
|
||||
// ... ...
|
||||
// mutex.release() if (last reader) mutex.release()
|
||||
//
|
||||
// One thread at maximum waits at the mutex, the others at the queueing
|
||||
// semaphore. Unfortunately features as nested locking and timeouts make
|
||||
// things more difficult. Therefore readers as well as writers need to check
|
||||
// whether they already own a lock before acquiring the queueing semaphore.
|
||||
// The data for the readers are stored in a list of ReadLockInfo structures;
|
||||
// the writer data are stored in some special fields. /fReaderCount/ and
|
||||
// /fWriterCount/ contain the total count of unbalanced Read/WriteLock()
|
||||
// calls, /fWriterReaderCount/ and /fWriterWriterCount/ only from those of
|
||||
// the current write lock owner (/fWriter/). To be a bit more precise:
|
||||
// /fWriterReaderCount/ is not contained in /fReaderCount/, but
|
||||
// /fWriterWriterCount/ is contained in /fWriterCount/. Therefore
|
||||
// /fReaderCount/ can be considered to be the count of true reader's read
|
||||
// locks.
|
||||
|
||||
#ifndef RW_LOCKER_H
|
||||
#define RW_LOCKER_H
|
||||
|
||||
#include <List.h>
|
||||
#include <Locker.h>
|
||||
|
||||
class RWLocker {
|
||||
public:
|
||||
RWLocker();
|
||||
RWLocker(const char* name);
|
||||
virtual ~RWLocker();
|
||||
|
||||
bool ReadLock();
|
||||
status_t ReadLockWithTimeout(bigtime_t timeout);
|
||||
void ReadUnlock();
|
||||
bool IsReadLocked() const;
|
||||
|
||||
bool WriteLock();
|
||||
status_t WriteLockWithTimeout(bigtime_t timeout);
|
||||
void WriteUnlock();
|
||||
bool IsWriteLocked() const;
|
||||
|
||||
private:
|
||||
struct ReadLockInfo;
|
||||
struct Benaphore {
|
||||
sem_id semaphore;
|
||||
int32 counter;
|
||||
};
|
||||
|
||||
private:
|
||||
void _Init(const char* name);
|
||||
status_t _ReadLock(bigtime_t timeout);
|
||||
status_t _WriteLock(bigtime_t timeout);
|
||||
|
||||
int32 _AddReadLockInfo(ReadLockInfo* info);
|
||||
int32 _NewReadLockInfo(thread_id thread,
|
||||
int32 count = 1);
|
||||
void _DeleteReadLockInfo(int32 index);
|
||||
ReadLockInfo* _ReadLockInfoAt(int32 index) const;
|
||||
int32 _IndexOf(thread_id thread) const;
|
||||
|
||||
static status_t _AcquireBenaphore(Benaphore& benaphore,
|
||||
bigtime_t timeout);
|
||||
static void _ReleaseBenaphore(Benaphore& benaphore);
|
||||
|
||||
private:
|
||||
mutable BLocker fLock; // data lock
|
||||
Benaphore fMutex; // critical code mutex
|
||||
Benaphore fQueue; // queueing semaphore
|
||||
int32 fReaderCount; // total count...
|
||||
int32 fWriterCount; // total count...
|
||||
BList fReadLockInfos;
|
||||
thread_id fWriter; // current write lock owner
|
||||
int32 fWriterWriterCount; // write lock owner count
|
||||
int32 fWriterReaderCount; // writer read lock owner
|
||||
// count
|
||||
};
|
||||
|
||||
#endif // RW_LOCKER_H
|
@ -11,12 +11,21 @@
|
||||
|
||||
#include "WindowLayer.h"
|
||||
|
||||
#define SLOW_DRAWING 0
|
||||
// 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 1
|
||||
|
||||
// constructor
|
||||
WindowLayer::WindowLayer(BRect frame, const char* name,
|
||||
DrawingEngine* drawingEngine, Desktop* desktop)
|
||||
: BLooper(name),
|
||||
: BLooper(name, B_DISPLAY_PRIORITY),
|
||||
fFrame(frame),
|
||||
|
||||
fVisibleRegion(),
|
||||
@ -188,20 +197,19 @@ WindowLayer::GetContentRegion(BRegion* region)
|
||||
void
|
||||
WindowLayer::SetFocus(bool focus)
|
||||
{
|
||||
if (Lock()) {
|
||||
// executed from Desktop thread, so it's fine
|
||||
// to use the clipping without locking
|
||||
if (fDesktop->ReadLockClipping()) {
|
||||
BRegion dirty(fBorderRegion);
|
||||
dirty.IntersectWith(&fVisibleRegion);
|
||||
fDesktop->MarkDirty(&dirty);
|
||||
// executed from Desktop thread
|
||||
|
||||
fDesktop->ReadUnlockClipping();
|
||||
}
|
||||
// since we don't mark parts dirty that
|
||||
// don't intersect with our own visible
|
||||
// region, readlocking is fine
|
||||
if (fDesktop->ReadLockClipping()) {
|
||||
BRegion dirty(fBorderRegion);
|
||||
dirty.IntersectWith(&fVisibleRegion);
|
||||
fDesktop->MarkDirty(&dirty);
|
||||
|
||||
fFocus = focus;
|
||||
|
||||
Unlock();
|
||||
fDesktop->ReadUnlockClipping();
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,6 +218,7 @@ void
|
||||
WindowLayer::MoveBy(int32 x, int32 y)
|
||||
{
|
||||
// this function is only called from the desktop thread
|
||||
// TODO: this fact needs review maybe
|
||||
|
||||
if (x == 0 && y == 0)
|
||||
return;
|
||||
@ -237,6 +246,9 @@ WindowLayer::MoveBy(int32 x, int32 y)
|
||||
void
|
||||
WindowLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
|
||||
{
|
||||
// this function is only called from the desktop thread
|
||||
// TODO: this fact needs review maybe
|
||||
|
||||
if (x == 0 && y == 0)
|
||||
return;
|
||||
|
||||
@ -316,17 +328,17 @@ WindowLayer::MarkDirty(BRegion* regionOnScreen)
|
||||
void
|
||||
WindowLayer::MarkContentDirty(BRegion* regionOnScreen)
|
||||
{
|
||||
// for triggering MSG_REDRAW
|
||||
/* // for triggering MSG_REDRAW
|
||||
if (fDesktop && fDesktop->LockClipping()) {
|
||||
|
||||
regionOnScreen->IntersectWith(&fVisibleContentRegion);
|
||||
fDesktop->MarkDirty(regionOnScreen);
|
||||
|
||||
fDesktop->UnlockClipping();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
# pragma mark -
|
||||
//# pragma mark -
|
||||
|
||||
// CopyContents
|
||||
void
|
||||
@ -338,28 +350,41 @@ WindowLayer::CopyContents(BRegion* region, int32 xOffset, int32 yOffset)
|
||||
|
||||
BRegion newDirty(*region);
|
||||
|
||||
// clip the region to the visible contents at the
|
||||
// source and destination location
|
||||
region->IntersectWith(&fVisibleContentRegion);
|
||||
region->OffsetBy(xOffset, yOffset);
|
||||
region->IntersectWith(&fVisibleContentRegion);
|
||||
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);
|
||||
|
||||
if (fDrawingEngine->Lock()) {
|
||||
fDrawingEngine->CopyRegion(region, xOffset, yOffset);
|
||||
fDrawingEngine->Unlock();
|
||||
}
|
||||
|
||||
region->OffsetBy(-xOffset, -yOffset);
|
||||
|
||||
newDirty.Exclude(region);
|
||||
|
||||
_ShiftPartOfRegion(fDesktop->DirtyRegion(), region, xOffset, yOffset);
|
||||
if (fDrawingEngine->Lock()) {
|
||||
fDrawingEngine->CopyRegion(region, xOffset, yOffset);
|
||||
fDrawingEngine->Unlock();
|
||||
// move along the already dirty regions that are common
|
||||
// with the region that we could copy
|
||||
_ShiftPartOfRegion(fDesktop->DirtyRegion(), region, xOffset, yOffset);
|
||||
if (fCurrentUpdateSession)
|
||||
_ShiftPartOfRegion(&fCurrentUpdateSession->DirtyRegion(), region, xOffset, yOffset);
|
||||
if (fPendingUpdateSession)
|
||||
_ShiftPartOfRegion(&fPendingUpdateSession->DirtyRegion(), region, xOffset, yOffset);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (fCurrentUpdateSession)
|
||||
_ShiftPartOfRegion(&fCurrentUpdateSession->DirtyRegion(), region, xOffset, yOffset);
|
||||
if (fPendingUpdateSession)
|
||||
_ShiftPartOfRegion(&fPendingUpdateSession->DirtyRegion(), region, xOffset, yOffset);
|
||||
|
||||
// what is left visible from the original region
|
||||
// at the destination after the region which could be
|
||||
// copied has been excluded, is considered dirty
|
||||
newDirty.OffsetBy(xOffset, yOffset);
|
||||
newDirty.IntersectWith(&fVisibleContentRegion);
|
||||
fDesktop->MarkDirty(&newDirty);
|
||||
if (newDirty.CountRects() > 0)
|
||||
fDesktop->MarkDirty(&newDirty);
|
||||
|
||||
fDesktop->UnlockClipping();
|
||||
}
|
||||
@ -372,11 +397,16 @@ void
|
||||
WindowLayer::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
|
||||
int32 xOffset, int32 yOffset)
|
||||
{
|
||||
BRegion common(*region);
|
||||
common.IntersectWith(regionToShift);
|
||||
region->Exclude(&common);
|
||||
common.OffsetBy(xOffset, yOffset);
|
||||
region->Include(&common);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// _TriggerContentRedraw
|
||||
@ -384,32 +414,32 @@ void
|
||||
WindowLayer::_TriggerContentRedraw()
|
||||
{
|
||||
//printf("%s - DrawContents()\n", Name());
|
||||
if (fDesktop->ReadLockClipping()) {
|
||||
|
||||
BRegion dirtyContentRegion(fVisibleContentRegion);
|
||||
dirtyContentRegion.IntersectWith(fDesktop->DirtyRegion());
|
||||
BRegion dirtyContentRegion(fVisibleContentRegion);
|
||||
dirtyContentRegion.IntersectWith(fDesktop->DirtyRegion());
|
||||
|
||||
if (dirtyContentRegion.CountRects() > 0) {
|
||||
if (dirtyContentRegion.CountRects() > 0) {
|
||||
|
||||
#if SHOW_WINDOW_CONTENT_DIRTY_REGION
|
||||
if (fDrawingEngine->Lock()) {
|
||||
fDrawingEngine->SetHighColor(0, 0, 255);
|
||||
fDrawingEngine->FillRegion(&dirtyContentRegion);
|
||||
fDrawingEngine->MarkDirty(&dirtyContentRegion);
|
||||
fDrawingEngine->Unlock();
|
||||
snooze(100000);
|
||||
fDrawingEngine->SetHighColor(0, 0, 255);
|
||||
fDrawingEngine->FillRegion(&dirtyContentRegion);
|
||||
fDrawingEngine->MarkDirty(&dirtyContentRegion);
|
||||
fDrawingEngine->Unlock();
|
||||
snooze(100000);
|
||||
}
|
||||
#endif
|
||||
// send UPDATE message to the client
|
||||
_MarkContentDirty(&dirtyContentRegion);
|
||||
// send UPDATE message to the client
|
||||
_MarkContentDirty(&dirtyContentRegion);
|
||||
|
||||
fDesktop->MarkClean(&dirtyContentRegion);
|
||||
fDesktop->MarkClean(&dirtyContentRegion);
|
||||
|
||||
fTopLayer->Draw(fDrawingEngine, &dirtyContentRegion,
|
||||
&fVisibleContentRegion, false);
|
||||
}
|
||||
|
||||
fDesktop->ReadUnlockClipping();
|
||||
#if DELAYED_BACKGROUND_CLEARING
|
||||
fTopLayer->Draw(fDrawingEngine, &dirtyContentRegion,
|
||||
&fVisibleContentRegion, false);
|
||||
#else
|
||||
fTopLayer->Draw(fDrawingEngine, &dirtyContentRegion,
|
||||
&fVisibleContentRegion, true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,6 +447,11 @@ if (fDrawingEngine->Lock()) {
|
||||
void
|
||||
WindowLayer::_DrawClient(int32 token)
|
||||
{
|
||||
// This function is only executed in the window thread.
|
||||
// It still needs to block on the clipping lock, since
|
||||
// We have to be sure that the clipping is up to date.
|
||||
// If true readlocking would work correctly, this would
|
||||
// not be an issue
|
||||
ViewLayer* layer = (ViewLayer*)fTokenViewMap.ItemAt(token);
|
||||
if (!layer)
|
||||
return;
|
||||
@ -434,15 +469,23 @@ WindowLayer::_DrawClient(int32 token)
|
||||
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.
|
||||
BRegion effectiveClipping(fEffectiveDrawingRegion);
|
||||
effectiveClipping.IntersectWith(&layer->ScreenClipping(&fVisibleContentRegion));
|
||||
|
||||
if (effectiveClipping.CountRects() > 0) {
|
||||
#if DELAYED_BACKGROUND_CLEARING
|
||||
// clear the back ground
|
||||
// TODO: only if this is the first drawing command for
|
||||
// this layer of course!
|
||||
// this layer of course! in the simulation, all client
|
||||
// drawing is done from a single command yet
|
||||
layer->Draw(fDrawingEngine, &effectiveClipping,
|
||||
&fVisibleContentRegion, false);
|
||||
#endif
|
||||
|
||||
layer->ClientDraw(fDrawingEngine, &effectiveClipping);
|
||||
}
|
||||
@ -455,53 +498,86 @@ WindowLayer::_DrawClient(int32 token)
|
||||
void
|
||||
WindowLayer::_DrawBorder()
|
||||
{
|
||||
//printf("%s - DrawBorder()\n", Name());
|
||||
#if SLOW_DRAWING
|
||||
snooze(10000);
|
||||
#endif
|
||||
if (fDesktop->ReadLockClipping()) {
|
||||
|
||||
// construct the region of the border that needs redrawing
|
||||
BRegion dirtyBorderRegion;
|
||||
GetBorderRegion(&dirtyBorderRegion);
|
||||
// intersect with our visible region
|
||||
dirtyBorderRegion.IntersectWith(&fVisibleRegion);
|
||||
// intersect with the Desktop's dirty region
|
||||
dirtyBorderRegion.IntersectWith(fDesktop->DirtyRegion());
|
||||
|
||||
if (dirtyBorderRegion.CountRects() > 0) {
|
||||
if (fDrawingEngine->Lock()) {
|
||||
// this is executed in the window thread, but only
|
||||
// in respond to MSG_REDRAW having been received, the
|
||||
// clipping lock is held for reading
|
||||
|
||||
fDrawingEngine->ConstrainClippingRegion(&dirtyBorderRegion);
|
||||
// construct the region of the border that needs redrawing
|
||||
BRegion dirtyBorderRegion;
|
||||
GetBorderRegion(&dirtyBorderRegion);
|
||||
// intersect with our visible region
|
||||
dirtyBorderRegion.IntersectWith(&fVisibleRegion);
|
||||
// intersect with the Desktop's dirty region
|
||||
dirtyBorderRegion.IntersectWith(fDesktop->DirtyRegion());
|
||||
|
||||
if (fFocus) {
|
||||
fDrawingEngine->SetLowColor(255, 203, 0, 255);
|
||||
fDrawingEngine->SetHighColor(0, 0, 0, 255);
|
||||
} else {
|
||||
fDrawingEngine->SetLowColor(216, 216, 216, 0);
|
||||
fDrawingEngine->SetHighColor(0, 0, 0, 255);
|
||||
}
|
||||
if (dirtyBorderRegion.CountRects() > 0) {
|
||||
if (fDrawingEngine->Lock()) {
|
||||
|
||||
fDrawingEngine->FillRect(dirtyBorderRegion.Frame(), B_SOLID_LOW);
|
||||
fDrawingEngine->DrawString(Name(), BPoint(fFrame.left, fFrame.top - 5));
|
||||
fDrawingEngine->ConstrainClippingRegion(&dirtyBorderRegion);
|
||||
|
||||
fDrawingEngine->ConstrainClippingRegion(NULL);
|
||||
fDrawingEngine->MarkDirty(&dirtyBorderRegion);
|
||||
fDrawingEngine->Unlock();
|
||||
if (fFocus) {
|
||||
fDrawingEngine->SetLowColor(255, 203, 0, 255);
|
||||
fDrawingEngine->SetHighColor(0, 0, 0, 255);
|
||||
} else {
|
||||
fDrawingEngine->SetLowColor(216, 216, 216, 0);
|
||||
fDrawingEngine->SetHighColor(30, 30, 30, 255);
|
||||
}
|
||||
fDesktop->MarkClean(&dirtyBorderRegion);
|
||||
rgb_color light = tint_color(fDrawingEngine->LowColor(), B_LIGHTEN_2_TINT);
|
||||
rgb_color shadow = tint_color(fDrawingEngine->LowColor(), B_DARKEN_2_TINT);
|
||||
|
||||
fDrawingEngine->FillRect(dirtyBorderRegion.Frame(), B_SOLID_LOW);
|
||||
|
||||
fDrawingEngine->DrawString(Name(), BPoint(fFrame.left, fFrame.top - 5));
|
||||
|
||||
BRect frame(fFrame);
|
||||
fDrawingEngine->BeginLineArray(12);
|
||||
frame.InsetBy(-1, -1);
|
||||
fDrawingEngine->AddLine(BPoint(frame.left, frame.bottom),
|
||||
BPoint(frame.left, frame.top), shadow);
|
||||
fDrawingEngine->AddLine(BPoint(frame.left + 1, frame.top),
|
||||
BPoint(frame.right, frame.top), shadow);
|
||||
fDrawingEngine->AddLine(BPoint(frame.right, frame.top + 1),
|
||||
BPoint(frame.right, frame.bottom - 11), light);
|
||||
fDrawingEngine->AddLine(BPoint(frame.right - 1, frame.bottom - 11),
|
||||
BPoint(frame.right - 11, frame.bottom - 11), light);
|
||||
fDrawingEngine->AddLine(BPoint(frame.right - 11, frame.bottom - 10),
|
||||
BPoint(frame.right - 11, frame.bottom), light);
|
||||
fDrawingEngine->AddLine(BPoint(frame.right - 12, frame.bottom),
|
||||
BPoint(frame.left + 1, frame.bottom), light);
|
||||
|
||||
frame.InsetBy(-3, -3);
|
||||
fDrawingEngine->AddLine(BPoint(frame.left, frame.bottom),
|
||||
BPoint(frame.left, frame.top - 16), light);
|
||||
fDrawingEngine->AddLine(BPoint(frame.left + 1, frame.top - 16),
|
||||
BPoint((frame.left + frame.right) / 2, frame.top - 16), light);
|
||||
fDrawingEngine->AddLine(BPoint((frame.left + frame.right) / 2, frame.top - 15),
|
||||
BPoint((frame.left + frame.right) / 2, frame.top), shadow);
|
||||
fDrawingEngine->AddLine(BPoint((frame.left + frame.right) / 2 + 1, frame.top),
|
||||
BPoint(frame.left + frame.right, frame.top), light);
|
||||
fDrawingEngine->AddLine(BPoint(frame.right, frame.top + 1),
|
||||
BPoint(frame.right, frame.bottom), shadow);
|
||||
fDrawingEngine->AddLine(BPoint(frame.right, frame.bottom),
|
||||
BPoint(frame.left + 1, frame.bottom), shadow);
|
||||
fDrawingEngine->EndLineArray();
|
||||
|
||||
fDrawingEngine->ConstrainClippingRegion(NULL);
|
||||
fDrawingEngine->MarkDirty(&dirtyBorderRegion);
|
||||
fDrawingEngine->Unlock();
|
||||
}
|
||||
|
||||
fDesktop->ReadUnlockClipping();
|
||||
fDesktop->MarkClean(&dirtyBorderRegion);
|
||||
}
|
||||
}
|
||||
|
||||
// _MarkContentDirty
|
||||
//
|
||||
// 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::_MarkContentDirty(BRegion* contentDirtyRegion)
|
||||
{
|
||||
if (fDesktop->ReadLockClipping()) {
|
||||
|
||||
if (contentDirtyRegion->CountRects() <= 0)
|
||||
return;
|
||||
|
||||
@ -513,9 +589,12 @@ if (fDesktop->ReadLockClipping()) {
|
||||
fPendingUpdateSession->Include(contentDirtyRegion);
|
||||
}
|
||||
|
||||
// clip pending update session from current,
|
||||
// current update session, because the backgrounds have been
|
||||
// cleared again already
|
||||
// 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) {
|
||||
fCurrentUpdateSession->Exclude(contentDirtyRegion);
|
||||
fEffectiveDrawingRegionValid = false;
|
||||
@ -525,58 +604,67 @@ if (fDesktop->ReadLockClipping()) {
|
||||
// send this to client
|
||||
fClient->PostMessage(MSG_UPDATE);
|
||||
fUpdateRequested = true;
|
||||
// as long as we have not received
|
||||
// the "begin update" command, the
|
||||
// pending session does not become the
|
||||
// current
|
||||
}
|
||||
|
||||
fDesktop->ReadUnlockClipping();
|
||||
}
|
||||
}
|
||||
|
||||
// _BeginUpdate
|
||||
void
|
||||
WindowLayer::_BeginUpdate()
|
||||
{
|
||||
if (fDesktop->ReadLockClipping()) {
|
||||
// TODO: since we might "shift" parts of the
|
||||
// internal dirty regions from the desktop thread
|
||||
// in respond 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 both threads
|
||||
// at the same time.
|
||||
if (fDesktop->ReadLockClipping()) {
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
fEffectiveDrawingRegionValid = false;
|
||||
}
|
||||
fEffectiveDrawingRegionValid = false;
|
||||
}
|
||||
|
||||
fDesktop->ReadUnlockClipping();
|
||||
}
|
||||
fDesktop->ReadUnlockClipping();
|
||||
}
|
||||
}
|
||||
|
||||
// _EndUpdate
|
||||
void
|
||||
WindowLayer::_EndUpdate()
|
||||
{
|
||||
if (fDesktop->ReadLockClipping()) {
|
||||
|
||||
if (fInUpdate) {
|
||||
delete fCurrentUpdateSession;
|
||||
fCurrentUpdateSession = NULL;
|
||||
// TODO: see comment in _BeginUpdate()
|
||||
if (fDesktop->ReadLockClipping()) {
|
||||
|
||||
fInUpdate = false;
|
||||
fEffectiveDrawingRegionValid = false;
|
||||
if (fInUpdate) {
|
||||
delete fCurrentUpdateSession;
|
||||
fCurrentUpdateSession = NULL;
|
||||
|
||||
fInUpdate = false;
|
||||
fEffectiveDrawingRegionValid = false;
|
||||
}
|
||||
if (fPendingUpdateSession) {
|
||||
// send this to client
|
||||
fClient->PostMessage(MSG_UPDATE);
|
||||
fUpdateRequested = true;
|
||||
} else {
|
||||
fUpdateRequested = false;
|
||||
}
|
||||
|
||||
fDesktop->ReadUnlockClipping();
|
||||
}
|
||||
if (fPendingUpdateSession) {
|
||||
// send this to client
|
||||
fClient->PostMessage(MSG_UPDATE);
|
||||
fUpdateRequested = true;
|
||||
} else {
|
||||
fUpdateRequested = false;
|
||||
}
|
||||
|
||||
fDesktop->ReadUnlockClipping();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
@ -172,6 +172,8 @@ void
|
||||
Window::Test()
|
||||
{
|
||||
BRect frame(20, 20, 330, 230);
|
||||
// AddWindow(frame, "Window 1");
|
||||
// AddWindow(frame, "Window 2");
|
||||
for (int32 i = 0; i < 20; i++) {
|
||||
BString name("Window ");
|
||||
frame.OffsetBy(20, 15);
|
||||
@ -179,7 +181,7 @@ Window::Test()
|
||||
AddWindow(frame, name.String());
|
||||
}
|
||||
|
||||
frame.Set(10, 80, 320, 290);
|
||||
/* frame.Set(10, 80, 320, 290);
|
||||
for (int32 i = 20; i < 40; i++) {
|
||||
BString name("Window ");
|
||||
frame.OffsetBy(20, 15);
|
||||
@ -187,7 +189,7 @@ Window::Test()
|
||||
AddWindow(frame, name.String());
|
||||
}
|
||||
|
||||
/* frame.Set(20, 140, 330, 230);
|
||||
frame.Set(20, 140, 330, 230);
|
||||
for (int32 i = 40; i < 60; i++) {
|
||||
BString name("Window ");
|
||||
frame.OffsetBy(20, 15);
|
||||
|
@ -35,6 +35,7 @@ SRCS= ClientLooper.cpp \
|
||||
DrawingEngine.cpp \
|
||||
main.cpp \
|
||||
MultiLocker.cpp \
|
||||
RWLocker.cpp \
|
||||
ViewLayer.cpp \
|
||||
WindowLayer.cpp
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user