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
WindowLayer(BRect frame,const char * name,DrawingEngine * drawingEngine,Desktop * desktop)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
~WindowLayer()79 WindowLayer::~WindowLayer()
80 {
81 delete fTopLayer;
82 }
83
84 // MessageReceived
85 void
MessageReceived(BMessage * message)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
QuitRequested()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
SetClipping(BRegion * stillAvailableOnScreen)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
GetFullRegion(BRegion * region) const188 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
GetBorderRegion(BRegion * region)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
GetContentRegion(BRegion * region)223 WindowLayer::GetContentRegion(BRegion* region)
224 {
225 if (!fContentRegionValid) {
226 _UpdateContentRegion();
227 }
228
229 *region = fContentRegion;
230 }
231
232 // VisibleContentRegion
233 BRegion&
VisibleContentRegion()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
SetFocus(bool focus)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
MoveBy(int32 x,int32 y)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
ResizeBy(int32 x,int32 y,BRegion * dirtyRegion)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
ScrollViewBy(ViewLayer * view,int32 dx,int32 dy)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
AddChild(ViewLayer * layer)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*
ViewAt(const BPoint & where)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
SetHidden(bool hidden)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
ProcessDirtyRegion(BRegion * region)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
MarkDirty(BRegion * regionOnScreen)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
MarkContentDirty(BRegion * regionOnScreen)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
InvalidateView(int32 token)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
CopyContents(BRegion * region,int32 xOffset,int32 yOffset)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
_ShiftPartOfRegion(BRegion * region,BRegion * regionToShift,int32 xOffset,int32 yOffset)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
_TriggerContentRedraw()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
_DrawClient(int32 token)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
_DrawClientPolygon(int32 token,BPoint polygon[4])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
_DrawBorder()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
_MarkContentDirty(BRegion * contentDirtyRegion)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
_BeginUpdate()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
_EndUpdate()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
_UpdateContentRegion()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
UpdateSession()886 UpdateSession::UpdateSession()
887 : fDirtyRegion(),
888 fInUse(false)
889 {
890 }
891
892 // destructor
~UpdateSession()893 UpdateSession::~UpdateSession()
894 {
895 }
896
897 // Include
898 void
Include(BRegion * additionalDirty)899 UpdateSession::Include(BRegion* additionalDirty)
900 {
901 fDirtyRegion.Include(additionalDirty);
902 }
903
904 // Exclude
905 void
Exclude(BRegion * dirtyInNextSession)906 UpdateSession::Exclude(BRegion* dirtyInNextSession)
907 {
908 fDirtyRegion.Exclude(dirtyInNextSession);
909 }
910
911 // MoveBy
912 void
MoveBy(int32 x,int32 y)913 UpdateSession::MoveBy(int32 x, int32 y)
914 {
915 fDirtyRegion.OffsetBy(x, y);
916 }
917
918 // SetUsed
919 void
SetUsed(bool used)920 UpdateSession::SetUsed(bool used)
921 {
922 fInUse = used;
923 if (!fInUse) {
924 fDirtyRegion.MakeEmpty();
925 }
926 }
927
928 // operator=
929 UpdateSession&
operator =(const UpdateSession & other)930 UpdateSession::operator=(const UpdateSession& other)
931 {
932 fDirtyRegion = other.fDirtyRegion;
933 fInUse = other.fInUse;
934 return *this;
935 }
936
937
938
939