2 * Copyright 2001-2011, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
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>
22 #include <DirectWindow.h>
25 #include <ViewPrivate.h>
26 #include <WindowPrivate.h>
28 #include "ClickTarget.h"
29 #include "Decorator.h"
30 #include "DecorManager.h"
32 #include "DrawingEngine.h"
33 #include "HWInterface.h"
34 #include "MessagePrivate.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
47 # define STRACE(x) printf x
52 // IMPORTANT: nested LockSingleWindow()s are not supported (by MultiLocker)
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
)
80 fVisibleContentRegion(),
85 fEffectiveDrawingRegion(),
87 fVisibleContentRegionValid(false),
88 fContentRegionValid(false),
89 fEffectiveDrawingRegionValid(false),
93 fWindowBehaviour(NULL
),
96 fDrawingEngine(drawingEngine
),
97 fDesktop(window
->Desktop()),
99 fCurrentUpdateSession(&fUpdateSessions
[0]),
100 fPendingUpdateSession(&fUpdateSessions
[1]),
101 fUpdateRequested(false),
103 fUpdatesEnabled(true),
105 // Windows start hidden
107 // Hidden is 1 or more
114 fWorkspaces(workspaces
),
115 fCurrentWorkspace(-1),
122 fWorkspacesViewCount(0)
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
,
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
;
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"));
173 fTopView
->DetachedFromWindow();
177 DetachFromWindowStack(false);
179 delete fWindowBehaviour
;
180 delete fDrawingEngine
;
182 gDecorManager
.CleanupForWindow(this);
187 Window::InitCheck() const
189 if (fDrawingEngine
== NULL
190 || (fFeel
!= kOffscreenWindowFeel
&& fWindowBehaviour
== NULL
))
192 // TODO: anything else?
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;
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
);
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();
232 *region
= decorator
->GetFootprint();
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
;
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
;
271 Window::_PropagatePosition()
273 if ((fFlags
& B_SAME_POSITION_IN_ALL_WORKSPACES
) == 0)
276 for (int32 i
= 0; i
< kListCount
; i
++) {
277 Anchor(i
).position
= fFrame
.LeftTop();
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)
290 fFrame
.OffsetBy(x
, y
);
291 _PropagatePosition();
293 // take along the dirty region which is not
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
);
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
);
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)
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
);
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
);
403 Window::ScrollViewBy(View
* view
, int32 dx
, int32 dy
)
405 // this is executed in ServerWindow with the Readlock
408 if (!view
|| view
== fTopView
|| (dx
== 0 && dy
== 0))
411 BRegion
* dirty
= fRegionPool
.GetRegion();
415 view
->ScrollBy(dx
, dy
, dirty
);
417 //fDrawingEngine->FillRegion(*dirty, (rgb_color){ 255, 0, 255, 255 });
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
431 Window::CopyContents(BRegion
* region
, int32 xOffset
, int32 yOffset
)
433 // executed in ServerWindow thread with the read lock held
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
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
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
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
);
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
);
522 Window::SetTopView(View
* topView
)
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);
547 Window::ViewAt(const BPoint
& where
)
549 return fTopView
->ViewAt(where
);
554 Window::Anchor(int32 index
)
556 return fAnchor
[index
];
561 Window::NextWindow(int32 index
) const
563 return fAnchor
[index
].next
;
568 Window::PreviousWindow(int32 index
) const
570 return fAnchor
[index
].previous
;
575 Window::Decorator() const
577 if (fCurrentStack
.Get() == NULL
)
579 return fCurrentStack
->Decorator();
584 Window::ReloadDecor()
586 ::Decorator
* decorator
= NULL
;
587 WindowBehaviour
* windowBehaviour
= NULL
;
588 WindowStack
* stack
= GetWindowStack();
592 // only reload the window at the first position
593 if (stack
->WindowAt(0) != this)
596 if (fLook
!= B_NO_BORDER_WINDOW_LOOK
) {
597 // we need a new decorator
598 decorator
= gDecorManager
.AllocateDecorator(this);
599 if (decorator
== NULL
)
602 // add all tabs to the decorator
603 for (int32 i
= 1; i
< stack
->CountWindows(); i
++) {
604 Window
* window
= stack
->WindowAt(i
);
606 DesktopSettings
settings(fDesktop
);
607 if (decorator
->AddTab(settings
, window
->Title(), window
->Look(),
608 window
->Flags(), -1, &dirty
) == NULL
) {
616 windowBehaviour
= gDecorManager
.AllocateWindowBehaviour(this);
617 if (windowBehaviour
== NULL
) {
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
);
641 Window::SetScreen(const ::Screen
* screen
)
643 // TODO this assert fails in Desktop::ShowWindow
644 //ASSERT_MULTI_WRITE_LOCKED(fDesktop->ScreenLocker());
650 Window::Screen() const
652 // TODO this assert also fails
653 //ASSERT_MULTI_READ_LOCKED(fDesktop->ScreenLocker());
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
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());
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
));
698 Window::DrawingRegionChanged(View
* view
) const
700 return !fEffectiveDrawingRegionValid
|| !view
->IsScreenClippingValid();
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(®ion
);
729 fDirtyCause
|= UPDATE_EXPOSE
;
734 Window::RedrawDirtyRegion()
736 if (TopLayerStackWindow() != this) {
737 fDirtyRegion
.MakeEmpty();
742 // executed from ServerWindow with the read lock held
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();
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
775 fDesktop
->MarkDirty(regionOnScreen
);
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())
789 regionOnScreen
.IntersectWith(&VisibleContentRegion());
790 fDirtyCause
|= UPDATE_REQUEST
;
791 _TriggerContentRedraw(regionOnScreen
);
796 Window::MarkContentDirtyAsync(BRegion
& regionOnScreen
)
798 // NOTE: see comments in ProcessDirtyRegion()
799 if (fHidden
|| IsOffscreenWindow())
802 regionOnScreen
.IntersectWith(&VisibleContentRegion());
804 if (fDirtyRegion
.CountRects() == 0) {
805 ServerWindow()->RequestRedraw();
808 fDirtyRegion
.Include(®ionOnScreen
);
809 fDirtyCause
|= UPDATE_REQUEST
;
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 });
828 fDirtyCause
|= UPDATE_REQUEST
;
829 _TriggerContentRedraw(viewRegion
);
834 // DisableUpdateRequests
836 Window::DisableUpdateRequests()
838 fUpdatesEnabled
= false;
842 // EnableUpdateRequests
844 Window::EnableUpdateRequests()
846 fUpdatesEnabled
= true;
847 if (!fUpdateRequested
&& fPendingUpdateSession
->IsUsed())
848 _SendUpdateMessage();
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.
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.
885 bool eventEaten
= fWindowBehaviour
->MouseDown(message
, where
, lastHitRegion
,
886 clickCount
, hitRegion
);
889 // click on the decorator (or equivalent)
890 _clickTarget
= ClickTarget(ClickTarget::TYPE_WINDOW_DECORATOR
,
891 windowToken
, (int32
)hitRegion
);
893 // click was inside the window contents
894 int32 viewToken
= B_NULL_TOKEN
;
895 if (View
* view
= ViewAt(where
)) {
899 // clicking a simple View
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
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()
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
);
937 Window::MouseUp(BMessage
* message
, BPoint where
, int32
* _viewToken
)
939 fWindowBehaviour
->MouseUp(message
, where
);
941 if (View
* view
= ViewAt(where
)) {
945 *_viewToken
= view
->Token();
946 view
->MouseUp(message
, where
);
952 Window::MouseMoved(BMessage
*message
, BPoint where
, int32
* _viewToken
,
953 bool isLatestMouseMoved
, bool isFake
)
955 View
* view
= ViewAt(where
);
957 *_viewToken
= view
->Token();
959 // ignore pointer history
960 if (!isLatestMouseMoved
)
963 fWindowBehaviour
->MouseMoved(message
, where
, isFake
);
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());
978 Window::ModifiersChanged(int32 modifiers
)
980 fWindowBehaviour
->ModifiersChanged(modifiers
);
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
);
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
);
1014 Window::Activated(bool active
)
1016 BMessage
msg(B_WINDOW_ACTIVATED
);
1017 msg
.AddBool("active", active
);
1018 ServerWindow()->SendMessageToClient(&msg
);
1026 Window::SetTitle(const char* name
, BRegion
& dirty
)
1028 // rebuild the clipping for the title area
1033 ::Decorator
* decorator
= Decorator();
1035 int32 index
= PositionInStack();
1036 decorator
->SetTitle(index
, name
, &dirty
);
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
;
1053 dirty
= fRegionPool
.GetRegion(decorator
->GetFootprint());
1055 dirty
->IntersectWith(&fVisibleRegion
);
1056 fDesktop
->MarkDirty(*dirty
);
1057 fRegionPool
.Recycle(dirty
);
1062 int32 index
= PositionInStack();
1063 decorator
->SetFocus(index
, focus
);
1071 Window::SetHidden(bool hidden
)
1073 // the desktop takes care of dirty regions
1074 if (fHidden
!= hidden
) {
1077 fTopView
->SetHidden(hidden
);
1079 // TODO: anything else?
1085 Window::SetShowLevel(int32 showLevel
)
1087 if (showLevel
== fShowLevel
)
1090 fShowLevel
= showLevel
;
1095 Window::SetMinimized(bool minimized
)
1097 if (minimized
== fMinimized
)
1100 fMinimized
= minimized
;
1105 Window::IsVisible() const
1107 if (IsOffscreenWindow())
1114 if (fVisibleRegion.CountRects() == 0)
1117 return fCurrentWorkspace
>= 0 && fCurrentWorkspace
< kWorkingList
;
1122 Window::IsDragging() const
1124 if (!fWindowBehaviour
)
1126 return fWindowBehaviour
->IsDragging();
1131 Window::IsResizing() const
1133 if (!fWindowBehaviour
)
1135 return fWindowBehaviour
->IsResizing();
1140 Window::SetSizeLimits(int32 minWidth
, int32 maxWidth
, int32 minHeight
,
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();
1157 decorator
->GetSizeLimits(&fMinWidth
, &fMinHeight
, &fMaxWidth
,
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
;
1177 Window::SetTabLocation(float location
, bool isShifting
, BRegion
& dirty
)
1179 ::Decorator
* decorator
= Decorator();
1181 int32 index
= PositionInStack();
1182 return decorator
->SetTabLocation(index
, location
, isShifting
, &dirty
);
1190 Window::TabLocation() const
1192 ::Decorator
* decorator
= Decorator();
1194 int32 index
= PositionInStack();
1195 return decorator
->TabLocation(index
);
1202 Window::SetDecoratorSettings(const BMessage
& settings
, BRegion
& dirty
)
1204 if (settings
.what
== 'prVu') {
1205 // 'prVu' == preview a decorator!
1207 if (settings
.FindString("preview", &path
) == B_OK
)
1208 return gDecorManager
.PreviewDecorator(path
, this) == B_OK
;
1212 ::Decorator
* decorator
= Decorator();
1214 return decorator
->SetSettings(settings
, &dirty
);
1221 Window::GetDecoratorSettings(BMessage
* settings
)
1224 fDesktop
->GetDecoratorSettings(this, *settings
);
1226 ::Decorator
* decorator
= Decorator();
1228 return decorator
->GetSettings(settings
);
1235 Window::FontsChanged(BRegion
* updateRegion
)
1237 ::Decorator
* decorator
= Decorator();
1238 if (decorator
!= NULL
) {
1239 DesktopSettings
settings(fDesktop
);
1240 decorator
->FontsChanged(settings
, updateRegion
);
1246 Window::ColorsChanged(BRegion
* updateRegion
)
1248 ::Decorator
* decorator
= Decorator();
1249 if (decorator
!= NULL
) {
1250 DesktopSettings
settings(fDesktop
);
1251 decorator
->ColorsChanged(settings
, updateRegion
);
1257 Window::SetLook(window_look look
, BRegion
* updateRegion
)
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
)
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
);
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
,
1291 if (look
== B_NO_BORDER_WINDOW_LOOK
) {
1292 // we don't need a decorator for this window
1293 fCurrentStack
->SetDecorator(NULL
);
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();
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
);
1316 fFlags
|= B_SAME_POSITION_IN_ALL_WORKSPACES
;
1317 _PropagatePosition();
1323 Window::SetFlags(uint32 flags
, BRegion
* updateRegion
)
1325 fOriginalFlags
= flags
;
1326 fFlags
= flags
& ValidWindowFlags(fFeel
);
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
)
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
);
1344 // TODO: not sure if we want to do this
1346 if ((fOriginalFlags
& kWindowScreenFlag
) != (flags
& kWindowScreenFlag
)) {
1347 // TODO: disabling needs to be nestable (or we might lose the previous
1349 if ((flags
& kWindowScreenFlag
) != 0)
1350 DisableUpdateRequests();
1352 EnableUpdateRequests();
1358 /*! Returns whether or not a window is in the workspace list with the
1362 Window::InWorkspace(int32 index
) const
1364 return (fWorkspaces
& (1UL << index
)) != 0;
1369 Window::SupportsFront()
1371 if (fFeel
== kDesktopWindowFeel
1372 || fFeel
== kMenuWindowFeel
1373 || (fFlags
& B_AVOID_FRONT
) != 0)
1381 Window::IsModal() const
1383 return IsModalFeel(fFeel
);
1388 Window::IsFloating() const
1390 return IsFloatingFeel(fFeel
);
1395 Window::IsNormal() const
1397 return !IsFloatingFeel(fFeel
) && !IsModalFeel(fFeel
);
1402 Window::HasModal() const
1404 for (Window
* window
= NextWindow(fCurrentWorkspace
); window
!= NULL
;
1405 window
= window
->NextWindow(fCurrentWorkspace
)) {
1406 if (window
->IsHidden() || !window
->IsModal())
1409 if (window
->HasInSubset(this))
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.
1425 Window::Backmost(Window
* window
, int32 workspace
)
1427 if (workspace
== -1)
1428 workspace
= fCurrentWorkspace
;
1430 ASSERT(workspace
!= -1);
1431 if (workspace
== -1)
1434 // Desktop windows are always backmost
1435 if (fFeel
== kDesktopWindowFeel
)
1439 window
= PreviousWindow(workspace
);
1441 for (; window
!= NULL
; window
= window
->PreviousWindow(workspace
)) {
1442 if (window
->IsHidden() || window
== this)
1445 if (HasInSubset(window
))
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.
1461 Window::Frontmost(Window
* first
, int32 workspace
)
1463 if (workspace
== -1)
1464 workspace
= fCurrentWorkspace
;
1466 ASSERT(workspace
!= -1);
1467 if (workspace
== -1)
1470 if (fFeel
== kDesktopWindowFeel
)
1471 return first
? first
: NextWindow(workspace
);
1474 first
= NextWindow(workspace
);
1476 for (Window
* window
= first
; window
!= NULL
;
1477 window
= window
->NextWindow(workspace
)) {
1478 if (window
->IsHidden() || window
== this)
1481 if (window
->HasInSubset(this))
1490 Window::AddToSubset(Window
* window
)
1492 return fSubsets
.AddItem(window
);
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.
1508 Window::HasInSubset(const Window
* window
) const
1510 if (window
== NULL
|| fFeel
== window
->Feel()
1511 || fFeel
== B_NORMAL_WINDOW_FEEL
)
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
)
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
])
1530 if (window
->Feel() == kFeels
[order
])
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
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.
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();
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();
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
)
1609 /*! Returns whether or not a window is in the subset workspace list with the
1611 See SubsetWorkspaces().
1614 Window::InSubsetWorkspace(int32 index
) const
1616 return (SubsetWorkspaces() & (1UL << index
)) != 0;
1620 // #pragma mark - static
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
;
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
;
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
;
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
;
1674 Window::ValidWindowFlags()
1676 return B_NOT_MOVABLE
1685 | B_WILL_ACCEPT_FIRST_CLICK
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
1694 | B_NO_SERVER_SIDE_WINDOW_MODIFIERS
1696 | kAcceptKeyboardFocusFlag
;
1701 Window::ValidWindowFlags(window_feel feel
)
1703 uint32 flags
= ValidWindowFlags();
1704 if (IsModalFeel(feel
))
1705 return flags
& ~(B_AVOID_FOCUS
| B_AVOID_FRONT
);
1711 // #pragma mark - private
1715 Window::_ShiftPartOfRegion(BRegion
* region
, BRegion
* regionToShift
,
1716 int32 xOffset
, int32 yOffset
)
1718 BRegion
* common
= fRegionPool
.GetRegion(*regionToShift
);
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
);
1735 Window::_TriggerContentRedraw(BRegion
& dirtyContentRegion
)
1737 if (!IsVisible() || dirtyContentRegion
.CountRects() == 0
1738 || (fFlags
& kWindowScreenFlag
) != 0)
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();
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);
1773 fTopView
->Draw(fDrawingEngine
, backgroundClearingRegion
,
1774 &fContentRegion
, true);
1776 fDrawingEngine
->Sync();
1777 fDrawingEngine
->SetCopyToFrontEnabled(copyToFrontEnabled
);
1778 fDrawingEngine
->UnlockParallelAccess();
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();
1794 // construct the region of the border that needs redrawing
1795 BRegion
* dirtyBorderRegion
= fRegionPool
.GetRegion();
1796 if (!dirtyBorderRegion
)
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
1835 Window::_TransferToUpdateSession(BRegion
* contentDirtyRegion
)
1837 if (contentDirtyRegion
->CountRects() <= 0)
1840 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor);
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
1860 Window::_SendUpdateMessage()
1862 if (!fUpdatesEnabled
)
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
1873 fUpdateRequested
= true;
1874 fEffectiveDrawingRegionValid
= false;
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
);
1892 fprintf(stderr
, "Window::BeginUpdate() - no update requested!\n");
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
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
1913 if (!fContentRegionValid
)
1914 _UpdateContentRegion();
1916 BRegion
* dirty
= fRegionPool
.GetRegion(
1917 fCurrentUpdateSession
->DirtyRegion());
1919 link
.StartMessage(B_ERROR
);
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);
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
1945 fTopView
->AddTokensForViewsInRegion(link
, *dirty
, &fContentRegion
);
1946 // mark the end of the token "list"
1947 link
.Attach
<int32
>(B_NULL_TOKEN
);
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
);
1970 // NOTE: see comment in _BeginUpdate()
1973 // reenable copy to front
1974 fDrawingEngine
->SetCopyToFrontEnabled(true);
1976 BRegion
* dirty
= fRegionPool
.GetRegion(
1977 fCurrentUpdateSession
->DirtyRegion());
1980 dirty
->IntersectWith(&VisibleContentRegion());
1982 fDrawingEngine
->CopyToFront(*dirty
);
1983 fRegionPool
.Recycle(dirty
);
1986 fCurrentUpdateSession
->SetUsed(false);
1989 fEffectiveDrawingRegionValid
= false;
1991 if (fPendingUpdateSession
->IsUsed()) {
1992 // send this to client
1993 _SendUpdateMessage();
1995 fUpdateRequested
= false;
2001 Window::_UpdateContentRegion()
2003 fContentRegion
.Set(fFrame
);
2006 ::Decorator
* decorator
= Decorator();
2008 fContentRegion
.Exclude(&decorator
->GetFootprint());
2010 fContentRegionValid
= true;
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();
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
;
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
;
2051 fDesktop
->ResizeWindowBy(this, xDiff
, yDiff
);
2053 ResizeBy((int32
)xDiff
, (int32
)yDiff
, NULL
);
2057 // #pragma mark - UpdateSession
2060 Window::UpdateSession::UpdateSession()
2069 Window::UpdateSession::~UpdateSession()
2075 Window::UpdateSession::Include(BRegion
* additionalDirty
)
2077 fDirtyRegion
.Include(additionalDirty
);
2082 Window::UpdateSession::Exclude(BRegion
* dirtyInNextSession
)
2084 fDirtyRegion
.Exclude(dirtyInNextSession
);
2089 Window::UpdateSession::MoveBy(int32 x
, int32 y
)
2091 fDirtyRegion
.OffsetBy(x
, y
);
2096 Window::UpdateSession::SetUsed(bool used
)
2100 fDirtyRegion
.MakeEmpty();
2107 Window::UpdateSession::AddCause(uint8 cause
)
2114 Window::PositionInStack() const
2116 if (fCurrentStack
.Get() == NULL
)
2118 return fCurrentStack
->WindowList().IndexOf(this);
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
)
2130 if (fCurrentStack
->CountWindows() == 1)
2133 int32 index
= PositionInStack();
2135 if (fCurrentStack
->RemoveWindow(this) == 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)
2156 // propagate focus to the new decorator
2157 SetFocus(IsFocus());
2159 if (remainingTop
!= NULL
) {
2160 fDesktop
->RebuildAndRedrawAfterWindowChange(remainingTop
,
2161 remainingTop
->VisibleRegion());
2168 Window::AddWindowToStack(Window
* window
)
2170 ASSERT_MULTI_WRITE_LOCKED(fDesktop
->WindowLocker());
2172 WindowStack
* stack
= GetWindowStack();
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())
2200 if (stack
->AddWindow(window
, position
) == 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());
2219 Window::StackedWindowAt(const BPoint
& where
)
2221 ::Decorator
* decorator
= Decorator();
2222 if (decorator
== NULL
)
2225 int tab
= decorator
->TabAt(where
);
2226 // if we have a decorator we also have a stack
2227 Window
* window
= fCurrentStack
->WindowAt(tab
);
2235 Window::TopLayerStackWindow()
2237 if (fCurrentStack
.Get() == NULL
)
2239 return fCurrentStack
->TopLayerWindow();
2244 Window::GetWindowStack()
2246 if (fCurrentStack
.Get() == NULL
)
2247 return _InitWindowStack();
2248 return fCurrentStack
;
2253 Window::MoveToTopStackLayer()
2255 ::Decorator
* decorator
= Decorator();
2256 if (decorator
== NULL
)
2258 decorator
->SetDrawingEngine(fDrawingEngine
);
2259 SetLook(Look(), NULL
);
2260 decorator
->SetTopTab(PositionInStack());
2261 return fCurrentStack
->MoveToTopLayer(this);
2266 Window::MoveToStackPosition(int32 to
, bool isMoving
)
2268 if (fCurrentStack
.Get() == NULL
)
2270 int32 index
= PositionInStack();
2271 if (fCurrentStack
->Move(index
, to
) == false)
2275 ::Decorator
* decorator
= Decorator();
2276 if (decorator
&& decorator
->MoveTab(index
, to
, isMoving
, &dirty
) == false)
2279 fDesktop
->RebuildAndRedrawAfterWindowChange(this, dirty
);
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
);
2296 if (stack
->AddWindow(this) != true) {
2300 fCurrentStack
.SetTo(stack
, true);
2305 WindowStack::WindowStack(::Decorator
* decorator
)
2307 fDecorator(decorator
)
2313 WindowStack::~WindowStack()
2320 WindowStack::SetDecorator(::Decorator
* decorator
)
2323 fDecorator
= decorator
;
2328 WindowStack::Decorator()
2335 WindowStack::TopLayerWindow() const
2337 return fWindowLayerOrder
.ItemAt(fWindowLayerOrder
.CountItems() - 1);
2342 WindowStack::CountWindows()
2344 return fWindowList
.CountItems();
2349 WindowStack::WindowAt(int32 index
)
2351 return fWindowList
.ItemAt(index
);
2356 WindowStack::AddWindow(Window
* window
, int32 position
)
2358 if (position
>= 0) {
2359 if (fWindowList
.AddItem(window
, position
) == false)
2361 } else if (fWindowList
.AddItem(window
) == false)
2364 if (fWindowLayerOrder
.AddItem(window
) == false) {
2365 fWindowList
.RemoveItem(window
);
2373 WindowStack::RemoveWindow(Window
* window
)
2375 if (fWindowList
.RemoveItem(window
) == false)
2378 fWindowLayerOrder
.RemoveItem(window
);
2384 WindowStack::MoveToTopLayer(Window
* window
)
2386 int32 index
= fWindowLayerOrder
.IndexOf(window
);
2387 return fWindowLayerOrder
.MoveItem(index
,
2388 fWindowLayerOrder
.CountItems() - 1);
2393 WindowStack::Move(int32 from
, int32 to
)
2395 return fWindowList
.MoveItem(from
, to
);