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