xref: /haiku/src/tests/servers/app/newerClipping/WindowLayer.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
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
29 WindowLayer::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
79 WindowLayer::~WindowLayer()
80 {
81 	delete fTopLayer;
82 }
83 
84 // MessageReceived
85 void
86 WindowLayer::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
157 bool
158 WindowLayer::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
172 void
173 WindowLayer::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
187 void
188 WindowLayer::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
199 void
200 WindowLayer::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
222 void
223 WindowLayer::GetContentRegion(BRegion* region)
224 {
225 	if (!fContentRegionValid) {
226 		_UpdateContentRegion();
227 	}
228 
229 	*region = fContentRegion;
230 }
231 
232 // VisibleContentRegion
233 BRegion&
234 WindowLayer::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
245 void
246 WindowLayer::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
261 void
262 WindowLayer::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
293 void
294 WindowLayer::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
322 void
323 WindowLayer::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
346 void
347 WindowLayer::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
363 ViewLayer*
364 WindowLayer::ViewAt(const BPoint& where)
365 {
366 	if (!fContentRegionValid)
367 		_UpdateContentRegion();
368 
369 	return fTopLayer->ViewAt(where, &fContentRegion);
370 }
371 
372 // SetHidden
373 void
374 WindowLayer::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
391 void
392 WindowLayer::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
419 void
420 WindowLayer::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
431 void
432 WindowLayer::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
448 void
449 WindowLayer::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
470 void
471 WindowLayer::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
528 void
529 WindowLayer::_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
545 void
546 WindowLayer::_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
555 if (fDrawingEngine->Lock()) {
556 fDrawingEngine->SetHighColor(0, 0, 255);
557 fDrawingEngine->FillRegion(&dirtyContentRegion);
558 fDrawingEngine->MarkDirty(&dirtyContentRegion);
559 fDrawingEngine->Unlock();
560 snooze(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
580 void
581 WindowLayer::_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
635 void
636 WindowLayer::_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
694 void
695 WindowLayer::_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
777 void
778 WindowLayer::_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
810 void
811 WindowLayer::_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
843 void
844 WindowLayer::_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
868 void
869 WindowLayer::_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
886 UpdateSession::UpdateSession()
887 	: fDirtyRegion(),
888 	  fInUse(false)
889 {
890 }
891 
892 // destructor
893 UpdateSession::~UpdateSession()
894 {
895 }
896 
897 // Include
898 void
899 UpdateSession::Include(BRegion* additionalDirty)
900 {
901 	fDirtyRegion.Include(additionalDirty);
902 }
903 
904 // Exclude
905 void
906 UpdateSession::Exclude(BRegion* dirtyInNextSession)
907 {
908 	fDirtyRegion.Exclude(dirtyInNextSession);
909 }
910 
911 // MoveBy
912 void
913 UpdateSession::MoveBy(int32 x, int32 y)
914 {
915 	fDirtyRegion.OffsetBy(x, y);
916 }
917 
918 // SetUsed
919 void
920 UpdateSession::SetUsed(bool used)
921 {
922 	fInUse = used;
923 	if (!fInUse) {
924 		fDirtyRegion.MakeEmpty();
925 	}
926 }
927 
928 // operator=
929 UpdateSession&
930 UpdateSession::operator=(const UpdateSession& other)
931 {
932 	fDirtyRegion = other.fDirtyRegion;
933 	fInUse = other.fInUse;
934 	return *this;
935 }
936 
937 
938 
939