xref: /haiku/src/tests/servers/app/newerClipping/Desktop.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
1 
2 #include <stdio.h>
3 
4 #include <Application.h>
5 #include <Message.h>
6 #include <MessageQueue.h>
7 #include <Messenger.h>
8 #include <Window.h>
9 
10 #include "DrawingEngine.h"
11 #include "DrawView.h"
12 #include "WindowLayer.h"
13 
14 #include "Desktop.h"
15 
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),
25 
26 	  fClippingLock("clipping lock"),
27 	  fBackgroundRegion(),
28 
29 	  fMasterClipping(),
30 	  fXOffset(0),
31 	  fYOffset(0),
32 
33 	  fDrawView(drawView),
34 	  fDrawingEngine(engine),
35 
36 	  fWindows(64),
37 
38 	  fFocusFollowsMouse(true),
39 	  fFocusWindow(NULL)
40 {
41 	fDrawView->SetDesktop(this);
42 
43 	BRegion stillAvailableOnScreen;
44 	_RebuildClippingForAllWindows(&stillAvailableOnScreen);
45 	_SetBackground(&stillAvailableOnScreen);
46 }
47 
48 // destructor
49 Desktop::~Desktop()
50 {
51 }
52 
53 // MouseDown
54 void
55 Desktop::MouseDown(BPoint where, uint32 buttons, int32 clicks)
56 {
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);
73 			}
74 		}
75 	} else if (buttons == B_SECONDARY_MOUSE_BUTTON) {
76 		if (fClickedWindow)
77 			SendToBack(fClickedWindow);
78 
79 		fIs2ndButton = true;
80 	} else if (buttons == B_TERTIARY_MOUSE_BUTTON) {
81 		fDrawView->Invalidate();
82 	}
83 }
84 
85 // MouseUp
86 void
87 Desktop::MouseUp(BPoint where)
88 {
89 	if (!fIs2ndButton && system_time() - fClickTime < 250000L && fClickedWindow) {
90 		BringToFront(fClickedWindow);
91 	}
92 	fTracking = false;
93 	fIs2ndButton = false;
94 	fClickedWindow = NULL;
95 	fScrollingView = NULL;
96 }
97 
98 // MouseMoved
99 void
100 Desktop::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
101 {
102 	WindowLayer* window;
103 	if (!fTracking && fFocusFollowsMouse && (window = WindowAt(where))) {
104 		SetFocusWindow(window);
105 	}
106 	if (fTracking) {
107 		int32 dx = (int32)(where.x - fLastMousePos.x);
108 		int32 dy = (int32)(where.y - fLastMousePos.y);
109 		fLastMousePos = where;
110 
111 		if (dx != 0 || dy != 0) {
112 			if (fClickedWindow) {
113 				if (fScrollingView) {
114 					if (LockClipping()) {
115 						fClickedWindow->ScrollViewBy(fScrollingView, -dx, -dy);
116 						UnlockClipping();
117 					}
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);
125 				}
126 			}
127 		}
128 	} else if (fIs2ndButton) {
129 		fDrawingEngine->StrokeLine(fLastMousePos, where, (rgb_color){ 0, 0, 0, 255 });
130 		fLastMousePos = where;
131 	}
132 }
133 
134 // MessageReceived
135 void
136 Desktop::MessageReceived(BMessage* message)
137 {
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) {
146 
147 				where.x += fXOffset;
148 				where.y += fYOffset;
149 
150 				MouseDown(where, buttons, clicks);
151 			}
152 			break;
153 		}
154 		case B_MOUSE_UP: {
155 			BPoint where;
156 			if (message->FindPoint("where", &where) >= B_OK) {
157 
158 				where.x += fXOffset;
159 				where.y += fYOffset;
160 
161 				MouseUp(where);
162 			}
163 			break;
164 		}
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) {
171 
172 					where.x += fXOffset;
173 					where.y += fYOffset;
174 
175 					MouseMoved(where, transit, NULL);
176 				}
177 			}
178 			break;
179 		}
180 
181 		case MSG_ADD_WINDOW: {
182 			WindowLayer* window;
183 			if (message->FindPointer("window", (void**)&window) >= B_OK)
184 				AddWindow(window);
185 			break;
186 		}
187 
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();
194 			}
195 			break;
196 
197 		default:
198 			BLooper::MessageReceived(message);
199 	}
200 }
201 
202 // #pragma mark -
203 
204 // SetMasterClipping
205 void
206 Desktop::SetMasterClipping(BRegion* clipping)
207 {
208 	BRegion update = *clipping;
209 	update.Exclude(&fMasterClipping);
210 
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);
217 
218 	fDrawingEngine->FillRegion(&fBackgroundRegion, (rgb_color){ 51, 102, 152, 255 });
219 
220 	// trigger redrawing windows
221 	update.Exclude(&fBackgroundRegion);
222 	MarkDirty(&update);
223 }
224 
225 // SetOffset
226 void
227 Desktop::SetOffset(int32 x, int32 y)
228 {
229 	fXOffset = x;
230 	fYOffset = y;
231 }
232 
233 // #pragma mark -
234 
235 // AddWindow
236 bool
237 Desktop::AddWindow(WindowLayer* window)
238 {
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);
248 
249 			UnlockClipping();
250 		}
251 		SetFocusWindow(window);
252 
253 		success = true;
254 	}
255 	return success;
256 }
257 
258 // RemoveWindow
259 bool
260 Desktop::RemoveWindow(WindowLayer* window)
261 {
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);
271 
272 			UnlockClipping();
273 		}
274 		success = true;
275 	}
276 	return success;
277 }
278 
279 // IndexOf
280 int32
281 Desktop::IndexOf(WindowLayer* window) const
282 {
283 	return fWindows.IndexOf((void*)window);
284 }
285 
286 // CountWindows
287 int32
288 Desktop::CountWindows() const
289 {
290 	return fWindows.CountItems();
291 }
292 
293 // HasWindow
294 bool
295 Desktop::HasWindow(WindowLayer* window) const
296 {
297 	return fWindows.HasItem((void*)window);
298 }
299 
300 // WindowAt
301 WindowLayer*
302 Desktop::WindowAt(int32 index) const
303 {
304 	return (WindowLayer*)fWindows.ItemAt(index);
305 }
306 
307 // WindowAtFast
308 WindowLayer*
309 Desktop::WindowAtFast(int32 index) const
310 {
311 	return (WindowLayer*)fWindows.ItemAtFast(index);
312 }
313 
314 // WindowAt
315 WindowLayer*
316 Desktop::WindowAt(const BPoint& where) const
317 {
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;
325 	}
326 	return NULL;
327 }
328 
329 // TopWindow
330 WindowLayer*
331 Desktop::TopWindow() const
332 {
333 	return (WindowLayer*)fWindows.LastItem();
334 }
335 
336 // BottomWindow
337 WindowLayer*
338 Desktop::BottomWindow() const
339 {
340 	return (WindowLayer*)fWindows.FirstItem();
341 }
342 
343 #pragma mark -
344 
345 // MoveWindowBy
346 void
347 Desktop::MoveWindowBy(WindowLayer* window, int32 x, int32 y)
348 {
349 	if (!Lock())
350 		return;
351 
352 	if (LockClipping()) {
353 		// the dirty region starts with the visible area of the window being moved
354 		BRegion newDirtyRegion(window->VisibleRegion());
355 
356 		window->MoveBy(x, y);
357 
358 		BRegion background;
359 		_RebuildClippingForAllWindows(&background);
360 
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);
366 
367 		// include the the new visible region of the window being
368 		// moved into the dirty region (for now)
369 		newDirtyRegion.Include(&window->VisibleRegion());
370 
371 		fDrawingEngine->CopyRegion(&copyRegion, x, y);
372 
373 		copyRegion.OffsetBy(x, y);
374 		newDirtyRegion.Exclude(&copyRegion);
375 
376 		MarkDirty(&newDirtyRegion);
377 		_SetBackground(&background);
378 
379 		UnlockClipping();
380 	}
381 
382 	Unlock();
383 }
384 
385 // ResizeWindowBy
386 void
387 Desktop::ResizeWindowBy(WindowLayer* window, int32 x, int32 y)
388 {
389 	if (!Lock())
390 		return;
391 
392 	if (LockClipping()) {
393 		BRegion newDirtyRegion;
394 		BRegion previouslyOccupiedRegion(window->VisibleRegion());
395 
396 		window->ResizeBy(x, y, &newDirtyRegion);
397 
398 		BRegion background;
399 		_RebuildClippingForAllWindows(&background);
400 
401 		previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
402 
403 		newDirtyRegion.IntersectWith(&window->VisibleRegion());
404 		newDirtyRegion.Include(&previouslyOccupiedRegion);
405 
406 		MarkDirty(&newDirtyRegion);
407 		_SetBackground(&background);
408 
409 		UnlockClipping();
410 	}
411 
412 	Unlock();
413 }
414 
415 // ShowWindow
416 void
417 Desktop::ShowWindow(WindowLayer* window)
418 {
419 	SetWindowHidden(window, false);
420 }
421 
422 // HideWindow
423 void
424 Desktop::HideWindow(WindowLayer* window)
425 {
426 	SetWindowHidden(window, true);
427 }
428 
429 // SetWindowHidden
430 void
431 Desktop::SetWindowHidden(WindowLayer* window, bool hidden)
432 {
433 	// TODO: if in ffm mode, make sure to switch focus
434 	// if appropriate
435 	if (LockClipping()) {
436 
437 		if (window->IsHidden() != hidden) {
438 
439 			window->SetHidden(hidden);
440 
441 			BRegion dirty;
442 
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();
452 			}
453 
454 			BRegion background;
455 			_RebuildClippingForAllWindows(&background);
456 			_SetBackground(&background);
457 
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);
469 			}
470 		}
471 
472 		UnlockClipping();
473 	}
474 }
475 
476 
477 // BringToFront
478 void
479 Desktop::BringToFront(WindowLayer* window)
480 {
481 	if (window == TopWindow())
482 		return;
483 
484 	if (LockClipping()) {
485 
486 		// we don't need to redraw what is currently
487 		// visible of the window
488 		BRegion clean(window->VisibleRegion());
489 
490 		// detach window and re-atach at last position
491 		if (fWindows.RemoveItem((void*)window) &&
492 			fWindows.AddItem((void*)window)) {
493 
494 			BRegion dummy;
495 			_RebuildClippingForAllWindows(&dummy);
496 
497 			// redraw what became visible of the window
498 			BRegion dirty(window->VisibleRegion());
499 			dirty.Exclude(&clean);
500 
501 			MarkDirty(&dirty);
502 		}
503 
504 		UnlockClipping();
505 	}
506 
507 	if (!fFocusFollowsMouse)
508 		SetFocusWindow(TopWindow());
509 }
510 
511 // SendToBack
512 void
513 Desktop::SendToBack(WindowLayer* window)
514 {
515 	if (window == BottomWindow())
516 		return;
517 
518 	if (LockClipping()) {
519 
520 		// what is currently visible of the window
521 		// might be dirty after the window is send to back
522 		BRegion dirty(window->VisibleRegion());
523 
524 		// detach window and re-atach at last position
525 		if (fWindows.RemoveItem((void*)window) &&
526 			fWindows.AddItem((void*)window, 0)) {
527 
528 			BRegion dummy;
529 			_RebuildClippingForAllWindows(&dummy);
530 
531 			// redraw what was previously visible of the window
532 			BRegion clean(window->VisibleRegion());
533 			dirty.Exclude(&clean);
534 
535 			MarkDirty(&dirty);
536 		}
537 
538 		UnlockClipping();
539 	}
540 
541 	if (!fFocusFollowsMouse)
542 		SetFocusWindow(TopWindow());
543 }
544 
545 // SetFocusWindow
546 void
547 Desktop::SetFocusWindow(WindowLayer* window)
548 {
549 	if (fFocusWindow == window)
550 		return;
551 
552 	if (LockClipping()) {
553 
554 		if (fFocusWindow)
555 			fFocusWindow->SetFocus(false);
556 
557 		fFocusWindow = window;
558 
559 		if (fFocusWindow)
560 			fFocusWindow->SetFocus(true);
561 
562 		UnlockClipping();
563 	}
564 }
565 
566 
567 
568 // #pragma mark -
569 
570 // MarkDirty
571 void
572 Desktop::MarkDirty(BRegion* region)
573 {
574 	if (region->CountRects() == 0)
575 		return;
576 
577 	if (LockClipping()) {
578 		// send redraw messages to all windows intersecting the dirty region
579 		_TriggerWindowRedrawing(region);
580 
581 		UnlockClipping();
582 	}
583 }
584 
585 // WindowDied
586 void
587 Desktop::WindowDied(WindowLayer* window)
588 {
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);
594 }
595 
596 // #pragma mark -
597 
598 // _RebuildClippingForAllWindows
599 void
600 Desktop::_RebuildClippingForAllWindows(BRegion* stillAvailableOnScreen)
601 {
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
604 
605 	// figure out what the entire screen area is
606 	*stillAvailableOnScreen = fMasterClipping;
607 
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());
616 		}
617 	}
618 }
619 
620 // _TriggerWindowRedrawing
621 void
622 Desktop::_TriggerWindowRedrawing(BRegion* newDirtyRegion)
623 {
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);
630 	}
631 }
632 
633 // _SetBackground
634 void
635 Desktop::_SetBackground(BRegion* background)
636 {
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
641 
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 });
650 	}
651 }
652