1
2#include <new>
3#include <stdio.h>
4
5#include <Message.h>
6#include <MessageQueue.h>
7
8#include "ClientLooper.h"
9#include "Desktop.h"
10#include "DrawingEngine.h"
11
12#include "WindowLayer.h"
13
14// if the background clearing is delayed until
15// the client draws the view, we have less flickering
16// when contents have to be redrawn because of resizing
17// a window or because the client invalidates parts.
18// when redrawing something that has been exposed from underneath
19// other windows, the other window will be seen longer at
20// its previous position though if the exposed parts are not
21// cleared right away. maybe there ought to be a flag in
22// the update session, which tells us the cause of the update
23#define DELAYED_BACKGROUND_CLEARING 1
24
25// IMPORTANT: nested ReadLockClipping()s are not supported (by MultiLocker)
26
27
28// constructor
29WindowLayer::WindowLayer(BRect frame, const char* name,
30						 DrawingEngine* drawingEngine, Desktop* desktop)
31	: BLooper(name, B_DISPLAY_PRIORITY),
32	  fFrame(frame),
33
34	  fVisibleRegion(),
35	  fVisibleContentRegion(),
36	  fVisibleContentRegionValid(false),
37	  fDirtyRegion(),
38
39	  fBorderRegion(),
40	  fBorderRegionValid(false),
41	  fContentRegion(),
42	  fContentRegionValid(false),
43	  fEffectiveDrawingRegion(),
44	  fEffectiveDrawingRegionValid(false),
45
46	  fFocus(false),
47
48	  fTopLayer(NULL),
49
50// TODO: windows must start hidden!
51	  fHidden(false),
52	  // windows start hidden
53//	  fHidden(true),
54
55	  fDrawingEngine(drawingEngine),
56	  fDesktop(desktop),
57
58	  fTokenViewMap(64),
59
60	  fClient(new ClientLooper(name, this)),
61	  fCurrentUpdateSession(),
62	  fPendingUpdateSession(),
63	  fUpdateRequested(false),
64	  fInUpdate(false)
65{
66	// the top layer is special, it has a coordinate system
67	// as if it was attached directly to the desktop, therefor,
68	// the coordinate conversion through the layer tree works
69	// as expected, since the top layer has no "parent" but has
70	// fFrame as if it had
71	fTopLayer = new(nothrow) ViewLayer(fFrame, "top view", B_FOLLOW_ALL, 0,
72									   (rgb_color){ 255, 255, 255, 255 });
73	fTopLayer->AttachedToWindow(this);
74
75	fClient->Run();
76}
77
78// destructor
79WindowLayer::~WindowLayer()
80{
81	delete fTopLayer;
82}
83
84// MessageReceived
85void
86WindowLayer::MessageReceived(BMessage* message)
87{
88	switch (message->what) {
89		case MSG_REDRAW: {
90			// there is only one MSG_REDRAW in the queue at anytime
91			if (fDesktop->ReadLockClipping()) {
92
93				_DrawBorder();
94				_TriggerContentRedraw();
95
96				// reset the dirty region, since
97				// we're fully clean. If the desktop
98				// thread wanted to mark something
99				// dirty in the mean time, it was
100				// blocking on the global region lock to
101				// get write access, since we held the
102				// read lock for the whole time.
103				fDirtyRegion.MakeEmpty();
104
105				fDesktop->ReadUnlockClipping();
106			} else {
107//printf("%s MSG_REDRAW -> pending redraws\n", Name());
108			}
109			break;
110		}
111		case MSG_BEGIN_UPDATE:
112			_BeginUpdate();
113			break;
114		case MSG_END_UPDATE:
115			_EndUpdate();
116			break;
117		case MSG_DRAWING_COMMAND: {
118			int32 token;
119			if (message->FindInt32("token", &token) >= B_OK)
120				_DrawClient(token);
121			break;
122		}
123		case MSG_DRAW_POLYGON: {
124			int32 token;
125			BPoint polygon[4];
126			if (message->FindInt32("token", &token) >= B_OK &&
127				message->FindPoint("point", 0, &polygon[0]) >= B_OK &&
128				message->FindPoint("point", 1, &polygon[1]) >= B_OK &&
129				message->FindPoint("point", 2, &polygon[2]) >= B_OK &&
130				message->FindPoint("point", 3, &polygon[3]) >= B_OK) {
131
132				_DrawClientPolygon(token, polygon);
133			}
134			break;
135
136		}
137
138		case MSG_INVALIDATE_VIEW: {
139			int32 token;
140			if (message->FindInt32("token", &token) >= B_OK)
141				InvalidateView(token);
142			break;
143		}
144
145		case MSG_SHOW:
146			if (IsHidden()) {
147				fDesktop->ShowWindow(this);
148			}
149			break;
150		default:
151			BLooper::MessageReceived(message);
152			break;
153	}
154}
155
156// QuitRequested
157bool
158WindowLayer::QuitRequested()
159{
160	if (fDesktop && fDesktop->LockClipping()) {
161		fDesktop->WindowDied(this);
162
163		fClient->Lock();
164		fClient->Quit();
165
166		fDesktop->UnlockClipping();
167	}
168	return true;
169}
170
171// SetClipping
172void
173WindowLayer::SetClipping(BRegion* stillAvailableOnScreen)
174{
175	// this function is only called from the Desktop thread
176
177	// start from full region (as if the window was fully visible)
178	GetFullRegion(&fVisibleRegion);
179	// clip to region still available on screen
180	fVisibleRegion.IntersectWith(stillAvailableOnScreen);
181
182	fVisibleContentRegionValid = false;
183	fEffectiveDrawingRegionValid = false;
184}
185
186// GetFullRegion
187void
188WindowLayer::GetFullRegion(BRegion* region) const
189{
190	// start from the frame, extend to include decorator border
191	region->Set(BRect(fFrame.left - 4, fFrame.top - 4,
192					  fFrame.right + 4, fFrame.bottom + 4));
193	// add the title tab
194	region->Include(BRect(fFrame.left - 4, fFrame.top - 20,
195						  ceilf((fFrame.left + fFrame.right) / 2), fFrame.top - 5));
196}
197
198// GetBorderRegion
199void
200WindowLayer::GetBorderRegion(BRegion* region)
201{
202	if (!fBorderRegionValid) {
203		fBorderRegion.Set(BRect(fFrame.left - 4, fFrame.top - 20,
204							  	ceilf((fFrame.left + fFrame.right) / 2), fFrame.top - 5));
205		fBorderRegion.Include(BRect(fFrame.left - 4, fFrame.top - 4,
206									fFrame.right + 4, fFrame.top - 1));
207		fBorderRegion.Include(BRect(fFrame.left - 4, fFrame.top,
208									fFrame.left - 1, fFrame.bottom));
209		fBorderRegion.Include(BRect(fFrame.right + 1, fFrame.top,
210									fFrame.right + 4, fFrame.bottom - 11));
211		fBorderRegion.Include(BRect(fFrame.left - 4, fFrame.bottom + 1,
212									fFrame.right - 11, fFrame.bottom + 4));
213		fBorderRegion.Include(BRect(fFrame.right - 10, fFrame.bottom - 10,
214									fFrame.right + 4, fFrame.bottom + 4));
215		fBorderRegionValid = true;
216	}
217
218	*region = fBorderRegion;
219}
220
221// GetContentRegion
222void
223WindowLayer::GetContentRegion(BRegion* region)
224{
225	if (!fContentRegionValid) {
226		_UpdateContentRegion();
227	}
228
229	*region = fContentRegion;
230}
231
232// VisibleContentRegion
233BRegion&
234WindowLayer::VisibleContentRegion()
235{
236	// regions expected to be locked
237	if (!fVisibleContentRegionValid) {
238		GetContentRegion(&fVisibleContentRegion);
239		fVisibleContentRegion.IntersectWith(&fVisibleRegion);
240	}
241	return fVisibleContentRegion;
242}
243
244// SetFocus
245void
246WindowLayer::SetFocus(bool focus)
247{
248	// executed from Desktop thread
249	// it holds the clipping write lock,
250	// so the window thread cannot be
251	// accessing fFocus
252
253	BRegion dirty(fBorderRegion);
254	dirty.IntersectWith(&fVisibleRegion);
255	fDesktop->MarkDirty(&dirty);
256
257	fFocus = focus;
258}
259
260// MoveBy
261void
262WindowLayer::MoveBy(int32 x, int32 y)
263{
264	// this function is only called from the desktop thread
265
266	if (x == 0 && y == 0)
267		return;
268
269	fFrame.OffsetBy(x, y);
270
271	// take along the dirty region which have not
272	// processed yet
273	fDirtyRegion.OffsetBy(x, y);
274
275	if (fBorderRegionValid)
276		fBorderRegion.OffsetBy(x, y);
277	if (fContentRegionValid)
278		fContentRegion.OffsetBy(x, y);
279
280	if (fCurrentUpdateSession.IsUsed())
281		fCurrentUpdateSession.MoveBy(x, y);
282	if (fPendingUpdateSession.IsUsed())
283		fPendingUpdateSession.MoveBy(x, y);
284
285	fEffectiveDrawingRegionValid = false;
286
287	fTopLayer->MoveBy(x, y, NULL);
288
289	// the desktop will take care of dirty regions
290}
291
292// ResizeBy
293void
294WindowLayer::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion)
295{
296	// this function is only called from the desktop thread
297
298	if (x == 0 && y == 0)
299		return;
300
301	fFrame.right += x;
302	fFrame.bottom += y;
303
304	// put the previous border region into the dirty region as well
305	// to handle the part that was overlapping a layer
306	dirtyRegion->Include(&fBorderRegion);
307
308	fBorderRegionValid = false;
309	fContentRegionValid = false;
310	fEffectiveDrawingRegionValid = false;
311
312	// the border is dirty, put it into
313	// dirtyRegion for a start
314	BRegion newBorderRegion;
315	GetBorderRegion(&newBorderRegion);
316	dirtyRegion->Include(&newBorderRegion);
317
318	fTopLayer->ResizeBy(x, y, dirtyRegion);
319}
320
321// ScrollViewBy
322void
323WindowLayer::ScrollViewBy(ViewLayer* view, int32 dx, int32 dy)
324{
325	// this can be executed from any thread, but if the
326	// desktop thread is executing this, it should have
327	// the write lock, otherwise it is not prevented
328	// from executing this at the same time as the window
329	// is doing something else here!
330
331	if (!view || view == fTopLayer || (dx == 0 && dy == 0))
332		return;
333
334	if (fDesktop && fDesktop->ReadLockClipping()) {
335
336		BRegion dirty;
337		view->ScrollBy(dx, dy, &dirty);
338
339		_MarkContentDirty(&dirty);
340
341		fDesktop->ReadUnlockClipping();
342	}
343}
344
345// AddChild
346void
347WindowLayer::AddChild(ViewLayer* layer)
348{
349	fTopLayer->AddChild(layer);
350
351	// inform client about the view
352	// (just a part of the simulation)
353	fTokenViewMap.MakeEmpty();
354	fTopLayer->CollectTokensForChildren(&fTokenViewMap);
355	BMessage message(MSG_VIEWS_ADDED);
356	message.AddInt32("count", fTokenViewMap.CountItems());
357	fClient->PostMessage(&message);
358
359	// TODO: trigger redraw for dirty regions
360}
361
362// ViewAt
363ViewLayer*
364WindowLayer::ViewAt(const BPoint& where)
365{
366	if (!fContentRegionValid)
367		_UpdateContentRegion();
368
369	return fTopLayer->ViewAt(where, &fContentRegion);
370}
371
372// SetHidden
373void
374WindowLayer::SetHidden(bool hidden)
375{
376	// the desktop takes care of dirty regions
377	if (fHidden != hidden) {
378		fHidden = hidden;
379
380		fTopLayer->SetHidden(hidden);
381
382		// this is only for simulation purposes:
383		if (fHidden)
384			fClient->PostMessage(MSG_WINDOW_HIDDEN);
385
386		// TODO: anything else?
387	}
388}
389
390// ProcessDirtyRegion
391void
392WindowLayer::ProcessDirtyRegion(BRegion* region)
393{
394	// if this is exectuted in the desktop thread,
395	// it means that the window thread currently
396	// blocks to get the read lock, if it is
397	// executed from the window thread, it should
398	// have the read lock and the desktop thread
399	// is blocking to get the write lock. IAW, this
400	// is only executed in one thread.
401	if (fDirtyRegion.CountRects() == 0) {
402		// the window needs to be informed
403		// when the dirty region was empty.
404		// NOTE: when the window thread has processed
405		// the dirty region in MessageReceived(),
406		// it will make the region empty again,
407		// when it is empty here, we need to send
408		// the message to initiate the next update round.
409		// Until the message is processed in the window
410		// thread, the desktop thread can add parts to
411		// the region as it likes.
412		PostMessage(MSG_REDRAW, this);
413	}
414	// this is executed from the desktop thread
415	fDirtyRegion.Include(region);
416}
417
418// MarkDirty
419void
420WindowLayer::MarkDirty(BRegion* regionOnScreen)
421{
422	// for marking any part of the desktop dirty
423	// this will get write access to the global
424	// region lock, and result in ProcessDirtyRegion()
425	// to be called for any windows affected
426	if (fDesktop)
427		fDesktop->MarkDirty(regionOnScreen);
428}
429
430// MarkContentDirty
431void
432WindowLayer::MarkContentDirty(BRegion* regionOnScreen)
433{
434	// for triggering MSG_REDRAW
435	// since this won't affect other windows, read locking
436	// is sufficient. If there was no dirty region before,
437	// an update message is triggered
438	if (fDesktop && fDesktop->ReadLockClipping()) {
439
440		regionOnScreen->IntersectWith(&VisibleContentRegion());
441		ProcessDirtyRegion(regionOnScreen);
442
443		fDesktop->ReadUnlockClipping();
444	}
445}
446
447// InvalidateView
448void
449WindowLayer::InvalidateView(int32 token)
450{
451	if (fDesktop && fDesktop->ReadLockClipping()) {
452
453		ViewLayer* layer = (ViewLayer*)fTokenViewMap.ItemAt(token);
454		if (!layer || !layer->IsVisible()) {
455			fDesktop->ReadUnlockClipping();
456			return;
457		}
458		if (!fContentRegionValid)
459			_UpdateContentRegion();
460
461		_MarkContentDirty(&layer->ScreenClipping(&fContentRegion));
462
463		fDesktop->ReadUnlockClipping();
464	}
465}
466
467//# pragma mark -
468
469// CopyContents
470void
471WindowLayer::CopyContents(BRegion* region, int32 xOffset, int32 yOffset)
472{
473	// this function takes care of invalidating parts that could not be copied
474
475	if (fDesktop->ReadLockClipping()) {
476
477		BRegion newDirty(*region);
478
479		// clip the region to the visible contents at the
480		// source and destination location (not that VisibleContentRegion()
481		// is used once to make sure it is valid, then fVisibleContentRegion
482		// is used directly)
483		region->IntersectWith(&VisibleContentRegion());
484		if (region->CountRects() > 0) {
485			region->OffsetBy(xOffset, yOffset);
486			region->IntersectWith(&fVisibleContentRegion);
487			if (region->CountRects() > 0) {
488				// if the region still contains any rects
489				// offset to source location again
490				region->OffsetBy(-xOffset, -yOffset);
491				// the part which we can copy is not dirty
492				newDirty.Exclude(region);
493
494				if (fDrawingEngine->Lock()) {
495					fDrawingEngine->CopyRegion(region, xOffset, yOffset);
496					fDrawingEngine->Unlock();
497				}
498
499				// move along the already dirty regions that are common
500				// with the region that we could copy
501				_ShiftPartOfRegion(&fDirtyRegion, region, xOffset, yOffset);
502				if (fCurrentUpdateSession.IsUsed())
503					_ShiftPartOfRegion(&fCurrentUpdateSession.DirtyRegion(), region, xOffset, yOffset);
504				if (fPendingUpdateSession.IsUsed())
505					_ShiftPartOfRegion(&fPendingUpdateSession.DirtyRegion(), region, xOffset, yOffset);
506
507			}
508		}
509		// what is left visible from the original region
510		// at the destination after the region which could be
511		// copied has been excluded, is considered dirty
512		// NOTE: it may look like dirty regions are not moved
513		// if no region could be copied, but that's alright,
514		// since these parts will now be in newDirty anyways
515		// (with the right offset)
516		newDirty.OffsetBy(xOffset, yOffset);
517		newDirty.IntersectWith(&fVisibleContentRegion);
518		if (newDirty.CountRects() > 0)
519			ProcessDirtyRegion(&newDirty);
520
521		fDesktop->ReadUnlockClipping();
522	}
523}
524
525// #pragma mark -
526
527// _ShiftPartOfRegion
528void
529WindowLayer::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
530								int32 xOffset, int32 yOffset)
531{
532	BRegion common(*regionToShift);
533	// see if there is a common part at all
534	common.IntersectWith(region);
535	if (common.CountRects() > 0) {
536		// cut the common part from the region,
537		// offset that to destination and include again
538		region->Exclude(&common);
539		common.OffsetBy(xOffset, yOffset);
540		region->Include(&common);
541	}
542}
543
544// _TriggerContentRedraw
545void
546WindowLayer::_TriggerContentRedraw()
547{
548//printf("%s - DrawContents()\n", Name());
549	BRegion dirtyContentRegion(VisibleContentRegion());
550	dirtyContentRegion.IntersectWith(&fDirtyRegion);
551
552	if (dirtyContentRegion.CountRects() > 0) {
553
554#if SHOW_WINDOW_CONTENT_DIRTY_REGION
555if (fDrawingEngine->Lock()) {
556fDrawingEngine->SetHighColor(0, 0, 255);
557fDrawingEngine->FillRegion(&dirtyContentRegion);
558fDrawingEngine->MarkDirty(&dirtyContentRegion);
559fDrawingEngine->Unlock();
560snooze(100000);
561}
562#endif
563		// send UPDATE message to the client
564		_MarkContentDirty(&dirtyContentRegion);
565
566		if (!fContentRegionValid)
567			_UpdateContentRegion();
568
569#if DELAYED_BACKGROUND_CLEARING
570		fTopLayer->Draw(fDrawingEngine, &dirtyContentRegion,
571						&fContentRegion, false);
572#else
573		fTopLayer->Draw(fDrawingEngine, &dirtyContentRegion,
574						&fContentRegion, true);
575#endif
576	}
577}
578
579// _DrawClient
580void
581WindowLayer::_DrawClient(int32 token)
582{
583	// This function is only executed in the window thread.
584	// It still needs to block on the clipping lock, since
585	// We have to be sure that the clipping is up to date.
586	// If true readlocking would work correctly, this would
587	// not be an issue
588	if (fDesktop->ReadLockClipping()) {
589
590		ViewLayer* layer = (ViewLayer*)fTokenViewMap.ItemAt(token);
591		if (!layer || !layer->IsVisible()) {
592			fDesktop->ReadUnlockClipping();
593			return;
594		}
595
596		if (!fEffectiveDrawingRegionValid) {
597			fEffectiveDrawingRegion = VisibleContentRegion();
598			if (fInUpdate) {
599				// enforce the dirty region of the update session
600				fEffectiveDrawingRegion.IntersectWith(&fCurrentUpdateSession.DirtyRegion());
601			} else {
602				printf("%s - _DrawClient(token: %ld) - not in update\n", Name(), token);
603			}
604			fEffectiveDrawingRegionValid = true;
605		}
606
607		// TODO: this is a region that needs to be cached later in the server
608		// when the current layer in ServerWindow is set, and we are currently
609		// in an update (fInUpdate), than we can set this region and remember
610		// it for the comming drawing commands until the current layer changes
611		// again or fEffectiveDrawingRegionValid is suddenly false.
612		BRegion effectiveClipping(fEffectiveDrawingRegion);
613		if (!fContentRegionValid)
614			_UpdateContentRegion();
615		effectiveClipping.IntersectWith(&layer->ScreenClipping(&fContentRegion));
616
617		if (effectiveClipping.CountRects() > 0) {
618#if DELAYED_BACKGROUND_CLEARING
619			// clear the back ground
620			// TODO: only if this is the first drawing command for
621			// this layer of course! in the simulation, all client
622			// drawing is done from a single command yet
623			layer->Draw(fDrawingEngine, &effectiveClipping,
624						&fContentRegion, false);
625#endif
626
627			layer->ClientDraw(fDrawingEngine, &effectiveClipping);
628		}
629
630		fDesktop->ReadUnlockClipping();
631	}
632}
633
634// _DrawClientPolygon
635void
636WindowLayer::_DrawClientPolygon(int32 token, BPoint polygon[4])
637{
638	if (fDesktop->ReadLockClipping()) {
639
640		ViewLayer* layer = (ViewLayer*)fTokenViewMap.ItemAt(token);
641		if (!layer || !layer->IsVisible()) {
642			fDesktop->ReadUnlockClipping();
643			return;
644		}
645
646		if (!fEffectiveDrawingRegionValid) {
647			fEffectiveDrawingRegion = VisibleContentRegion();
648			if (fInUpdate) {
649				// enforce the dirty region of the update session
650				fEffectiveDrawingRegion.IntersectWith(&fCurrentUpdateSession.DirtyRegion());
651			} else {
652				printf("%s - _DrawClientPolygon(token: %ld) - not in update\n", Name(), token);
653			}
654			fEffectiveDrawingRegionValid = true;
655		}
656
657		BRegion effectiveClipping(fEffectiveDrawingRegion);
658		if (!fContentRegionValid)
659			_UpdateContentRegion();
660		effectiveClipping.IntersectWith(&layer->ScreenClipping(&fContentRegion));
661
662		if (effectiveClipping.CountRects() > 0) {
663#if DELAYED_BACKGROUND_CLEARING
664			layer->Draw(fDrawingEngine, &effectiveClipping,
665						&fContentRegion, false);
666#endif
667
668			layer->ConvertToTop(&polygon[0]);
669			layer->ConvertToTop(&polygon[1]);
670			layer->ConvertToTop(&polygon[2]);
671			layer->ConvertToTop(&polygon[3]);
672
673			if (fDrawingEngine->Lock()) {
674
675				fDrawingEngine->ConstrainClipping(&effectiveClipping);
676
677//				fDrawingEngine->SetPenSize(3);
678//				fDrawingEngine->SetDrawingMode(B_OP_BLEND);
679				fDrawingEngine->StrokeLine(polygon[0], polygon[1], layer->ViewColor());
680				fDrawingEngine->StrokeLine(polygon[1], polygon[2], layer->ViewColor());
681				fDrawingEngine->StrokeLine(polygon[2], polygon[3], layer->ViewColor());
682				fDrawingEngine->StrokeLine(polygon[3], polygon[0], layer->ViewColor());
683
684				fDrawingEngine->Unlock();
685			}
686		}
687
688		fDesktop->ReadUnlockClipping();
689	}
690}
691
692
693// _DrawBorder
694void
695WindowLayer::_DrawBorder()
696{
697	// this is executed in the window thread, but only
698	// in respond to MSG_REDRAW having been received, the
699	// clipping lock is held for reading
700
701	// construct the region of the border that needs redrawing
702	BRegion dirtyBorderRegion;
703	GetBorderRegion(&dirtyBorderRegion);
704// TODO: why is it not enough to only intersect with the dirty region?
705// is it faster to intersect the dirty region with the visible when it
706// is set in ProcessDirtyRegion()?
707	// intersect with our visible region
708	dirtyBorderRegion.IntersectWith(&fVisibleRegion);
709	// intersect with the dirty region
710	dirtyBorderRegion.IntersectWith(&fDirtyRegion);
711
712	if (dirtyBorderRegion.CountRects() > 0) {
713
714		rgb_color lowColor;
715		rgb_color highColor;
716		if (fFocus) {
717			lowColor = (rgb_color){ 255, 203, 0, 255 };
718			highColor = (rgb_color){ 0, 0, 0, 255 };
719		} else {
720			lowColor = (rgb_color){ 216, 216, 216, 0 };
721			highColor = (rgb_color){ 30, 30, 30, 255 };
722		}
723
724		fDrawingEngine->FillRegion(&dirtyBorderRegion, lowColor);
725
726		rgb_color light = tint_color(lowColor, B_LIGHTEN_2_TINT);
727		rgb_color shadow = tint_color(lowColor, B_DARKEN_2_TINT);
728
729		if (fDrawingEngine->Lock()) {
730
731			fDrawingEngine->ConstrainClipping(&dirtyBorderRegion);
732
733			fDrawingEngine->DrawString(Name(), BPoint(fFrame.left, fFrame.top - 5), highColor);
734
735			BRect frame(fFrame);
736			frame.InsetBy(-1, -1);
737			fDrawingEngine->StrokeLine(BPoint(frame.left, frame.bottom),
738									   BPoint(frame.left, frame.top), shadow);
739			fDrawingEngine->StrokeLine(BPoint(frame.left + 1, frame.top),
740									   BPoint(frame.right, frame.top), shadow);
741			fDrawingEngine->StrokeLine(BPoint(frame.right, frame.top + 1),
742									   BPoint(frame.right, frame.bottom - 11), light);
743			fDrawingEngine->StrokeLine(BPoint(frame.right - 1, frame.bottom - 11),
744									   BPoint(frame.right - 11, frame.bottom - 11), light);
745			fDrawingEngine->StrokeLine(BPoint(frame.right - 11, frame.bottom - 10),
746									   BPoint(frame.right - 11, frame.bottom), light);
747			fDrawingEngine->StrokeLine(BPoint(frame.right - 12, frame.bottom),
748									   BPoint(frame.left + 1, frame.bottom), light);
749
750			frame.InsetBy(-3, -3);
751			int32 tabRight = ceilf((fFrame.left + fFrame.right) / 2);
752			fDrawingEngine->StrokeLine(BPoint(frame.left, frame.bottom),
753									   BPoint(frame.left, frame.top - 16), light);
754			fDrawingEngine->StrokeLine(BPoint(frame.left + 1, frame.top - 16),
755									   BPoint(tabRight, frame.top - 16), light);
756			fDrawingEngine->StrokeLine(BPoint(tabRight, frame.top - 15),
757									   BPoint(tabRight, frame.top), shadow);
758			fDrawingEngine->StrokeLine(BPoint(tabRight + 1, frame.top),
759									   BPoint(frame.right, frame.top), light);
760			fDrawingEngine->StrokeLine(BPoint(frame.right, frame.top + 1),
761									   BPoint(frame.right, frame.bottom), shadow);
762			fDrawingEngine->StrokeLine(BPoint(frame.right, frame.bottom),
763									   BPoint(frame.left + 1, frame.bottom), shadow);
764
765			fDrawingEngine->ConstrainClipping(NULL);
766			fDrawingEngine->Unlock();
767		}
768	}
769}
770
771// _MarkContentDirty
772//
773// pre: the clipping is readlocked (this function is
774// only called from _TriggerContentRedraw()), which
775// in turn is only called from MessageReceived() with
776// the clipping lock held
777void
778WindowLayer::_MarkContentDirty(BRegion* contentDirtyRegion)
779{
780	if (contentDirtyRegion->CountRects() <= 0)
781		return;
782
783	// add to pending
784	fPendingUpdateSession.SetUsed(true);
785	fPendingUpdateSession.Include(contentDirtyRegion);
786
787	// clip pending update session from current
788	// update session, it makes no sense to draw stuff
789	// already needing a redraw anyways. Theoretically,
790	// this could be done smarter (clip layers from pending
791	// that have not yet been redrawn in the current update
792	// session)
793	if (fCurrentUpdateSession.IsUsed()) {
794		fCurrentUpdateSession.Exclude(contentDirtyRegion);
795		fEffectiveDrawingRegionValid = false;
796	}
797
798	if (!fUpdateRequested) {
799		// send this to client
800		fClient->PostMessage(MSG_UPDATE);
801		fUpdateRequested = true;
802		// as long as we have not received
803		// the "begin update" command, the
804		// pending session does not become the
805		// current
806	}
807}
808
809// _BeginUpdate
810void
811WindowLayer::_BeginUpdate()
812{
813	// TODO: since we might "shift" parts of the
814	// internal dirty regions from the desktop thread
815	// in respond to WindowLayer::ResizeBy(), which
816	// might move arround views, this function needs to block
817	// on the global clipping lock so that the internal
818	// dirty regions are not messed with from both threads
819	// at the same time.
820	if (fDesktop->ReadLockClipping()) {
821
822		if (fUpdateRequested && !fCurrentUpdateSession.IsUsed()) {
823			if (fPendingUpdateSession.IsUsed()) {
824
825				// TODO: the toggling between the update sessions is too
826				// expensive, optimize with some pointer tricks
827				fCurrentUpdateSession = fPendingUpdateSession;
828				fPendingUpdateSession.SetUsed(false);
829
830				// all drawing command from the client
831				// will have the dirty region from the update
832				// session enforced
833				fInUpdate = true;
834			}
835			fEffectiveDrawingRegionValid = false;
836		}
837
838		fDesktop->ReadUnlockClipping();
839	}
840}
841
842// _EndUpdate
843void
844WindowLayer::_EndUpdate()
845{
846	// TODO: see comment in _BeginUpdate()
847	if (fDesktop->ReadLockClipping()) {
848
849		if (fInUpdate) {
850			fCurrentUpdateSession.SetUsed(false);
851
852			fInUpdate = false;
853			fEffectiveDrawingRegionValid = false;
854		}
855		if (fPendingUpdateSession.IsUsed()) {
856			// send this to client
857			fClient->PostMessage(MSG_UPDATE);
858			fUpdateRequested = true;
859		} else {
860			fUpdateRequested = false;
861		}
862
863	fDesktop->ReadUnlockClipping();
864	}
865}
866
867// _UpdateContentRegion
868void
869WindowLayer::_UpdateContentRegion()
870{
871	// TODO: speed up by avoiding "Exclude()"
872	// start from the frame, extend to include decorator border
873	fContentRegion.Set(fFrame);
874
875	// resize handle
876	// if (B_DOCUMENT_WINDOW_LOOK)
877		fContentRegion.Exclude(BRect(fFrame.right - 10, fFrame.bottom - 10,
878									 fFrame.right, fFrame.bottom));
879
880	fContentRegionValid = true;
881}
882
883// #pragma mark -
884
885// constructor
886UpdateSession::UpdateSession()
887	: fDirtyRegion(),
888	  fInUse(false)
889{
890}
891
892// destructor
893UpdateSession::~UpdateSession()
894{
895}
896
897// Include
898void
899UpdateSession::Include(BRegion* additionalDirty)
900{
901	fDirtyRegion.Include(additionalDirty);
902}
903
904// Exclude
905void
906UpdateSession::Exclude(BRegion* dirtyInNextSession)
907{
908	fDirtyRegion.Exclude(dirtyInNextSession);
909}
910
911// MoveBy
912void
913UpdateSession::MoveBy(int32 x, int32 y)
914{
915	fDirtyRegion.OffsetBy(x, y);
916}
917
918// SetUsed
919void
920UpdateSession::SetUsed(bool used)
921{
922	fInUse = used;
923	if (!fInUse) {
924		fDirtyRegion.MakeEmpty();
925	}
926}
927
928// operator=
929UpdateSession&
930UpdateSession::operator=(const UpdateSession& other)
931{
932	fDirtyRegion = other.fDirtyRegion;
933	fInUse = other.fInUse;
934	return *this;
935}
936
937
938
939