Make UEFI boot-platform build again
[haiku.git] / src / servers / app / Window.cpp
blob40e95b1896eefbc1b5b76e6d397c1c5029b933f7
1 /*
2 * Copyright 2001-2011, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
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>
15 #include "Window.h"
17 #include <new>
18 #include <stdio.h>
20 #include <Debug.h>
22 #include <DirectWindow.h>
23 #include <PortLink.h>
24 #include <View.h>
25 #include <ViewPrivate.h>
26 #include <WindowPrivate.h>
28 #include "ClickTarget.h"
29 #include "Decorator.h"
30 #include "DecorManager.h"
31 #include "Desktop.h"
32 #include "DrawingEngine.h"
33 #include "HWInterface.h"
34 #include "MessagePrivate.h"
35 #include "PortLink.h"
36 #include "ServerApp.h"
37 #include "ServerWindow.h"
38 #include "WindowBehaviour.h"
39 #include "Workspace.h"
40 #include "WorkspacesView.h"
43 // Toggle debug output
44 //#define DEBUG_WINDOW
46 #ifdef DEBUG_WINDOW
47 # define STRACE(x) printf x
48 #else
49 # define STRACE(x) ;
50 #endif
52 // IMPORTANT: nested LockSingleWindow()s are not supported (by MultiLocker)
54 using std::nothrow;
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
67 //static rgb_color sPendingColor = (rgb_color){ 255, 255, 0, 255 };
68 //static rgb_color sCurrentColor = (rgb_color){ 255, 0, 255, 255 };
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)
75 fTitle(name),
76 fFrame(frame),
77 fScreen(NULL),
79 fVisibleRegion(),
80 fVisibleContentRegion(),
81 fDirtyRegion(),
82 fDirtyCause(0),
84 fContentRegion(),
85 fEffectiveDrawingRegion(),
87 fVisibleContentRegionValid(false),
88 fContentRegionValid(false),
89 fEffectiveDrawingRegionValid(false),
91 fRegionPool(),
93 fWindowBehaviour(NULL),
94 fTopView(NULL),
95 fWindow(window),
96 fDrawingEngine(drawingEngine),
97 fDesktop(window->Desktop()),
99 fCurrentUpdateSession(&fUpdateSessions[0]),
100 fPendingUpdateSession(&fUpdateSessions[1]),
101 fUpdateRequested(false),
102 fInUpdate(false),
103 fUpdatesEnabled(true),
105 // Windows start hidden
106 fHidden(true),
107 // Hidden is 1 or more
108 fShowLevel(1),
109 fMinimized(false),
110 fIsFocus(false),
112 fLook(look),
113 fFeel(feel),
114 fWorkspaces(workspaces),
115 fCurrentWorkspace(-1),
117 fMinWidth(1),
118 fMaxWidth(32768),
119 fMinHeight(1),
120 fMaxHeight(32768),
122 fWorkspacesViewCount(0)
124 _InitWindowStack();
126 // make sure our arguments are valid
127 if (!IsValidLook(fLook))
128 fLook = B_TITLED_WINDOW_LOOK;
129 if (!IsValidFeel(fFeel))
130 fFeel = B_NORMAL_WINDOW_FEEL;
132 SetFlags(flags, NULL);
134 if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.Get() != NULL) {
135 // allocates a decorator
136 ::Decorator* decorator = Decorator();
137 if (decorator != NULL) {
138 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
139 &fMaxHeight);
142 if (fFeel != kOffscreenWindowFeel)
143 fWindowBehaviour = gDecorManager.AllocateWindowBehaviour(this);
145 // do we need to change our size to let the decorator fit?
146 // _ResizeBy() will adapt the frame for validity before resizing
147 if (feel == kDesktopWindowFeel) {
148 // the desktop window spans over the whole screen
149 // TODO: this functionality should be moved somewhere else
150 // (so that it is always used when the workspace is changed)
151 uint16 width, height;
152 uint32 colorSpace;
153 float frequency;
154 if (Screen() != NULL) {
155 Screen()->GetMode(width, height, colorSpace, frequency);
156 // TODO: MOVE THIS AWAY!!! ResizeBy contains calls to virtual methods!
157 // Also, there is no TopView()!
158 fFrame.OffsetTo(B_ORIGIN);
159 // ResizeBy(width - frame.Width(), height - frame.Height(), NULL);
163 STRACE(("Window %p, %s:\n", this, Name()));
164 STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top,
165 fFrame.right, fFrame.bottom));
166 STRACE(("\tWindow %s\n", window ? window->Title() : "NULL"));
170 Window::~Window()
172 if (fTopView) {
173 fTopView->DetachedFromWindow();
174 delete fTopView;
177 DetachFromWindowStack(false);
179 delete fWindowBehaviour;
180 delete fDrawingEngine;
182 gDecorManager.CleanupForWindow(this);
186 status_t
187 Window::InitCheck() const
189 if (fDrawingEngine == NULL
190 || (fFeel != kOffscreenWindowFeel && fWindowBehaviour == NULL))
191 return B_NO_MEMORY;
192 // TODO: anything else?
193 return B_OK;
197 void
198 Window::SetClipping(BRegion* stillAvailableOnScreen)
200 // this function is only called from the Desktop thread
202 // start from full region (as if the window was fully visible)
203 GetFullRegion(&fVisibleRegion);
204 // clip to region still available on screen
205 fVisibleRegion.IntersectWith(stillAvailableOnScreen);
207 fVisibleContentRegionValid = false;
208 fEffectiveDrawingRegionValid = false;
212 void
213 Window::GetFullRegion(BRegion* region)
215 // TODO: if someone needs to call this from
216 // the outside, the clipping needs to be readlocked!
218 // start from the decorator border, extend to use the frame
219 GetBorderRegion(region);
220 region->Include(fFrame);
224 void
225 Window::GetBorderRegion(BRegion* region)
227 // TODO: if someone needs to call this from
228 // the outside, the clipping needs to be readlocked!
230 ::Decorator* decorator = Decorator();
231 if (decorator)
232 *region = decorator->GetFootprint();
233 else
234 region->MakeEmpty();
238 void
239 Window::GetContentRegion(BRegion* region)
241 // TODO: if someone needs to call this from
242 // the outside, the clipping needs to be readlocked!
244 if (!fContentRegionValid) {
245 _UpdateContentRegion();
248 *region = fContentRegion;
252 BRegion&
253 Window::VisibleContentRegion()
255 // TODO: if someone needs to call this from
256 // the outside, the clipping needs to be readlocked!
258 // regions expected to be locked
259 if (!fVisibleContentRegionValid) {
260 GetContentRegion(&fVisibleContentRegion);
261 fVisibleContentRegion.IntersectWith(&fVisibleRegion);
263 return fVisibleContentRegion;
267 // #pragma mark -
270 void
271 Window::_PropagatePosition()
273 if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) == 0)
274 return;
276 for (int32 i = 0; i < kListCount; i++) {
277 Anchor(i).position = fFrame.LeftTop();
282 void
283 Window::MoveBy(int32 x, int32 y, bool moveStack)
285 // this function is only called from the desktop thread
287 if (x == 0 && y == 0)
288 return;
290 fFrame.OffsetBy(x, y);
291 _PropagatePosition();
293 // take along the dirty region which is not
294 // processed yet
295 fDirtyRegion.OffsetBy(x, y);
297 if (fContentRegionValid)
298 fContentRegion.OffsetBy(x, y);
300 if (fCurrentUpdateSession->IsUsed())
301 fCurrentUpdateSession->MoveBy(x, y);
302 if (fPendingUpdateSession->IsUsed())
303 fPendingUpdateSession->MoveBy(x, y);
305 fEffectiveDrawingRegionValid = false;
307 if (fTopView != NULL) {
308 fTopView->MoveBy(x, y, NULL);
309 fTopView->UpdateOverlay();
312 ::Decorator* decorator = Decorator();
313 if (moveStack && decorator)
314 decorator->MoveBy(x, y);
316 WindowStack* stack = GetWindowStack();
317 if (moveStack && stack) {
318 for (int32 i = 0; i < stack->CountWindows(); i++) {
319 Window* window = stack->WindowList().ItemAt(i);
320 if (window == this)
321 continue;
322 window->MoveBy(x, y, false);
326 // the desktop will take care of dirty regions
328 // dispatch a message to the client informing about the changed size
329 BMessage msg(B_WINDOW_MOVED);
330 msg.AddInt64("when", system_time());
331 msg.AddPoint("where", fFrame.LeftTop());
332 fWindow->SendMessageToClient(&msg);
336 void
337 Window::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion, bool resizeStack)
339 // this function is only called from the desktop thread
341 int32 wantWidth = fFrame.IntegerWidth() + x;
342 int32 wantHeight = fFrame.IntegerHeight() + y;
344 // enforce size limits
345 WindowStack* stack = GetWindowStack();
346 if (resizeStack && stack) {
347 for (int32 i = 0; i < stack->CountWindows(); i++) {
348 Window* window = stack->WindowList().ItemAt(i);
350 if (wantWidth < window->fMinWidth)
351 wantWidth = window->fMinWidth;
352 if (wantWidth > window->fMaxWidth)
353 wantWidth = window->fMaxWidth;
355 if (wantHeight < window->fMinHeight)
356 wantHeight = window->fMinHeight;
357 if (wantHeight > window->fMaxHeight)
358 wantHeight = window->fMaxHeight;
362 x = wantWidth - fFrame.IntegerWidth();
363 y = wantHeight - fFrame.IntegerHeight();
365 if (x == 0 && y == 0)
366 return;
368 fFrame.right += x;
369 fFrame.bottom += y;
371 fContentRegionValid = false;
372 fEffectiveDrawingRegionValid = false;
374 if (fTopView != NULL) {
375 fTopView->ResizeBy(x, y, dirtyRegion);
376 fTopView->UpdateOverlay();
379 ::Decorator* decorator = Decorator();
380 if (decorator && resizeStack)
381 decorator->ResizeBy(x, y, dirtyRegion);
383 if (resizeStack && stack) {
384 for (int32 i = 0; i < stack->CountWindows(); i++) {
385 Window* window = stack->WindowList().ItemAt(i);
386 if (window == this)
387 continue;
388 window->ResizeBy(x, y, dirtyRegion, false);
392 // send a message to the client informing about the changed size
393 BRect frame(Frame());
394 BMessage msg(B_WINDOW_RESIZED);
395 msg.AddInt64("when", system_time());
396 msg.AddInt32("width", frame.IntegerWidth());
397 msg.AddInt32("height", frame.IntegerHeight());
398 fWindow->SendMessageToClient(&msg);
402 void
403 Window::ScrollViewBy(View* view, int32 dx, int32 dy)
405 // this is executed in ServerWindow with the Readlock
406 // held
408 if (!view || view == fTopView || (dx == 0 && dy == 0))
409 return;
411 BRegion* dirty = fRegionPool.GetRegion();
412 if (!dirty)
413 return;
415 view->ScrollBy(dx, dy, dirty);
417 //fDrawingEngine->FillRegion(*dirty, (rgb_color){ 255, 0, 255, 255 });
418 //snooze(20000);
420 if (!IsOffscreenWindow() && IsVisible() && view->IsVisible()) {
421 dirty->IntersectWith(&VisibleContentRegion());
422 _TriggerContentRedraw(*dirty);
425 fRegionPool.Recycle(dirty);
429 //! Takes care of invalidating parts that could not be copied
430 void
431 Window::CopyContents(BRegion* region, int32 xOffset, int32 yOffset)
433 // executed in ServerWindow thread with the read lock held
434 if (!IsVisible())
435 return;
437 BRegion* newDirty = fRegionPool.GetRegion(*region);
439 // clip the region to the visible contents at the
440 // source and destination location (note that VisibleContentRegion()
441 // is used once to make sure it is valid, then fVisibleContentRegion
442 // is used directly)
443 region->IntersectWith(&VisibleContentRegion());
444 if (region->CountRects() > 0) {
445 // Constrain to content region at destination
446 region->OffsetBy(xOffset, yOffset);
447 region->IntersectWith(&fVisibleContentRegion);
448 if (region->CountRects() > 0) {
449 // if the region still contains any rects
450 // offset to source location again
451 region->OffsetBy(-xOffset, -yOffset);
453 BRegion* allDirtyRegions = fRegionPool.GetRegion(fDirtyRegion);
454 if (allDirtyRegions != NULL) {
455 if (fPendingUpdateSession->IsUsed()) {
456 allDirtyRegions->Include(
457 &fPendingUpdateSession->DirtyRegion());
459 if (fCurrentUpdateSession->IsUsed()) {
460 allDirtyRegions->Include(
461 &fCurrentUpdateSession->DirtyRegion());
463 // Get just the part of the dirty regions which is semantically
464 // copied along
465 allDirtyRegions->IntersectWith(region);
468 BRegion* copyRegion = fRegionPool.GetRegion(*region);
469 if (copyRegion != NULL) {
470 // never copy what's already dirty
471 if (allDirtyRegions != NULL)
472 copyRegion->Exclude(allDirtyRegions);
474 if (fDrawingEngine->LockParallelAccess()) {
475 fDrawingEngine->CopyRegion(copyRegion, xOffset, yOffset);
476 fDrawingEngine->UnlockParallelAccess();
478 // Prevent those parts from being added to the dirty region...
479 newDirty->Exclude(copyRegion);
481 // The parts that could be copied are not dirty (at the
482 // target location!)
483 copyRegion->OffsetBy(xOffset, yOffset);
484 // ... and even exclude them from the pending dirty region!
485 if (fPendingUpdateSession->IsUsed())
486 fPendingUpdateSession->DirtyRegion().Exclude(copyRegion);
489 fRegionPool.Recycle(copyRegion);
490 } else {
491 // Fallback, should never be here.
492 if (fDrawingEngine->LockParallelAccess()) {
493 fDrawingEngine->CopyRegion(region, xOffset, yOffset);
494 fDrawingEngine->UnlockParallelAccess();
498 if (allDirtyRegions != NULL)
499 fRegionPool.Recycle(allDirtyRegions);
502 // what is left visible from the original region
503 // at the destination after the region which could be
504 // copied has been excluded, is considered dirty
505 // NOTE: it may look like dirty regions are not moved
506 // if no region could be copied, but that's alright,
507 // since these parts will now be in newDirty anyways
508 // (with the right offset)
509 newDirty->OffsetBy(xOffset, yOffset);
510 newDirty->IntersectWith(&fVisibleContentRegion);
511 if (newDirty->CountRects() > 0)
512 ProcessDirtyRegion(*newDirty);
514 fRegionPool.Recycle(newDirty);
518 // #pragma mark -
521 void
522 Window::SetTopView(View* topView)
524 fTopView = topView;
526 if (fTopView) {
527 // the top view is special, it has a coordinate system
528 // as if it was attached directly to the desktop, therefor,
529 // the coordinate conversion through the view tree works
530 // as expected, since the top view has no "parent" but has
531 // fFrame as if it had
533 // make sure the location of the top view on screen matches ours
534 fTopView->MoveBy((int32)(fFrame.left - fTopView->Frame().left),
535 (int32)(fFrame.top - fTopView->Frame().top), NULL);
537 // make sure the size of the top view matches ours
538 fTopView->ResizeBy((int32)(fFrame.Width() - fTopView->Frame().Width()),
539 (int32)(fFrame.Height() - fTopView->Frame().Height()), NULL);
541 fTopView->AttachedToWindow(this);
546 View*
547 Window::ViewAt(const BPoint& where)
549 return fTopView->ViewAt(where);
553 window_anchor&
554 Window::Anchor(int32 index)
556 return fAnchor[index];
560 Window*
561 Window::NextWindow(int32 index) const
563 return fAnchor[index].next;
567 Window*
568 Window::PreviousWindow(int32 index) const
570 return fAnchor[index].previous;
574 ::Decorator*
575 Window::Decorator() const
577 if (fCurrentStack.Get() == NULL)
578 return NULL;
579 return fCurrentStack->Decorator();
583 bool
584 Window::ReloadDecor()
586 ::Decorator* decorator = NULL;
587 WindowBehaviour* windowBehaviour = NULL;
588 WindowStack* stack = GetWindowStack();
589 if (stack == NULL)
590 return false;
592 // only reload the window at the first position
593 if (stack->WindowAt(0) != this)
594 return true;
596 if (fLook != B_NO_BORDER_WINDOW_LOOK) {
597 // we need a new decorator
598 decorator = gDecorManager.AllocateDecorator(this);
599 if (decorator == NULL)
600 return false;
602 // add all tabs to the decorator
603 for (int32 i = 1; i < stack->CountWindows(); i++) {
604 Window* window = stack->WindowAt(i);
605 BRegion dirty;
606 DesktopSettings settings(fDesktop);
607 if (decorator->AddTab(settings, window->Title(), window->Look(),
608 window->Flags(), -1, &dirty) == NULL) {
609 delete decorator;
610 return false;
613 } else
614 return true;
616 windowBehaviour = gDecorManager.AllocateWindowBehaviour(this);
617 if (windowBehaviour == NULL) {
618 delete decorator;
619 return false;
622 stack->SetDecorator(decorator);
624 delete fWindowBehaviour;
625 fWindowBehaviour = windowBehaviour;
627 // set the correct focus and top layer tab
628 for (int32 i = 0; i < stack->CountWindows(); i++) {
629 Window* window = stack->WindowAt(i);
630 if (window->IsFocus())
631 decorator->SetFocus(i, true);
632 if (window == stack->TopLayerWindow())
633 decorator->SetTopTab(i);
636 return true;
640 void
641 Window::SetScreen(const ::Screen* screen)
643 // TODO this assert fails in Desktop::ShowWindow
644 //ASSERT_MULTI_WRITE_LOCKED(fDesktop->ScreenLocker());
645 fScreen = screen;
649 const ::Screen*
650 Window::Screen() const
652 // TODO this assert also fails
653 //ASSERT_MULTI_READ_LOCKED(fDesktop->ScreenLocker());
654 return fScreen;
658 // #pragma mark -
661 void
662 Window::GetEffectiveDrawingRegion(View* view, BRegion& region)
664 if (!fEffectiveDrawingRegionValid) {
665 fEffectiveDrawingRegion = VisibleContentRegion();
666 if (fUpdateRequested && !fInUpdate) {
667 // We requested an update, but the client has not started it yet,
668 // so it is only allowed to draw outside the pending update sessions
669 // region
670 fEffectiveDrawingRegion.Exclude(
671 &fPendingUpdateSession->DirtyRegion());
672 } else if (fInUpdate) {
673 // enforce the dirty region of the update session
674 fEffectiveDrawingRegion.IntersectWith(
675 &fCurrentUpdateSession->DirtyRegion());
676 } else {
677 // not in update, the view can draw everywhere
678 //printf("Window(%s)::GetEffectiveDrawingRegion(for %s) - outside update\n", Title(), view->Name());
681 fEffectiveDrawingRegionValid = true;
684 // TODO: this is a region that needs to be cached later in the server
685 // when the current view in ServerWindow is set, and we are currently
686 // in an update (fInUpdate), than we can set this region and remember
687 // it for the comming drawing commands until the current view changes
688 // again or fEffectiveDrawingRegionValid is suddenly false.
689 region = fEffectiveDrawingRegion;
690 if (!fContentRegionValid)
691 _UpdateContentRegion();
693 region.IntersectWith(&view->ScreenAndUserClipping(&fContentRegion));
697 bool
698 Window::DrawingRegionChanged(View* view) const
700 return !fEffectiveDrawingRegionValid || !view->IsScreenClippingValid();
704 void
705 Window::ProcessDirtyRegion(BRegion& region)
707 // if this is executed in the desktop thread,
708 // it means that the window thread currently
709 // blocks to get the read lock, if it is
710 // executed from the window thread, it should
711 // have the read lock and the desktop thread
712 // is blocking to get the write lock. IAW, this
713 // is only executed in one thread.
714 if (fDirtyRegion.CountRects() == 0) {
715 // the window needs to be informed
716 // when the dirty region was empty.
717 // NOTE: when the window thread has processed
718 // the dirty region in MessageReceived(),
719 // it will make the region empty again,
720 // when it is empty here, we need to send
721 // the message to initiate the next update round.
722 // Until the message is processed in the window
723 // thread, the desktop thread can add parts to
724 // the region as it likes.
725 ServerWindow()->RequestRedraw();
728 fDirtyRegion.Include(&region);
729 fDirtyCause |= UPDATE_EXPOSE;
733 void
734 Window::RedrawDirtyRegion()
736 if (TopLayerStackWindow() != this) {
737 fDirtyRegion.MakeEmpty();
738 fDirtyCause = 0;
739 return;
742 // executed from ServerWindow with the read lock held
743 if (IsVisible()) {
744 _DrawBorder();
746 BRegion* dirtyContentRegion =
747 fRegionPool.GetRegion(VisibleContentRegion());
748 dirtyContentRegion->IntersectWith(&fDirtyRegion);
750 _TriggerContentRedraw(*dirtyContentRegion);
752 fRegionPool.Recycle(dirtyContentRegion);
755 // reset the dirty region, since
756 // we're fully clean. If the desktop
757 // thread wanted to mark something
758 // dirty in the mean time, it was
759 // blocking on the global region lock to
760 // get write access, since we're holding
761 // the read lock for the whole time.
762 fDirtyRegion.MakeEmpty();
763 fDirtyCause = 0;
767 void
768 Window::MarkDirty(BRegion& regionOnScreen)
770 // for marking any part of the desktop dirty
771 // this will get write access to the global
772 // region lock, and result in ProcessDirtyRegion()
773 // to be called for any windows affected
774 if (fDesktop)
775 fDesktop->MarkDirty(regionOnScreen);
779 void
780 Window::MarkContentDirty(BRegion& regionOnScreen)
782 // for triggering AS_REDRAW
783 // since this won't affect other windows, read locking
784 // is sufficient. If there was no dirty region before,
785 // an update message is triggered
786 if (fHidden || IsOffscreenWindow())
787 return;
789 regionOnScreen.IntersectWith(&VisibleContentRegion());
790 fDirtyCause |= UPDATE_REQUEST;
791 _TriggerContentRedraw(regionOnScreen);
795 void
796 Window::MarkContentDirtyAsync(BRegion& regionOnScreen)
798 // NOTE: see comments in ProcessDirtyRegion()
799 if (fHidden || IsOffscreenWindow())
800 return;
802 regionOnScreen.IntersectWith(&VisibleContentRegion());
804 if (fDirtyRegion.CountRects() == 0) {
805 ServerWindow()->RequestRedraw();
808 fDirtyRegion.Include(&regionOnScreen);
809 fDirtyCause |= UPDATE_REQUEST;
813 void
814 Window::InvalidateView(View* view, BRegion& viewRegion)
816 if (view && IsVisible() && view->IsVisible()) {
817 if (!fContentRegionValid)
818 _UpdateContentRegion();
820 view->LocalToScreenTransform().Apply(&viewRegion);
821 viewRegion.IntersectWith(&VisibleContentRegion());
822 if (viewRegion.CountRects() > 0) {
823 viewRegion.IntersectWith(
824 &view->ScreenAndUserClipping(&fContentRegion));
826 //fDrawingEngine->FillRegion(viewRegion, rgb_color{ 0, 255, 0, 255 });
827 //snooze(10000);
828 fDirtyCause |= UPDATE_REQUEST;
829 _TriggerContentRedraw(viewRegion);
834 // DisableUpdateRequests
835 void
836 Window::DisableUpdateRequests()
838 fUpdatesEnabled = false;
842 // EnableUpdateRequests
843 void
844 Window::EnableUpdateRequests()
846 fUpdatesEnabled = true;
847 if (!fUpdateRequested && fPendingUpdateSession->IsUsed())
848 _SendUpdateMessage();
851 // #pragma mark -
854 /*! \brief Handles a mouse-down message for the window.
856 \param message The message.
857 \param where The point where the mouse click happened.
858 \param lastClickTarget The target of the previous click.
859 \param clickCount The number of subsequent, no longer than double-click
860 interval separated clicks that have happened so far. This number doesn't
861 necessarily match the value in the message. It has already been
862 pre-processed in order to avoid erroneous multi-clicks (e.g. when a
863 different button has been used or a different window was targeted). This
864 is an in-out variable. The method can reset the value to 1, if it
865 doesn't want this event handled as a multi-click. Returning a different
866 click target will also make the caller reset the click count.
867 \param _clickTarget Set by the method to a value identifying the clicked
868 element. If not explicitly set, an invalid click target is assumed.
870 void
871 Window::MouseDown(BMessage* message, BPoint where,
872 const ClickTarget& lastClickTarget, int32& clickCount,
873 ClickTarget& _clickTarget)
875 // If the previous click hit our decorator, get the hit region.
876 int32 windowToken = fWindow->ServerToken();
877 int32 lastHitRegion = 0;
878 if (lastClickTarget.GetType() == ClickTarget::TYPE_WINDOW_DECORATOR
879 && lastClickTarget.WindowToken() == windowToken) {
880 lastHitRegion = lastClickTarget.WindowElement();
883 // Let the window behavior process the mouse event.
884 int32 hitRegion = 0;
885 bool eventEaten = fWindowBehaviour->MouseDown(message, where, lastHitRegion,
886 clickCount, hitRegion);
888 if (eventEaten) {
889 // click on the decorator (or equivalent)
890 _clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_DECORATOR,
891 windowToken, (int32)hitRegion);
892 } else {
893 // click was inside the window contents
894 int32 viewToken = B_NULL_TOKEN;
895 if (View* view = ViewAt(where)) {
896 if (HasModal())
897 return;
899 // clicking a simple View
900 if (!IsFocus()) {
901 bool acceptFirstClick
902 = (Flags() & B_WILL_ACCEPT_FIRST_CLICK) != 0;
903 bool avoidFocus = (Flags() & B_AVOID_FOCUS) != 0;
905 // Activate or focus the window in case it doesn't accept first
906 // click, depending on the mouse mode
907 DesktopSettings desktopSettings(fDesktop);
908 if (desktopSettings.MouseMode() == B_NORMAL_MOUSE
909 && !acceptFirstClick)
910 fDesktop->ActivateWindow(this);
911 else if (!avoidFocus)
912 fDesktop->SetFocusWindow(this);
914 // Eat the click if we don't accept first click
915 // (B_AVOID_FOCUS never gets the focus, so they always accept
916 // the first click)
917 // TODO: the latter is unlike BeOS - if we really wanted to
918 // imitate this behaviour, we would need to check if we're
919 // the front window instead of the focus window
920 if (!acceptFirstClick && !desktopSettings.AcceptFirstClick()
921 && !avoidFocus)
922 return;
925 // fill out view token for the view under the mouse
926 viewToken = view->Token();
927 view->MouseDown(message, where);
930 _clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_CONTENTS,
931 windowToken, viewToken);
936 void
937 Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken)
939 fWindowBehaviour->MouseUp(message, where);
941 if (View* view = ViewAt(where)) {
942 if (HasModal())
943 return;
945 *_viewToken = view->Token();
946 view->MouseUp(message, where);
951 void
952 Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken,
953 bool isLatestMouseMoved, bool isFake)
955 View* view = ViewAt(where);
956 if (view != NULL)
957 *_viewToken = view->Token();
959 // ignore pointer history
960 if (!isLatestMouseMoved)
961 return;
963 fWindowBehaviour->MouseMoved(message, where, isFake);
965 // mouse cursor
967 if (view != NULL) {
968 view->MouseMoved(message, where);
970 // TODO: there is more for real cursor support, ie. if a window is closed,
971 // new app cursor shouldn't override view cursor, ...
972 ServerWindow()->App()->SetCurrentCursor(view->Cursor());
977 void
978 Window::ModifiersChanged(int32 modifiers)
980 fWindowBehaviour->ModifiersChanged(modifiers);
984 // #pragma mark -
987 void
988 Window::WorkspaceActivated(int32 index, bool active)
990 BMessage activatedMsg(B_WORKSPACE_ACTIVATED);
991 activatedMsg.AddInt64("when", system_time());
992 activatedMsg.AddInt32("workspace", index);
993 activatedMsg.AddBool("active", active);
995 ServerWindow()->SendMessageToClient(&activatedMsg);
999 void
1000 Window::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1002 fWorkspaces = newWorkspaces;
1004 BMessage changedMsg(B_WORKSPACES_CHANGED);
1005 changedMsg.AddInt64("when", system_time());
1006 changedMsg.AddInt32("old", oldWorkspaces);
1007 changedMsg.AddInt32("new", newWorkspaces);
1009 ServerWindow()->SendMessageToClient(&changedMsg);
1013 void
1014 Window::Activated(bool active)
1016 BMessage msg(B_WINDOW_ACTIVATED);
1017 msg.AddBool("active", active);
1018 ServerWindow()->SendMessageToClient(&msg);
1022 //# pragma mark -
1025 void
1026 Window::SetTitle(const char* name, BRegion& dirty)
1028 // rebuild the clipping for the title area
1029 // and redraw it.
1031 fTitle = name;
1033 ::Decorator* decorator = Decorator();
1034 if (decorator) {
1035 int32 index = PositionInStack();
1036 decorator->SetTitle(index, name, &dirty);
1041 void
1042 Window::SetFocus(bool focus)
1044 ::Decorator* decorator = Decorator();
1046 // executed from Desktop thread
1047 // it holds the clipping write lock,
1048 // so the window thread cannot be
1049 // accessing fIsFocus
1051 BRegion* dirty = NULL;
1052 if (decorator)
1053 dirty = fRegionPool.GetRegion(decorator->GetFootprint());
1054 if (dirty) {
1055 dirty->IntersectWith(&fVisibleRegion);
1056 fDesktop->MarkDirty(*dirty);
1057 fRegionPool.Recycle(dirty);
1060 fIsFocus = focus;
1061 if (decorator) {
1062 int32 index = PositionInStack();
1063 decorator->SetFocus(index, focus);
1066 Activated(focus);
1070 void
1071 Window::SetHidden(bool hidden)
1073 // the desktop takes care of dirty regions
1074 if (fHidden != hidden) {
1075 fHidden = hidden;
1077 fTopView->SetHidden(hidden);
1079 // TODO: anything else?
1084 void
1085 Window::SetShowLevel(int32 showLevel)
1087 if (showLevel == fShowLevel)
1088 return;
1090 fShowLevel = showLevel;
1094 void
1095 Window::SetMinimized(bool minimized)
1097 if (minimized == fMinimized)
1098 return;
1100 fMinimized = minimized;
1104 bool
1105 Window::IsVisible() const
1107 if (IsOffscreenWindow())
1108 return true;
1110 if (IsHidden())
1111 return false;
1114 if (fVisibleRegion.CountRects() == 0)
1115 return false;
1117 return fCurrentWorkspace >= 0 && fCurrentWorkspace < kWorkingList;
1121 bool
1122 Window::IsDragging() const
1124 if (!fWindowBehaviour)
1125 return false;
1126 return fWindowBehaviour->IsDragging();
1130 bool
1131 Window::IsResizing() const
1133 if (!fWindowBehaviour)
1134 return false;
1135 return fWindowBehaviour->IsResizing();
1139 void
1140 Window::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight,
1141 int32 maxHeight)
1143 if (minWidth < 0)
1144 minWidth = 0;
1146 if (minHeight < 0)
1147 minHeight = 0;
1149 fMinWidth = minWidth;
1150 fMaxWidth = maxWidth;
1151 fMinHeight = minHeight;
1152 fMaxHeight = maxHeight;
1154 // give the Decorator a say in this too
1155 ::Decorator* decorator = Decorator();
1156 if (decorator) {
1157 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
1158 &fMaxHeight);
1161 _ObeySizeLimits();
1165 void
1166 Window::GetSizeLimits(int32* minWidth, int32* maxWidth,
1167 int32* minHeight, int32* maxHeight) const
1169 *minWidth = fMinWidth;
1170 *maxWidth = fMaxWidth;
1171 *minHeight = fMinHeight;
1172 *maxHeight = fMaxHeight;
1176 bool
1177 Window::SetTabLocation(float location, bool isShifting, BRegion& dirty)
1179 ::Decorator* decorator = Decorator();
1180 if (decorator) {
1181 int32 index = PositionInStack();
1182 return decorator->SetTabLocation(index, location, isShifting, &dirty);
1185 return false;
1189 float
1190 Window::TabLocation() const
1192 ::Decorator* decorator = Decorator();
1193 if (decorator) {
1194 int32 index = PositionInStack();
1195 return decorator->TabLocation(index);
1197 return 0.0;
1201 bool
1202 Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty)
1204 if (settings.what == 'prVu') {
1205 // 'prVu' == preview a decorator!
1206 BString path;
1207 if (settings.FindString("preview", &path) == B_OK)
1208 return gDecorManager.PreviewDecorator(path, this) == B_OK;
1209 return false;
1212 ::Decorator* decorator = Decorator();
1213 if (decorator)
1214 return decorator->SetSettings(settings, &dirty);
1216 return false;
1220 bool
1221 Window::GetDecoratorSettings(BMessage* settings)
1223 if (fDesktop)
1224 fDesktop->GetDecoratorSettings(this, *settings);
1226 ::Decorator* decorator = Decorator();
1227 if (decorator)
1228 return decorator->GetSettings(settings);
1230 return false;
1234 void
1235 Window::FontsChanged(BRegion* updateRegion)
1237 ::Decorator* decorator = Decorator();
1238 if (decorator != NULL) {
1239 DesktopSettings settings(fDesktop);
1240 decorator->FontsChanged(settings, updateRegion);
1245 void
1246 Window::ColorsChanged(BRegion* updateRegion)
1248 ::Decorator* decorator = Decorator();
1249 if (decorator != NULL) {
1250 DesktopSettings settings(fDesktop);
1251 decorator->ColorsChanged(settings, updateRegion);
1256 void
1257 Window::SetLook(window_look look, BRegion* updateRegion)
1259 fLook = look;
1261 fContentRegionValid = false;
1262 // mabye a resize handle was added...
1263 fEffectiveDrawingRegionValid = false;
1264 // ...and therefor the drawing region is
1265 // likely not valid anymore either
1267 if (fCurrentStack.Get() == NULL)
1268 return;
1270 int32 stackPosition = PositionInStack();
1272 ::Decorator* decorator = Decorator();
1273 if (decorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) {
1274 // we need a new decorator
1275 decorator = gDecorManager.AllocateDecorator(this);
1276 fCurrentStack->SetDecorator(decorator);
1277 if (IsFocus())
1278 decorator->SetFocus(stackPosition, true);
1281 if (decorator != NULL) {
1282 DesktopSettings settings(fDesktop);
1283 decorator->SetLook(stackPosition, settings, look, updateRegion);
1285 // we might need to resize the window!
1286 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
1287 &fMaxHeight);
1288 _ObeySizeLimits();
1291 if (look == B_NO_BORDER_WINDOW_LOOK) {
1292 // we don't need a decorator for this window
1293 fCurrentStack->SetDecorator(NULL);
1298 void
1299 Window::SetFeel(window_feel feel)
1301 // if the subset list is no longer needed, clear it
1302 if ((fFeel == B_MODAL_SUBSET_WINDOW_FEEL
1303 || fFeel == B_FLOATING_SUBSET_WINDOW_FEEL)
1304 && feel != B_MODAL_SUBSET_WINDOW_FEEL
1305 && feel != B_FLOATING_SUBSET_WINDOW_FEEL)
1306 fSubsets.MakeEmpty();
1308 fFeel = feel;
1310 // having modal windows with B_AVOID_FRONT or B_AVOID_FOCUS doesn't
1311 // make that much sense, so we filter those flags out on demand
1312 fFlags = fOriginalFlags;
1313 fFlags &= ValidWindowFlags(fFeel);
1315 if (!IsNormal()) {
1316 fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
1317 _PropagatePosition();
1322 void
1323 Window::SetFlags(uint32 flags, BRegion* updateRegion)
1325 fOriginalFlags = flags;
1326 fFlags = flags & ValidWindowFlags(fFeel);
1327 if (!IsNormal())
1328 fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
1330 if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0)
1331 _PropagatePosition();
1333 ::Decorator* decorator = Decorator();
1334 if (decorator == NULL)
1335 return;
1337 int32 stackPosition = PositionInStack();
1338 decorator->SetFlags(stackPosition, flags, updateRegion);
1340 // we might need to resize the window!
1341 decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight);
1342 _ObeySizeLimits();
1344 // TODO: not sure if we want to do this
1345 #if 0
1346 if ((fOriginalFlags & kWindowScreenFlag) != (flags & kWindowScreenFlag)) {
1347 // TODO: disabling needs to be nestable (or we might lose the previous
1348 // update state)
1349 if ((flags & kWindowScreenFlag) != 0)
1350 DisableUpdateRequests();
1351 else
1352 EnableUpdateRequests();
1354 #endif
1358 /*! Returns whether or not a window is in the workspace list with the
1359 specified \a index.
1361 bool
1362 Window::InWorkspace(int32 index) const
1364 return (fWorkspaces & (1UL << index)) != 0;
1368 bool
1369 Window::SupportsFront()
1371 if (fFeel == kDesktopWindowFeel
1372 || fFeel == kMenuWindowFeel
1373 || (fFlags & B_AVOID_FRONT) != 0)
1374 return false;
1376 return true;
1380 bool
1381 Window::IsModal() const
1383 return IsModalFeel(fFeel);
1387 bool
1388 Window::IsFloating() const
1390 return IsFloatingFeel(fFeel);
1394 bool
1395 Window::IsNormal() const
1397 return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel);
1401 bool
1402 Window::HasModal() const
1404 for (Window* window = NextWindow(fCurrentWorkspace); window != NULL;
1405 window = window->NextWindow(fCurrentWorkspace)) {
1406 if (window->IsHidden() || !window->IsModal())
1407 continue;
1409 if (window->HasInSubset(this))
1410 return true;
1413 return false;
1417 /*! \brief Returns the windows that's in behind of the backmost position
1418 this window can get.
1419 Returns NULL is this window can be the backmost window.
1421 \param workspace the workspace on which this check should be made. If
1422 the value is -1, the window's current workspace will be used.
1424 Window*
1425 Window::Backmost(Window* window, int32 workspace)
1427 if (workspace == -1)
1428 workspace = fCurrentWorkspace;
1430 ASSERT(workspace != -1);
1431 if (workspace == -1)
1432 return NULL;
1434 // Desktop windows are always backmost
1435 if (fFeel == kDesktopWindowFeel)
1436 return NULL;
1438 if (window == NULL)
1439 window = PreviousWindow(workspace);
1441 for (; window != NULL; window = window->PreviousWindow(workspace)) {
1442 if (window->IsHidden() || window == this)
1443 continue;
1445 if (HasInSubset(window))
1446 return window;
1449 return NULL;
1453 /*! \brief Returns the window that's in front of the frontmost position
1454 this window can get.
1455 Returns NULL if this window can be the frontmost window.
1457 \param workspace the workspace on which this check should be made. If
1458 the value is -1, the window's current workspace will be used.
1460 Window*
1461 Window::Frontmost(Window* first, int32 workspace)
1463 if (workspace == -1)
1464 workspace = fCurrentWorkspace;
1466 ASSERT(workspace != -1);
1467 if (workspace == -1)
1468 return NULL;
1470 if (fFeel == kDesktopWindowFeel)
1471 return first ? first : NextWindow(workspace);
1473 if (first == NULL)
1474 first = NextWindow(workspace);
1476 for (Window* window = first; window != NULL;
1477 window = window->NextWindow(workspace)) {
1478 if (window->IsHidden() || window == this)
1479 continue;
1481 if (window->HasInSubset(this))
1482 return window;
1485 return NULL;
1489 bool
1490 Window::AddToSubset(Window* window)
1492 return fSubsets.AddItem(window);
1496 void
1497 Window::RemoveFromSubset(Window* window)
1499 fSubsets.RemoveItem(window);
1503 /*! Returns whether or not a window is in the subset of this window.
1504 If a window is in the subset of this window, it means it should always
1505 appear behind this window.
1507 bool
1508 Window::HasInSubset(const Window* window) const
1510 if (window == NULL || fFeel == window->Feel()
1511 || fFeel == B_NORMAL_WINDOW_FEEL)
1512 return false;
1514 // Menus are a special case: they will always be on-top of every window
1515 // of their application
1516 if (fFeel == kMenuWindowFeel)
1517 return window->ServerWindow()->App() == ServerWindow()->App();
1518 if (window->Feel() == kMenuWindowFeel)
1519 return false;
1521 // we have a few special feels that have a fixed order
1523 const int32 kFeels[] = {kPasswordWindowFeel, kWindowScreenFeel,
1524 B_MODAL_ALL_WINDOW_FEEL, B_FLOATING_ALL_WINDOW_FEEL};
1526 for (uint32 order = 0;
1527 order < sizeof(kFeels) / sizeof(kFeels[0]); order++) {
1528 if (fFeel == kFeels[order])
1529 return true;
1530 if (window->Feel() == kFeels[order])
1531 return false;
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();
1539 return fSubsets.HasItem(window);
1543 /*! \brief Collects all workspaces views in this window and puts it into \a list
1545 void
1546 Window::FindWorkspacesViews(BObjectList<WorkspacesView>& list) const
1548 int32 count = fWorkspacesViewCount;
1549 fTopView->FindViews(kWorkspacesViewFlag, (BObjectList<View>&)list, count);
1553 /*! \brief Returns on which workspaces the window should be visible.
1555 A modal or floating window may be visible on a workspace 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.
1559 uint32
1560 Window::SubsetWorkspaces() const
1562 if (fFeel == B_MODAL_ALL_WINDOW_FEEL
1563 || fFeel == B_FLOATING_ALL_WINDOW_FEEL)
1564 return B_ALL_WORKSPACES;
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();
1572 return 0;
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();
1583 return workspaces;
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);
1593 if (!window->IsHidden())
1594 workspaces |= window->Workspaces();
1595 if (window == fDesktop->FrontWindow() && window->IsNormal())
1596 hasNormalFront = true;
1599 if (fFeel == B_FLOATING_SUBSET_WINDOW_FEEL && !hasNormalFront)
1600 return 0;
1602 return workspaces;
1605 return 0;
1609 /*! Returns whether or not a window is in the subset workspace list with the
1610 specified \a index.
1611 See SubsetWorkspaces().
1613 bool
1614 Window::InSubsetWorkspace(int32 index) const
1616 return (SubsetWorkspaces() & (1UL << index)) != 0;
1620 // #pragma mark - static
1623 /*static*/ bool
1624 Window::IsValidLook(window_look look)
1626 return look == B_TITLED_WINDOW_LOOK
1627 || look == B_DOCUMENT_WINDOW_LOOK
1628 || look == B_MODAL_WINDOW_LOOK
1629 || look == B_FLOATING_WINDOW_LOOK
1630 || look == B_BORDERED_WINDOW_LOOK
1631 || look == B_NO_BORDER_WINDOW_LOOK
1632 || look == kDesktopWindowLook
1633 || look == kLeftTitledWindowLook;
1637 /*static*/ bool
1638 Window::IsValidFeel(window_feel feel)
1640 return feel == B_NORMAL_WINDOW_FEEL
1641 || feel == B_MODAL_SUBSET_WINDOW_FEEL
1642 || feel == B_MODAL_APP_WINDOW_FEEL
1643 || feel == B_MODAL_ALL_WINDOW_FEEL
1644 || feel == B_FLOATING_SUBSET_WINDOW_FEEL
1645 || feel == B_FLOATING_APP_WINDOW_FEEL
1646 || feel == B_FLOATING_ALL_WINDOW_FEEL
1647 || feel == kDesktopWindowFeel
1648 || feel == kMenuWindowFeel
1649 || feel == kWindowScreenFeel
1650 || feel == kPasswordWindowFeel
1651 || feel == kOffscreenWindowFeel;
1655 /*static*/ bool
1656 Window::IsModalFeel(window_feel feel)
1658 return feel == B_MODAL_SUBSET_WINDOW_FEEL
1659 || feel == B_MODAL_APP_WINDOW_FEEL
1660 || feel == B_MODAL_ALL_WINDOW_FEEL;
1664 /*static*/ bool
1665 Window::IsFloatingFeel(window_feel feel)
1667 return feel == B_FLOATING_SUBSET_WINDOW_FEEL
1668 || feel == B_FLOATING_APP_WINDOW_FEEL
1669 || feel == B_FLOATING_ALL_WINDOW_FEEL;
1673 /*static*/ uint32
1674 Window::ValidWindowFlags()
1676 return B_NOT_MOVABLE
1677 | B_NOT_CLOSABLE
1678 | B_NOT_ZOOMABLE
1679 | B_NOT_MINIMIZABLE
1680 | B_NOT_RESIZABLE
1681 | B_NOT_H_RESIZABLE
1682 | B_NOT_V_RESIZABLE
1683 | B_AVOID_FRONT
1684 | B_AVOID_FOCUS
1685 | B_WILL_ACCEPT_FIRST_CLICK
1686 | B_OUTLINE_RESIZE
1687 | B_NO_WORKSPACE_ACTIVATION
1688 | B_NOT_ANCHORED_ON_ACTIVATE
1689 | B_ASYNCHRONOUS_CONTROLS
1690 | B_QUIT_ON_WINDOW_CLOSE
1691 | B_SAME_POSITION_IN_ALL_WORKSPACES
1692 | B_AUTO_UPDATE_SIZE_LIMITS
1693 | B_CLOSE_ON_ESCAPE
1694 | B_NO_SERVER_SIDE_WINDOW_MODIFIERS
1695 | kWindowScreenFlag
1696 | kAcceptKeyboardFocusFlag;
1700 /*static*/ uint32
1701 Window::ValidWindowFlags(window_feel feel)
1703 uint32 flags = ValidWindowFlags();
1704 if (IsModalFeel(feel))
1705 return flags & ~(B_AVOID_FOCUS | B_AVOID_FRONT);
1707 return flags;
1711 // #pragma mark - private
1714 void
1715 Window::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
1716 int32 xOffset, int32 yOffset)
1718 BRegion* common = fRegionPool.GetRegion(*regionToShift);
1719 if (!common)
1720 return;
1721 // see if there is a common part at all
1722 common->IntersectWith(region);
1723 if (common->CountRects() > 0) {
1724 // cut the common part from the region,
1725 // offset that to destination and include again
1726 region->Exclude(common);
1727 common->OffsetBy(xOffset, yOffset);
1728 region->Include(common);
1730 fRegionPool.Recycle(common);
1734 void
1735 Window::_TriggerContentRedraw(BRegion& dirtyContentRegion)
1737 if (!IsVisible() || dirtyContentRegion.CountRects() == 0
1738 || (fFlags & kWindowScreenFlag) != 0)
1739 return;
1741 // put this into the pending dirty region
1742 // to eventually trigger a client redraw
1743 bool wasExpose = fPendingUpdateSession->IsExpose();
1744 BRegion* backgroundClearingRegion = &dirtyContentRegion;
1746 _TransferToUpdateSession(&dirtyContentRegion);
1748 if (fPendingUpdateSession->IsExpose()) {
1749 if (!fContentRegionValid)
1750 _UpdateContentRegion();
1752 if (!wasExpose) {
1753 // there was suddenly added a dirty region
1754 // caused by exposing content, we need to clear
1755 // the entire background
1756 backgroundClearingRegion = &fPendingUpdateSession->DirtyRegion();
1759 if (fDrawingEngine->LockParallelAccess()) {
1760 bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled();
1761 fDrawingEngine->SetCopyToFrontEnabled(true);
1762 fDrawingEngine->SuspendAutoSync();
1764 //sCurrentColor.red = rand() % 255;
1765 //sCurrentColor.green = rand() % 255;
1766 //sCurrentColor.blue = rand() % 255;
1767 //sPendingColor.red = rand() % 255;
1768 //sPendingColor.green = rand() % 255;
1769 //sPendingColor.blue = rand() % 255;
1770 //fDrawingEngine->FillRegion(*backgroundClearingRegion, sCurrentColor);
1771 //snooze(10000);
1773 fTopView->Draw(fDrawingEngine, backgroundClearingRegion,
1774 &fContentRegion, true);
1776 fDrawingEngine->Sync();
1777 fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled);
1778 fDrawingEngine->UnlockParallelAccess();
1784 void
1785 Window::_DrawBorder()
1787 // this is executed in the window thread, but only
1788 // in respond to a REDRAW message having been received, the
1789 // clipping lock is held for reading
1790 ::Decorator* decorator = Decorator();
1791 if (!decorator)
1792 return;
1794 // construct the region of the border that needs redrawing
1795 BRegion* dirtyBorderRegion = fRegionPool.GetRegion();
1796 if (!dirtyBorderRegion)
1797 return;
1798 GetBorderRegion(dirtyBorderRegion);
1799 // intersect with our visible region
1800 dirtyBorderRegion->IntersectWith(&fVisibleRegion);
1801 // intersect with the dirty region
1802 dirtyBorderRegion->IntersectWith(&fDirtyRegion);
1804 DrawingEngine* engine = decorator->GetDrawingEngine();
1805 if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) {
1806 engine->ConstrainClippingRegion(dirtyBorderRegion);
1807 bool copyToFrontEnabled = engine->CopyToFrontEnabled();
1808 engine->SetCopyToFrontEnabled(false);
1810 decorator->Draw(dirtyBorderRegion->Frame());
1812 engine->SetCopyToFrontEnabled(copyToFrontEnabled);
1813 engine->CopyToFront(*dirtyBorderRegion);
1815 // TODO: remove this once the DrawState stuff is handled
1816 // more cleanly. The reason why this is needed is that
1817 // when the decorator draws strings, a draw state is set
1818 // on the Painter object, and this is were it might get
1819 // out of sync with what the ServerWindow things is the
1820 // current DrawState set on the Painter
1821 fWindow->ResyncDrawState();
1823 engine->UnlockParallelAccess();
1825 fRegionPool.Recycle(dirtyBorderRegion);
1829 /*! pre: the clipping is readlocked (this function is
1830 only called from _TriggerContentRedraw()), which
1831 in turn is only called from MessageReceived() with
1832 the clipping lock held
1834 void
1835 Window::_TransferToUpdateSession(BRegion* contentDirtyRegion)
1837 if (contentDirtyRegion->CountRects() <= 0)
1838 return;
1840 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor);
1841 //snooze(20000);
1843 // add to pending
1844 fPendingUpdateSession->SetUsed(true);
1845 // if (!fPendingUpdateSession->IsExpose())
1846 fPendingUpdateSession->AddCause(fDirtyCause);
1847 fPendingUpdateSession->Include(contentDirtyRegion);
1849 if (!fUpdateRequested) {
1850 // send this to client
1851 _SendUpdateMessage();
1852 // the pending region is now the current,
1853 // though the update does not start until
1854 // we received BEGIN_UPDATE from the client
1859 void
1860 Window::_SendUpdateMessage()
1862 if (!fUpdatesEnabled)
1863 return;
1865 BMessage message(_UPDATE_);
1866 if (ServerWindow()->SendMessageToClient(&message) != B_OK) {
1867 // If sending the message failed, we'll just keep adding to the dirty
1868 // region until sending was successful.
1869 // TODO: we might want to automatically resend this message in this case
1870 return;
1873 fUpdateRequested = true;
1874 fEffectiveDrawingRegionValid = false;
1878 void
1879 Window::BeginUpdate(BPrivate::PortLink& link)
1881 // NOTE: since we might "shift" parts of the
1882 // internal dirty regions from the desktop thread
1883 // in response to Window::ResizeBy(), which
1884 // might move arround views, the user of this function
1885 // needs to hold the global clipping lock so that the internal
1886 // dirty regions are not messed with from the Desktop thread
1887 // and ServerWindow thread at the same time.
1889 if (!fUpdateRequested) {
1890 link.StartMessage(B_ERROR);
1891 link.Flush();
1892 fprintf(stderr, "Window::BeginUpdate() - no update requested!\n");
1893 return;
1896 // make the pending update session the current update session
1897 // (toggle the pointers)
1898 UpdateSession* temp = fCurrentUpdateSession;
1899 fCurrentUpdateSession = fPendingUpdateSession;
1900 fPendingUpdateSession = temp;
1901 fPendingUpdateSession->SetUsed(false);
1902 // all drawing command from the client
1903 // will have the dirty region from the update
1904 // session enforced
1905 fInUpdate = true;
1906 fEffectiveDrawingRegionValid = false;
1908 // TODO: each view could be drawn individually
1909 // right before carrying out the first drawing
1910 // command from the client during an update
1911 // (View::IsBackgroundDirty() can be used
1912 // for this)
1913 if (!fContentRegionValid)
1914 _UpdateContentRegion();
1916 BRegion* dirty = fRegionPool.GetRegion(
1917 fCurrentUpdateSession->DirtyRegion());
1918 if (!dirty) {
1919 link.StartMessage(B_ERROR);
1920 link.Flush();
1921 return;
1924 dirty->IntersectWith(&VisibleContentRegion());
1926 //if (!fCurrentUpdateSession->IsExpose()) {
1927 ////sCurrentColor.red = rand() % 255;
1928 ////sCurrentColor.green = rand() % 255;
1929 ////sCurrentColor.blue = rand() % 255;
1930 ////sPendingColor.red = rand() % 255;
1931 ////sPendingColor.green = rand() % 255;
1932 ////sPendingColor.blue = rand() % 255;
1933 //fDrawingEngine->FillRegion(*dirty, sCurrentColor);
1934 //snooze(10000);
1937 link.StartMessage(B_OK);
1938 // append the current window geometry to the
1939 // message, the client will need it
1940 link.Attach<BPoint>(fFrame.LeftTop());
1941 link.Attach<float>(fFrame.Width());
1942 link.Attach<float>(fFrame.Height());
1943 // find and attach all views that intersect with
1944 // the dirty region
1945 fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion);
1946 // mark the end of the token "list"
1947 link.Attach<int32>(B_NULL_TOKEN);
1948 link.Flush();
1950 // supress back to front buffer copies in the drawing engine
1951 fDrawingEngine->SetCopyToFrontEnabled(false);
1953 if (!fCurrentUpdateSession->IsExpose()
1954 && fDrawingEngine->LockParallelAccess()) {
1955 fDrawingEngine->SuspendAutoSync();
1957 fTopView->Draw(fDrawingEngine, dirty, &fContentRegion, true);
1959 fDrawingEngine->Sync();
1960 fDrawingEngine->UnlockParallelAccess();
1961 } // else the background was cleared already
1963 fRegionPool.Recycle(dirty);
1967 void
1968 Window::EndUpdate()
1970 // NOTE: see comment in _BeginUpdate()
1972 if (fInUpdate) {
1973 // reenable copy to front
1974 fDrawingEngine->SetCopyToFrontEnabled(true);
1976 BRegion* dirty = fRegionPool.GetRegion(
1977 fCurrentUpdateSession->DirtyRegion());
1979 if (dirty) {
1980 dirty->IntersectWith(&VisibleContentRegion());
1982 fDrawingEngine->CopyToFront(*dirty);
1983 fRegionPool.Recycle(dirty);
1986 fCurrentUpdateSession->SetUsed(false);
1988 fInUpdate = false;
1989 fEffectiveDrawingRegionValid = false;
1991 if (fPendingUpdateSession->IsUsed()) {
1992 // send this to client
1993 _SendUpdateMessage();
1994 } else {
1995 fUpdateRequested = false;
2000 void
2001 Window::_UpdateContentRegion()
2003 fContentRegion.Set(fFrame);
2005 // resize handle
2006 ::Decorator* decorator = Decorator();
2007 if (decorator)
2008 fContentRegion.Exclude(&decorator->GetFootprint());
2010 fContentRegionValid = true;
2014 void
2015 Window::_ObeySizeLimits()
2017 // make sure we even have valid size limits
2018 if (fMaxWidth < fMinWidth)
2019 fMaxWidth = fMinWidth;
2021 if (fMaxHeight < fMinHeight)
2022 fMaxHeight = fMinHeight;
2024 // Automatically resize the window to fit these new limits
2025 // if it does not already.
2027 // On R5, Windows don't automatically resize, but since
2028 // BWindow::ResizeTo() even honors the limits, I would guess
2029 // this is a bug that we don't have to adopt.
2030 // Note that most current apps will do unnecessary resizing
2031 // after having set the limits, but the overhead is neglible.
2033 float minWidthDiff = fMinWidth - fFrame.Width();
2034 float minHeightDiff = fMinHeight - fFrame.Height();
2035 float maxWidthDiff = fMaxWidth - fFrame.Width();
2036 float maxHeightDiff = fMaxHeight - fFrame.Height();
2038 float xDiff = 0.0;
2039 if (minWidthDiff > 0.0) // we're currently smaller than minWidth
2040 xDiff = minWidthDiff;
2041 else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth
2042 xDiff = maxWidthDiff;
2044 float yDiff = 0.0;
2045 if (minHeightDiff > 0.0) // we're currently smaller than minHeight
2046 yDiff = minHeightDiff;
2047 else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight
2048 yDiff = maxHeightDiff;
2050 if (fDesktop)
2051 fDesktop->ResizeWindowBy(this, xDiff, yDiff);
2052 else
2053 ResizeBy((int32)xDiff, (int32)yDiff, NULL);
2057 // #pragma mark - UpdateSession
2060 Window::UpdateSession::UpdateSession()
2062 fDirtyRegion(),
2063 fInUse(false),
2064 fCause(0)
2069 Window::UpdateSession::~UpdateSession()
2074 void
2075 Window::UpdateSession::Include(BRegion* additionalDirty)
2077 fDirtyRegion.Include(additionalDirty);
2081 void
2082 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession)
2084 fDirtyRegion.Exclude(dirtyInNextSession);
2088 void
2089 Window::UpdateSession::MoveBy(int32 x, int32 y)
2091 fDirtyRegion.OffsetBy(x, y);
2095 void
2096 Window::UpdateSession::SetUsed(bool used)
2098 fInUse = used;
2099 if (!fInUse) {
2100 fDirtyRegion.MakeEmpty();
2101 fCause = 0;
2106 void
2107 Window::UpdateSession::AddCause(uint8 cause)
2109 fCause |= cause;
2113 int32
2114 Window::PositionInStack() const
2116 if (fCurrentStack.Get() == NULL)
2117 return -1;
2118 return fCurrentStack->WindowList().IndexOf(this);
2122 bool
2123 Window::DetachFromWindowStack(bool ownStackNeeded)
2125 // The lock must normally be held but is not held when closing the window.
2126 //ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker());
2128 if (fCurrentStack.Get() == NULL)
2129 return false;
2130 if (fCurrentStack->CountWindows() == 1)
2131 return true;
2133 int32 index = PositionInStack();
2135 if (fCurrentStack->RemoveWindow(this) == false)
2136 return false;
2138 ::Decorator* decorator = fCurrentStack->Decorator();
2139 if (decorator != NULL) {
2140 decorator->RemoveTab(index);
2141 decorator->SetTopTab(fCurrentStack->LayerOrder().CountItems() - 1);
2144 Window* remainingTop = fCurrentStack->TopLayerWindow();
2145 if (remainingTop != NULL) {
2146 if (decorator != NULL)
2147 decorator->SetDrawingEngine(remainingTop->fDrawingEngine);
2148 // propagate focus to the decorator
2149 remainingTop->SetFocus(remainingTop->IsFocus());
2150 remainingTop->SetLook(remainingTop->Look(), NULL);
2153 fCurrentStack = NULL;
2154 if (ownStackNeeded == true)
2155 _InitWindowStack();
2156 // propagate focus to the new decorator
2157 SetFocus(IsFocus());
2159 if (remainingTop != NULL) {
2160 fDesktop->RebuildAndRedrawAfterWindowChange(remainingTop,
2161 remainingTop->VisibleRegion());
2163 return true;
2167 bool
2168 Window::AddWindowToStack(Window* window)
2170 ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker());
2172 WindowStack* stack = GetWindowStack();
2173 if (stack == NULL)
2174 return false;
2176 BRegion dirty;
2177 // move window to the own position
2178 BRect ownFrame = Frame();
2179 BRect frame = window->Frame();
2180 float deltaToX = round(ownFrame.left - frame.left);
2181 float deltaToY = round(ownFrame.top - frame.top);
2182 frame.OffsetBy(deltaToX, deltaToY);
2183 float deltaByX = round(ownFrame.right - frame.right);
2184 float deltaByY = round(ownFrame.bottom - frame.bottom);
2185 dirty.Include(&window->VisibleRegion());
2186 window->MoveBy(deltaToX, deltaToY, false);
2187 window->ResizeBy(deltaByX, deltaByY, &dirty, false);
2189 // first collect dirt from the window to add
2190 ::Decorator* otherDecorator = window->Decorator();
2191 if (otherDecorator != NULL)
2192 dirty.Include(otherDecorator->TitleBarRect());
2193 ::Decorator* decorator = stack->Decorator();
2194 if (decorator != NULL)
2195 dirty.Include(decorator->TitleBarRect());
2197 int32 position = PositionInStack() + 1;
2198 if (position >= stack->CountWindows())
2199 position = -1;
2200 if (stack->AddWindow(window, position) == false)
2201 return false;
2202 window->DetachFromWindowStack(false);
2203 window->fCurrentStack.SetTo(stack);
2205 if (decorator != NULL) {
2206 DesktopSettings settings(fDesktop);
2207 decorator->AddTab(settings, window->Title(), window->Look(),
2208 window->Flags(), position, &dirty);
2211 window->SetLook(window->Look(), &dirty);
2212 fDesktop->RebuildAndRedrawAfterWindowChange(TopLayerStackWindow(), dirty);
2213 window->SetFocus(window->IsFocus());
2214 return true;
2218 Window*
2219 Window::StackedWindowAt(const BPoint& where)
2221 ::Decorator* decorator = Decorator();
2222 if (decorator == NULL)
2223 return this;
2225 int tab = decorator->TabAt(where);
2226 // if we have a decorator we also have a stack
2227 Window* window = fCurrentStack->WindowAt(tab);
2228 if (window != NULL)
2229 return window;
2230 return this;
2234 Window*
2235 Window::TopLayerStackWindow()
2237 if (fCurrentStack.Get() == NULL)
2238 return this;
2239 return fCurrentStack->TopLayerWindow();
2243 WindowStack*
2244 Window::GetWindowStack()
2246 if (fCurrentStack.Get() == NULL)
2247 return _InitWindowStack();
2248 return fCurrentStack;
2252 bool
2253 Window::MoveToTopStackLayer()
2255 ::Decorator* decorator = Decorator();
2256 if (decorator == NULL)
2257 return false;
2258 decorator->SetDrawingEngine(fDrawingEngine);
2259 SetLook(Look(), NULL);
2260 decorator->SetTopTab(PositionInStack());
2261 return fCurrentStack->MoveToTopLayer(this);
2265 bool
2266 Window::MoveToStackPosition(int32 to, bool isMoving)
2268 if (fCurrentStack.Get() == NULL)
2269 return false;
2270 int32 index = PositionInStack();
2271 if (fCurrentStack->Move(index, to) == false)
2272 return false;
2274 BRegion dirty;
2275 ::Decorator* decorator = Decorator();
2276 if (decorator && decorator->MoveTab(index, to, isMoving, &dirty) == false)
2277 return false;
2279 fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty);
2280 return true;
2284 WindowStack*
2285 Window::_InitWindowStack()
2287 fCurrentStack = NULL;
2288 ::Decorator* decorator = NULL;
2289 if (fLook != B_NO_BORDER_WINDOW_LOOK)
2290 decorator = gDecorManager.AllocateDecorator(this);
2292 WindowStack* stack = new(std::nothrow) WindowStack(decorator);
2293 if (stack == NULL)
2294 return NULL;
2296 if (stack->AddWindow(this) != true) {
2297 delete stack;
2298 return NULL;
2300 fCurrentStack.SetTo(stack, true);
2301 return stack;
2305 WindowStack::WindowStack(::Decorator* decorator)
2307 fDecorator(decorator)
2313 WindowStack::~WindowStack()
2315 delete fDecorator;
2319 void
2320 WindowStack::SetDecorator(::Decorator* decorator)
2322 delete fDecorator;
2323 fDecorator = decorator;
2327 ::Decorator*
2328 WindowStack::Decorator()
2330 return fDecorator;
2334 Window*
2335 WindowStack::TopLayerWindow() const
2337 return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1);
2341 int32
2342 WindowStack::CountWindows()
2344 return fWindowList.CountItems();
2348 Window*
2349 WindowStack::WindowAt(int32 index)
2351 return fWindowList.ItemAt(index);
2355 bool
2356 WindowStack::AddWindow(Window* window, int32 position)
2358 if (position >= 0) {
2359 if (fWindowList.AddItem(window, position) == false)
2360 return false;
2361 } else if (fWindowList.AddItem(window) == false)
2362 return false;
2364 if (fWindowLayerOrder.AddItem(window) == false) {
2365 fWindowList.RemoveItem(window);
2366 return false;
2368 return true;
2372 bool
2373 WindowStack::RemoveWindow(Window* window)
2375 if (fWindowList.RemoveItem(window) == false)
2376 return false;
2378 fWindowLayerOrder.RemoveItem(window);
2379 return true;
2383 bool
2384 WindowStack::MoveToTopLayer(Window* window)
2386 int32 index = fWindowLayerOrder.IndexOf(window);
2387 return fWindowLayerOrder.MoveItem(index,
2388 fWindowLayerOrder.CountItems() - 1);
2392 bool
2393 WindowStack::Move(int32 from, int32 to)
2395 return fWindowList.MoveItem(from, to);