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