1 /* 2 * Copyright 2001-2006, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Adrian Oanca <adioanca@cotty.iren.ro> 7 * Stephan Aßmus <superstippi@gmx.de> 8 * Axel Dörfler, axeld@pinc-software.de 9 */ 10 11 /** Class used to encapsulate desktop management */ 12 13 14 #include "Desktop.h" 15 16 #include "AppServer.h" 17 #include "DesktopSettingsPrivate.h" 18 #include "DrawingEngine.h" 19 #include "HWInterface.h" 20 #include "InputManager.h" 21 #include "ServerApp.h" 22 #include "ServerConfig.h" 23 #include "ServerCursor.h" 24 #include "ServerScreen.h" 25 #include "ServerWindow.h" 26 #include "WindowPrivate.h" 27 #include "WindowLayer.h" 28 #include "Workspace.h" 29 #include "WorkspacesLayer.h" 30 31 #include <WindowInfo.h> 32 #include <ServerProtocol.h> 33 34 #include <DirectWindow.h> 35 #include <Entry.h> 36 #include <Message.h> 37 #include <MessageFilter.h> 38 #include <Region.h> 39 40 #include <stdio.h> 41 42 #if TEST_MODE 43 # include "EventStream.h" 44 #endif 45 46 //#define DEBUG_DESKTOP 47 #ifdef DEBUG_DESKTOP 48 # define STRACE(a) printf(a) 49 #else 50 # define STRACE(a) ; 51 #endif 52 53 #if !USE_MULTI_LOCKER 54 # define AutoWriteLocker BAutolock 55 #endif 56 57 class KeyboardFilter : public EventFilter { 58 public: 59 KeyboardFilter(Desktop* desktop); 60 61 virtual filter_result Filter(BMessage* message, EventTarget** _target, 62 int32* _viewToken, BMessage* latestMouseMoved); 63 virtual void RemoveTarget(EventTarget* target); 64 65 private: 66 void _UpdateFocus(int32 key, EventTarget** _target); 67 68 Desktop* fDesktop; 69 EventTarget* fLastFocus; 70 bigtime_t fTimestamp; 71 }; 72 73 class MouseFilter : public EventFilter { 74 public: 75 MouseFilter(Desktop* desktop); 76 77 virtual filter_result Filter(BMessage* message, EventTarget** _target, 78 int32* _viewToken, BMessage* latestMouseMoved); 79 80 private: 81 Desktop* fDesktop; 82 }; 83 84 85 // #pragma mark - 86 87 88 KeyboardFilter::KeyboardFilter(Desktop* desktop) 89 : 90 fDesktop(desktop), 91 fLastFocus(NULL), 92 fTimestamp(0) 93 { 94 } 95 96 97 void 98 KeyboardFilter::_UpdateFocus(int32 key, EventTarget** _target) 99 { 100 if (!fDesktop->LockSingleWindow()) 101 return; 102 103 bigtime_t now = system_time(); 104 105 EventTarget* focus = NULL; 106 if (fDesktop->FocusWindow() != NULL) 107 focus = &fDesktop->FocusWindow()->EventTarget(); 108 109 // TODO: this is a try to not steal focus from the current window 110 // in case you enter some text and a window pops up you haven't 111 // triggered yourself (like a pop-up window in your browser while 112 // you're typing a password in another window) - maybe this should 113 // be done differently, though (using something like B_LOCK_WINDOW_FOCUS) 114 // (at least B_WINDOW_ACTIVATED must be postponed) 115 116 if (fLastFocus == NULL || (focus != fLastFocus && now - fTimestamp > 100000)) { 117 // if the time span between the key presses is very short 118 // we keep our previous focus alive - this is save even 119 // if the target doesn't exist anymore, as we don't reset 120 // it, and the event focus passed in is always valid (or NULL) 121 *_target = focus; 122 fLastFocus = focus; 123 } 124 125 fDesktop->UnlockSingleWindow(); 126 127 // we always allow to switch focus after the enter key has pressed 128 if (key == B_ENTER) 129 fTimestamp = 0; 130 else 131 fTimestamp = now; 132 } 133 134 135 filter_result 136 KeyboardFilter::Filter(BMessage* message, EventTarget** _target, 137 int32* /*_viewToken*/, BMessage* /*latestMouseMoved*/) 138 { 139 int32 key = 0; 140 int32 modifiers; 141 142 if (message->what == B_KEY_DOWN 143 && message->FindInt32("key", &key) == B_OK 144 && message->FindInt32("modifiers", &modifiers) == B_OK) { 145 // TODO: for some reason, one of the above is failing when pressing 146 // a modifier key at least with the old BMessage implementation 147 // (a message dump shows all entries, though) 148 // Try again with BMessage4! 149 150 // Check for safe video mode (F12 + l-cmd + l-ctrl + l-shift) 151 if (key == 0x0d 152 && (modifiers & (B_LEFT_COMMAND_KEY 153 | B_LEFT_CONTROL_KEY | B_LEFT_SHIFT_KEY)) != 0) { 154 // TODO: Set to Safe Mode in KeyboardEventHandler:B_KEY_DOWN. 155 STRACE(("Safe Video Mode invoked - code unimplemented\n")); 156 return B_SKIP_MESSAGE; 157 } 158 159 if (key > 0x01 && key < 0x0e) { 160 // workspace change, F1-F12 161 162 #if !TEST_MODE 163 if (modifiers & B_COMMAND_KEY) 164 #else 165 if (modifiers & B_CONTROL_KEY) 166 #endif 167 { 168 STRACE(("Set Workspace %ld\n", key - 1)); 169 170 fDesktop->SetWorkspace(key - 2); 171 return B_SKIP_MESSAGE; 172 } 173 } 174 175 // TODO: this should be moved client side! 176 // (that's how it is done in BeOS, clients could need this key for 177 // different purposes - also, it's preferrable to let the client 178 // write the dump within his own environment) 179 if (key == 0xe) { 180 // screen dump, PrintScreen 181 char filename[128]; 182 BEntry entry; 183 184 int32 index = 1; 185 do { 186 sprintf(filename, "/boot/home/screen%ld.png", index++); 187 entry.SetTo(filename); 188 } while(entry.Exists()); 189 190 fDesktop->GetDrawingEngine()->DumpToFile(filename); 191 return B_SKIP_MESSAGE; 192 } 193 } 194 195 if (message->what == B_KEY_DOWN 196 || message->what == B_MODIFIERS_CHANGED 197 || message->what == B_UNMAPPED_KEY_DOWN) 198 _UpdateFocus(key, _target); 199 200 return B_DISPATCH_MESSAGE; 201 } 202 203 204 void 205 KeyboardFilter::RemoveTarget(EventTarget* target) 206 { 207 if (target == fLastFocus) 208 fLastFocus = NULL; 209 } 210 211 212 // #pragma mark - 213 214 215 MouseFilter::MouseFilter(Desktop* desktop) 216 : 217 fDesktop(desktop) 218 { 219 } 220 221 222 filter_result 223 MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken, 224 BMessage* latestMouseMoved) 225 { 226 BPoint where; 227 if (message->FindPoint("where", &where) != B_OK) 228 return B_DISPATCH_MESSAGE; 229 230 if (!fDesktop->LockAllWindows()) 231 return B_DISPATCH_MESSAGE; 232 233 int32 viewToken = B_NULL_TOKEN; 234 235 WindowLayer* window = fDesktop->MouseEventWindow(); 236 if (window == NULL) 237 window = fDesktop->WindowAt(where); 238 239 if (window != NULL) { 240 // dispatch event in the window layers 241 switch (message->what) { 242 case B_MOUSE_DOWN: 243 window->MouseDown(message, where, &viewToken); 244 break; 245 246 case B_MOUSE_UP: 247 window->MouseUp(message, where, &viewToken); 248 fDesktop->SetMouseEventWindow(NULL); 249 break; 250 251 case B_MOUSE_MOVED: 252 window->MouseMoved(message, where, &viewToken, 253 latestMouseMoved == NULL || latestMouseMoved == message); 254 break; 255 } 256 257 if (viewToken != B_NULL_TOKEN) { 258 fDesktop->SetViewUnderMouse(window, viewToken); 259 260 *_viewToken = viewToken; 261 *_target = &window->EventTarget(); 262 } 263 } 264 265 if (window == NULL || viewToken == B_NULL_TOKEN) { 266 // mouse is not over a window or over a decorator 267 fDesktop->SetViewUnderMouse(window, B_NULL_TOKEN); 268 fDesktop->SetCursor(NULL); 269 270 *_target = NULL; 271 } 272 273 fDesktop->UnlockAllWindows(); 274 275 return B_DISPATCH_MESSAGE; 276 } 277 278 279 // #pragma mark - 280 281 282 static inline uint32 283 workspace_to_workspaces(int32 index) 284 { 285 return 1UL << index; 286 } 287 288 289 static inline bool 290 workspace_in_workspaces(int32 index, uint32 workspaces) 291 { 292 return (workspaces & (1UL << index)) != 0; 293 } 294 295 296 // #pragma mark - 297 298 299 Desktop::Desktop(uid_t userID) 300 : MessageLooper("desktop"), 301 302 fUserID(userID), 303 fSettings(NULL), 304 fSharedReadOnlyArea(-1), 305 fApplicationsLock("application list"), 306 fShutdownSemaphore(-1), 307 fCurrentWorkspace(0), 308 fAllWindows(kAllWindowList), 309 fSubsetWindows(kSubsetList), 310 fWorkspacesLayer(NULL), 311 fActiveScreen(NULL), 312 fWindowLock("window lock"), 313 fMouseEventWindow(NULL), 314 fFocus(NULL), 315 fFront(NULL), 316 fBack(NULL) 317 { 318 char name[B_OS_NAME_LENGTH]; 319 Desktop::_GetLooperName(name, sizeof(name)); 320 321 fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name); 322 if (fMessagePort < B_OK) 323 return; 324 325 fLink.SetReceiverPort(fMessagePort); 326 } 327 328 329 Desktop::~Desktop() 330 { 331 delete fSettings; 332 333 delete_area(fSharedReadOnlyArea); 334 delete_port(fMessagePort); 335 gFontManager->DetachUser(fUserID); 336 } 337 338 339 status_t 340 Desktop::Init() 341 { 342 if (fMessagePort < B_OK) 343 return fMessagePort; 344 345 char name[B_OS_NAME_LENGTH]; 346 snprintf(name, sizeof(name), "d:%d:shared read only", /*id*/0); 347 fSharedReadOnlyArea = create_area(name, (void **)&fServerReadOnlyMemory, 348 B_ANY_ADDRESS, B_PAGE_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 349 if (fSharedReadOnlyArea < B_OK) 350 return fSharedReadOnlyArea; 351 352 fSettings = new DesktopSettings::Private(fServerReadOnlyMemory); 353 354 for (int32 i = 0; i < kMaxWorkspaces; i++) { 355 _Windows(i).SetIndex(i); 356 fWorkspaces[i].RestoreConfiguration(*fSettings->WorkspacesMessage(i)); 357 } 358 359 fVirtualScreen.RestoreConfiguration(*this, fSettings->WorkspacesMessage(0)); 360 361 // TODO: temporary workaround, fActiveScreen will be removed 362 fActiveScreen = fVirtualScreen.ScreenAt(0); 363 364 #if TEST_MODE 365 gInputManager->AddStream(new InputServerStream); 366 #endif 367 fEventDispatcher.SetTo(gInputManager->GetStream()); 368 fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface()); 369 370 fEventDispatcher.SetMouseFilter(new MouseFilter(this)); 371 fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this)); 372 373 SetCursor(NULL); 374 // this will set the default cursor 375 376 fVirtualScreen.HWInterface()->MoveCursorTo(fVirtualScreen.Frame().Width() / 2, 377 fVirtualScreen.Frame().Height() / 2); 378 fVirtualScreen.HWInterface()->SetCursorVisible(true); 379 380 // draw the background 381 382 fScreenRegion = fVirtualScreen.Frame(); 383 384 BRegion stillAvailableOnScreen; 385 _RebuildClippingForAllWindows(stillAvailableOnScreen); 386 _SetBackground(stillAvailableOnScreen); 387 388 gFontManager->AttachUser(fUserID); 389 390 return B_OK; 391 } 392 393 394 void 395 Desktop::_GetLooperName(char* name, size_t length) 396 { 397 snprintf(name, length, "d:%d:%s", /*id*/0, /*name*/"baron"); 398 } 399 400 401 void 402 Desktop::_PrepareQuit() 403 { 404 // let's kill all remaining applications 405 406 fApplicationsLock.Lock(); 407 408 int32 count = fApplications.CountItems(); 409 for (int32 i = 0; i < count; i++) { 410 ServerApp *app = fApplications.ItemAt(i); 411 team_id clientTeam = app->ClientTeam(); 412 413 app->Quit(); 414 kill_team(clientTeam); 415 } 416 417 // wait for the last app to die 418 if (count > 0) 419 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 250000); 420 421 fApplicationsLock.Unlock(); 422 } 423 424 425 void 426 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) 427 { 428 switch (code) { 429 case AS_CREATE_APP: 430 { 431 // Create the ServerApp to node monitor a new BApplication 432 433 // Attached data: 434 // 1) port_id - receiver port of a regular app 435 // 2) port_id - client looper port - for sending messages to the client 436 // 2) team_id - app's team ID 437 // 3) int32 - handler token of the regular app 438 // 4) char * - signature of the regular app 439 440 // Find the necessary data 441 team_id clientTeamID = -1; 442 port_id clientLooperPort = -1; 443 port_id clientReplyPort = -1; 444 int32 htoken = B_NULL_TOKEN; 445 char *appSignature = NULL; 446 447 link.Read<port_id>(&clientReplyPort); 448 link.Read<port_id>(&clientLooperPort); 449 link.Read<team_id>(&clientTeamID); 450 link.Read<int32>(&htoken); 451 if (link.ReadString(&appSignature) != B_OK) 452 break; 453 454 ServerApp *app = new ServerApp(this, clientReplyPort, 455 clientLooperPort, clientTeamID, htoken, appSignature); 456 if (app->InitCheck() == B_OK 457 && app->Run()) { 458 // add the new ServerApp to the known list of ServerApps 459 fApplicationsLock.Lock(); 460 fApplications.AddItem(app); 461 fApplicationsLock.Unlock(); 462 } else { 463 delete app; 464 465 // if everything went well, ServerApp::Run() will notify 466 // the client - but since it didn't, we do it here 467 BPrivate::LinkSender reply(clientReplyPort); 468 reply.StartMessage(B_ERROR); 469 reply.Flush(); 470 } 471 472 // This is necessary because BPortLink::ReadString allocates memory 473 free(appSignature); 474 break; 475 } 476 477 case AS_DELETE_APP: 478 { 479 // Delete a ServerApp. Received only from the respective ServerApp when a 480 // BApplication asks it to quit. 481 482 // Attached Data: 483 // 1) thread_id - thread ID of the ServerApp to be deleted 484 485 thread_id thread = -1; 486 if (link.Read<thread_id>(&thread) < B_OK) 487 break; 488 489 fApplicationsLock.Lock(); 490 491 // Run through the list of apps and nuke the proper one 492 493 int32 count = fApplications.CountItems(); 494 ServerApp *removeApp = NULL; 495 496 for (int32 i = 0; i < count; i++) { 497 ServerApp *app = fApplications.ItemAt(i); 498 499 if (app != NULL && app->Thread() == thread) { 500 fApplications.RemoveItemAt(i); 501 removeApp = app; 502 break; 503 } 504 } 505 506 fApplicationsLock.Unlock(); 507 508 if (removeApp != NULL) 509 removeApp->Quit(fShutdownSemaphore); 510 511 if (fQuitting && count <= 1) { 512 // wait for the last app to die 513 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 500000); 514 PostMessage(kMsgQuitLooper); 515 } 516 break; 517 } 518 519 case AS_ACTIVATE_APP: 520 { 521 // Someone is requesting to activation of a certain app. 522 523 // Attached data: 524 // 1) port_id reply port 525 // 2) team_id team 526 527 status_t status; 528 529 // get the parameters 530 port_id replyPort; 531 team_id team; 532 if (link.Read(&replyPort) == B_OK 533 && link.Read(&team) == B_OK) 534 status = _ActivateApp(team); 535 else 536 status = B_ERROR; 537 538 // send the reply 539 BPrivate::PortLink replyLink(replyPort); 540 replyLink.StartMessage(status); 541 replyLink.Flush(); 542 break; 543 } 544 545 case B_QUIT_REQUESTED: 546 // We've been asked to quit, so (for now) broadcast to all 547 // test apps to quit. This situation will occur only when the server 548 // is compiled as a regular Be application. 549 550 fApplicationsLock.Lock(); 551 fShutdownSemaphore = create_sem(0, "desktop shutdown"); 552 fShutdownCount = fApplications.CountItems(); 553 fApplicationsLock.Unlock(); 554 555 fQuitting = true; 556 BroadcastToAllApps(AS_QUIT_APP); 557 558 // We now need to process the remaining AS_DELETE_APP messages and 559 // wait for the kMsgShutdownServer message. 560 // If an application does not quit as asked, the picasso thread 561 // will send us this message in 2-3 seconds. 562 563 // if there are no apps to quit, shutdown directly 564 if (fShutdownCount == 0) 565 PostMessage(kMsgQuitLooper); 566 break; 567 568 default: 569 printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", code); 570 571 if (link.NeedsReply()) { 572 // the client is now blocking and waiting for a reply! 573 fLink.StartMessage(B_ERROR); 574 fLink.Flush(); 575 } 576 break; 577 } 578 } 579 580 581 /*! 582 \brief activate one of the app's windows. 583 */ 584 status_t 585 Desktop::_ActivateApp(team_id team) 586 { 587 status_t status = B_BAD_TEAM_ID; 588 589 // search for an unhidden window to give focus to 590 591 for (WindowLayer* window = fAllWindows.FirstWindow(); window != NULL; 592 window = window->NextWindow(kAllWindowList)) { 593 // if window is a normal window of the team, and not hidden, 594 // we've found our target 595 if (!window->IsHidden() && window->IsNormal() 596 && window->ServerWindow()->ClientTeam() == team) { 597 ActivateWindow(window); 598 return B_OK; 599 } 600 } 601 602 return status; 603 } 604 605 606 /*! 607 \brief Send a quick (no attachments) message to all applications 608 609 Quite useful for notification for things like server shutdown, system 610 color changes, etc. 611 */ 612 void 613 Desktop::BroadcastToAllApps(int32 code) 614 { 615 BAutolock locker(fApplicationsLock); 616 617 for (int32 i = 0; i < fApplications.CountItems(); i++) { 618 fApplications.ItemAt(i)->PostMessage(code); 619 } 620 } 621 622 623 ServerCursor* 624 Desktop::Cursor() const 625 { 626 return HWInterface()->Cursor(); 627 } 628 629 630 void 631 Desktop::SetCursor(ServerCursor* newCursor) 632 { 633 if (newCursor == NULL) 634 newCursor = fCursorManager.GetCursor(B_CURSOR_DEFAULT); 635 636 ServerCursor* oldCursor = Cursor(); 637 if (newCursor == oldCursor) 638 return; 639 640 HWInterface()->SetCursor(newCursor); 641 } 642 643 644 /*! 645 \brief Redraws the background (ie. the desktop window, if any). 646 */ 647 void 648 Desktop::RedrawBackground() 649 { 650 LockAllWindows(); 651 652 BRegion redraw; 653 654 WindowLayer* window = _CurrentWindows().FirstWindow(); 655 if (window->Feel() == kDesktopWindowFeel) { 656 redraw = window->VisibleContentRegion(); 657 658 // look for desktop background view, and update its background color 659 // TODO: is there a better way to do this? 660 ViewLayer* view = window->TopLayer(); 661 if (view != NULL) 662 view = view->FirstChild(); 663 664 while (view) { 665 if (view->IsDesktopBackground()) { 666 view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color()); 667 break; 668 } 669 view = view->NextSibling(); 670 } 671 672 window->ProcessDirtyRegion(redraw); 673 } else { 674 redraw = BackgroundRegion(); 675 fBackgroundRegion.MakeEmpty(); 676 _SetBackground(redraw); 677 } 678 679 _WindowChanged(NULL); 680 // update workspaces view as well 681 682 UnlockAllWindows(); 683 } 684 685 686 /*! 687 \brief Store the workspace configuration 688 */ 689 void 690 Desktop::StoreWorkspaceConfiguration(int32 index) 691 { 692 const BMessage *oldSettings = fSettings->WorkspacesMessage(index); 693 // store settings 694 BMessage settings; 695 if (oldSettings) 696 settings = *oldSettings; 697 fWorkspaces[index].StoreConfiguration(settings); 698 fSettings->SetWorkspacesMessage(index, settings); 699 fSettings->Save(kWorkspacesSettings); 700 } 701 702 703 void 704 Desktop::UpdateWorkspaces() 705 { 706 // TODO: maybe this should be replaced by a SetWorkspacesCount() method 707 708 _WindowChanged(NULL); 709 } 710 711 712 void 713 Desktop::SetWorkspace(int32 index) 714 { 715 LockAllWindows(); 716 DesktopSettings settings(this); 717 718 if (index < 0 || index >= settings.WorkspacesCount() || index == fCurrentWorkspace) { 719 UnlockAllWindows(); 720 return; 721 } 722 723 int32 previousIndex = fCurrentWorkspace; 724 RGBColor previousColor = fWorkspaces[fCurrentWorkspace].Color(); 725 726 if (fMouseEventWindow != NULL) { 727 if (!fMouseEventWindow->InWorkspace(index)) { 728 // the window currently being dragged will follow us to this workspace 729 // if it's not already on it 730 if (fMouseEventWindow->IsNormal()) { 731 // but only normal windows are following 732 uint32 oldWorkspaces = fMouseEventWindow->Workspaces(); 733 734 _Windows(index).AddWindow(fMouseEventWindow); 735 _Windows(previousIndex).RemoveWindow(fMouseEventWindow); 736 737 // send B_WORKSPACES_CHANGED message 738 fMouseEventWindow->WorkspacesChanged(oldWorkspaces, 739 fMouseEventWindow->Workspaces()); 740 } 741 } else { 742 // make sure it's frontmost 743 _Windows(index).RemoveWindow(fMouseEventWindow); 744 _Windows(index).AddWindow(fMouseEventWindow, 745 fMouseEventWindow->Frontmost(_Windows(index).FirstWindow(), index)); 746 } 747 748 fMouseEventWindow->Anchor(index).position = fMouseEventWindow->Frame().LeftTop(); 749 } 750 751 // build region of windows that are no longer visible in the new workspace 752 753 BRegion dirty; 754 755 for (WindowLayer* window = _CurrentWindows().FirstWindow(); 756 window != NULL; window = window->NextWindow(previousIndex)) { 757 // store current position in Workspace anchor 758 window->Anchor(previousIndex).position = window->Frame().LeftTop(); 759 760 window->WorkspaceActivated(previousIndex, false); 761 762 if (window->InWorkspace(index)) 763 continue; 764 765 if (!window->IsHidden()) { 766 // this window will no longer be visible 767 dirty.Include(&window->VisibleRegion()); 768 } 769 770 window->SetCurrentWorkspace(-1); 771 } 772 773 fCurrentWorkspace = index; 774 775 // show windows, and include them in the changed region - but only 776 // those that were not visible before (or whose position changed) 777 778 for (WindowLayer* window = _Windows(index).FirstWindow(); 779 window != NULL; window = window->NextWindow(index)) { 780 BPoint position = window->Anchor(index).position; 781 782 window->SetCurrentWorkspace(index); 783 784 if (window->IsHidden()) 785 continue; 786 787 if (position == kInvalidWindowPosition) { 788 // if you enter a workspace for the first time, the position 789 // of the window in the previous workspace is adopted 790 position = window->Frame().LeftTop(); 791 // TODO: make sure the window is still on-screen if it 792 // was before! 793 } 794 795 if (!window->InWorkspace(previousIndex)) { 796 // This window was not visible before, make sure its frame 797 // is up-to-date 798 if (window->Frame().LeftTop() != position) { 799 BPoint offset = position - window->Frame().LeftTop(); 800 window->MoveBy(offset.x, offset.y); 801 } 802 continue; 803 } 804 805 if (window->Frame().LeftTop() != position) { 806 // the window was visible before, but its on-screen location changed 807 BPoint offset = position - window->Frame().LeftTop(); 808 MoveWindowBy(window, offset.x, offset.y); 809 // TODO: be a bit smarter than this... 810 } 811 } 812 813 BRegion stillAvailableOnScreen; 814 _RebuildClippingForAllWindows(stillAvailableOnScreen); 815 _SetBackground(stillAvailableOnScreen); 816 817 for (WindowLayer* window = _Windows(index).FirstWindow(); window != NULL; 818 window = window->NextWindow(index)) { 819 // send B_WORKSPACE_ACTIVATED message 820 window->WorkspaceActivated(index, true); 821 822 if (window->InWorkspace(previousIndex) || window == fMouseEventWindow) { 823 // this window was visible before, and is already handled in the above loop 824 continue; 825 } 826 827 dirty.Include(&window->VisibleRegion()); 828 } 829 830 _UpdateFronts(false); 831 _UpdateFloating(previousIndex, index); 832 833 // Set new focus to the front window, but keep focus to a floating 834 // window if still visible 835 if (!_Windows(index).HasWindow(FocusWindow()) || !FocusWindow()->IsFloating()) 836 SetFocusWindow(FrontWindow()); 837 838 _WindowChanged(NULL); 839 MarkDirty(dirty); 840 841 if (previousColor != fWorkspaces[fCurrentWorkspace].Color()) 842 RedrawBackground(); 843 844 UnlockAllWindows(); 845 846 _SendFakeMouseMoved(); 847 } 848 849 850 void 851 Desktop::ScreenChanged(Screen* screen, bool makeDefault) 852 { 853 // TODO: confirm that everywhere this is used, 854 // the Window WriteLock is held 855 856 // the entire screen is dirty, because we're actually 857 // operating on an all new buffer in memory 858 BRegion dirty(screen->Frame()); 859 // update our cached screen region 860 fScreenRegion.Set(screen->Frame()); 861 862 BRegion background; 863 _RebuildClippingForAllWindows(background); 864 865 fBackgroundRegion.MakeEmpty(); 866 // makes sure that the complete background is redrawn 867 _SetBackground(background); 868 869 // figure out dirty region 870 dirty.Exclude(&background); 871 _TriggerWindowRedrawing(dirty); 872 873 // send B_SCREEN_CHANGED to windows on that screen 874 BMessage update(B_SCREEN_CHANGED); 875 update.AddInt64("when", real_time_clock_usecs()); 876 update.AddRect("frame", screen->Frame()); 877 update.AddInt32("mode", screen->ColorSpace()); 878 879 // TODO: currently ignores the screen argument! 880 for (WindowLayer* window = fAllWindows.FirstWindow(); window != NULL; 881 window = window->NextWindow(kAllWindowList)) { 882 window->ServerWindow()->SendMessageToClient(&update); 883 } 884 885 if (makeDefault) { 886 // store settings 887 BMessage settings; 888 fVirtualScreen.StoreConfiguration(settings); 889 fWorkspaces[fCurrentWorkspace].StoreConfiguration(settings); 890 891 fSettings->SetWorkspacesMessage(fCurrentWorkspace, settings); 892 fSettings->Save(kWorkspacesSettings); 893 } 894 } 895 896 897 // #pragma mark - Methods for WindowLayer manipulation 898 899 900 WindowList& 901 Desktop::_CurrentWindows() 902 { 903 return fWorkspaces[fCurrentWorkspace].Windows(); 904 } 905 906 907 WindowList& 908 Desktop::_Windows(int32 index) 909 { 910 return fWorkspaces[index].Windows(); 911 } 912 913 914 void 915 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace) 916 { 917 if (previousWorkspace == -1) 918 previousWorkspace = fCurrentWorkspace; 919 if (nextWorkspace == -1) 920 nextWorkspace = previousWorkspace; 921 922 for (WindowLayer* floating = fSubsetWindows.FirstWindow(); floating != NULL; 923 floating = floating->NextWindow(kSubsetList)) { 924 // we only care about app/subset floating windows 925 if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL 926 && floating->Feel() != B_FLOATING_APP_WINDOW_FEEL) 927 continue; 928 929 if (fFront != NULL && fFront->IsNormal() && floating->HasInSubset(fFront)) { 930 // is now visible 931 if (_Windows(previousWorkspace).HasWindow(floating) 932 && previousWorkspace != nextWorkspace) { 933 // but no longer on the previous workspace 934 _Windows(previousWorkspace).RemoveWindow(floating); 935 floating->SetCurrentWorkspace(-1); 936 } 937 if (!_Windows(nextWorkspace).HasWindow(floating)) { 938 // but wasn't before 939 _Windows(nextWorkspace).AddWindow(floating, 940 floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), nextWorkspace)); 941 floating->SetCurrentWorkspace(nextWorkspace); 942 _ShowWindow(floating); 943 944 // TODO: 945 // put the floating last in the floating window list to preserve 946 // the on screen window order 947 } 948 } else if (_Windows(previousWorkspace).HasWindow(floating)) { 949 // was visible, but is no longer 950 _Windows(previousWorkspace).RemoveWindow(floating); 951 floating->SetCurrentWorkspace(-1); 952 _HideWindow(floating); 953 } 954 } 955 } 956 957 958 /*! 959 Search the visible windows for a valid back window 960 (only desktop windows can't be back windows) 961 */ 962 void 963 Desktop::_UpdateBack() 964 { 965 fBack = NULL; 966 967 for (WindowLayer* window = _CurrentWindows().FirstWindow(); 968 window != NULL; window = window->NextWindow(fCurrentWorkspace)) { 969 if (window->IsHidden() || window->Feel() == kDesktopWindowFeel) 970 continue; 971 972 fBack = window; 973 break; 974 } 975 } 976 977 978 /*! 979 Search the visible windows for a valid front window 980 (only normal and modal windows can be front windows) 981 982 The only place where you don't want to update floating windows is 983 during a workspace change - because then you'll call _UpdateFloating() 984 yourself. 985 */ 986 void 987 Desktop::_UpdateFront(bool updateFloating) 988 { 989 fFront = NULL; 990 991 for (WindowLayer* window = _CurrentWindows().LastWindow(); 992 window != NULL; window = window->PreviousWindow(fCurrentWorkspace)) { 993 if (window->IsHidden() || window->IsFloating() || !window->SupportsFront()) 994 continue; 995 996 fFront = window; 997 break; 998 } 999 1000 if (updateFloating) 1001 _UpdateFloating(); 1002 } 1003 1004 1005 void 1006 Desktop::_UpdateFronts(bool updateFloating) 1007 { 1008 _UpdateBack(); 1009 _UpdateFront(updateFloating); 1010 } 1011 1012 1013 bool 1014 Desktop::_WindowHasModal(WindowLayer* window) 1015 { 1016 if (window == NULL) 1017 return false; 1018 1019 for (WindowLayer* modal = fSubsetWindows.FirstWindow(); modal != NULL; 1020 modal = modal->NextWindow(kSubsetList)) { 1021 // only visible modal windows count 1022 if (!modal->IsModal() || modal->IsHidden()) 1023 continue; 1024 1025 if (modal->HasInSubset(window)) 1026 return true; 1027 } 1028 1029 return false; 1030 } 1031 1032 1033 void 1034 Desktop::_WindowChanged(WindowLayer* window) 1035 { 1036 if (fWorkspacesLayer == NULL) 1037 return; 1038 1039 fWorkspacesLayer->WindowChanged(window); 1040 } 1041 1042 1043 /*! 1044 \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse, 1045 and also updates the current view under the mouse. 1046 1047 This has only to be done in case the view changed without user interaction, 1048 ie. because of a workspace change or a closing window. 1049 1050 Windows must not be locked when calling this method. 1051 */ 1052 void 1053 Desktop::_SendFakeMouseMoved(WindowLayer* window) 1054 { 1055 BPoint where; 1056 int32 buttons; 1057 EventDispatcher().GetMouse(where, buttons); 1058 1059 int32 viewToken = B_NULL_TOKEN; 1060 BMessenger target; 1061 1062 LockAllWindows(); 1063 1064 if (window == NULL) 1065 window = MouseEventWindow(); 1066 if (window == NULL) 1067 window = WindowAt(where); 1068 1069 if (window != NULL) { 1070 BMessage message; 1071 window->MouseMoved(&message, where, &viewToken, true); 1072 } 1073 1074 if (viewToken != B_NULL_TOKEN) 1075 SetViewUnderMouse(window, viewToken); 1076 else { 1077 SetViewUnderMouse(NULL, B_NULL_TOKEN); 1078 SetCursor(NULL); 1079 } 1080 1081 UnlockAllWindows(); 1082 1083 if (viewToken != B_NULL_TOKEN) 1084 EventDispatcher().SendFakeMouseMoved(target, viewToken); 1085 } 1086 1087 1088 void 1089 Desktop::SetFocusWindow(WindowLayer* focus) 1090 { 1091 if (!LockAllWindows()) 1092 return; 1093 1094 bool hasModal = _WindowHasModal(focus); 1095 1096 // TODO: test for FFM and B_LOCK_WINDOW_FOCUS 1097 1098 if (focus == fFocus && focus != NULL && !focus->IsHidden() 1099 && (focus->Flags() & B_AVOID_FOCUS) == 0 && !hasModal) { 1100 // the window that is supposed to get focus already has focus 1101 UnlockAllWindows(); 1102 return; 1103 } 1104 1105 if (focus == NULL || hasModal) { 1106 focus = FrontWindow(); 1107 if (focus == NULL) { 1108 // there might be no front window in case of only a single 1109 // window with B_FLOATING_ALL_WINDOW_FEEL 1110 focus = _CurrentWindows().LastWindow(); 1111 } 1112 } 1113 1114 // make sure no window is chosen that doesn't want focus or cannot have it 1115 while (focus != NULL 1116 && ((focus->Flags() & B_AVOID_FOCUS) != 0 1117 || _WindowHasModal(focus) 1118 || focus->IsHidden())) { 1119 focus = focus->PreviousWindow(fCurrentWorkspace); 1120 } 1121 1122 if (fFocus == focus) { 1123 // turns out the window that is supposed to get focus now already has it 1124 UnlockAllWindows(); 1125 return; 1126 } 1127 1128 ServerApp* oldActiveApp = NULL; 1129 ServerApp* newActiveApp = NULL; 1130 1131 if (fFocus != NULL) { 1132 fFocus->SetFocus(false); 1133 oldActiveApp = fFocus->ServerWindow()->App(); 1134 } 1135 1136 fFocus = focus; 1137 1138 if (fFocus != NULL) { 1139 fFocus->SetFocus(true); 1140 newActiveApp = fFocus->ServerWindow()->App(); 1141 } 1142 1143 UnlockAllWindows(); 1144 1145 // change the "active" app if appropriate 1146 if (oldActiveApp != newActiveApp) { 1147 if (oldActiveApp) { 1148 oldActiveApp->Activate(false); 1149 } 1150 1151 if (newActiveApp) { 1152 newActiveApp->Activate(true); 1153 } else { 1154 // make sure the cursor is visible 1155 HWInterface()->SetCursorVisible(true); 1156 } 1157 } 1158 } 1159 1160 1161 void 1162 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, 1163 bool wereVisible) 1164 { 1165 // we don't need to redraw what is currently 1166 // visible of the window 1167 BRegion clean; 1168 1169 for (WindowLayer* window = windows.FirstWindow(); window != NULL; 1170 window = window->NextWindow(list)) { 1171 if (wereVisible) 1172 clean.Include(&window->VisibleRegion()); 1173 1174 _CurrentWindows().AddWindow(window, 1175 window->Frontmost(_CurrentWindows().FirstWindow(), 1176 fCurrentWorkspace)); 1177 1178 _WindowChanged(window); 1179 } 1180 1181 BRegion dummy; 1182 _RebuildClippingForAllWindows(dummy); 1183 1184 // redraw what became visible of the window(s) 1185 1186 BRegion dirty; 1187 for (WindowLayer* window = windows.FirstWindow(); window != NULL; 1188 window = window->NextWindow(list)) { 1189 dirty.Include(&window->VisibleRegion()); 1190 } 1191 1192 dirty.Exclude(&clean); 1193 MarkDirty(dirty); 1194 1195 _UpdateFront(); 1196 1197 if (windows.FirstWindow() == fBack || fBack == NULL) 1198 _UpdateBack(); 1199 } 1200 1201 1202 /*! 1203 \brief Tries to move the specified window to the front of the screen, 1204 and make it the focus window. 1205 1206 If there are any modal windows on this screen, it might not actually 1207 become the frontmost window, though, as modal windows stay in front 1208 of their subset. 1209 */ 1210 void 1211 Desktop::ActivateWindow(WindowLayer* window) 1212 { 1213 STRACE(("ActivateWindow(%p, %s)\n", window, window ? window->Title() : "<none>")); 1214 1215 if (window == NULL) { 1216 fBack = NULL; 1217 fFront = NULL; 1218 return; 1219 } 1220 1221 bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace); 1222 1223 if (windowOnOtherWorkspace 1224 && (window->Flags() & B_NO_WORKSPACE_ACTIVATION) 1225 && !(window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE)) 1226 return; 1227 1228 // TODO: take care about floating windows 1229 1230 if (!LockAllWindows()) 1231 return; 1232 1233 if (windowOnOtherWorkspace) { 1234 // if the window wants to come to the current workspace, 1235 // do so here - else activate the workspace on which this 1236 // window is 1237 if (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) { 1238 // bring the window to the current workspace 1239 // TODO: what if this window is on multiple workspaces?!? 1240 uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace); 1241 SetWindowWorkspaces(window, workspaces); 1242 } else { 1243 // switch to the workspace on which this window is 1244 // (we'll take the first one that the window is on) 1245 uint32 workspaces = window->Workspaces(); 1246 for (int32 i = 0; i < 32; i++) { 1247 uint32 workspace = workspace_to_workspaces(i); 1248 if (workspaces & workspace) { 1249 SetWorkspace(i); 1250 break; 1251 } 1252 } 1253 } 1254 } 1255 1256 if (window == FrontWindow()) { 1257 // see if there is a normal B_AVOID_FRONT window still in front of us 1258 WindowLayer* avoidsFront = window->NextWindow(fCurrentWorkspace); 1259 while (avoidsFront && avoidsFront->IsNormal() 1260 && (avoidsFront->Flags() & B_AVOID_FRONT) == 0) { 1261 avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace); 1262 } 1263 1264 if (avoidsFront == NULL) { 1265 // we're already the frontmost window, we might just not have focus yet 1266 if ((window->Flags() & B_AVOID_FOCUS) == 0) 1267 SetFocusWindow(window); 1268 1269 UnlockAllWindows(); 1270 return; 1271 } 1272 } 1273 1274 // we don't need to redraw what is currently 1275 // visible of the window 1276 BRegion clean(window->VisibleRegion()); 1277 WindowList windows(kWorkingList); 1278 1279 WindowLayer* frontmost = window->Frontmost(); 1280 1281 _CurrentWindows().RemoveWindow(window); 1282 windows.AddWindow(window); 1283 1284 if (frontmost != NULL && frontmost->IsModal()) { 1285 // all modal windows follow their subsets to the front 1286 // (ie. they are staying in front of them, but they are 1287 // not supposed to change their order because of that) 1288 1289 WindowLayer* nextModal; 1290 for (WindowLayer* modal = frontmost; modal != NULL; modal = nextModal) { 1291 // get the next modal window 1292 nextModal = modal->NextWindow(fCurrentWorkspace); 1293 while (nextModal != NULL && !nextModal->IsModal()) { 1294 nextModal = nextModal->NextWindow(fCurrentWorkspace); 1295 } 1296 if (nextModal != NULL && !nextModal->HasInSubset(window)) 1297 nextModal = NULL; 1298 1299 _CurrentWindows().RemoveWindow(modal); 1300 windows.AddWindow(modal); 1301 } 1302 } 1303 1304 _BringWindowsToFront(windows, kWorkingList, true); 1305 if ((window->Flags() & B_AVOID_FOCUS) == 0) 1306 SetFocusWindow(window); 1307 1308 UnlockAllWindows(); 1309 } 1310 1311 1312 void 1313 Desktop::SendWindowBehind(WindowLayer* window, WindowLayer* behindOf) 1314 { 1315 // TODO: should the "not in current workspace" be handled anyway? 1316 // (the code below would have to be changed then, though) 1317 if (window == BackWindow() 1318 || !window->InWorkspace(fCurrentWorkspace) 1319 || (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace)) 1320 || !LockAllWindows()) 1321 return; 1322 1323 // Is this a valid behindOf window? 1324 if (behindOf != NULL && window->HasInSubset(behindOf)) 1325 behindOf = NULL; 1326 1327 // what is currently visible of the window 1328 // might be dirty after the window is send to back 1329 BRegion dirty(window->VisibleRegion()); 1330 1331 // detach window and re-attach at desired position 1332 WindowLayer* backmost = window->Backmost(behindOf); 1333 1334 _CurrentWindows().RemoveWindow(window); 1335 _CurrentWindows().AddWindow(window, backmost 1336 ? backmost->NextWindow(fCurrentWorkspace) : BackWindow()); 1337 1338 BRegion dummy; 1339 _RebuildClippingForAllWindows(dummy); 1340 1341 // mark everything dirty that is no longer visible 1342 BRegion clean(window->VisibleRegion()); 1343 dirty.Exclude(&clean); 1344 MarkDirty(dirty); 1345 1346 _UpdateFronts(); 1347 SetFocusWindow(_CurrentWindows().LastWindow()); 1348 _WindowChanged(window); 1349 1350 UnlockAllWindows(); 1351 } 1352 1353 1354 void 1355 Desktop::ShowWindow(WindowLayer* window) 1356 { 1357 if (!window->IsHidden()) 1358 return; 1359 1360 LockAllWindows(); 1361 1362 window->SetHidden(false); 1363 1364 if (window->InWorkspace(fCurrentWorkspace)) { 1365 _ShowWindow(window, true); 1366 _UpdateSubsetWorkspaces(window); 1367 ActivateWindow(window); 1368 } else { 1369 // then we don't need to send the fake mouse event either 1370 UnlockAllWindows(); 1371 return; 1372 } 1373 1374 if (WorkspacesLayer* layer = dynamic_cast<WorkspacesLayer*>(window->TopLayer())) 1375 fWorkspacesLayer = layer; 1376 1377 UnlockAllWindows(); 1378 1379 // If the mouse cursor is directly over the newly visible window, 1380 // we'll send a fake mouse moved message to the window, so that 1381 // it knows the mouse is over it. 1382 1383 _SendFakeMouseMoved(window); 1384 } 1385 1386 1387 void 1388 Desktop::HideWindow(WindowLayer* window) 1389 { 1390 if (window->IsHidden()) 1391 return; 1392 1393 if (!LockAllWindows()) 1394 return; 1395 1396 window->SetHidden(true); 1397 if (fMouseEventWindow == window) 1398 fMouseEventWindow = NULL; 1399 1400 if (window->InWorkspace(fCurrentWorkspace)) { 1401 _UpdateSubsetWorkspaces(window); 1402 _HideWindow(window); 1403 _UpdateFronts(); 1404 1405 if (FocusWindow() == window) 1406 SetFocusWindow(_CurrentWindows().LastWindow()); 1407 } 1408 1409 if (fWorkspacesLayer != NULL) 1410 fWorkspacesLayer->WindowRemoved(window); 1411 1412 if (dynamic_cast<WorkspacesLayer*>(window->TopLayer()) != NULL) 1413 fWorkspacesLayer = NULL; 1414 1415 UnlockAllWindows(); 1416 1417 if (window == fWindowUnderMouse) 1418 _SendFakeMouseMoved(); 1419 } 1420 1421 1422 /*! 1423 Shows the window on the screen - it does this independently of the 1424 WindowLayer::IsHidden() state. 1425 */ 1426 void 1427 Desktop::_ShowWindow(WindowLayer* window, bool affectsOtherWindows) 1428 { 1429 BRegion background; 1430 _RebuildClippingForAllWindows(background); 1431 _SetBackground(background); 1432 _WindowChanged(window); 1433 1434 BRegion dirty(window->VisibleRegion()); 1435 1436 if (!affectsOtherWindows) { 1437 // everything that is now visible in the 1438 // window needs a redraw, but other windows 1439 // are not affected, we can call ProcessDirtyRegion() 1440 // of the window, and don't have to use MarkDirty() 1441 window->ProcessDirtyRegion(dirty); 1442 } else 1443 MarkDirty(dirty); 1444 } 1445 1446 1447 /*! 1448 Hides the window from the screen - it does this independently of the 1449 WindowLayer::IsHidden() state. 1450 */ 1451 void 1452 Desktop::_HideWindow(WindowLayer* window) 1453 { 1454 // after rebuilding the clipping, 1455 // this window will not have a visible 1456 // region anymore, so we need to remember 1457 // it now 1458 // (actually that's not true, since 1459 // hidden windows are excluded from the 1460 // clipping calculation, but anyways) 1461 BRegion dirty(window->VisibleRegion()); 1462 1463 BRegion background; 1464 _RebuildClippingForAllWindows(background); 1465 _SetBackground(background); 1466 _WindowChanged(window); 1467 1468 MarkDirty(dirty); 1469 } 1470 1471 1472 void 1473 Desktop::MoveWindowBy(WindowLayer* window, float x, float y, int32 workspace) 1474 { 1475 if (!LockAllWindows()) 1476 return; 1477 1478 if (workspace == -1) 1479 workspace = fCurrentWorkspace; 1480 1481 if (!window->IsVisible() || workspace != fCurrentWorkspace) { 1482 if (workspace != fCurrentWorkspace) { 1483 // move the window on another workspace - this doesn't change it's 1484 // current position 1485 if (window->Anchor(workspace).position == kInvalidWindowPosition) 1486 window->Anchor(workspace).position = window->Frame().LeftTop(); 1487 1488 window->Anchor(workspace).position += BPoint(x, y); 1489 _WindowChanged(window); 1490 } else 1491 window->MoveBy(x, y); 1492 1493 UnlockAllWindows(); 1494 return; 1495 } 1496 1497 // the dirty region starts with the visible area of the window being moved 1498 BRegion newDirtyRegion(window->VisibleRegion()); 1499 1500 // no more drawing for DirectWindows 1501 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 1502 1503 window->MoveBy(x, y); 1504 1505 BRegion background; 1506 _RebuildClippingForAllWindows(background); 1507 1508 // construct the region that is possible to be blitted 1509 // to move the contents of the window 1510 BRegion copyRegion(window->VisibleRegion()); 1511 copyRegion.OffsetBy(-x, -y); 1512 copyRegion.IntersectWith(&newDirtyRegion); 1513 // newDirtyRegion == the windows old visible region 1514 1515 // include the the new visible region of the window being 1516 // moved into the dirty region (for now) 1517 newDirtyRegion.Include(&window->VisibleRegion()); 1518 1519 GetDrawingEngine()->CopyRegion(©Region, x, y); 1520 1521 // allow DirectWindows to draw again after the visual 1522 // content is at the new location 1523 window->ServerWindow()->HandleDirectConnection(B_DIRECT_START | B_BUFFER_MOVED); 1524 1525 // in the dirty region, exclude the parts that we 1526 // could move by blitting 1527 copyRegion.OffsetBy(x, y); 1528 newDirtyRegion.Exclude(©Region); 1529 1530 MarkDirty(newDirtyRegion); 1531 _SetBackground(background); 1532 _WindowChanged(window); 1533 1534 UnlockAllWindows(); 1535 } 1536 1537 1538 void 1539 Desktop::ResizeWindowBy(WindowLayer* window, float x, float y) 1540 { 1541 if (!LockAllWindows()) 1542 return; 1543 1544 if (!window->IsVisible()) { 1545 window->ResizeBy(x, y, NULL); 1546 UnlockAllWindows(); 1547 return; 1548 } 1549 1550 // the dirty region for the inside of the window is 1551 // constructed by the window itself in ResizeBy() 1552 BRegion newDirtyRegion; 1553 // track the dirty region outside the window in case 1554 // it is shrunk in "previouslyOccupiedRegion" 1555 BRegion previouslyOccupiedRegion(window->VisibleRegion()); 1556 1557 window->ResizeBy(x, y, &newDirtyRegion); 1558 1559 BRegion background; 1560 _RebuildClippingForAllWindows(background); 1561 1562 // we just care for the region outside the window 1563 previouslyOccupiedRegion.Exclude(&window->VisibleRegion()); 1564 1565 // make sure the window cannot mark stuff dirty outside 1566 // its visible region... 1567 newDirtyRegion.IntersectWith(&window->VisibleRegion()); 1568 // ...because we do this outself 1569 newDirtyRegion.Include(&previouslyOccupiedRegion); 1570 1571 MarkDirty(newDirtyRegion); 1572 _SetBackground(background); 1573 _WindowChanged(window); 1574 1575 UnlockAllWindows(); 1576 } 1577 1578 1579 bool 1580 Desktop::SetWindowTabLocation(WindowLayer* window, float location) 1581 { 1582 if (!LockAllWindows()) 1583 return false; 1584 1585 BRegion dirty; 1586 bool changed = window->SetTabLocation(location, dirty); 1587 1588 if (changed && window->IsVisible() && dirty.CountRects() > 0) { 1589 BRegion stillAvailableOnScreen; 1590 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1591 _SetBackground(stillAvailableOnScreen); 1592 1593 _WindowChanged(window); 1594 _TriggerWindowRedrawing(dirty); 1595 } 1596 1597 UnlockAllWindows(); 1598 1599 return changed; 1600 } 1601 1602 1603 bool 1604 Desktop::SetWindowDecoratorSettings(WindowLayer* window, 1605 const BMessage& settings) 1606 { 1607 // TODO: almost exact code duplication to above function... 1608 1609 if (!LockAllWindows()) 1610 return false; 1611 1612 BRegion dirty; 1613 bool changed = window->SetDecoratorSettings(settings, dirty); 1614 1615 if (changed && window->IsVisible() && dirty.CountRects() > 0) { 1616 BRegion stillAvailableOnScreen; 1617 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1618 _SetBackground(stillAvailableOnScreen); 1619 1620 _TriggerWindowRedrawing(dirty); 1621 } 1622 1623 UnlockAllWindows(); 1624 1625 return changed; 1626 } 1627 1628 1629 /*! 1630 Updates the workspaces of all subset windows with regard to the 1631 specifed window. 1632 */ 1633 void 1634 Desktop::_UpdateSubsetWorkspaces(WindowLayer* window) 1635 { 1636 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, window->Title())); 1637 1638 // if the window is hidden, the subset windows are up-to-date already 1639 if (!window->IsNormal() || window->IsHidden()) 1640 return; 1641 1642 for (WindowLayer* subset = fSubsetWindows.FirstWindow(); subset != NULL; 1643 subset = subset->NextWindow(kSubsetList)) { 1644 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL 1645 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) { 1646 // These windows are always visible on all workspaces, 1647 // no need to update them. 1648 continue; 1649 } 1650 1651 if (subset->IsFloating()) { 1652 // Floating windows are inserted and removed to the current 1653 // workspace as the need arises - they are not handled here 1654 // but in _UpdateFront() 1655 continue; 1656 } 1657 1658 if (subset->HasInSubset(window)) { 1659 // adopt the workspace change 1660 SetWindowWorkspaces(subset, subset->SubsetWorkspaces()); 1661 } 1662 } 1663 } 1664 1665 1666 /*! 1667 \brief Adds or removes the window to or from the workspaces it's on. 1668 */ 1669 void 1670 Desktop::_ChangeWindowWorkspaces(WindowLayer* window, uint32 oldWorkspaces, 1671 uint32 newWorkspaces) 1672 { 1673 // apply changes to the workspaces' window lists 1674 1675 LockAllWindows(); 1676 1677 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1678 if (workspace_in_workspaces(i, oldWorkspaces)) { 1679 // window is on this workspace, is it anymore? 1680 if (!workspace_in_workspaces(i, newWorkspaces)) { 1681 _Windows(i).RemoveWindow(window); 1682 1683 if (i == CurrentWorkspace()) { 1684 // remove its appearance from the current workspace 1685 window->SetCurrentWorkspace(-1); 1686 1687 if (!window->IsHidden()) 1688 _HideWindow(window); 1689 } 1690 } 1691 } else { 1692 // window was not on this workspace, is it now? 1693 if (workspace_in_workspaces(i, newWorkspaces)) { 1694 _Windows(i).AddWindow(window, 1695 window->Frontmost(_Windows(i).FirstWindow(), i)); 1696 1697 if (i == CurrentWorkspace()) { 1698 // make the window visible in current workspace 1699 window->SetCurrentWorkspace(fCurrentWorkspace); 1700 1701 if (!window->IsHidden()) { 1702 // this only affects other windows if this windows has floating or 1703 // modal windows that need to be shown as well 1704 // TODO: take care of this 1705 _ShowWindow(window, FrontWindow() == window); 1706 } 1707 } 1708 } 1709 } 1710 } 1711 1712 // take care about modals and floating windows 1713 _UpdateSubsetWorkspaces(window); 1714 1715 UnlockAllWindows(); 1716 } 1717 1718 1719 void 1720 Desktop::SetWindowWorkspaces(WindowLayer* window, uint32 workspaces) 1721 { 1722 LockAllWindows(); 1723 1724 if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE) 1725 workspaces = workspace_to_workspaces(CurrentWorkspace()); 1726 1727 uint32 oldWorkspaces = window->Workspaces(); 1728 1729 window->WorkspacesChanged(oldWorkspaces, workspaces); 1730 _ChangeWindowWorkspaces(window, oldWorkspaces, workspaces); 1731 1732 UnlockAllWindows(); 1733 } 1734 1735 1736 /*! \brief Adds the window to the desktop. 1737 At this point, the window is still hidden and must be shown explicetly 1738 via ShowWindow(). 1739 */ 1740 void 1741 Desktop::AddWindow(WindowLayer *window) 1742 { 1743 LockAllWindows(); 1744 1745 fAllWindows.AddWindow(window); 1746 if (!window->IsNormal()) 1747 fSubsetWindows.AddWindow(window); 1748 1749 if (window->IsNormal()) { 1750 if (window->Workspaces() == B_CURRENT_WORKSPACE) 1751 window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace())); 1752 } else { 1753 // subset windows are visible on all workspaces their subset is on 1754 window->SetWorkspaces(window->SubsetWorkspaces()); 1755 } 1756 1757 _ChangeWindowWorkspaces(window, 0, window->Workspaces()); 1758 UnlockAllWindows(); 1759 } 1760 1761 1762 void 1763 Desktop::RemoveWindow(WindowLayer *window) 1764 { 1765 LockAllWindows(); 1766 1767 if (!window->IsHidden()) 1768 HideWindow(window); 1769 1770 fAllWindows.RemoveWindow(window); 1771 if (!window->IsNormal()) 1772 fSubsetWindows.RemoveWindow(window); 1773 1774 _ChangeWindowWorkspaces(window, window->Workspaces(), 0); 1775 UnlockAllWindows(); 1776 1777 // make sure this window won't get any events anymore 1778 1779 EventDispatcher().RemoveTarget(window->EventTarget()); 1780 } 1781 1782 1783 bool 1784 Desktop::AddWindowToSubset(WindowLayer* subset, WindowLayer* window) 1785 { 1786 if (!subset->AddToSubset(window)) 1787 return false; 1788 1789 _ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces()); 1790 return true; 1791 } 1792 1793 1794 void 1795 Desktop::RemoveWindowFromSubset(WindowLayer* subset, WindowLayer* window) 1796 { 1797 subset->RemoveFromSubset(window); 1798 _ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces()); 1799 } 1800 1801 1802 void 1803 Desktop::SetWindowLook(WindowLayer *window, window_look newLook) 1804 { 1805 if (window->Look() == newLook) 1806 return; 1807 1808 if (!LockAllWindows()) 1809 return; 1810 1811 BRegion dirty; 1812 window->SetLook(newLook, &dirty); 1813 // TODO: test what happens when the window 1814 // finds out it needs to resize itself... 1815 1816 BRegion stillAvailableOnScreen; 1817 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1818 _SetBackground(stillAvailableOnScreen); 1819 _WindowChanged(window); 1820 1821 _TriggerWindowRedrawing(dirty); 1822 1823 UnlockAllWindows(); 1824 } 1825 1826 1827 void 1828 Desktop::SetWindowFeel(WindowLayer *window, window_feel newFeel) 1829 { 1830 if (window->Feel() == newFeel) 1831 return; 1832 1833 LockAllWindows(); 1834 1835 bool wasNormal = window->IsNormal(); 1836 1837 window->SetFeel(newFeel); 1838 1839 // move the window out of or into the subset window list as needed 1840 if (window->IsNormal() && !wasNormal) 1841 fSubsetWindows.RemoveWindow(window); 1842 else if (!window->IsNormal() && wasNormal) 1843 fSubsetWindows.AddWindow(window); 1844 1845 // A normal window that was once a floating or modal window will 1846 // adopt the window's current workspaces 1847 1848 if (!window->IsNormal()) 1849 _ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces()); 1850 1851 // make sure the window has the correct position in the window lists 1852 // (ie. all floating windows have to be on the top, ...) 1853 1854 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1855 if (!workspace_in_workspaces(i, window->Workspaces())) 1856 continue; 1857 1858 bool changed = false; 1859 BRegion visibleBefore; 1860 if (i == fCurrentWorkspace && window->IsVisible()) 1861 visibleBefore = window->VisibleRegion(); 1862 1863 WindowLayer* backmost = window->Backmost(_Windows(i).LastWindow(), i); 1864 if (backmost != NULL) { 1865 // check if the backmost window is really behind it 1866 WindowLayer* previous = window->PreviousWindow(i); 1867 while (previous != NULL) { 1868 if (previous == backmost) 1869 break; 1870 1871 previous = previous->PreviousWindow(i); 1872 } 1873 1874 if (previous == NULL) { 1875 // need to reinsert window before its backmost window 1876 _Windows(i).RemoveWindow(window); 1877 _Windows(i).AddWindow(window, backmost->NextWindow(i)); 1878 changed = true; 1879 } 1880 } 1881 1882 WindowLayer* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i); 1883 if (frontmost != NULL) { 1884 // check if the frontmost window is really in front of it 1885 WindowLayer* next = window->NextWindow(i); 1886 while (next != NULL) { 1887 if (next == frontmost) 1888 break; 1889 1890 next = next->NextWindow(i); 1891 } 1892 1893 if (next == NULL) { 1894 // need to reinsert window behind its frontmost window 1895 _Windows(i).RemoveWindow(window); 1896 _Windows(i).AddWindow(window, frontmost); 1897 changed = true; 1898 } 1899 } 1900 1901 if (i == fCurrentWorkspace && changed) { 1902 BRegion dummy; 1903 _RebuildClippingForAllWindows(dummy); 1904 1905 // mark everything dirty that is no longer visible, or 1906 // is now visible and wasn't before 1907 BRegion visibleAfter(window->VisibleRegion()); 1908 BRegion dirty(visibleAfter); 1909 dirty.Exclude(&visibleBefore); 1910 visibleBefore.Exclude(&visibleAfter); 1911 dirty.Include(&visibleBefore); 1912 1913 MarkDirty(dirty); 1914 } 1915 } 1916 1917 _UpdateFronts(); 1918 1919 if (window == FocusWindow() && !window->IsVisible()) 1920 SetFocusWindow(_CurrentWindows().LastWindow()); 1921 1922 UnlockAllWindows(); 1923 } 1924 1925 1926 void 1927 Desktop::SetWindowFlags(WindowLayer *window, uint32 newFlags) 1928 { 1929 if (window->Flags() == newFlags) 1930 return; 1931 1932 if (!LockAllWindows()) 1933 return; 1934 1935 BRegion dirty; 1936 window->SetFlags(newFlags, &dirty); 1937 // TODO: test what happens when the window 1938 // finds out it needs to resize itself... 1939 1940 BRegion stillAvailableOnScreen; 1941 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1942 _SetBackground(stillAvailableOnScreen); 1943 _WindowChanged(window); 1944 1945 _TriggerWindowRedrawing(dirty); 1946 1947 UnlockAllWindows(); 1948 } 1949 1950 1951 void 1952 Desktop::SetWindowTitle(WindowLayer *window, const char* title) 1953 { 1954 if (!LockAllWindows()) 1955 return; 1956 1957 BRegion dirty; 1958 window->SetTitle(title, dirty); 1959 1960 if (window->IsVisible() && dirty.CountRects() > 0) { 1961 BRegion stillAvailableOnScreen; 1962 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1963 _SetBackground(stillAvailableOnScreen); 1964 1965 _TriggerWindowRedrawing(dirty); 1966 } 1967 1968 UnlockAllWindows(); 1969 } 1970 1971 1972 /*! 1973 Returns the window under the mouse cursor. 1974 You need to have acquired the All Windows lock when calling this method. 1975 */ 1976 WindowLayer* 1977 Desktop::WindowAt(BPoint where) 1978 { 1979 for (WindowLayer* window = _CurrentWindows().LastWindow(); window; 1980 window = window->PreviousWindow(fCurrentWorkspace)) { 1981 if (window->IsVisible() && window->VisibleRegion().Contains(where)) 1982 return window; 1983 } 1984 1985 return NULL; 1986 } 1987 1988 1989 void 1990 Desktop::SetMouseEventWindow(WindowLayer* window) 1991 { 1992 fMouseEventWindow = window; 1993 } 1994 1995 1996 void 1997 Desktop::SetViewUnderMouse(const WindowLayer* window, int32 viewToken) 1998 { 1999 fWindowUnderMouse = window; 2000 fViewUnderMouse = viewToken; 2001 } 2002 2003 2004 int32 2005 Desktop::ViewUnderMouse(const WindowLayer* window) 2006 { 2007 if (fWindowUnderMouse == window) 2008 return fViewUnderMouse; 2009 2010 return B_NULL_TOKEN; 2011 } 2012 2013 2014 WindowLayer * 2015 Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID) 2016 { 2017 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2018 window = window->NextWindow(kAllWindowList)) { 2019 if (window->ServerWindow()->ClientToken() == token 2020 && window->ServerWindow()->ClientTeam() == teamID) { 2021 return window; 2022 } 2023 } 2024 2025 return NULL; 2026 } 2027 2028 2029 void 2030 Desktop::MinimizeApplication(team_id team) 2031 { 2032 AutoWriteLocker locker(fWindowLock); 2033 2034 // Just minimize all windows of that application 2035 2036 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2037 window = window->NextWindow(kAllWindowList)) { 2038 if (window->ServerWindow()->ClientTeam() != team) 2039 continue; 2040 2041 window->ServerWindow()->NotifyMinimize(true); 2042 } 2043 } 2044 2045 2046 void 2047 Desktop::BringApplicationToFront(team_id team) 2048 { 2049 AutoWriteLocker locker(fWindowLock); 2050 2051 // TODO: for now, just maximize all windows of that application 2052 2053 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2054 window = window->NextWindow(kAllWindowList)) { 2055 if (window->ServerWindow()->ClientTeam() != team) 2056 continue; 2057 2058 window->ServerWindow()->NotifyMinimize(false); 2059 } 2060 } 2061 2062 2063 void 2064 Desktop::WindowAction(int32 windowToken, int32 action) 2065 { 2066 if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT) 2067 return; 2068 2069 LockAllWindows(); 2070 2071 ::ServerWindow* serverWindow; 2072 if (BPrivate::gDefaultTokens.GetToken(windowToken, 2073 B_SERVER_TOKEN, (void**)&serverWindow) != B_OK) { 2074 UnlockAllWindows(); 2075 return; 2076 } 2077 2078 if (action == B_BRING_TO_FRONT 2079 && !serverWindow->Window()->IsMinimized()) { 2080 // the window is visible, we just need to make it the front window 2081 ActivateWindow(serverWindow->Window()); 2082 } else 2083 serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW); 2084 2085 UnlockAllWindows(); 2086 } 2087 2088 2089 void 2090 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 2091 { 2092 AutoWriteLocker locker(fWindowLock); 2093 2094 // compute the number of windows 2095 2096 int32 count = 0; 2097 2098 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2099 window = window->NextWindow(kAllWindowList)) { 2100 if (team < B_OK || window->ServerWindow()->ClientTeam() == team) 2101 count++; 2102 } 2103 2104 // write list 2105 2106 sender.StartMessage(B_OK); 2107 sender.Attach<int32>(count); 2108 2109 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2110 window = window->NextWindow(kAllWindowList)) { 2111 if (team >= B_OK && window->ServerWindow()->ClientTeam() != team) 2112 continue; 2113 2114 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2115 } 2116 2117 sender.Flush(); 2118 } 2119 2120 2121 void 2122 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 2123 { 2124 AutoWriteLocker locker(fWindowLock); 2125 BAutolock tokenLocker(BPrivate::gDefaultTokens); 2126 2127 ::ServerWindow* window; 2128 if (BPrivate::gDefaultTokens.GetToken(serverToken, 2129 B_SERVER_TOKEN, (void**)&window) != B_OK) { 2130 sender.StartMessage(B_ENTRY_NOT_FOUND); 2131 sender.Flush(); 2132 return; 2133 } 2134 2135 window_info info; 2136 window->GetInfo(info); 2137 2138 int32 length = window->Title() ? strlen(window->Title()) : 0; 2139 2140 sender.StartMessage(B_OK); 2141 sender.Attach<int32>(sizeof(window_info) + length + 1); 2142 sender.Attach(&info, sizeof(window_info)); 2143 if (length > 0) 2144 sender.Attach(window->Title(), length + 1); 2145 else 2146 sender.Attach<char>('\0'); 2147 sender.Flush(); 2148 } 2149 2150 2151 void 2152 Desktop::MarkDirty(BRegion& region) 2153 { 2154 if (region.CountRects() == 0) 2155 return; 2156 2157 if (LockAllWindows()) { 2158 // send redraw messages to all windows intersecting the dirty region 2159 _TriggerWindowRedrawing(region); 2160 2161 UnlockAllWindows(); 2162 } 2163 } 2164 2165 2166 void 2167 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen) 2168 { 2169 // the available region on screen starts with the entire screen area 2170 // each window on the screen will take a portion from that area 2171 2172 // figure out what the entire screen area is 2173 stillAvailableOnScreen = fScreenRegion; 2174 2175 // set clipping of each window 2176 for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL; 2177 window = window->PreviousWindow(fCurrentWorkspace)) { 2178 if (!window->IsHidden()) { 2179 window->SetClipping(&stillAvailableOnScreen); 2180 // that windows region is not available on screen anymore 2181 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 2182 } 2183 } 2184 } 2185 2186 2187 void 2188 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion) 2189 { 2190 // send redraw messages to all windows intersecting the dirty region 2191 for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL; 2192 window = window->PreviousWindow(fCurrentWorkspace)) { 2193 if (!window->IsHidden() 2194 && newDirtyRegion.Intersects(window->VisibleRegion().Frame())) 2195 window->ProcessDirtyRegion(newDirtyRegion); 2196 } 2197 } 2198 2199 2200 void 2201 Desktop::_SetBackground(BRegion& background) 2202 { 2203 // NOTE: the drawing operation is caried out 2204 // in the clipping region rebuild, but it is 2205 // ok actually, because it also avoids trails on 2206 // moving windows 2207 2208 // remember the region not covered by any windows 2209 // and redraw the dirty background 2210 BRegion dirtyBackground(background); 2211 dirtyBackground.Exclude(&fBackgroundRegion); 2212 dirtyBackground.IntersectWith(&background); 2213 fBackgroundRegion = background; 2214 if (dirtyBackground.Frame().IsValid()) { 2215 if (GetDrawingEngine()->Lock()) { 2216 GetDrawingEngine()->FillRegion(dirtyBackground, 2217 fWorkspaces[fCurrentWorkspace].Color()); 2218 2219 GetDrawingEngine()->Unlock(); 2220 } 2221 } 2222 } 2223