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