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