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(©Region, x, y); 372 373 copyRegion.OffsetBy(x, y); 374 newDirtyRegion.Exclude(©Region); 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