headers/bsd: Add sys/queue.h.
[haiku.git] / src / tests / servers / app / newerClipping / Desktop.cpp
blob76d39225e42147cec66651a790ba4b9e17c3880b
2 #include <stdio.h>
4 #include <Application.h>
5 #include <Message.h>
6 #include <MessageQueue.h>
7 #include <Messenger.h>
8 #include <Window.h>
10 #include "DrawingEngine.h"
11 #include "DrawView.h"
12 #include "WindowLayer.h"
14 #include "Desktop.h"
16 // constructor
17 Desktop::Desktop(DrawView* drawView, DrawingEngine* engine)
18 : BLooper("desktop", B_URGENT_DISPLAY_PRIORITY),
19 fTracking(false),
20 fLastMousePos(-1.0, -1.0),
21 fClickedWindow(NULL),
22 fScrollingView(NULL),
23 fResizing(false),
24 fIs2ndButton(false),
26 fClippingLock("clipping lock"),
27 fBackgroundRegion(),
29 fMasterClipping(),
30 fXOffset(0),
31 fYOffset(0),
33 fDrawView(drawView),
34 fDrawingEngine(engine),
36 fWindows(64),
38 fFocusFollowsMouse(true),
39 fFocusWindow(NULL)
41 fDrawView->SetDesktop(this);
43 BRegion stillAvailableOnScreen;
44 _RebuildClippingForAllWindows(&stillAvailableOnScreen);
45 _SetBackground(&stillAvailableOnScreen);
48 // destructor
49 Desktop::~Desktop()
53 // MouseDown
54 void
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) {
61 fTracking = true;
62 if (fClickedWindow) {
63 if (modifiers() & B_SHIFT_KEY) {
64 fScrollingView = fClickedWindow->ViewAt(where);
65 } else if (clicks >= 2) {
66 HideWindow(fClickedWindow);
67 fClickedWindow = NULL;
68 } else {
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) {
76 if (fClickedWindow)
77 SendToBack(fClickedWindow);
79 fIs2ndButton = true;
80 } else if (buttons == B_TERTIARY_MOUSE_BUTTON) {
81 fDrawView->Invalidate();
85 // MouseUp
86 void
87 Desktop::MouseUp(BPoint where)
89 if (!fIs2ndButton && system_time() - fClickTime < 250000L && fClickedWindow) {
90 BringToFront(fClickedWindow);
92 fTracking = false;
93 fIs2ndButton = false;
94 fClickedWindow = NULL;
95 fScrollingView = NULL;
98 // MouseMoved
99 void
100 Desktop::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
102 WindowLayer* window;
103 if (!fTracking && fFocusFollowsMouse && (window = WindowAt(where))) {
104 SetFocusWindow(window);
106 if (fTracking) {
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);
116 UnlockClipping();
118 } else if (fResizing) {
119 //bigtime_t now = system_time();
120 ResizeWindowBy(fClickedWindow, dx, dy);
121 //printf("resizing: %lld\n", system_time() - now);
122 } else {
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;
134 // MessageReceived
135 void
136 Desktop::MessageReceived(BMessage* message)
138 switch (message->what) {
139 case B_MOUSE_DOWN: {
140 BPoint where;
141 uint32 buttons;
142 int32 clicks;
143 if (message->FindPoint("where", &where) >= B_OK &&
144 message->FindInt32("buttons", (int32*)&buttons) >= B_OK &&
145 message->FindInt32("clicks", &clicks) >= B_OK) {
147 where.x += fXOffset;
148 where.y += fYOffset;
150 MouseDown(where, buttons, clicks);
152 break;
154 case B_MOUSE_UP: {
155 BPoint where;
156 if (message->FindPoint("where", &where) >= B_OK) {
158 where.x += fXOffset;
159 where.y += fYOffset;
161 MouseUp(where);
163 break;
165 case B_MOUSE_MOVED: {
166 if (!MessageQueue()->FindMessage(B_MOUSE_MOVED, 0)) {
167 BPoint where;
168 uint32 transit;
169 if (message->FindPoint("where", &where) >= B_OK &&
170 message->FindInt32("be:transit", (int32*)&transit) >= B_OK) {
172 where.x += fXOffset;
173 where.y += fYOffset;
175 MouseMoved(where, transit, NULL);
178 break;
181 case MSG_ADD_WINDOW: {
182 WindowLayer* window;
183 if (message->FindPointer("window", (void**)&window) >= B_OK)
184 AddWindow(window);
185 break;
188 case MSG_QUIT:
189 if (LockClipping()) {
190 int32 count = CountWindows();
191 for (int32 i = 0; i < count; i++)
192 WindowAtFast(i)->PostMessage(B_QUIT_REQUESTED);
193 UnlockClipping();
195 break;
197 default:
198 BLooper::MessageReceived(message);
202 // #pragma mark -
204 // SetMasterClipping
205 void
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
214 BRegion background;
215 _RebuildClippingForAllWindows(&background);
216 _SetBackground(&background);
218 fDrawingEngine->FillRegion(&fBackgroundRegion, (rgb_color){ 51, 102, 152, 255 });
220 // trigger redrawing windows
221 update.Exclude(&fBackgroundRegion);
222 MarkDirty(&update);
225 // SetOffset
226 void
227 Desktop::SetOffset(int32 x, int32 y)
229 fXOffset = x;
230 fYOffset = y;
233 // #pragma mark -
235 // AddWindow
236 bool
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()) {
243 BRegion background;
244 _RebuildClippingForAllWindows(&background);
245 fBackgroundRegion.Exclude(&window->VisibleRegion());
246 MarkDirty(&window->VisibleRegion());
247 _SetBackground(&background);
249 UnlockClipping();
251 SetFocusWindow(window);
253 success = true;
255 return success;
258 // RemoveWindow
259 bool
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();
267 BRegion background;
268 _RebuildClippingForAllWindows(&background);
269 MarkDirty(&dirty);
270 _SetBackground(&background);
272 UnlockClipping();
274 success = true;
276 return success;
279 // IndexOf
280 int32
281 Desktop::IndexOf(WindowLayer* window) const
283 return fWindows.IndexOf((void*)window);
286 // CountWindows
287 int32
288 Desktop::CountWindows() const
290 return fWindows.CountItems();
293 // HasWindow
294 bool
295 Desktop::HasWindow(WindowLayer* window) const
297 return fWindows.HasItem((void*)window);
300 // WindowAt
301 WindowLayer*
302 Desktop::WindowAt(int32 index) const
304 return (WindowLayer*)fWindows.ItemAt(index);
307 // WindowAtFast
308 WindowLayer*
309 Desktop::WindowAtFast(int32 index) const
311 return (WindowLayer*)fWindows.ItemAtFast(index);
314 // WindowAt
315 WindowLayer*
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))
324 return window;
326 return NULL;
329 // TopWindow
330 WindowLayer*
331 Desktop::TopWindow() const
333 return (WindowLayer*)fWindows.LastItem();
336 // BottomWindow
337 WindowLayer*
338 Desktop::BottomWindow() const
340 return (WindowLayer*)fWindows.FirstItem();
343 #pragma mark -
345 // MoveWindowBy
346 void
347 Desktop::MoveWindowBy(WindowLayer* window, int32 x, int32 y)
349 if (!Lock())
350 return;
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);
358 BRegion background;
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(&copyRegion, x, y);
373 copyRegion.OffsetBy(x, y);
374 newDirtyRegion.Exclude(&copyRegion);
376 MarkDirty(&newDirtyRegion);
377 _SetBackground(&background);
379 UnlockClipping();
382 Unlock();
385 // ResizeWindowBy
386 void
387 Desktop::ResizeWindowBy(WindowLayer* window, int32 x, int32 y)
389 if (!Lock())
390 return;
392 if (LockClipping()) {
393 BRegion newDirtyRegion;
394 BRegion previouslyOccupiedRegion(window->VisibleRegion());
396 window->ResizeBy(x, y, &newDirtyRegion);
398 BRegion background;
399 _RebuildClippingForAllWindows(&background);
401 previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
403 newDirtyRegion.IntersectWith(&window->VisibleRegion());
404 newDirtyRegion.Include(&previouslyOccupiedRegion);
406 MarkDirty(&newDirtyRegion);
407 _SetBackground(&background);
409 UnlockClipping();
412 Unlock();
415 // ShowWindow
416 void
417 Desktop::ShowWindow(WindowLayer* window)
419 SetWindowHidden(window, false);
422 // HideWindow
423 void
424 Desktop::HideWindow(WindowLayer* window)
426 SetWindowHidden(window, true);
429 // SetWindowHidden
430 void
431 Desktop::SetWindowHidden(WindowLayer* window, bool hidden)
433 // TODO: if in ffm mode, make sure to switch focus
434 // if appropriate
435 if (LockClipping()) {
437 if (window->IsHidden() != hidden) {
439 window->SetHidden(hidden);
441 BRegion dirty;
443 if (hidden) {
444 // after rebuilding the clipping,
445 // this window will not have a visible
446 // region anymore, so we need to remember
447 // it now
448 // (actually that's not true, since
449 // hidden windows are excluded from the
450 // clipping calculation, but anyways)
451 dirty = window->VisibleRegion();
454 BRegion background;
455 _RebuildClippingForAllWindows(&background);
456 _SetBackground(&background);
458 if (!hidden) {
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);
465 } else {
466 // when the window was hidden, the dirty region
467 // affects other windows
468 MarkDirty(&dirty);
472 UnlockClipping();
477 // BringToFront
478 void
479 Desktop::BringToFront(WindowLayer* window)
481 if (window == TopWindow())
482 return;
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)) {
494 BRegion dummy;
495 _RebuildClippingForAllWindows(&dummy);
497 // redraw what became visible of the window
498 BRegion dirty(window->VisibleRegion());
499 dirty.Exclude(&clean);
501 MarkDirty(&dirty);
504 UnlockClipping();
507 if (!fFocusFollowsMouse)
508 SetFocusWindow(TopWindow());
511 // SendToBack
512 void
513 Desktop::SendToBack(WindowLayer* window)
515 if (window == BottomWindow())
516 return;
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)) {
528 BRegion dummy;
529 _RebuildClippingForAllWindows(&dummy);
531 // redraw what was previously visible of the window
532 BRegion clean(window->VisibleRegion());
533 dirty.Exclude(&clean);
535 MarkDirty(&dirty);
538 UnlockClipping();
541 if (!fFocusFollowsMouse)
542 SetFocusWindow(TopWindow());
545 // SetFocusWindow
546 void
547 Desktop::SetFocusWindow(WindowLayer* window)
549 if (fFocusWindow == window)
550 return;
552 if (LockClipping()) {
554 if (fFocusWindow)
555 fFocusWindow->SetFocus(false);
557 fFocusWindow = window;
559 if (fFocusWindow)
560 fFocusWindow->SetFocus(true);
562 UnlockClipping();
568 // #pragma mark -
570 // MarkDirty
571 void
572 Desktop::MarkDirty(BRegion* region)
574 if (region->CountRects() == 0)
575 return;
577 if (LockClipping()) {
578 // send redraw messages to all windows intersecting the dirty region
579 _TriggerWindowRedrawing(region);
581 UnlockClipping();
585 // WindowDied
586 void
587 Desktop::WindowDied(WindowLayer* window)
589 // thread is expected expected to have the
590 // write lock!
591 fWindows.RemoveItem(window);
592 if (fWindows.CountItems() == 0)
593 be_app->PostMessage(B_QUIT_REQUESTED);
596 // #pragma mark -
598 // _RebuildClippingForAllWindows
599 void
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
621 void
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);
633 // _SetBackground
634 void
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
640 // moving windows
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 });