1 /* 2 * Copyright 2001-2010, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * DarkWyrm <bpmagic@columbus.rr.com> 7 * Adi Oanca <adioanca@gmail.com> 8 * Stephan Aßmus <superstippi@gmx.de> 9 * Axel Dörfler <axeld@pinc-software.de> 10 * Brecht Machiels <brecht@mos6581.org> 11 * Clemens Zeidler <haiku@clemens-zeidler.de> 12 */ 13 14 15 #include "Window.h" 16 17 #include "Decorator.h" 18 #include "DecorManager.h" 19 #include "Desktop.h" 20 #include "DrawingEngine.h" 21 #include "HWInterface.h" 22 #include "MessagePrivate.h" 23 #include "PortLink.h" 24 #include "ServerApp.h" 25 #include "ServerWindow.h" 26 #include "WindowBehaviour.h" 27 #include "Workspace.h" 28 #include "WorkspacesView.h" 29 30 #include <ViewPrivate.h> 31 #include <WindowPrivate.h> 32 33 #include <Debug.h> 34 #include <DirectWindow.h> 35 #include <PortLink.h> 36 #include <View.h> 37 38 #include <new> 39 #include <stdio.h> 40 41 42 // Toggle debug output 43 //#define DEBUG_WINDOW 44 45 #ifdef DEBUG_WINDOW 46 # define STRACE(x) printf x 47 #else 48 # define STRACE(x) ; 49 #endif 50 51 // IMPORTANT: nested LockSingleWindow()s are not supported (by MultiLocker) 52 53 using std::nothrow; 54 55 // if the background clearing is delayed until 56 // the client draws the view, we have less flickering 57 // when contents have to be redrawn because of resizing 58 // a window or because the client invalidates parts. 59 // when redrawing something that has been exposed from underneath 60 // other windows, the other window will be seen longer at 61 // its previous position though if the exposed parts are not 62 // cleared right away. maybe there ought to be a flag in 63 // the update session, which tells us the cause of the update 64 65 66 //static rgb_color sPendingColor = (rgb_color){ 255, 255, 0, 255 }; 67 //static rgb_color sCurrentColor = (rgb_color){ 255, 0, 255, 255 }; 68 69 70 Window::Window(const BRect& frame, const char *name, 71 window_look look, window_feel feel, uint32 flags, uint32 workspaces, 72 ::ServerWindow* window, DrawingEngine* drawingEngine) 73 : 74 fTitle(name), 75 fFrame(frame), 76 fScreen(NULL), 77 78 fVisibleRegion(), 79 fVisibleContentRegion(), 80 fDirtyRegion(), 81 fDirtyCause(0), 82 83 fContentRegion(), 84 fEffectiveDrawingRegion(), 85 86 fVisibleContentRegionValid(false), 87 fContentRegionValid(false), 88 fEffectiveDrawingRegionValid(false), 89 90 fRegionPool(), 91 92 fWindowBehaviour(NULL), 93 fDecorator(NULL), 94 fTopView(NULL), 95 fWindow(window), 96 fDrawingEngine(drawingEngine), 97 fDesktop(window->Desktop()), 98 99 fCurrentUpdateSession(&fUpdateSessions[0]), 100 fPendingUpdateSession(&fUpdateSessions[1]), 101 fUpdateRequested(false), 102 fInUpdate(false), 103 fUpdatesEnabled(true), 104 105 // windows start hidden 106 fHidden(true), 107 fMinimized(false), 108 fIsFocus(false), 109 110 fLook(look), 111 fFeel(feel), 112 fWorkspaces(workspaces), 113 fCurrentWorkspace(-1), 114 115 fMinWidth(1), 116 fMaxWidth(32768), 117 fMinHeight(1), 118 fMaxHeight(32768), 119 120 fWorkspacesViewCount(0) 121 { 122 // make sure our arguments are valid 123 if (!IsValidLook(fLook)) 124 fLook = B_TITLED_WINDOW_LOOK; 125 if (!IsValidFeel(fFeel)) 126 fFeel = B_NORMAL_WINDOW_FEEL; 127 128 SetFlags(flags, NULL); 129 130 if (fLook != B_NO_BORDER_WINDOW_LOOK) { 131 fDecorator = gDecorManager.AllocateDecorator(fDesktop, fDrawingEngine, 132 frame, name, fLook, fFlags); 133 if (fDecorator) { 134 fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, 135 &fMaxWidth, &fMaxHeight); 136 } 137 } 138 fWindowBehaviour = gDecorManager.AllocateWindowBehaviour(this); 139 140 // do we need to change our size to let the decorator fit? 141 // _ResizeBy() will adapt the frame for validity before resizing 142 if (feel == kDesktopWindowFeel) { 143 // the desktop window spans over the whole screen 144 // TODO: this functionality should be moved somewhere else 145 // (so that it is always used when the workspace is changed) 146 uint16 width, height; 147 uint32 colorSpace; 148 float frequency; 149 if (Screen() != NULL) { 150 Screen()->GetMode(width, height, colorSpace, frequency); 151 // TODO: MOVE THIS AWAY!!! ResizeBy contains calls to virtual methods! 152 // Also, there is no TopView()! 153 fFrame.OffsetTo(B_ORIGIN); 154 // ResizeBy(width - frame.Width(), height - frame.Height(), NULL); 155 } 156 } 157 158 STRACE(("Window %p, %s:\n", this, Name())); 159 STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top, 160 fFrame.right, fFrame.bottom)); 161 STRACE(("\tWindow %s\n", window ? window->Title() : "NULL")); 162 } 163 164 165 Window::~Window() 166 { 167 if (fTopView) { 168 fTopView->DetachedFromWindow(); 169 delete fTopView; 170 } 171 172 delete fWindowBehaviour; 173 delete fDecorator; 174 175 delete fDrawingEngine; 176 } 177 178 179 status_t 180 Window::InitCheck() const 181 { 182 if (!fDrawingEngine || !fWindowBehaviour) 183 return B_NO_MEMORY; 184 // TODO: anything else? 185 return B_OK; 186 } 187 188 189 void 190 Window::SetClipping(BRegion* stillAvailableOnScreen) 191 { 192 // this function is only called from the Desktop thread 193 194 // start from full region (as if the window was fully visible) 195 GetFullRegion(&fVisibleRegion); 196 // clip to region still available on screen 197 fVisibleRegion.IntersectWith(stillAvailableOnScreen); 198 199 fVisibleContentRegionValid = false; 200 fEffectiveDrawingRegionValid = false; 201 } 202 203 204 void 205 Window::GetFullRegion(BRegion* region) 206 { 207 // TODO: if someone needs to call this from 208 // the outside, the clipping needs to be readlocked! 209 210 // start from the decorator border, extend to use the frame 211 GetBorderRegion(region); 212 region->Include(fFrame); 213 } 214 215 216 void 217 Window::GetBorderRegion(BRegion* region) 218 { 219 // TODO: if someone needs to call this from 220 // the outside, the clipping needs to be readlocked! 221 222 if (fDecorator) 223 *region = fDecorator->GetFootprint(); 224 else 225 region->MakeEmpty(); 226 } 227 228 229 void 230 Window::GetContentRegion(BRegion* region) 231 { 232 // TODO: if someone needs to call this from 233 // the outside, the clipping needs to be readlocked! 234 235 if (!fContentRegionValid) { 236 _UpdateContentRegion(); 237 } 238 239 *region = fContentRegion; 240 } 241 242 243 BRegion& 244 Window::VisibleContentRegion() 245 { 246 // TODO: if someone needs to call this from 247 // the outside, the clipping needs to be readlocked! 248 249 // regions expected to be locked 250 if (!fVisibleContentRegionValid) { 251 GetContentRegion(&fVisibleContentRegion); 252 fVisibleContentRegion.IntersectWith(&fVisibleRegion); 253 } 254 return fVisibleContentRegion; 255 } 256 257 258 // #pragma mark - 259 260 261 void 262 Window::_PropagatePosition() 263 { 264 if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) == 0) 265 return; 266 267 for (int32 i = 0; i < kListCount; i++) { 268 Anchor(i).position = fFrame.LeftTop(); 269 } 270 } 271 272 273 void 274 Window::MoveBy(int32 x, int32 y) 275 { 276 // this function is only called from the desktop thread 277 278 if (x == 0 && y == 0) 279 return; 280 281 fFrame.OffsetBy(x, y); 282 _PropagatePosition(); 283 284 // take along the dirty region which is not 285 // processed yet 286 fDirtyRegion.OffsetBy(x, y); 287 288 if (fContentRegionValid) 289 fContentRegion.OffsetBy(x, y); 290 291 if (fCurrentUpdateSession->IsUsed()) 292 fCurrentUpdateSession->MoveBy(x, y); 293 if (fPendingUpdateSession->IsUsed()) 294 fPendingUpdateSession->MoveBy(x, y); 295 296 fEffectiveDrawingRegionValid = false; 297 298 if (fDecorator) 299 fDecorator->MoveBy(x, y); 300 301 if (fTopView != NULL) { 302 fTopView->MoveBy(x, y, NULL); 303 fTopView->UpdateOverlay(); 304 } 305 306 // the desktop will take care of dirty regions 307 308 // dispatch a message to the client informing about the changed size 309 BMessage msg(B_WINDOW_MOVED); 310 msg.AddInt64("when", system_time()); 311 msg.AddPoint("where", fFrame.LeftTop()); 312 fWindow->SendMessageToClient(&msg); 313 } 314 315 316 void 317 Window::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion) 318 { 319 // this function is only called from the desktop thread 320 321 int32 wantWidth = fFrame.IntegerWidth() + x; 322 int32 wantHeight = fFrame.IntegerHeight() + y; 323 324 // enforce size limits 325 if (wantWidth < fMinWidth) 326 wantWidth = fMinWidth; 327 if (wantWidth > fMaxWidth) 328 wantWidth = fMaxWidth; 329 330 if (wantHeight < fMinHeight) 331 wantHeight = fMinHeight; 332 if (wantHeight > fMaxHeight) 333 wantHeight = fMaxHeight; 334 335 x = wantWidth - fFrame.IntegerWidth(); 336 y = wantHeight - fFrame.IntegerHeight(); 337 338 if (x == 0 && y == 0) 339 return; 340 341 fFrame.right += x; 342 fFrame.bottom += y; 343 344 fContentRegionValid = false; 345 fEffectiveDrawingRegionValid = false; 346 347 if (fDecorator) { 348 fDecorator->ResizeBy(x, y, dirtyRegion); 349 //if (dirtyRegion) { 350 //fDrawingEngine->FillRegion(*dirtyRegion, (rgb_color){ 255, 255, 0, 255 }); 351 //snooze(40000); 352 //} 353 } 354 355 if (fTopView != NULL) { 356 fTopView->ResizeBy(x, y, dirtyRegion); 357 fTopView->UpdateOverlay(); 358 } 359 360 //if (dirtyRegion) 361 //fDrawingEngine->FillRegion(*dirtyRegion, (rgb_color){ 0, 255, 255, 255 }); 362 363 // send a message to the client informing about the changed size 364 BRect frame(Frame()); 365 BMessage msg(B_WINDOW_RESIZED); 366 msg.AddInt64("when", system_time()); 367 msg.AddInt32("width", frame.IntegerWidth()); 368 msg.AddInt32("height", frame.IntegerHeight()); 369 fWindow->SendMessageToClient(&msg); 370 } 371 372 373 void 374 Window::ScrollViewBy(View* view, int32 dx, int32 dy) 375 { 376 // this is executed in ServerWindow with the Readlock 377 // held 378 379 if (!view || view == fTopView || (dx == 0 && dy == 0)) 380 return; 381 382 BRegion* dirty = fRegionPool.GetRegion(); 383 if (!dirty) 384 return; 385 386 view->ScrollBy(dx, dy, dirty); 387 388 //fDrawingEngine->FillRegion(*dirty, (rgb_color){ 255, 0, 255, 255 }); 389 //snooze(20000); 390 391 if (!IsOffscreenWindow() && IsVisible() && view->IsVisible()) { 392 dirty->IntersectWith(&VisibleContentRegion()); 393 _TriggerContentRedraw(*dirty); 394 } 395 396 fRegionPool.Recycle(dirty); 397 } 398 399 400 //! Takes care of invalidating parts that could not be copied 401 void 402 Window::CopyContents(BRegion* region, int32 xOffset, int32 yOffset) 403 { 404 // executed in ServerWindow thread with the read lock held 405 if (!IsVisible()) 406 return; 407 408 BRegion* newDirty = fRegionPool.GetRegion(*region); 409 410 // clip the region to the visible contents at the 411 // source and destination location (note that VisibleContentRegion() 412 // is used once to make sure it is valid, then fVisibleContentRegion 413 // is used directly) 414 region->IntersectWith(&VisibleContentRegion()); 415 if (region->CountRects() > 0) { 416 // Constrain to content region at destination 417 region->OffsetBy(xOffset, yOffset); 418 region->IntersectWith(&fVisibleContentRegion); 419 if (region->CountRects() > 0) { 420 // if the region still contains any rects 421 // offset to source location again 422 region->OffsetBy(-xOffset, -yOffset); 423 424 BRegion* allDirtyRegions = fRegionPool.GetRegion(fDirtyRegion); 425 if (allDirtyRegions != NULL) { 426 if (fPendingUpdateSession->IsUsed()) { 427 allDirtyRegions->Include( 428 &fPendingUpdateSession->DirtyRegion()); 429 } 430 if (fCurrentUpdateSession->IsUsed()) { 431 allDirtyRegions->Include( 432 &fCurrentUpdateSession->DirtyRegion()); 433 } 434 // Get just the part of the dirty regions which is semantically 435 // copied along 436 allDirtyRegions->IntersectWith(region); 437 } 438 439 BRegion* copyRegion = fRegionPool.GetRegion(*region); 440 if (copyRegion != NULL) { 441 // never copy what's already dirty 442 if (allDirtyRegions != NULL) 443 copyRegion->Exclude(allDirtyRegions); 444 445 if (fDrawingEngine->LockParallelAccess()) { 446 fDrawingEngine->CopyRegion(copyRegion, xOffset, yOffset); 447 fDrawingEngine->UnlockParallelAccess(); 448 449 // Prevent those parts from being added to the dirty region... 450 newDirty->Exclude(copyRegion); 451 452 // The parts that could be copied are not dirty (at the 453 // target location!) 454 copyRegion->OffsetBy(xOffset, yOffset); 455 // ... and even exclude them from the pending dirty region! 456 if (fPendingUpdateSession->IsUsed()) 457 fPendingUpdateSession->DirtyRegion().Exclude(copyRegion); 458 } 459 460 fRegionPool.Recycle(copyRegion); 461 } else { 462 // Fallback, should never be here. 463 if (fDrawingEngine->LockParallelAccess()) { 464 fDrawingEngine->CopyRegion(region, xOffset, yOffset); 465 fDrawingEngine->UnlockParallelAccess(); 466 } 467 } 468 469 if (allDirtyRegions != NULL) 470 fRegionPool.Recycle(allDirtyRegions); 471 } 472 } 473 // what is left visible from the original region 474 // at the destination after the region which could be 475 // copied has been excluded, is considered dirty 476 // NOTE: it may look like dirty regions are not moved 477 // if no region could be copied, but that's alright, 478 // since these parts will now be in newDirty anyways 479 // (with the right offset) 480 newDirty->OffsetBy(xOffset, yOffset); 481 newDirty->IntersectWith(&fVisibleContentRegion); 482 if (newDirty->CountRects() > 0) 483 ProcessDirtyRegion(*newDirty); 484 485 fRegionPool.Recycle(newDirty); 486 } 487 488 489 // #pragma mark - 490 491 492 void 493 Window::SetTopView(View* topView) 494 { 495 fTopView = topView; 496 497 if (fTopView) { 498 // the top view is special, it has a coordinate system 499 // as if it was attached directly to the desktop, therefor, 500 // the coordinate conversion through the view tree works 501 // as expected, since the top view has no "parent" but has 502 // fFrame as if it had 503 504 // make sure the location of the top view on screen matches ours 505 fTopView->MoveBy((int32)(fFrame.left - fTopView->Frame().left), 506 (int32)(fFrame.top - fTopView->Frame().top), NULL); 507 508 // make sure the size of the top view matches ours 509 fTopView->ResizeBy((int32)(fFrame.Width() - fTopView->Frame().Width()), 510 (int32)(fFrame.Height() - fTopView->Frame().Height()), NULL); 511 512 fTopView->AttachedToWindow(this); 513 } 514 } 515 516 517 View* 518 Window::ViewAt(const BPoint& where) 519 { 520 return fTopView->ViewAt(where); 521 } 522 523 524 window_anchor& 525 Window::Anchor(int32 index) 526 { 527 return fAnchor[index]; 528 } 529 530 531 Window* 532 Window::NextWindow(int32 index) const 533 { 534 return fAnchor[index].next; 535 } 536 537 538 Window* 539 Window::PreviousWindow(int32 index) const 540 { 541 return fAnchor[index].previous; 542 } 543 544 545 bool 546 Window::ReloadDecor() 547 { 548 ::Decorator* decorator = NULL; 549 WindowBehaviour* windowBehaviour = NULL; 550 551 if (fLook != B_NO_BORDER_WINDOW_LOOK) { 552 // we need a new decorator 553 decorator = gDecorManager.AllocateDecorator(fDesktop, fDrawingEngine, 554 Frame(), Title(), fLook, fFlags); 555 if (!decorator) 556 return false; 557 if (IsFocus()) 558 decorator->SetFocus(true); 559 } 560 561 windowBehaviour = gDecorManager.AllocateWindowBehaviour(this); 562 if (!windowBehaviour) { 563 delete decorator; 564 return false; 565 } 566 567 delete fDecorator; 568 fDecorator = decorator; 569 570 delete fWindowBehaviour; 571 fWindowBehaviour = windowBehaviour; 572 573 return true; 574 } 575 576 577 void 578 Window::SetScreen(const ::Screen* screen) 579 { 580 ASSERT_MULTI_WRITE_LOCKED(fDesktop->ScreenLocker()); 581 fScreen = screen; 582 } 583 584 585 const ::Screen* 586 Window::Screen() const 587 { 588 ASSERT_MULTI_READ_LOCKED(fDesktop->ScreenLocker()); 589 return fScreen; 590 } 591 592 593 // #pragma mark - 594 595 596 void 597 Window::GetEffectiveDrawingRegion(View* view, BRegion& region) 598 { 599 if (!fEffectiveDrawingRegionValid) { 600 fEffectiveDrawingRegion = VisibleContentRegion(); 601 if (fUpdateRequested && !fInUpdate) { 602 // We requested an update, but the client has not started it yet, 603 // so it is only allowed to draw outside the pending update sessions 604 // region 605 fEffectiveDrawingRegion.Exclude( 606 &fPendingUpdateSession->DirtyRegion()); 607 } else if (fInUpdate) { 608 // enforce the dirty region of the update session 609 fEffectiveDrawingRegion.IntersectWith( 610 &fCurrentUpdateSession->DirtyRegion()); 611 } else { 612 // not in update, the view can draw everywhere 613 //printf("Window(%s)::GetEffectiveDrawingRegion(for %s) - outside update\n", Title(), view->Name()); 614 } 615 616 fEffectiveDrawingRegionValid = true; 617 } 618 619 // TODO: this is a region that needs to be cached later in the server 620 // when the current view in ServerWindow is set, and we are currently 621 // in an update (fInUpdate), than we can set this region and remember 622 // it for the comming drawing commands until the current view changes 623 // again or fEffectiveDrawingRegionValid is suddenly false. 624 region = fEffectiveDrawingRegion; 625 if (!fContentRegionValid) 626 _UpdateContentRegion(); 627 628 region.IntersectWith(&view->ScreenAndUserClipping(&fContentRegion)); 629 } 630 631 632 bool 633 Window::DrawingRegionChanged(View* view) const 634 { 635 return !fEffectiveDrawingRegionValid || !view->IsScreenClippingValid(); 636 } 637 638 639 void 640 Window::ProcessDirtyRegion(BRegion& region) 641 { 642 // if this is exectuted in the desktop thread, 643 // it means that the window thread currently 644 // blocks to get the read lock, if it is 645 // executed from the window thread, it should 646 // have the read lock and the desktop thread 647 // is blocking to get the write lock. IAW, this 648 // is only executed in one thread. 649 if (fDirtyRegion.CountRects() == 0) { 650 // the window needs to be informed 651 // when the dirty region was empty. 652 // NOTE: when the window thread has processed 653 // the dirty region in MessageReceived(), 654 // it will make the region empty again, 655 // when it is empty here, we need to send 656 // the message to initiate the next update round. 657 // Until the message is processed in the window 658 // thread, the desktop thread can add parts to 659 // the region as it likes. 660 ServerWindow()->RequestRedraw(); 661 } 662 663 fDirtyRegion.Include(®ion); 664 fDirtyCause |= UPDATE_EXPOSE; 665 } 666 667 668 void 669 Window::RedrawDirtyRegion() 670 { 671 // executed from ServerWindow with the read lock held 672 673 if (IsVisible()) { 674 _DrawBorder(); 675 676 BRegion* dirtyContentRegion = 677 fRegionPool.GetRegion(VisibleContentRegion()); 678 dirtyContentRegion->IntersectWith(&fDirtyRegion); 679 680 _TriggerContentRedraw(*dirtyContentRegion); 681 682 fRegionPool.Recycle(dirtyContentRegion); 683 } 684 685 // reset the dirty region, since 686 // we're fully clean. If the desktop 687 // thread wanted to mark something 688 // dirty in the mean time, it was 689 // blocking on the global region lock to 690 // get write access, since we're holding 691 // the read lock for the whole time. 692 fDirtyRegion.MakeEmpty(); 693 fDirtyCause = 0; 694 } 695 696 697 void 698 Window::MarkDirty(BRegion& regionOnScreen) 699 { 700 // for marking any part of the desktop dirty 701 // this will get write access to the global 702 // region lock, and result in ProcessDirtyRegion() 703 // to be called for any windows affected 704 if (fDesktop) 705 fDesktop->MarkDirty(regionOnScreen); 706 } 707 708 709 void 710 Window::MarkContentDirty(BRegion& regionOnScreen) 711 { 712 // for triggering AS_REDRAW 713 // since this won't affect other windows, read locking 714 // is sufficient. If there was no dirty region before, 715 // an update message is triggered 716 if (fHidden || IsOffscreenWindow()) 717 return; 718 719 regionOnScreen.IntersectWith(&VisibleContentRegion()); 720 fDirtyCause |= UPDATE_REQUEST; 721 _TriggerContentRedraw(regionOnScreen); 722 } 723 724 725 void 726 Window::MarkContentDirtyAsync(BRegion& regionOnScreen) 727 { 728 // NOTE: see comments in ProcessDirtyRegion() 729 if (fHidden || IsOffscreenWindow()) 730 return; 731 732 regionOnScreen.IntersectWith(&VisibleContentRegion()); 733 734 if (fDirtyRegion.CountRects() == 0) { 735 ServerWindow()->RequestRedraw(); 736 } 737 738 fDirtyRegion.Include(®ionOnScreen); 739 fDirtyCause |= UPDATE_REQUEST; 740 } 741 742 743 void 744 Window::InvalidateView(View* view, BRegion& viewRegion) 745 { 746 if (view && IsVisible() && view->IsVisible()) { 747 if (!fContentRegionValid) 748 _UpdateContentRegion(); 749 750 view->ConvertToScreen(&viewRegion); 751 viewRegion.IntersectWith(&VisibleContentRegion()); 752 if (viewRegion.CountRects() > 0) { 753 viewRegion.IntersectWith( 754 &view->ScreenAndUserClipping(&fContentRegion)); 755 756 //fDrawingEngine->FillRegion(viewRegion, rgb_color{ 0, 255, 0, 255 }); 757 //snooze(10000); 758 fDirtyCause |= UPDATE_REQUEST; 759 _TriggerContentRedraw(viewRegion); 760 } 761 } 762 } 763 764 // DisableUpdateRequests 765 void 766 Window::DisableUpdateRequests() 767 { 768 fUpdatesEnabled = false; 769 } 770 771 772 // EnableUpdateRequests 773 void 774 Window::EnableUpdateRequests() 775 { 776 fUpdatesEnabled = true; 777 if (!fUpdateRequested && fPendingUpdateSession->IsUsed()) 778 _SendUpdateMessage(); 779 } 780 781 // #pragma mark - 782 783 784 void 785 Window::MouseDown(BMessage* message, BPoint where, int32* _viewToken) 786 { 787 DesktopSettings desktopSettings(fDesktop); 788 789 bool eventEaten = fWindowBehaviour->MouseDown(message, where); 790 if (!eventEaten) { 791 // click was inside the window contents 792 if (View* view = ViewAt(where)) { 793 if (HasModal()) 794 return; 795 796 // clicking a simple View 797 if (!IsFocus()) { 798 bool acceptFirstClick 799 = (Flags() & B_WILL_ACCEPT_FIRST_CLICK) != 0; 800 bool avoidFocus = (Flags() & B_AVOID_FOCUS) != 0; 801 802 // Activate or focus the window in case it doesn't accept first 803 // click, depending on the mouse mode 804 if (desktopSettings.MouseMode() == B_NORMAL_MOUSE 805 && !acceptFirstClick) 806 fDesktop->ActivateWindow(this); 807 else if (!avoidFocus) 808 fDesktop->SetFocusWindow(this); 809 810 // Eat the click if we don't accept first click 811 // (B_AVOID_FOCUS never gets the focus, so they always accept 812 // the first click) 813 // TODO: the latter is unlike BeOS - if we really wanted to 814 // imitate this behaviour, we would need to check if we're 815 // the front window instead of the focus window 816 if (!acceptFirstClick && !desktopSettings.AcceptFirstClick() 817 && !avoidFocus) 818 return; 819 } 820 821 // fill out view token for the view under the mouse 822 *_viewToken = view->Token(); 823 view->MouseDown(message, where); 824 } 825 } 826 } 827 828 829 void 830 Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken) 831 { 832 fWindowBehaviour->MouseUp(message, where); 833 834 if (View* view = ViewAt(where)) { 835 if (HasModal()) 836 return; 837 838 *_viewToken = view->Token(); 839 view->MouseUp(message, where); 840 } 841 } 842 843 844 void 845 Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken, 846 bool isLatestMouseMoved, bool isFake) 847 { 848 View* view = ViewAt(where); 849 if (view != NULL) 850 *_viewToken = view->Token(); 851 852 // ignore pointer history 853 if (!isLatestMouseMoved) 854 return; 855 856 fWindowBehaviour->MouseMoved(message, where, isFake); 857 858 // mouse cursor 859 860 if (view != NULL) { 861 view->MouseMoved(message, where); 862 863 // TODO: there is more for real cursor support, ie. if a window is closed, 864 // new app cursor shouldn't override view cursor, ... 865 ServerWindow()->App()->SetCurrentCursor(view->Cursor()); 866 } 867 } 868 869 870 // #pragma mark - 871 872 873 void 874 Window::WorkspaceActivated(int32 index, bool active) 875 { 876 BMessage activatedMsg(B_WORKSPACE_ACTIVATED); 877 activatedMsg.AddInt64("when", system_time()); 878 activatedMsg.AddInt32("workspace", index); 879 activatedMsg.AddBool("active", active); 880 881 ServerWindow()->SendMessageToClient(&activatedMsg); 882 } 883 884 885 void 886 Window::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces) 887 { 888 fWorkspaces = newWorkspaces; 889 890 BMessage changedMsg(B_WORKSPACES_CHANGED); 891 changedMsg.AddInt64("when", system_time()); 892 changedMsg.AddInt32("old", oldWorkspaces); 893 changedMsg.AddInt32("new", newWorkspaces); 894 895 ServerWindow()->SendMessageToClient(&changedMsg); 896 } 897 898 899 void 900 Window::Activated(bool active) 901 { 902 BMessage msg(B_WINDOW_ACTIVATED); 903 msg.AddBool("active", active); 904 ServerWindow()->SendMessageToClient(&msg); 905 } 906 907 908 //# pragma mark - 909 910 911 void 912 Window::SetTitle(const char* name, BRegion& dirty) 913 { 914 // rebuild the clipping for the title area 915 // and redraw it. 916 917 fTitle = name; 918 919 if (fDecorator) 920 fDecorator->SetTitle(name, &dirty); 921 } 922 923 924 void 925 Window::SetFocus(bool focus) 926 { 927 // executed from Desktop thread 928 // it holds the clipping write lock, 929 // so the window thread cannot be 930 // accessing fIsFocus 931 932 BRegion* dirty = NULL; 933 if (fDecorator) 934 dirty = fRegionPool.GetRegion(fDecorator->GetFootprint()); 935 if (dirty) { 936 dirty->IntersectWith(&fVisibleRegion); 937 fDesktop->MarkDirty(*dirty); 938 fRegionPool.Recycle(dirty); 939 } 940 941 fIsFocus = focus; 942 if (fDecorator) 943 fDecorator->SetFocus(focus); 944 945 Activated(focus); 946 } 947 948 949 void 950 Window::SetHidden(bool hidden) 951 { 952 // the desktop takes care of dirty regions 953 if (fHidden != hidden) { 954 fHidden = hidden; 955 956 fTopView->SetHidden(hidden); 957 958 // TODO: anything else? 959 } 960 } 961 962 963 void 964 Window::SetMinimized(bool minimized) 965 { 966 if (minimized == fMinimized) 967 return; 968 969 fMinimized = minimized; 970 } 971 972 973 bool 974 Window::IsVisible() const 975 { 976 if (IsOffscreenWindow()) 977 return true; 978 979 if (IsHidden()) 980 return false; 981 982 /* 983 if (fVisibleRegion.CountRects() == 0) 984 return false; 985 */ 986 return fCurrentWorkspace >= 0 && fCurrentWorkspace < kWorkingList; 987 } 988 989 990 bool 991 Window::IsDragging() const 992 { 993 if (!fWindowBehaviour) 994 return false; 995 return fWindowBehaviour->IsDragging(); 996 } 997 998 999 bool 1000 Window::IsResizing() const 1001 { 1002 if (!fWindowBehaviour) 1003 return false; 1004 return fWindowBehaviour->IsResizing(); 1005 } 1006 1007 1008 void 1009 Window::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight, 1010 int32 maxHeight) 1011 { 1012 if (minWidth < 0) 1013 minWidth = 0; 1014 1015 if (minHeight < 0) 1016 minHeight = 0; 1017 1018 fMinWidth = minWidth; 1019 fMaxWidth = maxWidth; 1020 fMinHeight = minHeight; 1021 fMaxHeight = maxHeight; 1022 1023 // give the Decorator a say in this too 1024 if (fDecorator) { 1025 fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, 1026 &fMaxHeight); 1027 } 1028 1029 _ObeySizeLimits(); 1030 } 1031 1032 1033 void 1034 Window::GetSizeLimits(int32* minWidth, int32* maxWidth, 1035 int32* minHeight, int32* maxHeight) const 1036 { 1037 *minWidth = fMinWidth; 1038 *maxWidth = fMaxWidth; 1039 *minHeight = fMinHeight; 1040 *maxHeight = fMaxHeight; 1041 } 1042 1043 1044 bool 1045 Window::SetTabLocation(float location, BRegion& dirty) 1046 { 1047 if (fDecorator) 1048 return fDecorator->SetTabLocation(location, &dirty); 1049 1050 return false; 1051 } 1052 1053 1054 float 1055 Window::TabLocation() const 1056 { 1057 if (fDecorator) 1058 return fDecorator->TabLocation(); 1059 return 0.0; 1060 } 1061 1062 1063 bool 1064 Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty) 1065 { 1066 if (fDecorator) 1067 return fDecorator->SetSettings(settings, &dirty); 1068 return false; 1069 } 1070 1071 1072 bool 1073 Window::GetDecoratorSettings(BMessage* settings) 1074 { 1075 if (fDecorator) 1076 return fDecorator->GetSettings(settings); 1077 1078 return false; 1079 } 1080 1081 1082 void 1083 Window::FontsChanged(BRegion* updateRegion) 1084 { 1085 if (fDecorator != NULL) { 1086 DesktopSettings settings(fDesktop); 1087 fDecorator->FontsChanged(settings, updateRegion); 1088 } 1089 } 1090 1091 1092 void 1093 Window::SetLook(window_look look, BRegion* updateRegion) 1094 { 1095 if (fDecorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) { 1096 // we need a new decorator 1097 fDecorator = gDecorManager.AllocateDecorator(fDesktop, fDrawingEngine, 1098 Frame(), Title(), fLook, fFlags); 1099 if (IsFocus()) 1100 fDecorator->SetFocus(true); 1101 } 1102 1103 fLook = look; 1104 1105 fContentRegionValid = false; 1106 // mabye a resize handle was added... 1107 fEffectiveDrawingRegionValid = false; 1108 // ...and therefor the drawing region is 1109 // likely not valid anymore either 1110 1111 if (fDecorator != NULL) { 1112 DesktopSettings settings(fDesktop); 1113 fDecorator->SetLook(settings, look, updateRegion); 1114 1115 // we might need to resize the window! 1116 fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, 1117 &fMaxHeight); 1118 _ObeySizeLimits(); 1119 } 1120 1121 if (look == B_NO_BORDER_WINDOW_LOOK) { 1122 // we don't need a decorator for this window 1123 delete fDecorator; 1124 fDecorator = NULL; 1125 } 1126 } 1127 1128 1129 void 1130 Window::SetFeel(window_feel feel) 1131 { 1132 // if the subset list is no longer needed, clear it 1133 if ((fFeel == B_MODAL_SUBSET_WINDOW_FEEL 1134 || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) 1135 && feel != B_MODAL_SUBSET_WINDOW_FEEL 1136 && feel != B_FLOATING_SUBSET_WINDOW_FEEL) 1137 fSubsets.MakeEmpty(); 1138 1139 fFeel = feel; 1140 1141 // having modal windows with B_AVOID_FRONT or B_AVOID_FOCUS doesn't 1142 // make that much sense, so we filter those flags out on demand 1143 fFlags = fOriginalFlags; 1144 fFlags &= ValidWindowFlags(fFeel); 1145 1146 if (!IsNormal()) { 1147 fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES; 1148 _PropagatePosition(); 1149 } 1150 } 1151 1152 1153 void 1154 Window::SetFlags(uint32 flags, BRegion* updateRegion) 1155 { 1156 fOriginalFlags = flags; 1157 fFlags = flags & ValidWindowFlags(fFeel); 1158 if (!IsNormal()) 1159 fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES; 1160 1161 if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0) 1162 _PropagatePosition(); 1163 1164 if (fDecorator == NULL) 1165 return; 1166 1167 fDecorator->SetFlags(flags, updateRegion); 1168 1169 // we might need to resize the window! 1170 if (fDecorator) { 1171 fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight); 1172 _ObeySizeLimits(); 1173 } 1174 1175 // TODO: not sure if we want to do this 1176 #if 0 1177 if ((fOriginalFlags & kWindowScreenFlag) != (flags & kWindowScreenFlag)) { 1178 // TODO: disabling needs to be nestable (or we might lose the previous 1179 // update state) 1180 if ((flags & kWindowScreenFlag) != 0) 1181 DisableUpdateRequests(); 1182 else 1183 EnableUpdateRequests(); 1184 } 1185 #endif 1186 } 1187 1188 1189 /*! Returns whether or not a window is in the workspace list with the 1190 specified \a index. 1191 */ 1192 bool 1193 Window::InWorkspace(int32 index) const 1194 { 1195 return (fWorkspaces & (1UL << index)) != 0; 1196 } 1197 1198 1199 bool 1200 Window::SupportsFront() 1201 { 1202 if (fFeel == kDesktopWindowFeel 1203 || fFeel == kMenuWindowFeel 1204 || (fFlags & B_AVOID_FRONT) != 0) 1205 return false; 1206 1207 return true; 1208 } 1209 1210 1211 bool 1212 Window::IsModal() const 1213 { 1214 return IsModalFeel(fFeel); 1215 } 1216 1217 1218 bool 1219 Window::IsFloating() const 1220 { 1221 return IsFloatingFeel(fFeel); 1222 } 1223 1224 1225 bool 1226 Window::IsNormal() const 1227 { 1228 return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel); 1229 } 1230 1231 1232 bool 1233 Window::HasModal() const 1234 { 1235 for (Window* window = NextWindow(fCurrentWorkspace); window != NULL; 1236 window = window->NextWindow(fCurrentWorkspace)) { 1237 if (window->IsHidden() || !window->IsModal()) 1238 continue; 1239 1240 if (window->HasInSubset(this)) 1241 return true; 1242 } 1243 1244 return false; 1245 } 1246 1247 1248 /*! \brief Returns the windows that's in behind of the backmost position 1249 this window can get. 1250 Returns NULL is this window can be the backmost window. 1251 1252 \param workspace the workspace on which this check should be made. If 1253 the value is -1, the window's current workspace will be used. 1254 */ 1255 Window* 1256 Window::Backmost(Window* window, int32 workspace) 1257 { 1258 if (workspace == -1) 1259 workspace = fCurrentWorkspace; 1260 1261 ASSERT(workspace != -1); 1262 if (workspace == -1) 1263 return NULL; 1264 1265 // Desktop windows are always backmost 1266 if (fFeel == kDesktopWindowFeel) 1267 return NULL; 1268 1269 if (window == NULL) 1270 window = PreviousWindow(workspace); 1271 1272 for (; window != NULL; window = window->PreviousWindow(workspace)) { 1273 if (window->IsHidden() || window == this) 1274 continue; 1275 1276 if (HasInSubset(window)) 1277 return window; 1278 } 1279 1280 return NULL; 1281 } 1282 1283 1284 /*! \brief Returns the window that's in front of the frontmost position 1285 this window can get. 1286 Returns NULL if this window can be the frontmost window. 1287 1288 \param workspace the workspace on which this check should be made. If 1289 the value is -1, the window's current workspace will be used. 1290 */ 1291 Window* 1292 Window::Frontmost(Window* first, int32 workspace) 1293 { 1294 if (workspace == -1) 1295 workspace = fCurrentWorkspace; 1296 1297 ASSERT(workspace != -1); 1298 if (workspace == -1) 1299 return NULL; 1300 1301 if (fFeel == kDesktopWindowFeel) 1302 return first ? first : NextWindow(workspace); 1303 1304 if (first == NULL) 1305 first = NextWindow(workspace); 1306 1307 for (Window* window = first; window != NULL; 1308 window = window->NextWindow(workspace)) { 1309 if (window->IsHidden() || window == this) 1310 continue; 1311 1312 if (window->HasInSubset(this)) 1313 return window; 1314 } 1315 1316 return NULL; 1317 } 1318 1319 1320 bool 1321 Window::AddToSubset(Window* window) 1322 { 1323 return fSubsets.AddItem(window); 1324 } 1325 1326 1327 void 1328 Window::RemoveFromSubset(Window* window) 1329 { 1330 fSubsets.RemoveItem(window); 1331 } 1332 1333 1334 /*! Returns whether or not a window is in the subset of this window. 1335 If a window is in the subset of this window, it means it should always 1336 appear behind this window. 1337 */ 1338 bool 1339 Window::HasInSubset(const Window* window) const 1340 { 1341 if (window == NULL || fFeel == window->Feel() 1342 || fFeel == B_NORMAL_WINDOW_FEEL) 1343 return false; 1344 1345 // Menus are a special case: they will always be on-top of every window 1346 // of their application 1347 if (fFeel == kMenuWindowFeel) 1348 return window->ServerWindow()->App() == ServerWindow()->App(); 1349 if (window->Feel() == kMenuWindowFeel) 1350 return false; 1351 1352 // we have a few special feels that have a fixed order 1353 1354 const int32 kFeels[] = {kPasswordWindowFeel, kWindowScreenFeel, 1355 B_MODAL_ALL_WINDOW_FEEL, B_FLOATING_ALL_WINDOW_FEEL}; 1356 1357 for (uint32 order = 0; 1358 order < sizeof(kFeels) / sizeof(kFeels[0]); order++) { 1359 if (fFeel == kFeels[order]) 1360 return true; 1361 if (window->Feel() == kFeels[order]) 1362 return false; 1363 } 1364 1365 if ((fFeel == B_FLOATING_APP_WINDOW_FEEL 1366 && window->Feel() != B_MODAL_APP_WINDOW_FEEL) 1367 || fFeel == B_MODAL_APP_WINDOW_FEEL) 1368 return window->ServerWindow()->App() == ServerWindow()->App(); 1369 1370 return fSubsets.HasItem(window); 1371 } 1372 1373 1374 /*! \brief Collects all workspaces views in this window and puts it into \a list 1375 */ 1376 void 1377 Window::FindWorkspacesViews(BObjectList<WorkspacesView>& list) const 1378 { 1379 int32 count = fWorkspacesViewCount; 1380 fTopView->FindViews(kWorkspacesViewFlag, (BObjectList<View>&)list, count); 1381 } 1382 1383 1384 /*! \brief Returns on which workspaces the window should be visible. 1385 1386 A modal or floating window may be visible on a workspace if one 1387 of its subset windows is visible there. Floating windows also need 1388 to have a subset as front window to be visible. 1389 */ 1390 uint32 1391 Window::SubsetWorkspaces() const 1392 { 1393 if (fFeel == B_MODAL_ALL_WINDOW_FEEL 1394 || fFeel == B_FLOATING_ALL_WINDOW_FEEL) 1395 return B_ALL_WORKSPACES; 1396 1397 if (fFeel == B_FLOATING_APP_WINDOW_FEEL) { 1398 Window* front = fDesktop->FrontWindow(); 1399 if (front != NULL && front->IsNormal() 1400 && front->ServerWindow()->App() == ServerWindow()->App()) 1401 return ServerWindow()->App()->Workspaces(); 1402 1403 return 0; 1404 } 1405 1406 if (fFeel == B_MODAL_APP_WINDOW_FEEL) { 1407 uint32 workspaces = ServerWindow()->App()->Workspaces(); 1408 if (workspaces == 0) { 1409 // The application doesn't seem to have any other windows 1410 // open or visible - but we'd like to see modal windows 1411 // anyway, at least when they are first opened. 1412 return 1UL << fDesktop->CurrentWorkspace(); 1413 } 1414 return workspaces; 1415 } 1416 1417 if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL 1418 || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) { 1419 uint32 workspaces = 0; 1420 bool hasNormalFront = false; 1421 for (int32 i = 0; i < fSubsets.CountItems(); i++) { 1422 Window* window = fSubsets.ItemAt(i); 1423 1424 if (!window->IsHidden()) 1425 workspaces |= window->Workspaces(); 1426 if (window == fDesktop->FrontWindow() && window->IsNormal()) 1427 hasNormalFront = true; 1428 } 1429 1430 if (fFeel == B_FLOATING_SUBSET_WINDOW_FEEL && !hasNormalFront) 1431 return 0; 1432 1433 return workspaces; 1434 } 1435 1436 return 0; 1437 } 1438 1439 1440 /*! Returns wether or not a window is in the subset workspace list with the 1441 specified \a index. 1442 See SubsetWorkspaces(). 1443 */ 1444 bool 1445 Window::InSubsetWorkspace(int32 index) const 1446 { 1447 return (SubsetWorkspaces() & (1UL << index)) != 0; 1448 } 1449 1450 1451 // #pragma mark - static 1452 1453 1454 /*static*/ bool 1455 Window::IsValidLook(window_look look) 1456 { 1457 return look == B_TITLED_WINDOW_LOOK 1458 || look == B_DOCUMENT_WINDOW_LOOK 1459 || look == B_MODAL_WINDOW_LOOK 1460 || look == B_FLOATING_WINDOW_LOOK 1461 || look == B_BORDERED_WINDOW_LOOK 1462 || look == B_NO_BORDER_WINDOW_LOOK 1463 || look == kDesktopWindowLook 1464 || look == kLeftTitledWindowLook; 1465 } 1466 1467 1468 /*static*/ bool 1469 Window::IsValidFeel(window_feel feel) 1470 { 1471 return feel == B_NORMAL_WINDOW_FEEL 1472 || feel == B_MODAL_SUBSET_WINDOW_FEEL 1473 || feel == B_MODAL_APP_WINDOW_FEEL 1474 || feel == B_MODAL_ALL_WINDOW_FEEL 1475 || feel == B_FLOATING_SUBSET_WINDOW_FEEL 1476 || feel == B_FLOATING_APP_WINDOW_FEEL 1477 || feel == B_FLOATING_ALL_WINDOW_FEEL 1478 || feel == kDesktopWindowFeel 1479 || feel == kMenuWindowFeel 1480 || feel == kWindowScreenFeel 1481 || feel == kPasswordWindowFeel; 1482 } 1483 1484 1485 /*static*/ bool 1486 Window::IsModalFeel(window_feel feel) 1487 { 1488 return feel == B_MODAL_SUBSET_WINDOW_FEEL 1489 || feel == B_MODAL_APP_WINDOW_FEEL 1490 || feel == B_MODAL_ALL_WINDOW_FEEL; 1491 } 1492 1493 1494 /*static*/ bool 1495 Window::IsFloatingFeel(window_feel feel) 1496 { 1497 return feel == B_FLOATING_SUBSET_WINDOW_FEEL 1498 || feel == B_FLOATING_APP_WINDOW_FEEL 1499 || feel == B_FLOATING_ALL_WINDOW_FEEL; 1500 } 1501 1502 1503 /*static*/ uint32 1504 Window::ValidWindowFlags() 1505 { 1506 return B_NOT_MOVABLE 1507 | B_NOT_CLOSABLE 1508 | B_NOT_ZOOMABLE 1509 | B_NOT_MINIMIZABLE 1510 | B_NOT_RESIZABLE 1511 | B_NOT_H_RESIZABLE 1512 | B_NOT_V_RESIZABLE 1513 | B_AVOID_FRONT 1514 | B_AVOID_FOCUS 1515 | B_WILL_ACCEPT_FIRST_CLICK 1516 | B_OUTLINE_RESIZE 1517 | B_NO_WORKSPACE_ACTIVATION 1518 | B_NOT_ANCHORED_ON_ACTIVATE 1519 | B_ASYNCHRONOUS_CONTROLS 1520 | B_QUIT_ON_WINDOW_CLOSE 1521 | B_SAME_POSITION_IN_ALL_WORKSPACES 1522 | B_AUTO_UPDATE_SIZE_LIMITS 1523 | B_CLOSE_ON_ESCAPE 1524 | B_NO_SERVER_SIDE_WINDOW_MODIFIERS 1525 | kWindowScreenFlag 1526 | kAcceptKeyboardFocusFlag; 1527 } 1528 1529 1530 /*static*/ uint32 1531 Window::ValidWindowFlags(window_feel feel) 1532 { 1533 uint32 flags = ValidWindowFlags(); 1534 if (IsModalFeel(feel)) 1535 return flags & ~(B_AVOID_FOCUS | B_AVOID_FRONT); 1536 1537 return flags; 1538 } 1539 1540 1541 // #pragma mark - private 1542 1543 1544 void 1545 Window::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift, 1546 int32 xOffset, int32 yOffset) 1547 { 1548 BRegion* common = fRegionPool.GetRegion(*regionToShift); 1549 if (!common) 1550 return; 1551 // see if there is a common part at all 1552 common->IntersectWith(region); 1553 if (common->CountRects() > 0) { 1554 // cut the common part from the region, 1555 // offset that to destination and include again 1556 region->Exclude(common); 1557 common->OffsetBy(xOffset, yOffset); 1558 region->Include(common); 1559 } 1560 fRegionPool.Recycle(common); 1561 } 1562 1563 1564 void 1565 Window::_TriggerContentRedraw(BRegion& dirtyContentRegion) 1566 { 1567 if (!IsVisible() || dirtyContentRegion.CountRects() == 0 1568 || (fFlags & kWindowScreenFlag) != 0) 1569 return; 1570 1571 // put this into the pending dirty region 1572 // to eventually trigger a client redraw 1573 bool wasExpose = fPendingUpdateSession->IsExpose(); 1574 BRegion* backgroundClearingRegion = &dirtyContentRegion; 1575 1576 _TransferToUpdateSession(&dirtyContentRegion); 1577 1578 if (fPendingUpdateSession->IsExpose()) { 1579 if (!fContentRegionValid) 1580 _UpdateContentRegion(); 1581 1582 if (!wasExpose) { 1583 // there was suddenly added a dirty region 1584 // caused by exposing content, we need to clear 1585 // the entire background 1586 backgroundClearingRegion = &fPendingUpdateSession->DirtyRegion(); 1587 } 1588 1589 if (fDrawingEngine->LockParallelAccess()) { 1590 bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled(); 1591 fDrawingEngine->SetCopyToFrontEnabled(true); 1592 fDrawingEngine->SuspendAutoSync(); 1593 1594 //sCurrentColor.red = rand() % 255; 1595 //sCurrentColor.green = rand() % 255; 1596 //sCurrentColor.blue = rand() % 255; 1597 //sPendingColor.red = rand() % 255; 1598 //sPendingColor.green = rand() % 255; 1599 //sPendingColor.blue = rand() % 255; 1600 //fDrawingEngine->FillRegion(*backgroundClearingRegion, sCurrentColor); 1601 //snooze(10000); 1602 1603 fTopView->Draw(fDrawingEngine, backgroundClearingRegion, 1604 &fContentRegion, true); 1605 1606 fDrawingEngine->Sync(); 1607 fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled); 1608 fDrawingEngine->UnlockParallelAccess(); 1609 } 1610 } 1611 } 1612 1613 1614 void 1615 Window::_DrawBorder() 1616 { 1617 // this is executed in the window thread, but only 1618 // in respond to a REDRAW message having been received, the 1619 // clipping lock is held for reading 1620 1621 if (!fDecorator) 1622 return; 1623 1624 // construct the region of the border that needs redrawing 1625 BRegion* dirtyBorderRegion = fRegionPool.GetRegion(); 1626 if (!dirtyBorderRegion) 1627 return; 1628 GetBorderRegion(dirtyBorderRegion); 1629 // intersect with our visible region 1630 dirtyBorderRegion->IntersectWith(&fVisibleRegion); 1631 // intersect with the dirty region 1632 dirtyBorderRegion->IntersectWith(&fDirtyRegion); 1633 1634 DrawingEngine* engine = fDecorator->GetDrawingEngine(); 1635 if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) { 1636 engine->ConstrainClippingRegion(dirtyBorderRegion); 1637 bool copyToFrontEnabled = engine->CopyToFrontEnabled(); 1638 engine->SetCopyToFrontEnabled(true); 1639 1640 fDecorator->Draw(dirtyBorderRegion->Frame()); 1641 1642 engine->SetCopyToFrontEnabled(copyToFrontEnabled); 1643 1644 // TODO: remove this once the DrawState stuff is handled 1645 // more cleanly. The reason why this is needed is that 1646 // when the decorator draws strings, a draw state is set 1647 // on the Painter object, and this is were it might get 1648 // out of sync with what the ServerWindow things is the 1649 // current DrawState set on the Painter 1650 fWindow->ResyncDrawState(); 1651 1652 engine->UnlockParallelAccess(); 1653 } 1654 fRegionPool.Recycle(dirtyBorderRegion); 1655 } 1656 1657 1658 /*! pre: the clipping is readlocked (this function is 1659 only called from _TriggerContentRedraw()), which 1660 in turn is only called from MessageReceived() with 1661 the clipping lock held 1662 */ 1663 void 1664 Window::_TransferToUpdateSession(BRegion* contentDirtyRegion) 1665 { 1666 if (contentDirtyRegion->CountRects() <= 0) 1667 return; 1668 1669 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor); 1670 //snooze(20000); 1671 1672 // add to pending 1673 fPendingUpdateSession->SetUsed(true); 1674 // if (!fPendingUpdateSession->IsExpose()) 1675 fPendingUpdateSession->AddCause(fDirtyCause); 1676 fPendingUpdateSession->Include(contentDirtyRegion); 1677 1678 if (!fUpdateRequested) { 1679 // send this to client 1680 _SendUpdateMessage(); 1681 // the pending region is now the current, 1682 // though the update does not start until 1683 // we received BEGIN_UPDATE from the client 1684 } 1685 } 1686 1687 1688 void 1689 Window::_SendUpdateMessage() 1690 { 1691 if (!fUpdatesEnabled) 1692 return; 1693 1694 BMessage message(_UPDATE_); 1695 if (ServerWindow()->SendMessageToClient(&message) != B_OK) { 1696 // If sending the message failed, we'll just keep adding to the dirty 1697 // region until sending was successful. 1698 // TODO: we might want to automatically resend this message in this case 1699 return; 1700 } 1701 1702 fUpdateRequested = true; 1703 fEffectiveDrawingRegionValid = false; 1704 } 1705 1706 1707 void 1708 Window::BeginUpdate(BPrivate::PortLink& link) 1709 { 1710 // NOTE: since we might "shift" parts of the 1711 // internal dirty regions from the desktop thread 1712 // in response to Window::ResizeBy(), which 1713 // might move arround views, the user of this function 1714 // needs to hold the global clipping lock so that the internal 1715 // dirty regions are not messed with from the Desktop thread 1716 // and ServerWindow thread at the same time. 1717 1718 if (!fUpdateRequested) { 1719 link.StartMessage(B_ERROR); 1720 link.Flush(); 1721 fprintf(stderr, "Window::BeginUpdate() - no update requested!\n"); 1722 return; 1723 } 1724 1725 // make the pending update session the current update session 1726 // (toggle the pointers) 1727 UpdateSession* temp = fCurrentUpdateSession; 1728 fCurrentUpdateSession = fPendingUpdateSession; 1729 fPendingUpdateSession = temp; 1730 fPendingUpdateSession->SetUsed(false); 1731 // all drawing command from the client 1732 // will have the dirty region from the update 1733 // session enforced 1734 fInUpdate = true; 1735 fEffectiveDrawingRegionValid = false; 1736 1737 // TODO: each view could be drawn individually 1738 // right before carrying out the first drawing 1739 // command from the client during an update 1740 // (View::IsBackgroundDirty() can be used 1741 // for this) 1742 if (!fContentRegionValid) 1743 _UpdateContentRegion(); 1744 1745 BRegion* dirty = fRegionPool.GetRegion( 1746 fCurrentUpdateSession->DirtyRegion()); 1747 if (!dirty) { 1748 link.StartMessage(B_ERROR); 1749 link.Flush(); 1750 return; 1751 } 1752 1753 dirty->IntersectWith(&VisibleContentRegion()); 1754 1755 //if (!fCurrentUpdateSession->IsExpose()) { 1756 ////sCurrentColor.red = rand() % 255; 1757 ////sCurrentColor.green = rand() % 255; 1758 ////sCurrentColor.blue = rand() % 255; 1759 ////sPendingColor.red = rand() % 255; 1760 ////sPendingColor.green = rand() % 255; 1761 ////sPendingColor.blue = rand() % 255; 1762 //fDrawingEngine->FillRegion(*dirty, sCurrentColor); 1763 //snooze(10000); 1764 //} 1765 1766 link.StartMessage(B_OK); 1767 // append the current window geometry to the 1768 // message, the client will need it 1769 link.Attach<BPoint>(fFrame.LeftTop()); 1770 link.Attach<float>(fFrame.Width()); 1771 link.Attach<float>(fFrame.Height()); 1772 // find and attach all views that intersect with 1773 // the dirty region 1774 fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion); 1775 // mark the end of the token "list" 1776 link.Attach<int32>(B_NULL_TOKEN); 1777 link.Flush(); 1778 1779 // supress back to front buffer copies in the drawing engine 1780 fDrawingEngine->SetCopyToFrontEnabled(false); 1781 1782 if (!fCurrentUpdateSession->IsExpose() 1783 && fDrawingEngine->LockParallelAccess()) { 1784 fDrawingEngine->SuspendAutoSync(); 1785 1786 fTopView->Draw(fDrawingEngine, dirty, &fContentRegion, true); 1787 1788 fDrawingEngine->Sync(); 1789 fDrawingEngine->UnlockParallelAccess(); 1790 } // else the background was cleared already 1791 1792 fRegionPool.Recycle(dirty); 1793 } 1794 1795 1796 void 1797 Window::EndUpdate() 1798 { 1799 // NOTE: see comment in _BeginUpdate() 1800 1801 if (fInUpdate) { 1802 // reenable copy to front 1803 fDrawingEngine->SetCopyToFrontEnabled(true); 1804 1805 BRegion* dirty = fRegionPool.GetRegion( 1806 fCurrentUpdateSession->DirtyRegion()); 1807 1808 if (dirty) { 1809 dirty->IntersectWith(&VisibleContentRegion()); 1810 1811 fDrawingEngine->CopyToFront(*dirty); 1812 fRegionPool.Recycle(dirty); 1813 } 1814 1815 fCurrentUpdateSession->SetUsed(false); 1816 1817 fInUpdate = false; 1818 fEffectiveDrawingRegionValid = false; 1819 } 1820 if (fPendingUpdateSession->IsUsed()) { 1821 // send this to client 1822 _SendUpdateMessage(); 1823 } else { 1824 fUpdateRequested = false; 1825 } 1826 } 1827 1828 1829 void 1830 Window::_UpdateContentRegion() 1831 { 1832 fContentRegion.Set(fFrame); 1833 1834 // resize handle 1835 if (fDecorator) 1836 fContentRegion.Exclude(&fDecorator->GetFootprint()); 1837 1838 fContentRegionValid = true; 1839 } 1840 1841 1842 void 1843 Window::_ObeySizeLimits() 1844 { 1845 // make sure we even have valid size limits 1846 if (fMaxWidth < fMinWidth) 1847 fMaxWidth = fMinWidth; 1848 1849 if (fMaxHeight < fMinHeight) 1850 fMaxHeight = fMinHeight; 1851 1852 // Automatically resize the window to fit these new limits 1853 // if it does not already. 1854 1855 // On R5, Windows don't automatically resize, but since 1856 // BWindow::ResizeTo() even honors the limits, I would guess 1857 // this is a bug that we don't have to adopt. 1858 // Note that most current apps will do unnecessary resizing 1859 // after having set the limits, but the overhead is neglible. 1860 1861 float minWidthDiff = fMinWidth - fFrame.Width(); 1862 float minHeightDiff = fMinHeight - fFrame.Height(); 1863 float maxWidthDiff = fMaxWidth - fFrame.Width(); 1864 float maxHeightDiff = fMaxHeight - fFrame.Height(); 1865 1866 float xDiff = 0.0; 1867 if (minWidthDiff > 0.0) // we're currently smaller than minWidth 1868 xDiff = minWidthDiff; 1869 else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth 1870 xDiff = maxWidthDiff; 1871 1872 float yDiff = 0.0; 1873 if (minHeightDiff > 0.0) // we're currently smaller than minHeight 1874 yDiff = minHeightDiff; 1875 else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight 1876 yDiff = maxHeightDiff; 1877 1878 if (fDesktop) 1879 fDesktop->ResizeWindowBy(this, xDiff, yDiff); 1880 else 1881 ResizeBy((int32)xDiff, (int32)yDiff, NULL); 1882 } 1883 1884 1885 // #pragma mark - UpdateSession 1886 1887 1888 Window::UpdateSession::UpdateSession() 1889 : 1890 fDirtyRegion(), 1891 fInUse(false), 1892 fCause(0) 1893 { 1894 } 1895 1896 1897 Window::UpdateSession::~UpdateSession() 1898 { 1899 } 1900 1901 1902 void 1903 Window::UpdateSession::Include(BRegion* additionalDirty) 1904 { 1905 fDirtyRegion.Include(additionalDirty); 1906 } 1907 1908 1909 void 1910 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession) 1911 { 1912 fDirtyRegion.Exclude(dirtyInNextSession); 1913 } 1914 1915 1916 void 1917 Window::UpdateSession::MoveBy(int32 x, int32 y) 1918 { 1919 fDirtyRegion.OffsetBy(x, y); 1920 } 1921 1922 1923 void 1924 Window::UpdateSession::SetUsed(bool used) 1925 { 1926 fInUse = used; 1927 if (!fInUse) { 1928 fDirtyRegion.MakeEmpty(); 1929 fCause = 0; 1930 } 1931 } 1932 1933 1934 void 1935 Window::UpdateSession::AddCause(uint8 cause) 1936 { 1937 fCause |= cause; 1938 } 1939