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