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(fDesktop, fDrawingEngine, 133 frame, name, fLook, fFlags); 134 if (fDecorator) { 135 fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, 136 &fMaxWidth, &fMaxHeight); 137 } 138 } 139 fWindowBehaviour = gDecorManager.AllocateWindowBehaviour(this); 140 141 // do we need to change our size to let the decorator fit? 142 // _ResizeBy() will adapt the frame for validity before resizing 143 if (feel == kDesktopWindowFeel) { 144 // the desktop window spans over the whole screen 145 // TODO: this functionality should be moved somewhere else 146 // (so that it is always used when the workspace is changed) 147 uint16 width, height; 148 uint32 colorSpace; 149 float frequency; 150 if (Screen() != NULL) { 151 Screen()->GetMode(width, height, colorSpace, frequency); 152 // TODO: MOVE THIS AWAY!!! ResizeBy contains calls to virtual methods! 153 // Also, there is no TopView()! 154 fFrame.OffsetTo(B_ORIGIN); 155 // ResizeBy(width - frame.Width(), height - frame.Height(), NULL); 156 } 157 } 158 159 STRACE(("Window %p, %s:\n", this, Name())); 160 STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top, 161 fFrame.right, fFrame.bottom)); 162 STRACE(("\tWindow %s\n", window ? window->Title() : "NULL")); 163 } 164 165 166 Window::~Window() 167 { 168 if (fTopView) { 169 fTopView->DetachedFromWindow(); 170 delete fTopView; 171 } 172 173 delete fWindowBehaviour; 174 delete fDecorator; 175 176 delete fDrawingEngine; 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 if (fLook != B_NO_BORDER_WINDOW_LOOK) { 553 // we need a new decorator 554 decorator = gDecorManager.AllocateDecorator(fDesktop, fDrawingEngine, 555 Frame(), Title(), fLook, fFlags); 556 if (!decorator) 557 return false; 558 if (IsFocus()) 559 decorator->SetFocus(true); 560 } 561 562 windowBehaviour = gDecorManager.AllocateWindowBehaviour(this); 563 if (!windowBehaviour) { 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 (fDecorator) 1112 return fDecorator->SetSettings(settings, &dirty); 1113 return false; 1114 } 1115 1116 1117 bool 1118 Window::GetDecoratorSettings(BMessage* settings) 1119 { 1120 if (fDesktop) 1121 fDesktop->GetDecoratorSettings(this, *settings); 1122 1123 if (fDecorator) 1124 return fDecorator->GetSettings(settings); 1125 1126 return false; 1127 } 1128 1129 1130 void 1131 Window::FontsChanged(BRegion* updateRegion) 1132 { 1133 if (fDecorator != NULL) { 1134 DesktopSettings settings(fDesktop); 1135 fDecorator->FontsChanged(settings, updateRegion); 1136 } 1137 } 1138 1139 1140 void 1141 Window::SetLook(window_look look, BRegion* updateRegion) 1142 { 1143 if (fDecorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) { 1144 // we need a new decorator 1145 fDecorator = gDecorManager.AllocateDecorator(fDesktop, fDrawingEngine, 1146 Frame(), Title(), fLook, fFlags); 1147 if (IsFocus()) 1148 fDecorator->SetFocus(true); 1149 } 1150 1151 fLook = look; 1152 1153 fContentRegionValid = false; 1154 // mabye a resize handle was added... 1155 fEffectiveDrawingRegionValid = false; 1156 // ...and therefor the drawing region is 1157 // likely not valid anymore either 1158 1159 if (fDecorator != NULL) { 1160 DesktopSettings settings(fDesktop); 1161 fDecorator->SetLook(settings, look, updateRegion); 1162 1163 // we might need to resize the window! 1164 fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, 1165 &fMaxHeight); 1166 _ObeySizeLimits(); 1167 } 1168 1169 if (look == B_NO_BORDER_WINDOW_LOOK) { 1170 // we don't need a decorator for this window 1171 delete fDecorator; 1172 fDecorator = NULL; 1173 } 1174 } 1175 1176 1177 void 1178 Window::SetFeel(window_feel feel) 1179 { 1180 // if the subset list is no longer needed, clear it 1181 if ((fFeel == B_MODAL_SUBSET_WINDOW_FEEL 1182 || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) 1183 && feel != B_MODAL_SUBSET_WINDOW_FEEL 1184 && feel != B_FLOATING_SUBSET_WINDOW_FEEL) 1185 fSubsets.MakeEmpty(); 1186 1187 fFeel = feel; 1188 1189 // having modal windows with B_AVOID_FRONT or B_AVOID_FOCUS doesn't 1190 // make that much sense, so we filter those flags out on demand 1191 fFlags = fOriginalFlags; 1192 fFlags &= ValidWindowFlags(fFeel); 1193 1194 if (!IsNormal()) { 1195 fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES; 1196 _PropagatePosition(); 1197 } 1198 } 1199 1200 1201 void 1202 Window::SetFlags(uint32 flags, BRegion* updateRegion) 1203 { 1204 fOriginalFlags = flags; 1205 fFlags = flags & ValidWindowFlags(fFeel); 1206 if (!IsNormal()) 1207 fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES; 1208 1209 if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0) 1210 _PropagatePosition(); 1211 1212 if (fDecorator == NULL) 1213 return; 1214 1215 fDecorator->SetFlags(flags, updateRegion); 1216 1217 // we might need to resize the window! 1218 if (fDecorator) { 1219 fDecorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight); 1220 _ObeySizeLimits(); 1221 } 1222 1223 // TODO: not sure if we want to do this 1224 #if 0 1225 if ((fOriginalFlags & kWindowScreenFlag) != (flags & kWindowScreenFlag)) { 1226 // TODO: disabling needs to be nestable (or we might lose the previous 1227 // update state) 1228 if ((flags & kWindowScreenFlag) != 0) 1229 DisableUpdateRequests(); 1230 else 1231 EnableUpdateRequests(); 1232 } 1233 #endif 1234 } 1235 1236 1237 /*! Returns whether or not a window is in the workspace list with the 1238 specified \a index. 1239 */ 1240 bool 1241 Window::InWorkspace(int32 index) const 1242 { 1243 return (fWorkspaces & (1UL << index)) != 0; 1244 } 1245 1246 1247 bool 1248 Window::SupportsFront() 1249 { 1250 if (fFeel == kDesktopWindowFeel 1251 || fFeel == kMenuWindowFeel 1252 || (fFlags & B_AVOID_FRONT) != 0) 1253 return false; 1254 1255 return true; 1256 } 1257 1258 1259 bool 1260 Window::IsModal() const 1261 { 1262 return IsModalFeel(fFeel); 1263 } 1264 1265 1266 bool 1267 Window::IsFloating() const 1268 { 1269 return IsFloatingFeel(fFeel); 1270 } 1271 1272 1273 bool 1274 Window::IsNormal() const 1275 { 1276 return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel); 1277 } 1278 1279 1280 bool 1281 Window::HasModal() const 1282 { 1283 for (Window* window = NextWindow(fCurrentWorkspace); window != NULL; 1284 window = window->NextWindow(fCurrentWorkspace)) { 1285 if (window->IsHidden() || !window->IsModal()) 1286 continue; 1287 1288 if (window->HasInSubset(this)) 1289 return true; 1290 } 1291 1292 return false; 1293 } 1294 1295 1296 /*! \brief Returns the windows that's in behind of the backmost position 1297 this window can get. 1298 Returns NULL is this window can be the backmost window. 1299 1300 \param workspace the workspace on which this check should be made. If 1301 the value is -1, the window's current workspace will be used. 1302 */ 1303 Window* 1304 Window::Backmost(Window* window, int32 workspace) 1305 { 1306 if (workspace == -1) 1307 workspace = fCurrentWorkspace; 1308 1309 ASSERT(workspace != -1); 1310 if (workspace == -1) 1311 return NULL; 1312 1313 // Desktop windows are always backmost 1314 if (fFeel == kDesktopWindowFeel) 1315 return NULL; 1316 1317 if (window == NULL) 1318 window = PreviousWindow(workspace); 1319 1320 for (; window != NULL; window = window->PreviousWindow(workspace)) { 1321 if (window->IsHidden() || window == this) 1322 continue; 1323 1324 if (HasInSubset(window)) 1325 return window; 1326 } 1327 1328 return NULL; 1329 } 1330 1331 1332 /*! \brief Returns the window that's in front of the frontmost position 1333 this window can get. 1334 Returns NULL if this window can be the frontmost window. 1335 1336 \param workspace the workspace on which this check should be made. If 1337 the value is -1, the window's current workspace will be used. 1338 */ 1339 Window* 1340 Window::Frontmost(Window* first, int32 workspace) 1341 { 1342 if (workspace == -1) 1343 workspace = fCurrentWorkspace; 1344 1345 ASSERT(workspace != -1); 1346 if (workspace == -1) 1347 return NULL; 1348 1349 if (fFeel == kDesktopWindowFeel) 1350 return first ? first : NextWindow(workspace); 1351 1352 if (first == NULL) 1353 first = NextWindow(workspace); 1354 1355 for (Window* window = first; window != NULL; 1356 window = window->NextWindow(workspace)) { 1357 if (window->IsHidden() || window == this) 1358 continue; 1359 1360 if (window->HasInSubset(this)) 1361 return window; 1362 } 1363 1364 return NULL; 1365 } 1366 1367 1368 bool 1369 Window::AddToSubset(Window* window) 1370 { 1371 return fSubsets.AddItem(window); 1372 } 1373 1374 1375 void 1376 Window::RemoveFromSubset(Window* window) 1377 { 1378 fSubsets.RemoveItem(window); 1379 } 1380 1381 1382 /*! Returns whether or not a window is in the subset of this window. 1383 If a window is in the subset of this window, it means it should always 1384 appear behind this window. 1385 */ 1386 bool 1387 Window::HasInSubset(const Window* window) const 1388 { 1389 if (window == NULL || fFeel == window->Feel() 1390 || fFeel == B_NORMAL_WINDOW_FEEL) 1391 return false; 1392 1393 // Menus are a special case: they will always be on-top of every window 1394 // of their application 1395 if (fFeel == kMenuWindowFeel) 1396 return window->ServerWindow()->App() == ServerWindow()->App(); 1397 if (window->Feel() == kMenuWindowFeel) 1398 return false; 1399 1400 // we have a few special feels that have a fixed order 1401 1402 const int32 kFeels[] = {kPasswordWindowFeel, kWindowScreenFeel, 1403 B_MODAL_ALL_WINDOW_FEEL, B_FLOATING_ALL_WINDOW_FEEL}; 1404 1405 for (uint32 order = 0; 1406 order < sizeof(kFeels) / sizeof(kFeels[0]); order++) { 1407 if (fFeel == kFeels[order]) 1408 return true; 1409 if (window->Feel() == kFeels[order]) 1410 return false; 1411 } 1412 1413 if ((fFeel == B_FLOATING_APP_WINDOW_FEEL 1414 && window->Feel() != B_MODAL_APP_WINDOW_FEEL) 1415 || fFeel == B_MODAL_APP_WINDOW_FEEL) 1416 return window->ServerWindow()->App() == ServerWindow()->App(); 1417 1418 return fSubsets.HasItem(window); 1419 } 1420 1421 1422 /*! \brief Collects all workspaces views in this window and puts it into \a list 1423 */ 1424 void 1425 Window::FindWorkspacesViews(BObjectList<WorkspacesView>& list) const 1426 { 1427 int32 count = fWorkspacesViewCount; 1428 fTopView->FindViews(kWorkspacesViewFlag, (BObjectList<View>&)list, count); 1429 } 1430 1431 1432 /*! \brief Returns on which workspaces the window should be visible. 1433 1434 A modal or floating window may be visible on a workspace if one 1435 of its subset windows is visible there. Floating windows also need 1436 to have a subset as front window to be visible. 1437 */ 1438 uint32 1439 Window::SubsetWorkspaces() const 1440 { 1441 if (fFeel == B_MODAL_ALL_WINDOW_FEEL 1442 || fFeel == B_FLOATING_ALL_WINDOW_FEEL) 1443 return B_ALL_WORKSPACES; 1444 1445 if (fFeel == B_FLOATING_APP_WINDOW_FEEL) { 1446 Window* front = fDesktop->FrontWindow(); 1447 if (front != NULL && front->IsNormal() 1448 && front->ServerWindow()->App() == ServerWindow()->App()) 1449 return ServerWindow()->App()->Workspaces(); 1450 1451 return 0; 1452 } 1453 1454 if (fFeel == B_MODAL_APP_WINDOW_FEEL) { 1455 uint32 workspaces = ServerWindow()->App()->Workspaces(); 1456 if (workspaces == 0) { 1457 // The application doesn't seem to have any other windows 1458 // open or visible - but we'd like to see modal windows 1459 // anyway, at least when they are first opened. 1460 return 1UL << fDesktop->CurrentWorkspace(); 1461 } 1462 return workspaces; 1463 } 1464 1465 if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL 1466 || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) { 1467 uint32 workspaces = 0; 1468 bool hasNormalFront = false; 1469 for (int32 i = 0; i < fSubsets.CountItems(); i++) { 1470 Window* window = fSubsets.ItemAt(i); 1471 1472 if (!window->IsHidden()) 1473 workspaces |= window->Workspaces(); 1474 if (window == fDesktop->FrontWindow() && window->IsNormal()) 1475 hasNormalFront = true; 1476 } 1477 1478 if (fFeel == B_FLOATING_SUBSET_WINDOW_FEEL && !hasNormalFront) 1479 return 0; 1480 1481 return workspaces; 1482 } 1483 1484 return 0; 1485 } 1486 1487 1488 /*! Returns wether or not a window is in the subset workspace list with the 1489 specified \a index. 1490 See SubsetWorkspaces(). 1491 */ 1492 bool 1493 Window::InSubsetWorkspace(int32 index) const 1494 { 1495 return (SubsetWorkspaces() & (1UL << index)) != 0; 1496 } 1497 1498 1499 // #pragma mark - static 1500 1501 1502 /*static*/ bool 1503 Window::IsValidLook(window_look look) 1504 { 1505 return look == B_TITLED_WINDOW_LOOK 1506 || look == B_DOCUMENT_WINDOW_LOOK 1507 || look == B_MODAL_WINDOW_LOOK 1508 || look == B_FLOATING_WINDOW_LOOK 1509 || look == B_BORDERED_WINDOW_LOOK 1510 || look == B_NO_BORDER_WINDOW_LOOK 1511 || look == kDesktopWindowLook 1512 || look == kLeftTitledWindowLook; 1513 } 1514 1515 1516 /*static*/ bool 1517 Window::IsValidFeel(window_feel feel) 1518 { 1519 return feel == B_NORMAL_WINDOW_FEEL 1520 || feel == B_MODAL_SUBSET_WINDOW_FEEL 1521 || feel == B_MODAL_APP_WINDOW_FEEL 1522 || feel == B_MODAL_ALL_WINDOW_FEEL 1523 || feel == B_FLOATING_SUBSET_WINDOW_FEEL 1524 || feel == B_FLOATING_APP_WINDOW_FEEL 1525 || feel == B_FLOATING_ALL_WINDOW_FEEL 1526 || feel == kDesktopWindowFeel 1527 || feel == kMenuWindowFeel 1528 || feel == kWindowScreenFeel 1529 || feel == kPasswordWindowFeel; 1530 } 1531 1532 1533 /*static*/ bool 1534 Window::IsModalFeel(window_feel feel) 1535 { 1536 return feel == B_MODAL_SUBSET_WINDOW_FEEL 1537 || feel == B_MODAL_APP_WINDOW_FEEL 1538 || feel == B_MODAL_ALL_WINDOW_FEEL; 1539 } 1540 1541 1542 /*static*/ bool 1543 Window::IsFloatingFeel(window_feel feel) 1544 { 1545 return feel == B_FLOATING_SUBSET_WINDOW_FEEL 1546 || feel == B_FLOATING_APP_WINDOW_FEEL 1547 || feel == B_FLOATING_ALL_WINDOW_FEEL; 1548 } 1549 1550 1551 /*static*/ uint32 1552 Window::ValidWindowFlags() 1553 { 1554 return B_NOT_MOVABLE 1555 | B_NOT_CLOSABLE 1556 | B_NOT_ZOOMABLE 1557 | B_NOT_MINIMIZABLE 1558 | B_NOT_RESIZABLE 1559 | B_NOT_H_RESIZABLE 1560 | B_NOT_V_RESIZABLE 1561 | B_AVOID_FRONT 1562 | B_AVOID_FOCUS 1563 | B_WILL_ACCEPT_FIRST_CLICK 1564 | B_OUTLINE_RESIZE 1565 | B_NO_WORKSPACE_ACTIVATION 1566 | B_NOT_ANCHORED_ON_ACTIVATE 1567 | B_ASYNCHRONOUS_CONTROLS 1568 | B_QUIT_ON_WINDOW_CLOSE 1569 | B_SAME_POSITION_IN_ALL_WORKSPACES 1570 | B_AUTO_UPDATE_SIZE_LIMITS 1571 | B_CLOSE_ON_ESCAPE 1572 | B_NO_SERVER_SIDE_WINDOW_MODIFIERS 1573 | kWindowScreenFlag 1574 | kAcceptKeyboardFocusFlag; 1575 } 1576 1577 1578 /*static*/ uint32 1579 Window::ValidWindowFlags(window_feel feel) 1580 { 1581 uint32 flags = ValidWindowFlags(); 1582 if (IsModalFeel(feel)) 1583 return flags & ~(B_AVOID_FOCUS | B_AVOID_FRONT); 1584 1585 return flags; 1586 } 1587 1588 1589 // #pragma mark - private 1590 1591 1592 void 1593 Window::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift, 1594 int32 xOffset, int32 yOffset) 1595 { 1596 BRegion* common = fRegionPool.GetRegion(*regionToShift); 1597 if (!common) 1598 return; 1599 // see if there is a common part at all 1600 common->IntersectWith(region); 1601 if (common->CountRects() > 0) { 1602 // cut the common part from the region, 1603 // offset that to destination and include again 1604 region->Exclude(common); 1605 common->OffsetBy(xOffset, yOffset); 1606 region->Include(common); 1607 } 1608 fRegionPool.Recycle(common); 1609 } 1610 1611 1612 void 1613 Window::_TriggerContentRedraw(BRegion& dirtyContentRegion) 1614 { 1615 if (!IsVisible() || dirtyContentRegion.CountRects() == 0 1616 || (fFlags & kWindowScreenFlag) != 0) 1617 return; 1618 1619 // put this into the pending dirty region 1620 // to eventually trigger a client redraw 1621 bool wasExpose = fPendingUpdateSession->IsExpose(); 1622 BRegion* backgroundClearingRegion = &dirtyContentRegion; 1623 1624 _TransferToUpdateSession(&dirtyContentRegion); 1625 1626 if (fPendingUpdateSession->IsExpose()) { 1627 if (!fContentRegionValid) 1628 _UpdateContentRegion(); 1629 1630 if (!wasExpose) { 1631 // there was suddenly added a dirty region 1632 // caused by exposing content, we need to clear 1633 // the entire background 1634 backgroundClearingRegion = &fPendingUpdateSession->DirtyRegion(); 1635 } 1636 1637 if (fDrawingEngine->LockParallelAccess()) { 1638 bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled(); 1639 fDrawingEngine->SetCopyToFrontEnabled(true); 1640 fDrawingEngine->SuspendAutoSync(); 1641 1642 //sCurrentColor.red = rand() % 255; 1643 //sCurrentColor.green = rand() % 255; 1644 //sCurrentColor.blue = rand() % 255; 1645 //sPendingColor.red = rand() % 255; 1646 //sPendingColor.green = rand() % 255; 1647 //sPendingColor.blue = rand() % 255; 1648 //fDrawingEngine->FillRegion(*backgroundClearingRegion, sCurrentColor); 1649 //snooze(10000); 1650 1651 fTopView->Draw(fDrawingEngine, backgroundClearingRegion, 1652 &fContentRegion, true); 1653 1654 fDrawingEngine->Sync(); 1655 fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled); 1656 fDrawingEngine->UnlockParallelAccess(); 1657 } 1658 } 1659 } 1660 1661 1662 void 1663 Window::_DrawBorder() 1664 { 1665 // this is executed in the window thread, but only 1666 // in respond to a REDRAW message having been received, the 1667 // clipping lock is held for reading 1668 1669 if (!fDecorator) 1670 return; 1671 1672 // construct the region of the border that needs redrawing 1673 BRegion* dirtyBorderRegion = fRegionPool.GetRegion(); 1674 if (!dirtyBorderRegion) 1675 return; 1676 GetBorderRegion(dirtyBorderRegion); 1677 // intersect with our visible region 1678 dirtyBorderRegion->IntersectWith(&fVisibleRegion); 1679 // intersect with the dirty region 1680 dirtyBorderRegion->IntersectWith(&fDirtyRegion); 1681 1682 DrawingEngine* engine = fDecorator->GetDrawingEngine(); 1683 if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) { 1684 engine->ConstrainClippingRegion(dirtyBorderRegion); 1685 bool copyToFrontEnabled = engine->CopyToFrontEnabled(); 1686 engine->SetCopyToFrontEnabled(true); 1687 1688 fDecorator->Draw(dirtyBorderRegion->Frame()); 1689 1690 engine->SetCopyToFrontEnabled(copyToFrontEnabled); 1691 1692 // TODO: remove this once the DrawState stuff is handled 1693 // more cleanly. The reason why this is needed is that 1694 // when the decorator draws strings, a draw state is set 1695 // on the Painter object, and this is were it might get 1696 // out of sync with what the ServerWindow things is the 1697 // current DrawState set on the Painter 1698 fWindow->ResyncDrawState(); 1699 1700 engine->UnlockParallelAccess(); 1701 } 1702 fRegionPool.Recycle(dirtyBorderRegion); 1703 } 1704 1705 1706 /*! pre: the clipping is readlocked (this function is 1707 only called from _TriggerContentRedraw()), which 1708 in turn is only called from MessageReceived() with 1709 the clipping lock held 1710 */ 1711 void 1712 Window::_TransferToUpdateSession(BRegion* contentDirtyRegion) 1713 { 1714 if (contentDirtyRegion->CountRects() <= 0) 1715 return; 1716 1717 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor); 1718 //snooze(20000); 1719 1720 // add to pending 1721 fPendingUpdateSession->SetUsed(true); 1722 // if (!fPendingUpdateSession->IsExpose()) 1723 fPendingUpdateSession->AddCause(fDirtyCause); 1724 fPendingUpdateSession->Include(contentDirtyRegion); 1725 1726 if (!fUpdateRequested) { 1727 // send this to client 1728 _SendUpdateMessage(); 1729 // the pending region is now the current, 1730 // though the update does not start until 1731 // we received BEGIN_UPDATE from the client 1732 } 1733 } 1734 1735 1736 void 1737 Window::_SendUpdateMessage() 1738 { 1739 if (!fUpdatesEnabled) 1740 return; 1741 1742 BMessage message(_UPDATE_); 1743 if (ServerWindow()->SendMessageToClient(&message) != B_OK) { 1744 // If sending the message failed, we'll just keep adding to the dirty 1745 // region until sending was successful. 1746 // TODO: we might want to automatically resend this message in this case 1747 return; 1748 } 1749 1750 fUpdateRequested = true; 1751 fEffectiveDrawingRegionValid = false; 1752 } 1753 1754 1755 void 1756 Window::BeginUpdate(BPrivate::PortLink& link) 1757 { 1758 // NOTE: since we might "shift" parts of the 1759 // internal dirty regions from the desktop thread 1760 // in response to Window::ResizeBy(), which 1761 // might move arround views, the user of this function 1762 // needs to hold the global clipping lock so that the internal 1763 // dirty regions are not messed with from the Desktop thread 1764 // and ServerWindow thread at the same time. 1765 1766 if (!fUpdateRequested) { 1767 link.StartMessage(B_ERROR); 1768 link.Flush(); 1769 fprintf(stderr, "Window::BeginUpdate() - no update requested!\n"); 1770 return; 1771 } 1772 1773 // make the pending update session the current update session 1774 // (toggle the pointers) 1775 UpdateSession* temp = fCurrentUpdateSession; 1776 fCurrentUpdateSession = fPendingUpdateSession; 1777 fPendingUpdateSession = temp; 1778 fPendingUpdateSession->SetUsed(false); 1779 // all drawing command from the client 1780 // will have the dirty region from the update 1781 // session enforced 1782 fInUpdate = true; 1783 fEffectiveDrawingRegionValid = false; 1784 1785 // TODO: each view could be drawn individually 1786 // right before carrying out the first drawing 1787 // command from the client during an update 1788 // (View::IsBackgroundDirty() can be used 1789 // for this) 1790 if (!fContentRegionValid) 1791 _UpdateContentRegion(); 1792 1793 BRegion* dirty = fRegionPool.GetRegion( 1794 fCurrentUpdateSession->DirtyRegion()); 1795 if (!dirty) { 1796 link.StartMessage(B_ERROR); 1797 link.Flush(); 1798 return; 1799 } 1800 1801 dirty->IntersectWith(&VisibleContentRegion()); 1802 1803 //if (!fCurrentUpdateSession->IsExpose()) { 1804 ////sCurrentColor.red = rand() % 255; 1805 ////sCurrentColor.green = rand() % 255; 1806 ////sCurrentColor.blue = rand() % 255; 1807 ////sPendingColor.red = rand() % 255; 1808 ////sPendingColor.green = rand() % 255; 1809 ////sPendingColor.blue = rand() % 255; 1810 //fDrawingEngine->FillRegion(*dirty, sCurrentColor); 1811 //snooze(10000); 1812 //} 1813 1814 link.StartMessage(B_OK); 1815 // append the current window geometry to the 1816 // message, the client will need it 1817 link.Attach<BPoint>(fFrame.LeftTop()); 1818 link.Attach<float>(fFrame.Width()); 1819 link.Attach<float>(fFrame.Height()); 1820 // find and attach all views that intersect with 1821 // the dirty region 1822 fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion); 1823 // mark the end of the token "list" 1824 link.Attach<int32>(B_NULL_TOKEN); 1825 link.Flush(); 1826 1827 // supress back to front buffer copies in the drawing engine 1828 fDrawingEngine->SetCopyToFrontEnabled(false); 1829 1830 if (!fCurrentUpdateSession->IsExpose() 1831 && fDrawingEngine->LockParallelAccess()) { 1832 fDrawingEngine->SuspendAutoSync(); 1833 1834 fTopView->Draw(fDrawingEngine, dirty, &fContentRegion, true); 1835 1836 fDrawingEngine->Sync(); 1837 fDrawingEngine->UnlockParallelAccess(); 1838 } // else the background was cleared already 1839 1840 fRegionPool.Recycle(dirty); 1841 } 1842 1843 1844 void 1845 Window::EndUpdate() 1846 { 1847 // NOTE: see comment in _BeginUpdate() 1848 1849 if (fInUpdate) { 1850 // reenable copy to front 1851 fDrawingEngine->SetCopyToFrontEnabled(true); 1852 1853 BRegion* dirty = fRegionPool.GetRegion( 1854 fCurrentUpdateSession->DirtyRegion()); 1855 1856 if (dirty) { 1857 dirty->IntersectWith(&VisibleContentRegion()); 1858 1859 fDrawingEngine->CopyToFront(*dirty); 1860 fRegionPool.Recycle(dirty); 1861 } 1862 1863 fCurrentUpdateSession->SetUsed(false); 1864 1865 fInUpdate = false; 1866 fEffectiveDrawingRegionValid = false; 1867 } 1868 if (fPendingUpdateSession->IsUsed()) { 1869 // send this to client 1870 _SendUpdateMessage(); 1871 } else { 1872 fUpdateRequested = false; 1873 } 1874 } 1875 1876 1877 void 1878 Window::_UpdateContentRegion() 1879 { 1880 fContentRegion.Set(fFrame); 1881 1882 // resize handle 1883 if (fDecorator) 1884 fContentRegion.Exclude(&fDecorator->GetFootprint()); 1885 1886 fContentRegionValid = true; 1887 } 1888 1889 1890 void 1891 Window::_ObeySizeLimits() 1892 { 1893 // make sure we even have valid size limits 1894 if (fMaxWidth < fMinWidth) 1895 fMaxWidth = fMinWidth; 1896 1897 if (fMaxHeight < fMinHeight) 1898 fMaxHeight = fMinHeight; 1899 1900 // Automatically resize the window to fit these new limits 1901 // if it does not already. 1902 1903 // On R5, Windows don't automatically resize, but since 1904 // BWindow::ResizeTo() even honors the limits, I would guess 1905 // this is a bug that we don't have to adopt. 1906 // Note that most current apps will do unnecessary resizing 1907 // after having set the limits, but the overhead is neglible. 1908 1909 float minWidthDiff = fMinWidth - fFrame.Width(); 1910 float minHeightDiff = fMinHeight - fFrame.Height(); 1911 float maxWidthDiff = fMaxWidth - fFrame.Width(); 1912 float maxHeightDiff = fMaxHeight - fFrame.Height(); 1913 1914 float xDiff = 0.0; 1915 if (minWidthDiff > 0.0) // we're currently smaller than minWidth 1916 xDiff = minWidthDiff; 1917 else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth 1918 xDiff = maxWidthDiff; 1919 1920 float yDiff = 0.0; 1921 if (minHeightDiff > 0.0) // we're currently smaller than minHeight 1922 yDiff = minHeightDiff; 1923 else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight 1924 yDiff = maxHeightDiff; 1925 1926 if (fDesktop) 1927 fDesktop->ResizeWindowBy(this, xDiff, yDiff); 1928 else 1929 ResizeBy((int32)xDiff, (int32)yDiff, NULL); 1930 } 1931 1932 1933 // #pragma mark - UpdateSession 1934 1935 1936 Window::UpdateSession::UpdateSession() 1937 : 1938 fDirtyRegion(), 1939 fInUse(false), 1940 fCause(0) 1941 { 1942 } 1943 1944 1945 Window::UpdateSession::~UpdateSession() 1946 { 1947 } 1948 1949 1950 void 1951 Window::UpdateSession::Include(BRegion* additionalDirty) 1952 { 1953 fDirtyRegion.Include(additionalDirty); 1954 } 1955 1956 1957 void 1958 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession) 1959 { 1960 fDirtyRegion.Exclude(dirtyInNextSession); 1961 } 1962 1963 1964 void 1965 Window::UpdateSession::MoveBy(int32 x, int32 y) 1966 { 1967 fDirtyRegion.OffsetBy(x, y); 1968 } 1969 1970 1971 void 1972 Window::UpdateSession::SetUsed(bool used) 1973 { 1974 fInUse = used; 1975 if (!fInUse) { 1976 fDirtyRegion.MakeEmpty(); 1977 fCause = 0; 1978 } 1979 } 1980 1981 1982 void 1983 Window::UpdateSession::AddCause(uint8 cause) 1984 { 1985 fCause |= cause; 1986 } 1987