4 #include <Application.h>
6 #include <MessageQueue.h>
10 #include "DrawingEngine.h"
12 #include "WindowLayer.h"
17 Desktop::Desktop(DrawView
* drawView
, DrawingEngine
* engine
)
18 : BLooper("desktop", B_URGENT_DISPLAY_PRIORITY
),
20 fLastMousePos(-1.0, -1.0),
26 fClippingLock("clipping lock"),
34 fDrawingEngine(engine
),
38 fFocusFollowsMouse(true),
41 fDrawView
->SetDesktop(this);
43 BRegion stillAvailableOnScreen
;
44 _RebuildClippingForAllWindows(&stillAvailableOnScreen
);
45 _SetBackground(&stillAvailableOnScreen
);
55 Desktop::MouseDown(BPoint where
, uint32 buttons
, int32 clicks
)
57 fLastMousePos
= where
;
58 fClickedWindow
= WindowAt(where
);
59 fClickTime
= system_time();
60 if (buttons
== B_PRIMARY_MOUSE_BUTTON
) {
63 if (modifiers() & B_SHIFT_KEY
) {
64 fScrollingView
= fClickedWindow
->ViewAt(where
);
65 } else if (clicks
>= 2) {
66 HideWindow(fClickedWindow
);
67 fClickedWindow
= NULL
;
69 BRect
frame(fClickedWindow
->Frame());
70 BRect
resizeRect(frame
.right
- 10, frame
.bottom
- 10,
71 frame
.right
+ 4, frame
.bottom
+ 4);
72 fResizing
= resizeRect
.Contains(where
);
75 } else if (buttons
== B_SECONDARY_MOUSE_BUTTON
) {
77 SendToBack(fClickedWindow
);
80 } else if (buttons
== B_TERTIARY_MOUSE_BUTTON
) {
81 fDrawView
->Invalidate();
87 Desktop::MouseUp(BPoint where
)
89 if (!fIs2ndButton
&& system_time() - fClickTime
< 250000L && fClickedWindow
) {
90 BringToFront(fClickedWindow
);
94 fClickedWindow
= NULL
;
95 fScrollingView
= NULL
;
100 Desktop::MouseMoved(BPoint where
, uint32 code
, const BMessage
* dragMessage
)
103 if (!fTracking
&& fFocusFollowsMouse
&& (window
= WindowAt(where
))) {
104 SetFocusWindow(window
);
107 int32 dx
= (int32
)(where
.x
- fLastMousePos
.x
);
108 int32 dy
= (int32
)(where
.y
- fLastMousePos
.y
);
109 fLastMousePos
= where
;
111 if (dx
!= 0 || dy
!= 0) {
112 if (fClickedWindow
) {
113 if (fScrollingView
) {
114 if (LockClipping()) {
115 fClickedWindow
->ScrollViewBy(fScrollingView
, -dx
, -dy
);
118 } else if (fResizing
) {
119 //bigtime_t now = system_time();
120 ResizeWindowBy(fClickedWindow
, dx
, dy
);
121 //printf("resizing: %lld\n", system_time() - now);
123 MoveWindowBy(fClickedWindow
, dx
, dy
);
124 //printf("moving: %lld\n", system_time() - now);
128 } else if (fIs2ndButton
) {
129 fDrawingEngine
->StrokeLine(fLastMousePos
, where
, (rgb_color
){ 0, 0, 0, 255 });
130 fLastMousePos
= where
;
136 Desktop::MessageReceived(BMessage
* message
)
138 switch (message
->what
) {
143 if (message
->FindPoint("where", &where
) >= B_OK
&&
144 message
->FindInt32("buttons", (int32
*)&buttons
) >= B_OK
&&
145 message
->FindInt32("clicks", &clicks
) >= B_OK
) {
150 MouseDown(where
, buttons
, clicks
);
156 if (message
->FindPoint("where", &where
) >= B_OK
) {
165 case B_MOUSE_MOVED
: {
166 if (!MessageQueue()->FindMessage(B_MOUSE_MOVED
, 0)) {
169 if (message
->FindPoint("where", &where
) >= B_OK
&&
170 message
->FindInt32("be:transit", (int32
*)&transit
) >= B_OK
) {
175 MouseMoved(where
, transit
, NULL
);
181 case MSG_ADD_WINDOW
: {
183 if (message
->FindPointer("window", (void**)&window
) >= B_OK
)
189 if (LockClipping()) {
190 int32 count
= CountWindows();
191 for (int32 i
= 0; i
< count
; i
++)
192 WindowAtFast(i
)->PostMessage(B_QUIT_REQUESTED
);
198 BLooper::MessageReceived(message
);
206 Desktop::SetMasterClipping(BRegion
* clipping
)
208 BRegion update
= *clipping
;
209 update
.Exclude(&fMasterClipping
);
211 fMasterClipping
= *clipping
;
212 // since parts of the view might have been exposed,
213 // we need a clipping rebuild
215 _RebuildClippingForAllWindows(&background
);
216 _SetBackground(&background
);
218 fDrawingEngine
->FillRegion(&fBackgroundRegion
, (rgb_color
){ 51, 102, 152, 255 });
220 // trigger redrawing windows
221 update
.Exclude(&fBackgroundRegion
);
227 Desktop::SetOffset(int32 x
, int32 y
)
237 Desktop::AddWindow(WindowLayer
* window
)
239 bool success
= false;
240 if (fWindows
.AddItem((void*)window
)) {
241 // rebuild the entire screen clipping and draw the new window
242 if (LockClipping()) {
244 _RebuildClippingForAllWindows(&background
);
245 fBackgroundRegion
.Exclude(&window
->VisibleRegion());
246 MarkDirty(&window
->VisibleRegion());
247 _SetBackground(&background
);
251 SetFocusWindow(window
);
260 Desktop::RemoveWindow(WindowLayer
* window
)
262 bool success
= false;
263 if (fWindows
.RemoveItem((void*)window
)) {
264 // rebuild the entire screen clipping and redraw the exposed windows
265 if (LockClipping()) {
266 BRegion dirty
= window
->VisibleRegion();
268 _RebuildClippingForAllWindows(&background
);
270 _SetBackground(&background
);
281 Desktop::IndexOf(WindowLayer
* window
) const
283 return fWindows
.IndexOf((void*)window
);
288 Desktop::CountWindows() const
290 return fWindows
.CountItems();
295 Desktop::HasWindow(WindowLayer
* window
) const
297 return fWindows
.HasItem((void*)window
);
302 Desktop::WindowAt(int32 index
) const
304 return (WindowLayer
*)fWindows
.ItemAt(index
);
309 Desktop::WindowAtFast(int32 index
) const
311 return (WindowLayer
*)fWindows
.ItemAtFast(index
);
316 Desktop::WindowAt(const BPoint
& where
) const
318 // NOTE, since the clipping is only changed from this thread,
319 // it is save to use it without locking
320 int32 count
= CountWindows();
321 for (int32 i
= count
- 1; i
>= 0; i
--) {
322 WindowLayer
* window
= WindowAtFast(i
);
323 if (!window
->IsHidden() && window
->VisibleRegion().Contains(where
))
331 Desktop::TopWindow() const
333 return (WindowLayer
*)fWindows
.LastItem();
338 Desktop::BottomWindow() const
340 return (WindowLayer
*)fWindows
.FirstItem();
347 Desktop::MoveWindowBy(WindowLayer
* window
, int32 x
, int32 y
)
352 if (LockClipping()) {
353 // the dirty region starts with the visible area of the window being moved
354 BRegion
newDirtyRegion(window
->VisibleRegion());
356 window
->MoveBy(x
, y
);
359 _RebuildClippingForAllWindows(&background
);
361 // construct the region that is possible to be blitted
362 // to move the contents of the window
363 BRegion
copyRegion(window
->VisibleRegion());
364 copyRegion
.OffsetBy(-x
, -y
);
365 copyRegion
.IntersectWith(&newDirtyRegion
);
367 // include the the new visible region of the window being
368 // moved into the dirty region (for now)
369 newDirtyRegion
.Include(&window
->VisibleRegion());
371 fDrawingEngine
->CopyRegion(©Region
, x
, y
);
373 copyRegion
.OffsetBy(x
, y
);
374 newDirtyRegion
.Exclude(©Region
);
376 MarkDirty(&newDirtyRegion
);
377 _SetBackground(&background
);
387 Desktop::ResizeWindowBy(WindowLayer
* window
, int32 x
, int32 y
)
392 if (LockClipping()) {
393 BRegion newDirtyRegion
;
394 BRegion
previouslyOccupiedRegion(window
->VisibleRegion());
396 window
->ResizeBy(x
, y
, &newDirtyRegion
);
399 _RebuildClippingForAllWindows(&background
);
401 previouslyOccupiedRegion
.Exclude(&window
->VisibleRegion());
403 newDirtyRegion
.IntersectWith(&window
->VisibleRegion());
404 newDirtyRegion
.Include(&previouslyOccupiedRegion
);
406 MarkDirty(&newDirtyRegion
);
407 _SetBackground(&background
);
417 Desktop::ShowWindow(WindowLayer
* window
)
419 SetWindowHidden(window
, false);
424 Desktop::HideWindow(WindowLayer
* window
)
426 SetWindowHidden(window
, true);
431 Desktop::SetWindowHidden(WindowLayer
* window
, bool hidden
)
433 // TODO: if in ffm mode, make sure to switch focus
435 if (LockClipping()) {
437 if (window
->IsHidden() != hidden
) {
439 window
->SetHidden(hidden
);
444 // after rebuilding the clipping,
445 // this window will not have a visible
446 // region anymore, so we need to remember
448 // (actually that's not true, since
449 // hidden windows are excluded from the
450 // clipping calculation, but anyways)
451 dirty
= window
->VisibleRegion();
455 _RebuildClippingForAllWindows(&background
);
456 _SetBackground(&background
);
459 // everything that is now visible in the
460 // window needs a redraw, but other windows
461 // are not affected, we can call ProcessDirtyRegion()
462 // of the window, and don't have to use MarkDirty()
463 dirty
= window
->VisibleRegion();
464 window
->ProcessDirtyRegion(&dirty
);
466 // when the window was hidden, the dirty region
467 // affects other windows
479 Desktop::BringToFront(WindowLayer
* window
)
481 if (window
== TopWindow())
484 if (LockClipping()) {
486 // we don't need to redraw what is currently
487 // visible of the window
488 BRegion
clean(window
->VisibleRegion());
490 // detach window and re-atach at last position
491 if (fWindows
.RemoveItem((void*)window
) &&
492 fWindows
.AddItem((void*)window
)) {
495 _RebuildClippingForAllWindows(&dummy
);
497 // redraw what became visible of the window
498 BRegion
dirty(window
->VisibleRegion());
499 dirty
.Exclude(&clean
);
507 if (!fFocusFollowsMouse
)
508 SetFocusWindow(TopWindow());
513 Desktop::SendToBack(WindowLayer
* window
)
515 if (window
== BottomWindow())
518 if (LockClipping()) {
520 // what is currently visible of the window
521 // might be dirty after the window is send to back
522 BRegion
dirty(window
->VisibleRegion());
524 // detach window and re-atach at last position
525 if (fWindows
.RemoveItem((void*)window
) &&
526 fWindows
.AddItem((void*)window
, 0)) {
529 _RebuildClippingForAllWindows(&dummy
);
531 // redraw what was previously visible of the window
532 BRegion
clean(window
->VisibleRegion());
533 dirty
.Exclude(&clean
);
541 if (!fFocusFollowsMouse
)
542 SetFocusWindow(TopWindow());
547 Desktop::SetFocusWindow(WindowLayer
* window
)
549 if (fFocusWindow
== window
)
552 if (LockClipping()) {
555 fFocusWindow
->SetFocus(false);
557 fFocusWindow
= window
;
560 fFocusWindow
->SetFocus(true);
572 Desktop::MarkDirty(BRegion
* region
)
574 if (region
->CountRects() == 0)
577 if (LockClipping()) {
578 // send redraw messages to all windows intersecting the dirty region
579 _TriggerWindowRedrawing(region
);
587 Desktop::WindowDied(WindowLayer
* window
)
589 // thread is expected expected to have the
591 fWindows
.RemoveItem(window
);
592 if (fWindows
.CountItems() == 0)
593 be_app
->PostMessage(B_QUIT_REQUESTED
);
598 // _RebuildClippingForAllWindows
600 Desktop::_RebuildClippingForAllWindows(BRegion
* stillAvailableOnScreen
)
602 // the available region on screen starts with the entire screen area
603 // each window on the screen will take a portion from that area
605 // figure out what the entire screen area is
606 *stillAvailableOnScreen
= fMasterClipping
;
608 // set clipping of each window
609 int32 count
= CountWindows();
610 for (int32 i
= count
- 1; i
>= 0; i
--) {
611 WindowLayer
* window
= WindowAtFast(i
);
612 if (!window
->IsHidden()) {
613 window
->SetClipping(stillAvailableOnScreen
);
614 // that windows region is not available on screen anymore
615 stillAvailableOnScreen
->Exclude(&window
->VisibleRegion());
620 // _TriggerWindowRedrawing
622 Desktop::_TriggerWindowRedrawing(BRegion
* newDirtyRegion
)
624 // send redraw messages to all windows intersecting the dirty region
625 int32 count
= CountWindows();
626 for (int32 i
= count
- 1; i
>= 0; i
--) {
627 WindowLayer
* window
= WindowAtFast(i
);
628 if (!window
->IsHidden() && newDirtyRegion
->Intersects(window
->VisibleRegion().Frame()))
629 window
->ProcessDirtyRegion(newDirtyRegion
);
635 Desktop::_SetBackground(BRegion
* background
)
637 // NOTE: the drawing operation is caried out
638 // in the clipping region rebuild, but it is
639 // ok actually, because it also avoids trails on
642 // remember the region not covered by any windows
643 // and redraw the dirty background
644 BRegion
dirtyBackground(*background
);
645 dirtyBackground
.Exclude(&fBackgroundRegion
);
646 dirtyBackground
.IntersectWith(background
);
647 fBackgroundRegion
= *background
;
648 if (dirtyBackground
.Frame().IsValid()) {
649 fDrawingEngine
->FillRegion(&dirtyBackground
, (rgb_color
){ 51, 102, 152, 255 });