1 /* 2 * Copyright 2001-2009, 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 * Andrej Spielmann, <andrej.spielmann@seh.ox.ac.uk> 10 */ 11 12 13 /*! Class used to encapsulate desktop management */ 14 15 16 #include "Desktop.h" 17 18 #include <stdio.h> 19 #include <string.h> 20 #include <syslog.h> 21 22 #include <Debug.h> 23 #include <debugger.h> 24 #include <DirectWindow.h> 25 #include <Entry.h> 26 #include <Message.h> 27 #include <MessageFilter.h> 28 #include <Region.h> 29 #include <Roster.h> 30 31 #include <PrivateScreen.h> 32 #include <ServerProtocol.h> 33 #include <ViewPrivate.h> 34 #include <WindowInfo.h> 35 36 #include "AppServer.h" 37 #include "DesktopSettingsPrivate.h" 38 #include "DrawingEngine.h" 39 #include "HWInterface.h" 40 #include "InputManager.h" 41 #include "Screen.h" 42 #include "ServerApp.h" 43 #include "ServerConfig.h" 44 #include "ServerCursor.h" 45 #include "ServerWindow.h" 46 #include "SystemPalette.h" 47 #include "WindowPrivate.h" 48 #include "Window.h" 49 #include "Workspace.h" 50 #include "WorkspacesView.h" 51 52 #if TEST_MODE 53 # include "EventStream.h" 54 #endif 55 56 57 //#define DEBUG_DESKTOP 58 #ifdef DEBUG_DESKTOP 59 # define STRACE(a) printf a 60 #else 61 # define STRACE(a) ; 62 #endif 63 64 #if !USE_MULTI_LOCKER 65 # define AutoWriteLocker BAutolock 66 #endif 67 68 class KeyboardFilter : public EventFilter { 69 public: 70 KeyboardFilter(Desktop* desktop); 71 72 virtual filter_result Filter(BMessage* message, EventTarget** _target, 73 int32* _viewToken, BMessage* latestMouseMoved); 74 virtual void RemoveTarget(EventTarget* target); 75 76 private: 77 void _UpdateFocus(int32 key, EventTarget** _target); 78 79 Desktop* fDesktop; 80 EventTarget* fLastFocus; 81 bigtime_t fTimestamp; 82 }; 83 84 class MouseFilter : public EventFilter { 85 public: 86 MouseFilter(Desktop* desktop); 87 88 virtual filter_result Filter(BMessage* message, EventTarget** _target, 89 int32* _viewToken, BMessage* latestMouseMoved); 90 91 private: 92 Desktop* fDesktop; 93 }; 94 95 96 // #pragma mark - 97 98 99 KeyboardFilter::KeyboardFilter(Desktop* desktop) 100 : 101 fDesktop(desktop), 102 fLastFocus(NULL), 103 fTimestamp(0) 104 { 105 } 106 107 108 void 109 KeyboardFilter::_UpdateFocus(int32 key, EventTarget** _target) 110 { 111 if (!fDesktop->LockSingleWindow()) 112 return; 113 114 EventTarget* focus = fDesktop->KeyboardEventTarget(); 115 bigtime_t now = system_time(); 116 117 // TODO: this is a try to not steal focus from the current window 118 // in case you enter some text and a window pops up you haven't 119 // triggered yourself (like a pop-up window in your browser while 120 // you're typing a password in another window) - maybe this should 121 // be done differently, though (using something like B_LOCK_WINDOW_FOCUS) 122 // (at least B_WINDOW_ACTIVATED must be postponed) 123 124 if (fLastFocus == NULL || (focus != fLastFocus && now - fTimestamp > 100000)) { 125 // if the time span between the key presses is very short 126 // we keep our previous focus alive - this is save even 127 // if the target doesn't exist anymore, as we don't reset 128 // it, and the event focus passed in is always valid (or NULL) 129 *_target = focus; 130 fLastFocus = focus; 131 } 132 133 fDesktop->UnlockSingleWindow(); 134 135 // we always allow to switch focus after the enter key has pressed 136 if (key == B_ENTER) 137 fTimestamp = 0; 138 else 139 fTimestamp = now; 140 } 141 142 143 filter_result 144 KeyboardFilter::Filter(BMessage* message, EventTarget** _target, 145 int32* /*_viewToken*/, BMessage* /*latestMouseMoved*/) 146 { 147 int32 key = 0; 148 int32 modifiers; 149 150 if (message->what == B_KEY_DOWN 151 && message->FindInt32("key", &key) == B_OK 152 && message->FindInt32("modifiers", &modifiers) == B_OK) { 153 // Check for safe video mode (cmd + ctrl + escape) 154 if (key == 0x01 && (modifiers & B_COMMAND_KEY) != 0 155 && (modifiers & B_CONTROL_KEY) != 0) { 156 system("screenmode --fall-back &"); 157 return B_SKIP_MESSAGE; 158 } 159 160 if (key >= B_F1_KEY && key <= B_F12_KEY) { 161 // workspace change 162 163 #if !TEST_MODE 164 if (modifiers & B_COMMAND_KEY) 165 #else 166 if (modifiers & B_CONTROL_KEY) 167 #endif 168 { 169 STRACE(("Set Workspace %ld\n", key - 1)); 170 171 fDesktop->SetWorkspaceAsync(key - 2); 172 return B_SKIP_MESSAGE; 173 } 174 } if (key == 0x11 && (modifiers & B_COMMAND_KEY) != 0) { 175 // switch to previous workspace (command + `) 176 fDesktop->SetWorkspaceAsync(-1); 177 } 178 } 179 180 if (message->what == B_KEY_DOWN 181 || message->what == B_MODIFIERS_CHANGED 182 || message->what == B_UNMAPPED_KEY_DOWN 183 || message->what == B_INPUT_METHOD_EVENT) 184 _UpdateFocus(key, _target); 185 186 return B_DISPATCH_MESSAGE; 187 } 188 189 190 void 191 KeyboardFilter::RemoveTarget(EventTarget* target) 192 { 193 if (target == fLastFocus) 194 fLastFocus = NULL; 195 } 196 197 198 // #pragma mark - 199 200 201 MouseFilter::MouseFilter(Desktop* desktop) 202 : 203 fDesktop(desktop) 204 { 205 } 206 207 208 filter_result 209 MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken, 210 BMessage* latestMouseMoved) 211 { 212 BPoint where; 213 if (message->FindPoint("where", &where) != B_OK) 214 return B_DISPATCH_MESSAGE; 215 216 int32 buttons; 217 if (message->FindInt32("buttons", &buttons) != B_OK) 218 buttons = 0; 219 220 if (!fDesktop->LockAllWindows()) 221 return B_DISPATCH_MESSAGE; 222 223 int32 viewToken = B_NULL_TOKEN; 224 225 Window* window = fDesktop->MouseEventWindow(); 226 if (window == NULL) 227 window = fDesktop->WindowAt(where); 228 229 if (window != NULL) { 230 // dispatch event to the window 231 switch (message->what) { 232 case B_MOUSE_DOWN: 233 window->MouseDown(message, where, &viewToken); 234 break; 235 236 case B_MOUSE_UP: 237 window->MouseUp(message, where, &viewToken); 238 fDesktop->SetMouseEventWindow(NULL); 239 break; 240 241 case B_MOUSE_MOVED: 242 window->MouseMoved(message, where, &viewToken, 243 latestMouseMoved == NULL || latestMouseMoved == message, 244 false); 245 break; 246 } 247 248 if (viewToken != B_NULL_TOKEN) { 249 fDesktop->SetViewUnderMouse(window, viewToken); 250 251 *_viewToken = viewToken; 252 *_target = &window->EventTarget(); 253 } 254 } 255 256 if (window == NULL || viewToken == B_NULL_TOKEN) { 257 // mouse is not over a window or over a decorator 258 fDesktop->SetViewUnderMouse(window, B_NULL_TOKEN); 259 fDesktop->SetCursor(NULL); 260 261 *_target = NULL; 262 } 263 264 fDesktop->SetLastMouseState(where, buttons, window); 265 266 fDesktop->UnlockAllWindows(); 267 268 return B_DISPATCH_MESSAGE; 269 } 270 271 272 // #pragma mark - 273 274 275 static inline uint32 276 workspace_to_workspaces(int32 index) 277 { 278 return 1UL << index; 279 } 280 281 282 static inline bool 283 workspace_in_workspaces(int32 index, uint32 workspaces) 284 { 285 return (workspaces & (1UL << index)) != 0; 286 } 287 288 289 // #pragma mark - 290 291 292 Desktop::Desktop(uid_t userID) 293 : 294 MessageLooper("desktop"), 295 296 fUserID(userID), 297 fSettings(NULL), 298 fSharedReadOnlyArea(-1), 299 fApplicationsLock("application list"), 300 fShutdownSemaphore(-1), 301 fScreenLock("screen lock"), 302 fDirectScreenLock("direct screen lock"), 303 fDirectScreenTeam(-1), 304 fCurrentWorkspace(0), 305 fPreviousWorkspace(0), 306 fAllWindows(kAllWindowList), 307 fSubsetWindows(kSubsetList), 308 fFocusList(kFocusList), 309 fWorkspacesViews(false), 310 311 fWorkspacesLock("workspaces list"), 312 fWindowLock("window lock"), 313 314 fMouseEventWindow(NULL), 315 fWindowUnderMouse(NULL), 316 fLockedFocusWindow(NULL), 317 fViewUnderMouse(B_NULL_TOKEN), 318 fLastMousePosition(B_ORIGIN), 319 fLastMouseButtons(0), 320 321 fFocus(NULL), 322 fFront(NULL), 323 fBack(NULL) 324 { 325 char name[B_OS_NAME_LENGTH]; 326 Desktop::_GetLooperName(name, sizeof(name)); 327 328 fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name); 329 if (fMessagePort < B_OK) 330 return; 331 332 fLink.SetReceiverPort(fMessagePort); 333 } 334 335 336 Desktop::~Desktop() 337 { 338 delete fSettings; 339 340 delete_area(fSharedReadOnlyArea); 341 delete_port(fMessagePort); 342 gFontManager->DetachUser(fUserID); 343 } 344 345 346 status_t 347 Desktop::Init() 348 { 349 if (fMessagePort < B_OK) 350 return fMessagePort; 351 352 // the system palette needs to be initialized before the 353 // desktop settings, since it is used there already 354 InitializeColorMap(); 355 356 const size_t areaSize = B_PAGE_SIZE; 357 char name[B_OS_NAME_LENGTH]; 358 snprintf(name, sizeof(name), "d:%d:shared read only", /*id*/0); 359 fSharedReadOnlyArea = create_area(name, (void **)&fServerReadOnlyMemory, 360 B_ANY_ADDRESS, areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 361 if (fSharedReadOnlyArea < B_OK) 362 return fSharedReadOnlyArea; 363 364 gFontManager->AttachUser(fUserID); 365 366 fSettings = new DesktopSettingsPrivate(fServerReadOnlyMemory); 367 368 for (int32 i = 0; i < kMaxWorkspaces; i++) { 369 _Windows(i).SetIndex(i); 370 fWorkspaces[i].RestoreConfiguration(*fSettings->WorkspacesMessage(i)); 371 } 372 373 fVirtualScreen.SetConfiguration(*this, 374 fWorkspaces[0].CurrentScreenConfiguration()); 375 376 if (fVirtualScreen.HWInterface() == NULL) { 377 debug_printf("Could not initialize graphics output. Exiting.\n"); 378 return B_ERROR; 379 } 380 381 fVirtualScreen.HWInterface()->MoveCursorTo(fVirtualScreen.Frame().Width() / 2, 382 fVirtualScreen.Frame().Height() / 2); 383 384 #if TEST_MODE 385 gInputManager->AddStream(new InputServerStream); 386 #endif 387 388 fEventDispatcher.SetDesktop(this); 389 fEventDispatcher.SetTo(gInputManager->GetStream()); 390 if (fEventDispatcher.InitCheck() != B_OK) 391 _LaunchInputServer(); 392 393 fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface()); 394 395 fEventDispatcher.SetMouseFilter(new MouseFilter(this)); 396 fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this)); 397 398 // draw the background 399 400 fScreenRegion = fVirtualScreen.Frame(); 401 402 BRegion stillAvailableOnScreen; 403 _RebuildClippingForAllWindows(stillAvailableOnScreen); 404 _SetBackground(stillAvailableOnScreen); 405 406 SetCursor(NULL); 407 // this will set the default cursor 408 409 fVirtualScreen.HWInterface()->SetCursorVisible(true); 410 411 return B_OK; 412 } 413 414 415 /*! \brief Send a quick (no attachments) message to all applications. 416 417 Quite useful for notification for things like server shutdown, system 418 color changes, etc. 419 */ 420 void 421 Desktop::BroadcastToAllApps(int32 code) 422 { 423 BAutolock locker(fApplicationsLock); 424 425 for (int32 i = fApplications.CountItems(); i-- > 0;) { 426 fApplications.ItemAt(i)->PostMessage(code); 427 } 428 } 429 430 431 /*! \brief Send a quick (no attachments) message to all windows. 432 */ 433 void 434 Desktop::BroadcastToAllWindows(int32 code) 435 { 436 AutoWriteLocker _(fWindowLock); 437 438 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 439 window = window->NextWindow(kAllWindowList)) { 440 window->ServerWindow()->PostMessage(code); 441 } 442 } 443 444 445 // #pragma mark - Mouse and cursor methods 446 447 448 void 449 Desktop::SetCursor(ServerCursor* newCursor) 450 { 451 if (newCursor == NULL) 452 newCursor = fCursorManager.GetCursor(B_CURSOR_DEFAULT); 453 454 ServerCursorReference oldCursor = Cursor(); 455 if (newCursor == oldCursor.Cursor()) 456 return; 457 458 HWInterface()->SetCursor(newCursor); 459 } 460 461 462 ServerCursorReference 463 Desktop::Cursor() const 464 { 465 return HWInterface()->Cursor(); 466 } 467 468 469 void 470 Desktop::SetLastMouseState(const BPoint& position, int32 buttons, 471 Window* windowUnderMouse) 472 { 473 // The all-window-lock is write-locked. 474 fLastMousePosition = position; 475 fLastMouseButtons = buttons; 476 477 if (fLastMouseButtons == 0 && fLockedFocusWindow) { 478 fLockedFocusWindow = NULL; 479 if (fSettings->FocusFollowsMouse()) 480 SetFocusWindow(windowUnderMouse); 481 } 482 } 483 484 485 void 486 Desktop::GetLastMouseState(BPoint* position, int32* buttons) const 487 { 488 *position = fLastMousePosition; 489 *buttons = fLastMouseButtons; 490 } 491 492 493 // #pragma mark - Screen methods 494 495 496 status_t 497 Desktop::SetScreenMode(int32 workspace, int32 id, const display_mode& mode, 498 bool makeDefault) 499 { 500 AutoWriteLocker _(fWindowLock); 501 502 if (workspace == B_CURRENT_WORKSPACE_INDEX) 503 workspace = fCurrentWorkspace; 504 505 if (workspace < 0 || workspace > kMaxWorkspaces) 506 return B_BAD_VALUE; 507 508 Screen* screen = fVirtualScreen.ScreenByID(id); 509 if (screen == NULL) 510 return B_NAME_NOT_FOUND; 511 512 // Check if the mode has actually changed 513 514 if (workspace == fCurrentWorkspace) { 515 // retrieve from current screen 516 display_mode oldMode; 517 screen->GetMode(oldMode); 518 519 if (!memcmp(&oldMode, &mode, sizeof(display_mode))) 520 return B_OK; 521 522 // Set the new one 523 524 _SuspendDirectFrameBufferAccess(); 525 526 AutoWriteLocker locker(fScreenLock); 527 528 status_t status = screen->SetMode(mode); 529 if (status != B_OK) { 530 locker.Unlock(); 531 532 _ResumeDirectFrameBufferAccess(); 533 return status; 534 } 535 } else { 536 // retrieve from settings 537 screen_configuration* configuration 538 = fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID( 539 screen->ID()); 540 if (configuration != NULL 541 && !memcmp(&configuration->mode, &mode, sizeof(display_mode))) 542 return B_OK; 543 } 544 545 // Update our configurations 546 547 monitor_info info; 548 bool hasInfo = screen->GetMonitorInfo(info) == B_OK; 549 550 fWorkspaces[workspace].CurrentScreenConfiguration().Set(id, 551 hasInfo ? &info : NULL, screen->Frame(), mode); 552 if (makeDefault) { 553 fWorkspaces[workspace].StoredScreenConfiguration().Set(id, 554 hasInfo ? &info : NULL, screen->Frame(), mode); 555 StoreWorkspaceConfiguration(workspace); 556 } 557 558 _ScreenChanged(screen); 559 if (workspace == fCurrentWorkspace) 560 _ResumeDirectFrameBufferAccess(); 561 562 return B_OK; 563 } 564 565 566 status_t 567 Desktop::GetScreenMode(int32 workspace, int32 id, display_mode& mode) 568 { 569 AutoReadLocker _(fScreenLock); 570 571 if (workspace == B_CURRENT_WORKSPACE_INDEX) 572 workspace = fCurrentWorkspace; 573 574 if (workspace < 0 || workspace > kMaxWorkspaces) 575 return B_BAD_VALUE; 576 577 if (workspace == fCurrentWorkspace) { 578 // retrieve from current screen 579 Screen* screen = fVirtualScreen.ScreenByID(id); 580 if (screen == NULL) 581 return B_NAME_NOT_FOUND; 582 583 screen->GetMode(mode); 584 return B_OK; 585 } 586 587 // retrieve from settings 588 screen_configuration* configuration 589 = fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(id); 590 if (configuration == NULL) 591 return B_NAME_NOT_FOUND; 592 593 mode = configuration->mode; 594 return B_OK; 595 } 596 597 598 status_t 599 Desktop::GetScreenFrame(int32 workspace, int32 id, BRect& frame) 600 { 601 AutoReadLocker _(fScreenLock); 602 603 if (workspace == B_CURRENT_WORKSPACE_INDEX) 604 workspace = fCurrentWorkspace; 605 606 if (workspace < 0 || workspace > kMaxWorkspaces) 607 return B_BAD_VALUE; 608 609 if (workspace == fCurrentWorkspace) { 610 // retrieve from current screen 611 Screen* screen = fVirtualScreen.ScreenByID(id); 612 if (screen == NULL) 613 return B_NAME_NOT_FOUND; 614 615 frame = screen->Frame(); 616 return B_OK; 617 } 618 619 // retrieve from settings 620 screen_configuration* configuration 621 = fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(id); 622 if (configuration == NULL) 623 return B_NAME_NOT_FOUND; 624 625 frame = configuration->frame; 626 return B_OK; 627 } 628 629 630 void 631 Desktop::RevertScreenModes(uint32 workspaces) 632 { 633 if (workspaces == 0) 634 return; 635 636 AutoWriteLocker _(fWindowLock); 637 638 for (int32 workspace = 0; workspace < kMaxWorkspaces; workspace++) { 639 if ((workspaces & (1U << workspace)) == 0) 640 continue; 641 642 // Revert all screens on this workspace 643 644 // TODO: ideally, we would know which screens to revert - this way, too 645 // many of them could be reverted 646 647 for (int32 index = 0; index < fVirtualScreen.CountScreens(); index++) { 648 Screen* screen = fVirtualScreen.ScreenAt(index); 649 650 // retrieve configurations 651 screen_configuration* stored = fWorkspaces[workspace] 652 .StoredScreenConfiguration().CurrentByID(screen->ID()); 653 screen_configuration* current = fWorkspaces[workspace] 654 .CurrentScreenConfiguration().CurrentByID(screen->ID()); 655 656 if ((stored != NULL && current != NULL 657 && !memcmp(&stored->mode, ¤t->mode, 658 sizeof(display_mode))) 659 || (stored == NULL && current == NULL)) 660 continue; 661 662 if (stored == NULL) { 663 fWorkspaces[workspace].CurrentScreenConfiguration() 664 .Remove(current); 665 666 if (workspace == fCurrentWorkspace) { 667 _SuspendDirectFrameBufferAccess(); 668 _SetCurrentWorkspaceConfiguration(); 669 _ResumeDirectFrameBufferAccess(); 670 } 671 } else 672 SetScreenMode(workspace, screen->ID(), stored->mode, false); 673 } 674 } 675 } 676 677 678 status_t 679 Desktop::LockDirectScreen(team_id team) 680 { 681 // TODO: BWindowScreens should use the same mechanism as BDirectWindow, 682 // which would make this method superfluous. 683 684 status_t status = fDirectScreenLock.LockWithTimeout(1000000L); 685 if (status == B_OK) 686 fDirectScreenTeam = team; 687 688 return status; 689 } 690 691 692 status_t 693 Desktop::UnlockDirectScreen(team_id team) 694 { 695 if (fDirectScreenTeam == team) { 696 fDirectScreenLock.Unlock(); 697 fDirectScreenTeam = -1; 698 return B_OK; 699 } 700 701 return B_PERMISSION_DENIED; 702 } 703 704 705 // #pragma mark - Workspaces methods 706 707 708 /*! Changes the current workspace to the one specified by \a index. 709 */ 710 void 711 Desktop::SetWorkspaceAsync(int32 index) 712 { 713 BPrivate::LinkSender link(MessagePort()); 714 link.StartMessage(AS_ACTIVATE_WORKSPACE); 715 link.Attach<int32>(index); 716 link.Flush(); 717 } 718 719 720 /*! Changes the current workspace to the one specified by \a index. 721 You must not hold any window lock when calling this method. 722 */ 723 void 724 Desktop::SetWorkspace(int32 index) 725 { 726 LockAllWindows(); 727 DesktopSettings settings(this); 728 729 if (index < 0 || index >= settings.WorkspacesCount() 730 || index == fCurrentWorkspace) { 731 UnlockAllWindows(); 732 return; 733 } 734 735 _SetWorkspace(index); 736 UnlockAllWindows(); 737 738 _SendFakeMouseMoved(); 739 } 740 741 742 status_t 743 Desktop::SetWorkspacesLayout(int32 newColumns, int32 newRows) 744 { 745 int32 newCount = newColumns * newRows; 746 if (newCount < 1 || newCount > kMaxWorkspaces) 747 return B_BAD_VALUE; 748 749 if (!LockAllWindows()) 750 return B_ERROR; 751 752 fSettings->SetWorkspacesLayout(newColumns, newRows); 753 754 // either update the workspaces window, or switch to 755 // the last available workspace - which will update 756 // the workspaces window automatically 757 bool workspaceChanged = CurrentWorkspace() >= newCount; 758 if (workspaceChanged) 759 _SetWorkspace(newCount - 1); 760 else 761 _WindowChanged(NULL); 762 763 UnlockAllWindows(); 764 765 if (workspaceChanged) 766 _SendFakeMouseMoved(); 767 768 return B_OK; 769 } 770 771 772 /*! Returns the virtual screen frame of the workspace specified by \a index. 773 */ 774 BRect 775 Desktop::WorkspaceFrame(int32 index) const 776 { 777 BRect frame; 778 if (index == fCurrentWorkspace) 779 frame = fVirtualScreen.Frame(); 780 else if (index >= 0 && index < fSettings->WorkspacesCount()) { 781 BMessage screenData; 782 fSettings->WorkspacesMessage(index)->FindMessage("screen", &screenData); 783 if (screenData.FindRect("frame", &frame) != B_OK) 784 frame = fVirtualScreen.Frame(); 785 } 786 return frame; 787 } 788 789 790 /*! \brief Stores the workspace configuration. 791 You must hold the window lock when calling this method. 792 */ 793 void 794 Desktop::StoreWorkspaceConfiguration(int32 index) 795 { 796 // Retrieve settings 797 798 BMessage settings; 799 fWorkspaces[index].StoreConfiguration(settings); 800 801 // and store them 802 803 fSettings->SetWorkspacesMessage(index, settings); 804 fSettings->Save(kWorkspacesSettings); 805 } 806 807 808 void 809 Desktop::AddWorkspacesView(WorkspacesView* view) 810 { 811 if (view->Window() == NULL || view->Window()->IsHidden()) 812 return; 813 814 BAutolock _(fWorkspacesLock); 815 816 if (!fWorkspacesViews.HasItem(view)) 817 fWorkspacesViews.AddItem(view); 818 } 819 820 821 void 822 Desktop::RemoveWorkspacesView(WorkspacesView* view) 823 { 824 BAutolock _(fWorkspacesLock); 825 fWorkspacesViews.RemoveItem(view); 826 } 827 828 829 // #pragma mark - Methods for Window manipulation 830 831 832 /*! \brief Tries to move the specified window to the front of the screen, 833 and make it the focus window. 834 835 If there are any modal windows on this screen, it might not actually 836 become the frontmost window, though, as modal windows stay in front 837 of their subset. 838 */ 839 void 840 Desktop::ActivateWindow(Window* window) 841 { 842 STRACE(("ActivateWindow(%p, %s)\n", window, window ? window->Title() : "<none>")); 843 844 if (window == NULL) { 845 fBack = NULL; 846 fFront = NULL; 847 return; 848 } 849 if (window->Workspaces() == 0 && window->IsNormal()) 850 return; 851 852 AutoWriteLocker _(fWindowLock); 853 854 bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace); 855 if (windowOnOtherWorkspace 856 && (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) { 857 if ((window->Flags() & B_NO_WORKSPACE_ACTIVATION) == 0) { 858 // Switch to the workspace on which this window is 859 // (we'll take the first one that the window is on) 860 uint32 workspaces = window->Workspaces(); 861 for (int32 i = 0; i < fSettings->WorkspacesCount(); i++) { 862 uint32 workspace = workspace_to_workspaces(i); 863 if (workspaces & workspace) { 864 SetWorkspace(i); 865 windowOnOtherWorkspace = false; 866 break; 867 } 868 } 869 } else 870 return; 871 } 872 873 if (windowOnOtherWorkspace) { 874 if (!window->IsNormal()) { 875 // Bring a window to front that this floating window belongs to 876 Window* front = _LastFocusSubsetWindow(window); 877 if (front == NULL) { 878 // We can't do anything about those. 879 return; 880 } 881 882 ActivateWindow(front); 883 884 if (!window->InWorkspace(fCurrentWorkspace)) { 885 // This window can't be made active 886 return; 887 } 888 } else { 889 // Bring the window to the current workspace 890 // TODO: what if this window is on multiple workspaces?!? 891 uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace); 892 SetWindowWorkspaces(window, workspaces); 893 } 894 } 895 896 if (window->IsMinimized()) { 897 // Unlike WindowAction(), this is called from the application itself, 898 // so we will just unminimize the window here. 899 window->SetMinimized(false); 900 ShowWindow(window); 901 } 902 903 if (window == FrontWindow()) { 904 // see if there is a normal B_AVOID_FRONT window still in front of us 905 Window* avoidsFront = window->NextWindow(fCurrentWorkspace); 906 while (avoidsFront && avoidsFront->IsNormal() 907 && (avoidsFront->Flags() & B_AVOID_FRONT) == 0) { 908 avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace); 909 } 910 911 if (avoidsFront == NULL) { 912 // we're already the frontmost window, we might just not have focus 913 // yet 914 if ((window->Flags() & B_AVOID_FOCUS) == 0) 915 SetFocusWindow(window); 916 return; 917 } 918 } 919 920 // we don't need to redraw what is currently 921 // visible of the window 922 BRegion clean(window->VisibleRegion()); 923 WindowList windows(kWorkingList); 924 925 Window* frontmost = window->Frontmost(); 926 927 _CurrentWindows().RemoveWindow(window); 928 windows.AddWindow(window); 929 930 if (frontmost != NULL && frontmost->IsModal()) { 931 // all modal windows follow their subsets to the front 932 // (ie. they are staying in front of them, but they are 933 // not supposed to change their order because of that) 934 935 Window* nextModal; 936 for (Window* modal = frontmost; modal != NULL; modal = nextModal) { 937 // get the next modal window 938 nextModal = modal->NextWindow(fCurrentWorkspace); 939 while (nextModal != NULL && !nextModal->IsModal()) { 940 nextModal = nextModal->NextWindow(fCurrentWorkspace); 941 } 942 if (nextModal != NULL && !nextModal->HasInSubset(window)) 943 nextModal = NULL; 944 945 _CurrentWindows().RemoveWindow(modal); 946 windows.AddWindow(modal); 947 } 948 } 949 950 _BringWindowsToFront(windows, kWorkingList, true); 951 952 if ((window->Flags() & B_AVOID_FOCUS) == 0) 953 SetFocusWindow(window); 954 } 955 956 957 void 958 Desktop::SendWindowBehind(Window* window, Window* behindOf) 959 { 960 // TODO: should the "not in current workspace" be handled anyway? 961 // (the code below would have to be changed then, though) 962 if (window == BackWindow() 963 || !window->InWorkspace(fCurrentWorkspace) 964 || (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace)) 965 || !LockAllWindows()) 966 return; 967 968 // Is this a valid behindOf window? 969 if (behindOf != NULL && window->HasInSubset(behindOf)) 970 behindOf = NULL; 971 972 // what is currently visible of the window 973 // might be dirty after the window is send to back 974 BRegion dirty(window->VisibleRegion()); 975 976 // detach window and re-attach at desired position 977 Window* backmost = window->Backmost(behindOf); 978 979 _CurrentWindows().RemoveWindow(window); 980 _CurrentWindows().AddWindow(window, backmost 981 ? backmost->NextWindow(fCurrentWorkspace) : BackWindow()); 982 983 BRegion dummy; 984 _RebuildClippingForAllWindows(dummy); 985 986 // mark everything dirty that is no longer visible 987 BRegion clean(window->VisibleRegion()); 988 dirty.Exclude(&clean); 989 MarkDirty(dirty); 990 991 _UpdateFronts(); 992 SetFocusWindow(fSettings->FocusFollowsMouse() ? 993 WindowAt(fLastMousePosition) : NULL); 994 995 bool sendFakeMouseMoved = false; 996 if (FocusWindow() != window) 997 sendFakeMouseMoved = true; 998 999 _WindowChanged(window); 1000 1001 UnlockAllWindows(); 1002 1003 if (sendFakeMouseMoved) 1004 _SendFakeMouseMoved(); 1005 } 1006 1007 1008 void 1009 Desktop::ShowWindow(Window* window) 1010 { 1011 if (!window->IsHidden()) 1012 return; 1013 1014 AutoWriteLocker locker(fWindowLock); 1015 1016 window->SetHidden(false); 1017 fFocusList.AddWindow(window); 1018 1019 // If the window is on the current workspace, we'll show it. Special 1020 // handling for floating windows, as they can only be shown if their 1021 // subset is. 1022 if (window->InWorkspace(fCurrentWorkspace) 1023 || (window->IsFloating() && _LastFocusSubsetWindow(window) != NULL)) { 1024 _ShowWindow(window, true); 1025 _UpdateSubsetWorkspaces(window); 1026 ActivateWindow(window); 1027 } else { 1028 // then we don't need to send the fake mouse event either 1029 _WindowChanged(window); 1030 return; 1031 } 1032 1033 if (window->HasWorkspacesViews()) { 1034 // find workspaces views in view hierarchy 1035 BAutolock _(fWorkspacesLock); 1036 window->FindWorkspacesViews(fWorkspacesViews); 1037 } 1038 1039 // If the mouse cursor is directly over the newly visible window, 1040 // we'll send a fake mouse moved message to the window, so that 1041 // it knows the mouse is over it. 1042 1043 _SendFakeMouseMoved(window); 1044 } 1045 1046 1047 void 1048 Desktop::HideWindow(Window* window) 1049 { 1050 if (window->IsHidden()) 1051 return; 1052 1053 if (!LockAllWindows()) 1054 return; 1055 1056 window->SetHidden(true); 1057 fFocusList.RemoveWindow(window); 1058 1059 if (fMouseEventWindow == window) { 1060 // Make its decorator lose the current mouse action 1061 BMessage message; 1062 int32 viewToken; 1063 window->MouseUp(&message, fLastMousePosition, &viewToken); 1064 1065 fMouseEventWindow = NULL; 1066 } 1067 1068 if (fLockedFocusWindow == window) { 1069 // Remove the focus lock so the focus can be changed below 1070 fLockedFocusWindow = NULL; 1071 } 1072 1073 if (window->InWorkspace(fCurrentWorkspace)) { 1074 _UpdateSubsetWorkspaces(window); 1075 _HideWindow(window); 1076 _UpdateFronts(); 1077 } else 1078 _WindowChanged(window); 1079 1080 if (FocusWindow() == window) 1081 SetFocusWindow(); 1082 1083 _WindowRemoved(window); 1084 1085 if (window->HasWorkspacesViews()) { 1086 // remove workspaces views from this window 1087 BObjectList<WorkspacesView> list(false); 1088 window->FindWorkspacesViews(list); 1089 1090 BAutolock _(fWorkspacesLock); 1091 1092 while (WorkspacesView* view = list.RemoveItemAt(0)) { 1093 fWorkspacesViews.RemoveItem(view); 1094 } 1095 } 1096 1097 UnlockAllWindows(); 1098 1099 if (window == fWindowUnderMouse) 1100 _SendFakeMouseMoved(); 1101 } 1102 1103 1104 void 1105 Desktop::MoveWindowBy(Window* window, float x, float y, int32 workspace) 1106 { 1107 if (x == 0 && y == 0) 1108 return; 1109 1110 if (!LockAllWindows()) 1111 return; 1112 1113 if (workspace == -1) 1114 workspace = fCurrentWorkspace; 1115 1116 if (!window->IsVisible() || workspace != fCurrentWorkspace) { 1117 if (workspace != fCurrentWorkspace) { 1118 // move the window on another workspace - this doesn't change it's 1119 // current position 1120 if (window->Anchor(workspace).position == kInvalidWindowPosition) 1121 window->Anchor(workspace).position = window->Frame().LeftTop(); 1122 1123 window->Anchor(workspace).position += BPoint(x, y); 1124 _WindowChanged(window); 1125 } else 1126 window->MoveBy((int32)x, (int32)y); 1127 1128 UnlockAllWindows(); 1129 return; 1130 } 1131 1132 // the dirty region starts with the visible area of the window being moved 1133 BRegion newDirtyRegion(window->VisibleRegion()); 1134 1135 // stop direct frame buffer access 1136 bool direct = false; 1137 if (window->ServerWindow()->IsDirectlyAccessing()) { 1138 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 1139 direct = true; 1140 } 1141 1142 window->MoveBy((int32)x, (int32)y); 1143 1144 BRegion background; 1145 _RebuildClippingForAllWindows(background); 1146 1147 // construct the region that is possible to be blitted 1148 // to move the contents of the window 1149 BRegion copyRegion(window->VisibleRegion()); 1150 copyRegion.OffsetBy((int32)-x, (int32)-y); 1151 copyRegion.IntersectWith(&newDirtyRegion); 1152 // newDirtyRegion == the windows old visible region 1153 1154 // include the the new visible region of the window being 1155 // moved into the dirty region (for now) 1156 newDirtyRegion.Include(&window->VisibleRegion()); 1157 1158 GetDrawingEngine()->CopyRegion(©Region, (int32)x, (int32)y); 1159 1160 // in the dirty region, exclude the parts that we 1161 // could move by blitting 1162 copyRegion.OffsetBy((int32)x, (int32)y); 1163 newDirtyRegion.Exclude(©Region); 1164 1165 MarkDirty(newDirtyRegion); 1166 _SetBackground(background); 1167 _WindowChanged(window); 1168 1169 // resume direct frame buffer access 1170 if (direct) { 1171 // TODO: the clipping actually only changes when we move our window 1172 // off screen, or behind some other window 1173 window->ServerWindow()->HandleDirectConnection( 1174 B_DIRECT_START | B_BUFFER_MOVED | B_CLIPPING_MODIFIED); 1175 } 1176 1177 UnlockAllWindows(); 1178 } 1179 1180 1181 void 1182 Desktop::ResizeWindowBy(Window* window, float x, float y) 1183 { 1184 if (x == 0 && y == 0) 1185 return; 1186 1187 if (!LockAllWindows()) 1188 return; 1189 1190 if (!window->IsVisible()) { 1191 window->ResizeBy((int32)x, (int32)y, NULL); 1192 UnlockAllWindows(); 1193 return; 1194 } 1195 1196 // the dirty region for the inside of the window is 1197 // constructed by the window itself in ResizeBy() 1198 BRegion newDirtyRegion; 1199 // track the dirty region outside the window in case 1200 // it is shrunk in "previouslyOccupiedRegion" 1201 BRegion previouslyOccupiedRegion(window->VisibleRegion()); 1202 1203 // stop direct frame buffer access 1204 bool direct = false; 1205 if (window->ServerWindow()->IsDirectlyAccessing()) { 1206 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 1207 direct = true; 1208 } 1209 1210 window->ResizeBy((int32)x, (int32)y, &newDirtyRegion); 1211 1212 BRegion background; 1213 _RebuildClippingForAllWindows(background); 1214 1215 // we just care for the region outside the window 1216 previouslyOccupiedRegion.Exclude(&window->VisibleRegion()); 1217 1218 // make sure the window cannot mark stuff dirty outside 1219 // its visible region... 1220 newDirtyRegion.IntersectWith(&window->VisibleRegion()); 1221 // ...because we do this outself 1222 newDirtyRegion.Include(&previouslyOccupiedRegion); 1223 1224 MarkDirty(newDirtyRegion); 1225 _SetBackground(background); 1226 _WindowChanged(window); 1227 1228 // resume direct frame buffer access 1229 if (direct) { 1230 window->ServerWindow()->HandleDirectConnection( 1231 B_DIRECT_START | B_BUFFER_RESIZED | B_CLIPPING_MODIFIED); 1232 } 1233 1234 UnlockAllWindows(); 1235 } 1236 1237 1238 bool 1239 Desktop::SetWindowTabLocation(Window* window, float location) 1240 { 1241 AutoWriteLocker _(fWindowLock); 1242 1243 BRegion dirty; 1244 bool changed = window->SetTabLocation(location, dirty); 1245 if (changed) 1246 _RebuildAndRedrawAfterWindowChange(window, dirty); 1247 1248 return changed; 1249 } 1250 1251 1252 bool 1253 Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings) 1254 { 1255 AutoWriteLocker _(fWindowLock); 1256 1257 BRegion dirty; 1258 bool changed = window->SetDecoratorSettings(settings, dirty); 1259 if (changed) 1260 _RebuildAndRedrawAfterWindowChange(window, dirty); 1261 1262 return changed; 1263 } 1264 1265 1266 void 1267 Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces) 1268 { 1269 LockAllWindows(); 1270 1271 if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE) 1272 workspaces = workspace_to_workspaces(CurrentWorkspace()); 1273 1274 uint32 oldWorkspaces = window->Workspaces(); 1275 1276 window->WorkspacesChanged(oldWorkspaces, workspaces); 1277 _ChangeWindowWorkspaces(window, oldWorkspaces, workspaces); 1278 1279 UnlockAllWindows(); 1280 } 1281 1282 1283 /*! \brief Adds the window to the desktop. 1284 At this point, the window is still hidden and must be shown explicetly 1285 via ShowWindow(). 1286 */ 1287 void 1288 Desktop::AddWindow(Window *window) 1289 { 1290 LockAllWindows(); 1291 1292 fAllWindows.AddWindow(window); 1293 if (!window->IsNormal()) 1294 fSubsetWindows.AddWindow(window); 1295 1296 if (window->IsNormal()) { 1297 if (window->Workspaces() == B_CURRENT_WORKSPACE) 1298 window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace())); 1299 } else { 1300 // subset windows are visible on all workspaces their subset is on 1301 window->SetWorkspaces(window->SubsetWorkspaces()); 1302 } 1303 1304 _ChangeWindowWorkspaces(window, 0, window->Workspaces()); 1305 UnlockAllWindows(); 1306 } 1307 1308 1309 void 1310 Desktop::RemoveWindow(Window *window) 1311 { 1312 LockAllWindows(); 1313 1314 if (!window->IsHidden()) 1315 HideWindow(window); 1316 1317 fAllWindows.RemoveWindow(window); 1318 if (!window->IsNormal()) 1319 fSubsetWindows.RemoveWindow(window); 1320 1321 _ChangeWindowWorkspaces(window, window->Workspaces(), 0); 1322 UnlockAllWindows(); 1323 1324 // make sure this window won't get any events anymore 1325 1326 EventDispatcher().RemoveTarget(window->EventTarget()); 1327 } 1328 1329 1330 bool 1331 Desktop::AddWindowToSubset(Window* subset, Window* window) 1332 { 1333 if (!subset->AddToSubset(window)) 1334 return false; 1335 1336 _ChangeWindowWorkspaces(subset, subset->Workspaces(), 1337 subset->SubsetWorkspaces()); 1338 return true; 1339 } 1340 1341 1342 void 1343 Desktop::RemoveWindowFromSubset(Window* subset, Window* window) 1344 { 1345 subset->RemoveFromSubset(window); 1346 _ChangeWindowWorkspaces(subset, subset->Workspaces(), 1347 subset->SubsetWorkspaces()); 1348 } 1349 1350 1351 void 1352 Desktop::FontsChanged(Window* window) 1353 { 1354 AutoWriteLocker _(fWindowLock); 1355 1356 BRegion dirty; 1357 window->FontsChanged(&dirty); 1358 1359 _RebuildAndRedrawAfterWindowChange(window, dirty); 1360 } 1361 1362 1363 void 1364 Desktop::SetWindowLook(Window* window, window_look newLook) 1365 { 1366 if (window->Look() == newLook) 1367 return; 1368 1369 AutoWriteLocker _(fWindowLock); 1370 1371 BRegion dirty; 1372 window->SetLook(newLook, &dirty); 1373 // TODO: test what happens when the window 1374 // finds out it needs to resize itself... 1375 1376 _RebuildAndRedrawAfterWindowChange(window, dirty); 1377 } 1378 1379 1380 void 1381 Desktop::SetWindowFeel(Window* window, window_feel newFeel) 1382 { 1383 if (window->Feel() == newFeel) 1384 return; 1385 1386 LockAllWindows(); 1387 1388 bool wasNormal = window->IsNormal(); 1389 1390 window->SetFeel(newFeel); 1391 1392 // move the window out of or into the subset window list as needed 1393 if (window->IsNormal() && !wasNormal) 1394 fSubsetWindows.RemoveWindow(window); 1395 else if (!window->IsNormal() && wasNormal) 1396 fSubsetWindows.AddWindow(window); 1397 1398 // A normal window that was once a floating or modal window will 1399 // adopt the window's current workspaces 1400 1401 if (!window->IsNormal()) 1402 _ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces()); 1403 1404 // make sure the window has the correct position in the window lists 1405 // (ie. all floating windows have to be on the top, ...) 1406 1407 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1408 if (!workspace_in_workspaces(i, window->Workspaces())) 1409 continue; 1410 1411 bool changed = false; 1412 BRegion visibleBefore; 1413 if (i == fCurrentWorkspace && window->IsVisible()) 1414 visibleBefore = window->VisibleRegion(); 1415 1416 Window* backmost = window->Backmost(_Windows(i).LastWindow(), i); 1417 if (backmost != NULL) { 1418 // check if the backmost window is really behind it 1419 Window* previous = window->PreviousWindow(i); 1420 while (previous != NULL) { 1421 if (previous == backmost) 1422 break; 1423 1424 previous = previous->PreviousWindow(i); 1425 } 1426 1427 if (previous == NULL) { 1428 // need to reinsert window before its backmost window 1429 _Windows(i).RemoveWindow(window); 1430 _Windows(i).AddWindow(window, backmost->NextWindow(i)); 1431 changed = true; 1432 } 1433 } 1434 1435 Window* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i); 1436 if (frontmost != NULL) { 1437 // check if the frontmost window is really in front of it 1438 Window* next = window->NextWindow(i); 1439 while (next != NULL) { 1440 if (next == frontmost) 1441 break; 1442 1443 next = next->NextWindow(i); 1444 } 1445 1446 if (next == NULL) { 1447 // need to reinsert window behind its frontmost window 1448 _Windows(i).RemoveWindow(window); 1449 _Windows(i).AddWindow(window, frontmost); 1450 changed = true; 1451 } 1452 } 1453 1454 if (i == fCurrentWorkspace && changed) { 1455 BRegion dummy; 1456 _RebuildClippingForAllWindows(dummy); 1457 1458 // mark everything dirty that is no longer visible, or 1459 // is now visible and wasn't before 1460 BRegion visibleAfter(window->VisibleRegion()); 1461 BRegion dirty(visibleAfter); 1462 dirty.Exclude(&visibleBefore); 1463 visibleBefore.Exclude(&visibleAfter); 1464 dirty.Include(&visibleBefore); 1465 1466 MarkDirty(dirty); 1467 } 1468 } 1469 1470 _UpdateFronts(); 1471 1472 if (window == FocusWindow() && !window->IsVisible()) 1473 SetFocusWindow(); 1474 1475 UnlockAllWindows(); 1476 } 1477 1478 1479 void 1480 Desktop::SetWindowFlags(Window *window, uint32 newFlags) 1481 { 1482 if (window->Flags() == newFlags) 1483 return; 1484 1485 AutoWriteLocker _(fWindowLock); 1486 1487 BRegion dirty; 1488 window->SetFlags(newFlags, &dirty); 1489 // TODO: test what happens when the window 1490 // finds out it needs to resize itself... 1491 1492 _RebuildAndRedrawAfterWindowChange(window, dirty); 1493 } 1494 1495 1496 void 1497 Desktop::SetWindowTitle(Window *window, const char* title) 1498 { 1499 AutoWriteLocker _(fWindowLock); 1500 1501 BRegion dirty; 1502 window->SetTitle(title, dirty); 1503 1504 _RebuildAndRedrawAfterWindowChange(window, dirty); 1505 } 1506 1507 1508 /*! Returns the window under the mouse cursor. 1509 You need to have acquired the All Windows lock when calling this method. 1510 */ 1511 Window* 1512 Desktop::WindowAt(BPoint where) 1513 { 1514 for (Window* window = _CurrentWindows().LastWindow(); window; 1515 window = window->PreviousWindow(fCurrentWorkspace)) { 1516 if (window->IsVisible() && window->VisibleRegion().Contains(where)) 1517 return window; 1518 } 1519 1520 return NULL; 1521 } 1522 1523 1524 void 1525 Desktop::SetMouseEventWindow(Window* window) 1526 { 1527 fMouseEventWindow = window; 1528 } 1529 1530 1531 void 1532 Desktop::SetViewUnderMouse(const Window* window, int32 viewToken) 1533 { 1534 fWindowUnderMouse = window; 1535 fViewUnderMouse = viewToken; 1536 } 1537 1538 1539 int32 1540 Desktop::ViewUnderMouse(const Window* window) 1541 { 1542 if (window != NULL && fWindowUnderMouse == window) 1543 return fViewUnderMouse; 1544 1545 return B_NULL_TOKEN; 1546 } 1547 1548 1549 /*! Returns the current keyboard event target candidate - which is either the 1550 top-most window (in case it's a menu), or the one having focus. 1551 The window lock must be held when calling this function. 1552 */ 1553 EventTarget* 1554 Desktop::KeyboardEventTarget() 1555 { 1556 Window* window = _CurrentWindows().LastWindow(); 1557 while (window != NULL && window->IsHidden()) { 1558 window = window->PreviousWindow(fCurrentWorkspace); 1559 } 1560 if (window != NULL && window->Feel() == kMenuWindowFeel) 1561 return &window->EventTarget(); 1562 1563 if (FocusWindow() != NULL) 1564 return &FocusWindow()->EventTarget(); 1565 1566 return NULL; 1567 } 1568 1569 1570 /*! Tries to set the focus to the specified \a focus window. It will make sure, 1571 however, that the window actually can have focus. 1572 1573 Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both 1574 prevent it from getting focus. 1575 1576 In any case, this method makes sure that there is a focus window, if there 1577 is any window at all, that is. 1578 */ 1579 void 1580 Desktop::SetFocusWindow(Window* focus) 1581 { 1582 if (!LockAllWindows()) 1583 return; 1584 1585 // test for B_LOCK_WINDOW_FOCUS 1586 if (fLockedFocusWindow && focus != fLockedFocusWindow) { 1587 UnlockAllWindows(); 1588 return; 1589 } 1590 1591 bool hasModal = _WindowHasModal(focus); 1592 bool hasWindowScreen = false; 1593 1594 if (!hasModal && focus != NULL) { 1595 // Check whether or not a window screen is in front of the window 1596 // (if it has a modal, the right thing is done, anyway) 1597 Window* window = focus; 1598 while (true) { 1599 window = window->NextWindow(fCurrentWorkspace); 1600 if (window == NULL || window->Feel() == kWindowScreenFeel) 1601 break; 1602 } 1603 if (window != NULL) 1604 hasWindowScreen = true; 1605 } 1606 1607 if (focus == fFocus && focus != NULL && !focus->IsHidden() 1608 && (focus->Flags() & B_AVOID_FOCUS) == 0 1609 && !hasModal && !hasWindowScreen) { 1610 // the window that is supposed to get focus already has focus 1611 UnlockAllWindows(); 1612 return; 1613 } 1614 1615 uint32 list = fCurrentWorkspace; 1616 1617 if (fSettings->FocusFollowsMouse()) 1618 list = kFocusList; 1619 1620 if (focus == NULL || hasModal || hasWindowScreen) { 1621 if (!fSettings->FocusFollowsMouse()) { 1622 focus = FrontWindow(); 1623 if (focus == NULL) { 1624 // there might be no front window in case of only a single 1625 // window with B_FLOATING_ALL_WINDOW_FEEL 1626 focus = _CurrentWindows().LastWindow(); 1627 } 1628 } else 1629 focus = fFocusList.LastWindow(); 1630 } 1631 1632 // make sure no window is chosen that doesn't want focus or cannot have it 1633 while (focus != NULL 1634 && (!focus->InWorkspace(fCurrentWorkspace) 1635 || (focus->Flags() & B_AVOID_FOCUS) != 0 1636 || _WindowHasModal(focus) 1637 || focus->IsHidden())) { 1638 focus = focus->PreviousWindow(list); 1639 } 1640 1641 if (fFocus == focus) { 1642 // turns out the window that is supposed to get focus now already has it 1643 UnlockAllWindows(); 1644 return; 1645 } 1646 1647 team_id oldActiveApp = -1; 1648 team_id newActiveApp = -1; 1649 1650 if (fFocus != NULL) { 1651 fFocus->SetFocus(false); 1652 oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 1653 } 1654 1655 fFocus = focus; 1656 1657 if (fFocus != NULL) { 1658 fFocus->SetFocus(true); 1659 newActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 1660 1661 // move current focus to the end of the focus list 1662 fFocusList.RemoveWindow(fFocus); 1663 fFocusList.AddWindow(fFocus); 1664 } 1665 1666 if (newActiveApp == -1) { 1667 // make sure the cursor is visible 1668 HWInterface()->SetCursorVisible(true); 1669 } 1670 1671 UnlockAllWindows(); 1672 1673 // change the "active" app if appropriate 1674 if (oldActiveApp == newActiveApp) 1675 return; 1676 1677 BAutolock locker(fApplicationsLock); 1678 1679 for (int32 i = 0; i < fApplications.CountItems(); i++) { 1680 ServerApp* app = fApplications.ItemAt(i); 1681 1682 if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp) 1683 app->Activate(false); 1684 else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp) 1685 app->Activate(true); 1686 } 1687 } 1688 1689 1690 void 1691 Desktop::SetFocusLocked(const Window* window) 1692 { 1693 AutoWriteLocker _(fWindowLock); 1694 1695 if (window != NULL) { 1696 // Don't allow this to be set when no mouse buttons 1697 // are pressed. (BView::SetMouseEventMask() should only be called 1698 // from mouse hooks.) 1699 if (fLastMouseButtons == 0) 1700 return; 1701 } 1702 1703 fLockedFocusWindow = window; 1704 } 1705 1706 1707 Window* 1708 Desktop::FindWindowByClientToken(int32 token, team_id teamID) 1709 { 1710 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1711 window = window->NextWindow(kAllWindowList)) { 1712 if (window->ServerWindow()->ClientToken() == token 1713 && window->ServerWindow()->ClientTeam() == teamID) { 1714 return window; 1715 } 1716 } 1717 1718 return NULL; 1719 } 1720 1721 1722 ::EventTarget* 1723 Desktop::FindTarget(BMessenger& messenger) 1724 { 1725 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1726 window = window->NextWindow(kAllWindowList)) { 1727 if (window->EventTarget().Messenger() == messenger) 1728 return &window->EventTarget(); 1729 } 1730 1731 return NULL; 1732 } 1733 1734 1735 void 1736 Desktop::MarkDirty(BRegion& region) 1737 { 1738 if (region.CountRects() == 0) 1739 return; 1740 1741 if (LockAllWindows()) { 1742 // send redraw messages to all windows intersecting the dirty region 1743 _TriggerWindowRedrawing(region); 1744 1745 UnlockAllWindows(); 1746 } 1747 } 1748 1749 1750 void 1751 Desktop::Redraw() 1752 { 1753 BRegion dirty(fVirtualScreen.Frame()); 1754 MarkDirty(dirty); 1755 } 1756 1757 1758 /*! \brief Redraws the background (ie. the desktop window, if any). 1759 */ 1760 void 1761 Desktop::RedrawBackground() 1762 { 1763 LockAllWindows(); 1764 1765 BRegion redraw; 1766 1767 Window* window = _CurrentWindows().FirstWindow(); 1768 if (window->Feel() == kDesktopWindowFeel) { 1769 redraw = window->VisibleContentRegion(); 1770 1771 // look for desktop background view, and update its background color 1772 // TODO: is there a better way to do this? 1773 View* view = window->TopView(); 1774 if (view != NULL) 1775 view = view->FirstChild(); 1776 1777 while (view) { 1778 if (view->IsDesktopBackground()) { 1779 view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color()); 1780 break; 1781 } 1782 view = view->NextSibling(); 1783 } 1784 1785 window->ProcessDirtyRegion(redraw); 1786 } else { 1787 redraw = BackgroundRegion(); 1788 fBackgroundRegion.MakeEmpty(); 1789 _SetBackground(redraw); 1790 } 1791 1792 _WindowChanged(NULL); 1793 // update workspaces view as well 1794 1795 UnlockAllWindows(); 1796 } 1797 1798 1799 void 1800 Desktop::MinimizeApplication(team_id team) 1801 { 1802 AutoWriteLocker locker(fWindowLock); 1803 1804 // Just minimize all windows of that application 1805 1806 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1807 window = window->NextWindow(kAllWindowList)) { 1808 if (window->ServerWindow()->ClientTeam() != team) 1809 continue; 1810 1811 window->ServerWindow()->NotifyMinimize(true); 1812 } 1813 } 1814 1815 1816 void 1817 Desktop::BringApplicationToFront(team_id team) 1818 { 1819 AutoWriteLocker locker(fWindowLock); 1820 1821 // TODO: for now, just maximize all windows of that application 1822 // TODO: have the ability to lock the current workspace 1823 1824 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1825 window = window->NextWindow(kAllWindowList)) { 1826 if (window->ServerWindow()->ClientTeam() != team) 1827 continue; 1828 1829 window->ServerWindow()->NotifyMinimize(false); 1830 } 1831 } 1832 1833 1834 void 1835 Desktop::WindowAction(int32 windowToken, int32 action) 1836 { 1837 if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT) 1838 return; 1839 1840 LockAllWindows(); 1841 1842 ::ServerWindow* serverWindow; 1843 Window* window; 1844 if (BPrivate::gDefaultTokens.GetToken(windowToken, 1845 B_SERVER_TOKEN, (void**)&serverWindow) != B_OK 1846 || (window = serverWindow->Window()) == NULL) { 1847 UnlockAllWindows(); 1848 return; 1849 } 1850 1851 if (action == B_BRING_TO_FRONT && !window->IsMinimized()) { 1852 // the window is visible, we just need to make it the front window 1853 ActivateWindow(window); 1854 } else { 1855 // if not, ask the window if it wants to be unminimized 1856 serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW); 1857 } 1858 1859 UnlockAllWindows(); 1860 } 1861 1862 1863 void 1864 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 1865 { 1866 AutoWriteLocker locker(fWindowLock); 1867 1868 // compute the number of windows 1869 1870 int32 count = 0; 1871 1872 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1873 window = window->NextWindow(kAllWindowList)) { 1874 if (team < B_OK || window->ServerWindow()->ClientTeam() == team) 1875 count++; 1876 } 1877 1878 // write list 1879 1880 sender.StartMessage(B_OK); 1881 sender.Attach<int32>(count); 1882 1883 // first write the windows of the current workspace correctly ordered 1884 for (Window *window = _CurrentWindows().LastWindow(); window != NULL; 1885 window = window->PreviousWindow(fCurrentWorkspace)) { 1886 if (team >= B_OK && window->ServerWindow()->ClientTeam() != team) 1887 continue; 1888 1889 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 1890 } 1891 1892 // then write all the other windows 1893 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1894 window = window->NextWindow(kAllWindowList)) { 1895 if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team) 1896 || window->InWorkspace(fCurrentWorkspace)) 1897 continue; 1898 1899 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 1900 } 1901 1902 sender.Flush(); 1903 } 1904 1905 1906 void 1907 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 1908 { 1909 AutoWriteLocker locker(fWindowLock); 1910 BAutolock tokenLocker(BPrivate::gDefaultTokens); 1911 1912 ::ServerWindow* window; 1913 if (BPrivate::gDefaultTokens.GetToken(serverToken, 1914 B_SERVER_TOKEN, (void**)&window) != B_OK) { 1915 sender.StartMessage(B_ENTRY_NOT_FOUND); 1916 sender.Flush(); 1917 return; 1918 } 1919 1920 window_info info; 1921 window->GetInfo(info); 1922 1923 float tabSize = 0.0; 1924 float borderSize = 0.0; 1925 ::Window* tmp = window->Window(); 1926 if (tmp) { 1927 BMessage message; 1928 if (tmp->GetDecoratorSettings(&message)) { 1929 BRect tabFrame; 1930 message.FindRect("tab frame", &tabFrame); 1931 tabSize = tabFrame.bottom - tabFrame.top; 1932 message.FindFloat("border width", &borderSize); 1933 } 1934 } 1935 1936 int32 length = window->Title() ? strlen(window->Title()) : 0; 1937 1938 sender.StartMessage(B_OK); 1939 sender.Attach<int32>(sizeof(client_window_info) + length); 1940 sender.Attach(&info, sizeof(window_info)); 1941 sender.Attach<float>(tabSize); 1942 sender.Attach<float>(borderSize); 1943 1944 if (length > 0) 1945 sender.Attach(window->Title(), length + 1); 1946 else 1947 sender.Attach<char>('\0'); 1948 1949 sender.Flush(); 1950 } 1951 1952 1953 void 1954 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender) 1955 { 1956 LockSingleWindow(); 1957 1958 if (workspace < 0) 1959 workspace = fCurrentWorkspace; 1960 else if (workspace >= kMaxWorkspaces) { 1961 sender.StartMessage(B_BAD_VALUE); 1962 sender.Flush(); 1963 UnlockSingleWindow(); 1964 return; 1965 } 1966 1967 int32 count = _Windows(workspace).Count(); 1968 1969 // write list 1970 1971 sender.StartMessage(B_OK); 1972 sender.Attach<int32>(count); 1973 1974 for (Window *window = _Windows(workspace).LastWindow(); window != NULL; 1975 window = window->PreviousWindow(workspace)) { 1976 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 1977 } 1978 1979 sender.Flush(); 1980 1981 UnlockSingleWindow(); 1982 } 1983 1984 1985 void 1986 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender) 1987 { 1988 fApplicationsLock.Lock(); 1989 LockSingleWindow(); 1990 1991 int32 maxCount = fApplications.CountItems(); 1992 1993 fApplicationsLock.Unlock(); 1994 // as long as we hold the window lock, no new window can appear 1995 1996 if (workspace < 0) 1997 workspace = fCurrentWorkspace; 1998 else if (workspace >= kMaxWorkspaces) { 1999 sender.StartMessage(B_BAD_VALUE); 2000 sender.Flush(); 2001 UnlockSingleWindow(); 2002 return; 2003 } 2004 2005 // compute the list of applications on this workspace 2006 2007 team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id)); 2008 if (teams == NULL) { 2009 sender.StartMessage(B_NO_MEMORY); 2010 sender.Flush(); 2011 UnlockSingleWindow(); 2012 return; 2013 } 2014 2015 int32 count = 0; 2016 2017 for (Window *window = _Windows(workspace).LastWindow(); window != NULL; 2018 window = window->PreviousWindow(workspace)) { 2019 team_id team = window->ServerWindow()->ClientTeam(); 2020 if (count > 1) { 2021 // see if we already have this team 2022 bool found = false; 2023 for (int32 i = 0; i < count; i++) { 2024 if (teams[i] == team) { 2025 found = true; 2026 break; 2027 } 2028 } 2029 if (found) 2030 continue; 2031 } 2032 2033 ASSERT(count < maxCount); 2034 teams[count++] = team; 2035 } 2036 2037 UnlockSingleWindow(); 2038 2039 // write list 2040 2041 sender.StartMessage(B_OK); 2042 sender.Attach<int32>(count); 2043 2044 for (int32 i = 0; i < count; i++) { 2045 sender.Attach<int32>(teams[i]); 2046 } 2047 2048 sender.Flush(); 2049 free(teams); 2050 } 2051 2052 2053 void 2054 Desktop::_LaunchInputServer() 2055 { 2056 BRoster roster; 2057 status_t status = roster.Launch("application/x-vnd.Be-input_server"); 2058 if (status == B_OK || status == B_ALREADY_RUNNING) 2059 return; 2060 2061 // Could not load input_server by signature, try well-known location 2062 2063 BEntry entry("/system/servers/input_server"); 2064 entry_ref ref; 2065 status_t entryStatus = entry.GetRef(&ref); 2066 if (entryStatus == B_OK) 2067 entryStatus = roster.Launch(&ref); 2068 if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) { 2069 syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n", 2070 strerror(status)); 2071 return; 2072 } 2073 2074 syslog(LOG_ERR, "Failed to launch the input server: %s!\n", 2075 strerror(entryStatus)); 2076 } 2077 2078 2079 void 2080 Desktop::_GetLooperName(char* name, size_t length) 2081 { 2082 snprintf(name, length, "d:%d:%s", /*id*/0, /*name*/"baron"); 2083 } 2084 2085 2086 void 2087 Desktop::_PrepareQuit() 2088 { 2089 // let's kill all remaining applications 2090 2091 fApplicationsLock.Lock(); 2092 2093 int32 count = fApplications.CountItems(); 2094 for (int32 i = 0; i < count; i++) { 2095 ServerApp *app = fApplications.ItemAt(i); 2096 team_id clientTeam = app->ClientTeam(); 2097 2098 app->Quit(); 2099 kill_team(clientTeam); 2100 } 2101 2102 // wait for the last app to die 2103 if (count > 0) 2104 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 250000); 2105 2106 fApplicationsLock.Unlock(); 2107 } 2108 2109 2110 void 2111 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) 2112 { 2113 switch (code) { 2114 case AS_CREATE_APP: 2115 { 2116 // Create the ServerApp to node monitor a new BApplication 2117 2118 // Attached data: 2119 // 1) port_id - receiver port of a regular app 2120 // 2) port_id - client looper port - for sending messages to the client 2121 // 2) team_id - app's team ID 2122 // 3) int32 - handler token of the regular app 2123 // 4) char * - signature of the regular app 2124 2125 // Find the necessary data 2126 team_id clientTeamID = -1; 2127 port_id clientLooperPort = -1; 2128 port_id clientReplyPort = -1; 2129 int32 htoken = B_NULL_TOKEN; 2130 char *appSignature = NULL; 2131 2132 link.Read<port_id>(&clientReplyPort); 2133 link.Read<port_id>(&clientLooperPort); 2134 link.Read<team_id>(&clientTeamID); 2135 link.Read<int32>(&htoken); 2136 if (link.ReadString(&appSignature) != B_OK) 2137 break; 2138 2139 ServerApp *app = new ServerApp(this, clientReplyPort, 2140 clientLooperPort, clientTeamID, htoken, appSignature); 2141 if (app->InitCheck() == B_OK 2142 && app->Run()) { 2143 // add the new ServerApp to the known list of ServerApps 2144 fApplicationsLock.Lock(); 2145 fApplications.AddItem(app); 2146 fApplicationsLock.Unlock(); 2147 } else { 2148 delete app; 2149 2150 // if everything went well, ServerApp::Run() will notify 2151 // the client - but since it didn't, we do it here 2152 BPrivate::LinkSender reply(clientReplyPort); 2153 reply.StartMessage(B_ERROR); 2154 reply.Flush(); 2155 } 2156 2157 // This is necessary because BPortLink::ReadString allocates memory 2158 free(appSignature); 2159 break; 2160 } 2161 2162 case AS_DELETE_APP: 2163 { 2164 // Delete a ServerApp. Received only from the respective ServerApp when a 2165 // BApplication asks it to quit. 2166 2167 // Attached Data: 2168 // 1) thread_id - thread ID of the ServerApp to be deleted 2169 2170 thread_id thread = -1; 2171 if (link.Read<thread_id>(&thread) < B_OK) 2172 break; 2173 2174 fApplicationsLock.Lock(); 2175 2176 // Run through the list of apps and nuke the proper one 2177 2178 int32 count = fApplications.CountItems(); 2179 ServerApp *removeApp = NULL; 2180 2181 for (int32 i = 0; i < count; i++) { 2182 ServerApp *app = fApplications.ItemAt(i); 2183 2184 if (app->Thread() == thread) { 2185 fApplications.RemoveItemAt(i); 2186 removeApp = app; 2187 break; 2188 } 2189 } 2190 2191 fApplicationsLock.Unlock(); 2192 2193 if (removeApp != NULL) 2194 removeApp->Quit(fShutdownSemaphore); 2195 2196 if (fQuitting && count <= 1) { 2197 // wait for the last app to die 2198 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 500000); 2199 PostMessage(kMsgQuitLooper); 2200 } 2201 break; 2202 } 2203 2204 case AS_ACTIVATE_APP: 2205 { 2206 // Someone is requesting to activation of a certain app. 2207 2208 // Attached data: 2209 // 1) port_id reply port 2210 // 2) team_id team 2211 2212 status_t status; 2213 2214 // get the parameters 2215 port_id replyPort; 2216 team_id team; 2217 if (link.Read(&replyPort) == B_OK 2218 && link.Read(&team) == B_OK) 2219 status = _ActivateApp(team); 2220 else 2221 status = B_ERROR; 2222 2223 // send the reply 2224 BPrivate::PortLink replyLink(replyPort); 2225 replyLink.StartMessage(status); 2226 replyLink.Flush(); 2227 break; 2228 } 2229 2230 case AS_APP_CRASHED: 2231 { 2232 BAutolock locker(fApplicationsLock); 2233 2234 team_id team; 2235 if (link.Read(&team) != B_OK) 2236 break; 2237 2238 for (int32 i = 0; i < fApplications.CountItems(); i++) { 2239 ServerApp* app = fApplications.ItemAt(i); 2240 2241 if (app->ClientTeam() == team) 2242 app->PostMessage(AS_APP_CRASHED); 2243 } 2244 break; 2245 } 2246 2247 case AS_EVENT_STREAM_CLOSED: 2248 _LaunchInputServer(); 2249 break; 2250 2251 case B_QUIT_REQUESTED: 2252 // We've been asked to quit, so (for now) broadcast to all 2253 // test apps to quit. This situation will occur only when the server 2254 // is compiled as a regular Be application. 2255 2256 fApplicationsLock.Lock(); 2257 fShutdownSemaphore = create_sem(0, "desktop shutdown"); 2258 fShutdownCount = fApplications.CountItems(); 2259 fApplicationsLock.Unlock(); 2260 2261 fQuitting = true; 2262 BroadcastToAllApps(AS_QUIT_APP); 2263 2264 // We now need to process the remaining AS_DELETE_APP messages and 2265 // wait for the kMsgShutdownServer message. 2266 // If an application does not quit as asked, the picasso thread 2267 // will send us this message in 2-3 seconds. 2268 2269 // if there are no apps to quit, shutdown directly 2270 if (fShutdownCount == 0) 2271 PostMessage(kMsgQuitLooper); 2272 break; 2273 2274 case AS_ACTIVATE_WORKSPACE: 2275 { 2276 int32 index; 2277 link.Read<int32>(&index); 2278 2279 if (index == -1) 2280 index = fPreviousWorkspace; 2281 2282 SetWorkspace(index); 2283 break; 2284 } 2285 2286 // ToDo: Remove this again. It is a message sent by the 2287 // invalidate_on_exit kernel debugger add-on to trigger a redraw 2288 // after exiting a kernel debugger session. 2289 case 'KDLE': 2290 { 2291 BRegion dirty; 2292 dirty.Include(fVirtualScreen.Frame()); 2293 MarkDirty(dirty); 2294 break; 2295 } 2296 2297 default: 2298 printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", code); 2299 2300 if (link.NeedsReply()) { 2301 // the client is now blocking and waiting for a reply! 2302 fLink.StartMessage(B_ERROR); 2303 fLink.Flush(); 2304 } 2305 break; 2306 } 2307 } 2308 2309 2310 WindowList& 2311 Desktop::_CurrentWindows() 2312 { 2313 return fWorkspaces[fCurrentWorkspace].Windows(); 2314 } 2315 2316 2317 WindowList& 2318 Desktop::_Windows(int32 index) 2319 { 2320 return fWorkspaces[index].Windows(); 2321 } 2322 2323 2324 void 2325 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace, 2326 Window* mouseEventWindow) 2327 { 2328 if (previousWorkspace == -1) 2329 previousWorkspace = fCurrentWorkspace; 2330 if (nextWorkspace == -1) 2331 nextWorkspace = previousWorkspace; 2332 2333 for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL; 2334 floating = floating->NextWindow(kSubsetList)) { 2335 // we only care about app/subset floating windows 2336 if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL 2337 && floating->Feel() != B_FLOATING_APP_WINDOW_FEEL) 2338 continue; 2339 2340 if (fFront != NULL && fFront->IsNormal() 2341 && floating->HasInSubset(fFront)) { 2342 // is now visible 2343 if (_Windows(previousWorkspace).HasWindow(floating) 2344 && previousWorkspace != nextWorkspace 2345 && !floating->InSubsetWorkspace(previousWorkspace)) { 2346 // but no longer on the previous workspace 2347 _Windows(previousWorkspace).RemoveWindow(floating); 2348 floating->SetCurrentWorkspace(-1); 2349 } 2350 2351 if (!_Windows(nextWorkspace).HasWindow(floating)) { 2352 // but wasn't before 2353 _Windows(nextWorkspace).AddWindow(floating, 2354 floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), 2355 nextWorkspace)); 2356 floating->SetCurrentWorkspace(nextWorkspace); 2357 if (mouseEventWindow != fFront) 2358 _ShowWindow(floating); 2359 2360 // TODO: put the floating last in the floating window list to 2361 // preserve the on screen window order 2362 } 2363 } else if (_Windows(previousWorkspace).HasWindow(floating) 2364 && !floating->InSubsetWorkspace(previousWorkspace)) { 2365 // was visible, but is no longer 2366 2367 _Windows(previousWorkspace).RemoveWindow(floating); 2368 floating->SetCurrentWorkspace(-1); 2369 _HideWindow(floating); 2370 2371 if (FocusWindow() == floating) 2372 SetFocusWindow(); 2373 } 2374 } 2375 } 2376 2377 2378 /*! Search the visible windows for a valid back window 2379 (only desktop windows can't be back windows) 2380 */ 2381 void 2382 Desktop::_UpdateBack() 2383 { 2384 fBack = NULL; 2385 2386 for (Window* window = _CurrentWindows().FirstWindow(); 2387 window != NULL; window = window->NextWindow(fCurrentWorkspace)) { 2388 if (window->IsHidden() || window->Feel() == kDesktopWindowFeel) 2389 continue; 2390 2391 fBack = window; 2392 break; 2393 } 2394 } 2395 2396 2397 /*! Search the visible windows for a valid front window 2398 (only normal and modal windows can be front windows) 2399 2400 The only place where you don't want to update floating windows is 2401 during a workspace change - because then you'll call _UpdateFloating() 2402 yourself. 2403 */ 2404 void 2405 Desktop::_UpdateFront(bool updateFloating) 2406 { 2407 fFront = NULL; 2408 2409 for (Window* window = _CurrentWindows().LastWindow(); 2410 window != NULL; window = window->PreviousWindow(fCurrentWorkspace)) { 2411 if (window->IsHidden() || window->IsFloating() || !window->SupportsFront()) 2412 continue; 2413 2414 fFront = window; 2415 break; 2416 } 2417 2418 if (updateFloating) 2419 _UpdateFloating(); 2420 } 2421 2422 2423 void 2424 Desktop::_UpdateFronts(bool updateFloating) 2425 { 2426 _UpdateBack(); 2427 _UpdateFront(updateFloating); 2428 } 2429 2430 2431 bool 2432 Desktop::_WindowHasModal(Window* window) 2433 { 2434 if (window == NULL) 2435 return false; 2436 2437 for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL; 2438 modal = modal->NextWindow(kSubsetList)) { 2439 // only visible modal windows count 2440 if (!modal->IsModal() || modal->IsHidden()) 2441 continue; 2442 2443 if (modal->HasInSubset(window)) 2444 return true; 2445 } 2446 2447 return false; 2448 } 2449 2450 2451 /*! You must at least hold a single window lock when calling this method. 2452 */ 2453 void 2454 Desktop::_WindowChanged(Window* window) 2455 { 2456 ASSERT_MULTI_LOCKED(fWindowLock); 2457 2458 BAutolock _(fWorkspacesLock); 2459 2460 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 2461 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 2462 view->WindowChanged(window); 2463 } 2464 } 2465 2466 2467 /*! You must at least hold a single window lock when calling this method. 2468 */ 2469 void 2470 Desktop::_WindowRemoved(Window* window) 2471 { 2472 ASSERT_MULTI_LOCKED(fWindowLock); 2473 2474 BAutolock _(fWorkspacesLock); 2475 2476 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 2477 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 2478 view->WindowRemoved(window); 2479 } 2480 } 2481 2482 2483 /*! Shows the window on the screen - it does this independently of the 2484 Window::IsHidden() state. 2485 */ 2486 void 2487 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows) 2488 { 2489 BRegion background; 2490 _RebuildClippingForAllWindows(background); 2491 _SetBackground(background); 2492 _WindowChanged(window); 2493 2494 BRegion dirty(window->VisibleRegion()); 2495 2496 if (!affectsOtherWindows) { 2497 // everything that is now visible in the 2498 // window needs a redraw, but other windows 2499 // are not affected, we can call ProcessDirtyRegion() 2500 // of the window, and don't have to use MarkDirty() 2501 window->ProcessDirtyRegion(dirty); 2502 } else 2503 MarkDirty(dirty); 2504 2505 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 2506 window->ServerWindow()->HandleDirectConnection( 2507 B_DIRECT_START | B_BUFFER_RESET); 2508 } 2509 } 2510 2511 2512 /*! Hides the window from the screen - it does this independently of the 2513 Window::IsHidden() state. 2514 */ 2515 void 2516 Desktop::_HideWindow(Window* window) 2517 { 2518 if (window->ServerWindow()->IsDirectlyAccessing()) 2519 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 2520 2521 // after rebuilding the clipping, 2522 // this window will not have a visible 2523 // region anymore, so we need to remember 2524 // it now 2525 // (actually that's not true, since 2526 // hidden windows are excluded from the 2527 // clipping calculation, but anyways) 2528 BRegion dirty(window->VisibleRegion()); 2529 2530 BRegion background; 2531 _RebuildClippingForAllWindows(background); 2532 _SetBackground(background); 2533 _WindowChanged(window); 2534 2535 MarkDirty(dirty); 2536 } 2537 2538 2539 /*! Updates the workspaces of all subset windows with regard to the 2540 specifed window. 2541 If newIndex is not -1, it will move all subset windows that belong to 2542 the specifed window to the new workspace; this form is only called by 2543 SetWorkspace(). 2544 */ 2545 void 2546 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex, 2547 int32 newIndex) 2548 { 2549 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, window->Title())); 2550 2551 // if the window is hidden, the subset windows are up-to-date already 2552 if (!window->IsNormal() || window->IsHidden()) 2553 return; 2554 2555 for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL; 2556 subset = subset->NextWindow(kSubsetList)) { 2557 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL 2558 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) { 2559 // These windows are always visible on all workspaces, 2560 // no need to update them. 2561 continue; 2562 } 2563 2564 if (subset->IsFloating()) { 2565 // Floating windows are inserted and removed to the current 2566 // workspace as the need arises - they are not handled here 2567 // but in _UpdateFront() 2568 continue; 2569 } 2570 2571 if (subset->HasInSubset(window)) { 2572 // adopt the workspace change 2573 SetWindowWorkspaces(subset, subset->SubsetWorkspaces()); 2574 } 2575 } 2576 } 2577 2578 2579 /*! \brief Adds or removes the window to or from the workspaces it's on. 2580 */ 2581 void 2582 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces, 2583 uint32 newWorkspaces) 2584 { 2585 if (oldWorkspaces == newWorkspaces) 2586 return; 2587 2588 // apply changes to the workspaces' window lists 2589 2590 LockAllWindows(); 2591 2592 // NOTE: we bypass the anchor-mechanism by intention when switching 2593 // the workspace programmatically. 2594 2595 for (int32 i = 0; i < kMaxWorkspaces; i++) { 2596 if (workspace_in_workspaces(i, oldWorkspaces)) { 2597 // window is on this workspace, is it anymore? 2598 if (!workspace_in_workspaces(i, newWorkspaces)) { 2599 _Windows(i).RemoveWindow(window); 2600 2601 if (i == CurrentWorkspace()) { 2602 // remove its appearance from the current workspace 2603 window->SetCurrentWorkspace(-1); 2604 2605 if (!window->IsHidden()) 2606 _HideWindow(window); 2607 } 2608 } 2609 } else { 2610 // window was not on this workspace, is it now? 2611 if (workspace_in_workspaces(i, newWorkspaces)) { 2612 _Windows(i).AddWindow(window, 2613 window->Frontmost(_Windows(i).FirstWindow(), i)); 2614 2615 if (i == CurrentWorkspace()) { 2616 // make the window visible in current workspace 2617 window->SetCurrentWorkspace(fCurrentWorkspace); 2618 2619 if (!window->IsHidden()) { 2620 // This only affects other windows if this window has 2621 // floating or modal windows that need to be shown as 2622 // well 2623 // TODO: take care of this 2624 _ShowWindow(window, FrontWindow() == window); 2625 } 2626 } 2627 } 2628 } 2629 } 2630 2631 // If the window is visible only on one workspace, we set it's current 2632 // position in that workspace (so that WorkspacesView will find us). 2633 int32 firstWorkspace = -1; 2634 for (int32 i = 0; i < kMaxWorkspaces; i++) { 2635 if ((newWorkspaces & (1L << i)) != 0) { 2636 if (firstWorkspace != -1) { 2637 firstWorkspace = -1; 2638 break; 2639 } 2640 firstWorkspace = i; 2641 } 2642 } 2643 if (firstWorkspace >= 0) 2644 window->Anchor(firstWorkspace).position = window->Frame().LeftTop(); 2645 2646 // take care about modals and floating windows 2647 _UpdateSubsetWorkspaces(window); 2648 2649 UnlockAllWindows(); 2650 } 2651 2652 2653 void 2654 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, 2655 bool wereVisible) 2656 { 2657 // we don't need to redraw what is currently 2658 // visible of the window 2659 BRegion clean; 2660 2661 for (Window* window = windows.FirstWindow(); window != NULL; 2662 window = window->NextWindow(list)) { 2663 if (wereVisible) 2664 clean.Include(&window->VisibleRegion()); 2665 2666 _CurrentWindows().AddWindow(window, 2667 window->Frontmost(_CurrentWindows().FirstWindow(), 2668 fCurrentWorkspace)); 2669 2670 _WindowChanged(window); 2671 } 2672 2673 BRegion dummy; 2674 _RebuildClippingForAllWindows(dummy); 2675 2676 // redraw what became visible of the window(s) 2677 2678 BRegion dirty; 2679 for (Window* window = windows.FirstWindow(); window != NULL; 2680 window = window->NextWindow(list)) { 2681 dirty.Include(&window->VisibleRegion()); 2682 } 2683 2684 dirty.Exclude(&clean); 2685 MarkDirty(dirty); 2686 2687 _UpdateFront(); 2688 2689 if (windows.FirstWindow() == fBack || fBack == NULL) 2690 _UpdateBack(); 2691 } 2692 2693 2694 /*! Returns the last focussed non-hidden subset window belonging to the 2695 specified \a window. 2696 */ 2697 Window* 2698 Desktop::_LastFocusSubsetWindow(Window* window) 2699 { 2700 if (window == NULL) 2701 return NULL; 2702 2703 for (Window* front = fFocusList.LastWindow(); front != NULL; 2704 front = front->PreviousWindow(kFocusList)) { 2705 if (front != window && !front->IsHidden() && window->HasInSubset(front)) 2706 return front; 2707 } 2708 2709 return NULL; 2710 } 2711 2712 2713 /*! \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse, 2714 and also updates the current view under the mouse. 2715 2716 This has only to be done in case the view changed without user interaction, 2717 ie. because of a workspace change or a closing window. 2718 */ 2719 void 2720 Desktop::_SendFakeMouseMoved(Window* window) 2721 { 2722 int32 viewToken = B_NULL_TOKEN; 2723 EventTarget* target = NULL; 2724 2725 LockAllWindows(); 2726 2727 if (window == NULL) 2728 window = MouseEventWindow(); 2729 if (window == NULL) 2730 window = WindowAt(fLastMousePosition); 2731 2732 if (window != NULL) { 2733 BMessage message; 2734 window->MouseMoved(&message, fLastMousePosition, &viewToken, true, 2735 true); 2736 2737 if (viewToken != B_NULL_TOKEN) 2738 target = &window->EventTarget(); 2739 } 2740 2741 if (viewToken != B_NULL_TOKEN) 2742 SetViewUnderMouse(window, viewToken); 2743 else { 2744 SetViewUnderMouse(NULL, B_NULL_TOKEN); 2745 SetCursor(NULL); 2746 } 2747 2748 UnlockAllWindows(); 2749 2750 if (target != NULL) 2751 EventDispatcher().SendFakeMouseMoved(*target, viewToken); 2752 } 2753 2754 2755 Screen* 2756 Desktop::_DetermineScreenFor(BRect frame) 2757 { 2758 AutoReadLocker _(fScreenLock); 2759 2760 // TODO: choose the screen depending on where most of the area is 2761 return fVirtualScreen.ScreenAt(0); 2762 } 2763 2764 2765 void 2766 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen) 2767 { 2768 // the available region on screen starts with the entire screen area 2769 // each window on the screen will take a portion from that area 2770 2771 // figure out what the entire screen area is 2772 stillAvailableOnScreen = fScreenRegion; 2773 2774 // set clipping of each window 2775 for (Window* window = _CurrentWindows().LastWindow(); window != NULL; 2776 window = window->PreviousWindow(fCurrentWorkspace)) { 2777 if (!window->IsHidden()) { 2778 window->SetClipping(&stillAvailableOnScreen); 2779 window->SetScreen(_DetermineScreenFor(window->Frame())); 2780 2781 if (window->ServerWindow()->IsDirectlyAccessing()) { 2782 window->ServerWindow()->HandleDirectConnection( 2783 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 2784 } 2785 2786 // that windows region is not available on screen anymore 2787 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 2788 } 2789 } 2790 } 2791 2792 2793 void 2794 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion) 2795 { 2796 // send redraw messages to all windows intersecting the dirty region 2797 for (Window* window = _CurrentWindows().LastWindow(); window != NULL; 2798 window = window->PreviousWindow(fCurrentWorkspace)) { 2799 if (!window->IsHidden() 2800 && newDirtyRegion.Intersects(window->VisibleRegion().Frame())) 2801 window->ProcessDirtyRegion(newDirtyRegion); 2802 } 2803 } 2804 2805 2806 void 2807 Desktop::_SetBackground(BRegion& background) 2808 { 2809 // NOTE: the drawing operation is caried out 2810 // in the clipping region rebuild, but it is 2811 // ok actually, because it also avoids trails on 2812 // moving windows 2813 2814 // remember the region not covered by any windows 2815 // and redraw the dirty background 2816 BRegion dirtyBackground(background); 2817 dirtyBackground.Exclude(&fBackgroundRegion); 2818 dirtyBackground.IntersectWith(&background); 2819 fBackgroundRegion = background; 2820 if (dirtyBackground.Frame().IsValid()) { 2821 if (GetDrawingEngine()->LockParallelAccess()) { 2822 GetDrawingEngine()->FillRegion(dirtyBackground, 2823 fWorkspaces[fCurrentWorkspace].Color()); 2824 2825 GetDrawingEngine()->UnlockParallelAccess(); 2826 } 2827 } 2828 } 2829 2830 2831 //! The all window lock must be held when calling this function. 2832 void 2833 Desktop::_RebuildAndRedrawAfterWindowChange(Window* changedWindow, 2834 BRegion& dirty) 2835 { 2836 if (!changedWindow->IsVisible() || dirty.CountRects() == 0) 2837 return; 2838 2839 // The following loop is pretty much a copy of 2840 // _RebuildClippingForAllWindows(), but will also 2841 // take care about restricting our dirty region. 2842 2843 // figure out what the entire screen area is 2844 BRegion stillAvailableOnScreen(fScreenRegion); 2845 2846 // set clipping of each window 2847 for (Window* window = _CurrentWindows().LastWindow(); window != NULL; 2848 window = window->PreviousWindow(fCurrentWorkspace)) { 2849 if (!window->IsHidden()) { 2850 if (window == changedWindow) 2851 dirty.IntersectWith(&stillAvailableOnScreen); 2852 2853 window->SetClipping(&stillAvailableOnScreen); 2854 window->SetScreen(_DetermineScreenFor(window->Frame())); 2855 2856 if (window->ServerWindow()->IsDirectlyAccessing()) { 2857 window->ServerWindow()->HandleDirectConnection( 2858 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 2859 } 2860 2861 // that windows region is not available on screen anymore 2862 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 2863 } 2864 } 2865 2866 _SetBackground(stillAvailableOnScreen); 2867 _WindowChanged(changedWindow); 2868 2869 _TriggerWindowRedrawing(dirty); 2870 } 2871 2872 2873 //! Suspend all windows with direct access to the frame buffer 2874 void 2875 Desktop::_SuspendDirectFrameBufferAccess() 2876 { 2877 ASSERT_MULTI_LOCKED(fWindowLock); 2878 2879 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2880 window = window->NextWindow(kAllWindowList)) { 2881 if (window->ServerWindow()->IsDirectlyAccessing()) 2882 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 2883 } 2884 } 2885 2886 2887 //! Resume all windows with direct access to the frame buffer 2888 void 2889 Desktop::_ResumeDirectFrameBufferAccess() 2890 { 2891 ASSERT_MULTI_LOCKED(fWindowLock); 2892 2893 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2894 window = window->NextWindow(kAllWindowList)) { 2895 if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace)) 2896 continue; 2897 2898 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 2899 window->ServerWindow()->HandleDirectConnection( 2900 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 2901 } 2902 } 2903 } 2904 2905 2906 void 2907 Desktop::_ScreenChanged(Screen* screen) 2908 { 2909 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 2910 2911 // the entire screen is dirty, because we're actually 2912 // operating on an all new buffer in memory 2913 BRegion dirty(screen->Frame()); 2914 2915 // update our cached screen region 2916 fScreenRegion.Set(screen->Frame()); 2917 gInputManager->UpdateScreenBounds(screen->Frame()); 2918 2919 BRegion background; 2920 _RebuildClippingForAllWindows(background); 2921 2922 fBackgroundRegion.MakeEmpty(); 2923 // makes sure that the complete background is redrawn 2924 _SetBackground(background); 2925 2926 // figure out dirty region 2927 dirty.Exclude(&background); 2928 _TriggerWindowRedrawing(dirty); 2929 2930 // send B_SCREEN_CHANGED to windows on that screen 2931 BMessage update(B_SCREEN_CHANGED); 2932 update.AddInt64("when", real_time_clock_usecs()); 2933 update.AddRect("frame", screen->Frame()); 2934 update.AddInt32("mode", screen->ColorSpace()); 2935 2936 fVirtualScreen.UpdateFrame(); 2937 2938 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2939 window = window->NextWindow(kAllWindowList)) { 2940 if (window->Screen() == screen) 2941 window->ServerWindow()->ScreenChanged(&update); 2942 } 2943 } 2944 2945 2946 /*! \brief activate one of the app's windows. 2947 */ 2948 status_t 2949 Desktop::_ActivateApp(team_id team) 2950 { 2951 // search for an unhidden window in the current workspace 2952 2953 AutoWriteLocker locker(fWindowLock); 2954 2955 for (Window* window = _CurrentWindows().LastWindow(); window != NULL; 2956 window = window->PreviousWindow(fCurrentWorkspace)) { 2957 if (!window->IsHidden() && window->IsNormal() 2958 && window->ServerWindow()->ClientTeam() == team) { 2959 ActivateWindow(window); 2960 return B_OK; 2961 } 2962 } 2963 2964 // search for an unhidden window to give focus to 2965 2966 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2967 window = window->NextWindow(kAllWindowList)) { 2968 // if window is a normal window of the team, and not hidden, 2969 // we've found our target 2970 if (!window->IsHidden() && window->IsNormal() 2971 && window->ServerWindow()->ClientTeam() == team) { 2972 ActivateWindow(window); 2973 return B_OK; 2974 } 2975 } 2976 2977 // TODO: we cannot maximize minimized windows here (with the window lock 2978 // write locked). To work-around this, we could forward the request to 2979 // the ServerApp of this team - it maintains its own window list, and can 2980 // therefore call ActivateWindow() without holding the window lock. 2981 return B_BAD_VALUE; 2982 } 2983 2984 2985 void 2986 Desktop::_SetCurrentWorkspaceConfiguration() 2987 { 2988 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 2989 2990 status_t status = fDirectScreenLock.LockWithTimeout(1000000L); 2991 if (status != B_OK) { 2992 // The application having the direct screen lock didn't give it up in 2993 // time, make it crash 2994 syslog(LOG_ERR, "Team %ld did not give up its direct screen lock.\n", 2995 fDirectScreenTeam); 2996 2997 debug_thread(fDirectScreenTeam); 2998 fDirectScreenTeam = -1; 2999 } else 3000 fDirectScreenLock.Unlock(); 3001 3002 AutoWriteLocker _(fScreenLock); 3003 3004 uint32 changedScreens; 3005 fVirtualScreen.SetConfiguration(*this, 3006 fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(), 3007 &changedScreens); 3008 3009 for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) { 3010 if ((changedScreens & (1 << i)) != 0) 3011 _ScreenChanged(fVirtualScreen.ScreenAt(i)); 3012 } 3013 } 3014 3015 3016 /*! Changes the current workspace to the one specified by \a index. 3017 You must hold the all window lock when calling this method. 3018 */ 3019 void 3020 Desktop::_SetWorkspace(int32 index) 3021 { 3022 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3023 3024 int32 previousIndex = fCurrentWorkspace; 3025 rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color(); 3026 bool movedMouseEventWindow = false; 3027 3028 if (fMouseEventWindow != NULL) { 3029 if (fMouseEventWindow->IsNormal()) { 3030 if (!fMouseEventWindow->InWorkspace(index)) { 3031 // The window currently being dragged will follow us to this 3032 // workspace if it's not already on it. 3033 // But only normal windows are following 3034 uint32 oldWorkspaces = fMouseEventWindow->Workspaces(); 3035 3036 _Windows(index).AddWindow(fMouseEventWindow); 3037 _Windows(previousIndex).RemoveWindow(fMouseEventWindow); 3038 3039 _UpdateSubsetWorkspaces(fMouseEventWindow, previousIndex, 3040 index); 3041 movedMouseEventWindow = true; 3042 3043 // send B_WORKSPACES_CHANGED message 3044 fMouseEventWindow->WorkspacesChanged(oldWorkspaces, 3045 fMouseEventWindow->Workspaces()); 3046 } else { 3047 // make sure it's frontmost 3048 _Windows(index).RemoveWindow(fMouseEventWindow); 3049 _Windows(index).AddWindow(fMouseEventWindow, 3050 fMouseEventWindow->Frontmost(_Windows(index).FirstWindow(), 3051 index)); 3052 } 3053 } 3054 3055 fMouseEventWindow->Anchor(index).position 3056 = fMouseEventWindow->Frame().LeftTop(); 3057 } 3058 3059 // build region of windows that are no longer visible in the new workspace 3060 3061 BRegion dirty; 3062 3063 for (Window* window = _CurrentWindows().FirstWindow(); 3064 window != NULL; window = window->NextWindow(previousIndex)) { 3065 // store current position in Workspace anchor 3066 window->Anchor(previousIndex).position = window->Frame().LeftTop(); 3067 3068 if (!window->IsHidden() 3069 && window->ServerWindow()->IsDirectlyAccessing()) 3070 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3071 3072 window->WorkspaceActivated(previousIndex, false); 3073 3074 if (window->InWorkspace(index)) 3075 continue; 3076 3077 if (!window->IsHidden()) { 3078 // this window will no longer be visible 3079 dirty.Include(&window->VisibleRegion()); 3080 } 3081 3082 window->SetCurrentWorkspace(-1); 3083 } 3084 3085 fPreviousWorkspace = fCurrentWorkspace; 3086 fCurrentWorkspace = index; 3087 3088 // Change the display modes, if needed 3089 _SetCurrentWorkspaceConfiguration(); 3090 3091 // Show windows, and include them in the changed region - but only 3092 // those that were not visible before (or whose position changed) 3093 3094 WindowList windows(kWorkingList); 3095 BList previousRegions; 3096 3097 for (Window* window = _Windows(index).FirstWindow(); 3098 window != NULL; window = window->NextWindow(index)) { 3099 BPoint position = window->Anchor(index).position; 3100 3101 window->SetCurrentWorkspace(index); 3102 3103 if (window->IsHidden()) 3104 continue; 3105 3106 if (position == kInvalidWindowPosition) { 3107 // if you enter a workspace for the first time, the position 3108 // of the window in the previous workspace is adopted 3109 position = window->Frame().LeftTop(); 3110 // TODO: make sure the window is still on-screen if it 3111 // was before! 3112 } 3113 3114 if (!window->InWorkspace(previousIndex)) { 3115 // This window was not visible before, make sure its frame 3116 // is up-to-date 3117 if (window->Frame().LeftTop() != position) { 3118 BPoint offset = position - window->Frame().LeftTop(); 3119 window->MoveBy((int32)offset.x, (int32)offset.y); 3120 } 3121 continue; 3122 } 3123 3124 if (window->Frame().LeftTop() != position) { 3125 // the window was visible before, but its on-screen location changed 3126 BPoint offset = position - window->Frame().LeftTop(); 3127 MoveWindowBy(window, offset.x, offset.y); 3128 // TODO: be a bit smarter than this... 3129 } else { 3130 // We need to remember the previous visible region of the 3131 // window if they changed their order 3132 BRegion* region = new (std::nothrow) 3133 BRegion(window->VisibleRegion()); 3134 if (region != NULL) { 3135 if (previousRegions.AddItem(region)) 3136 windows.AddWindow(window); 3137 else 3138 delete region; 3139 } 3140 } 3141 } 3142 3143 _UpdateFronts(false); 3144 _UpdateFloating(previousIndex, index, 3145 movedMouseEventWindow ? fMouseEventWindow : NULL); 3146 3147 BRegion stillAvailableOnScreen; 3148 _RebuildClippingForAllWindows(stillAvailableOnScreen); 3149 _SetBackground(stillAvailableOnScreen); 3150 3151 for (Window* window = _Windows(index).FirstWindow(); window != NULL; 3152 window = window->NextWindow(index)) { 3153 // send B_WORKSPACE_ACTIVATED message 3154 window->WorkspaceActivated(index, true); 3155 3156 if (!window->IsHidden() 3157 && window->ServerWindow()->HasDirectFrameBufferAccess()) { 3158 window->ServerWindow()->HandleDirectConnection( 3159 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 3160 } 3161 3162 if (window->InWorkspace(previousIndex) || window->IsHidden() 3163 || (window == fMouseEventWindow && fMouseEventWindow->IsNormal()) 3164 || (!window->IsNormal() 3165 && window->HasInSubset(fMouseEventWindow))) { 3166 // This window was visible before, and is already handled in the 3167 // above loop 3168 continue; 3169 } 3170 3171 dirty.Include(&window->VisibleRegion()); 3172 } 3173 3174 // Catch order changes in the new workspaces window list 3175 int32 i = 0; 3176 for (Window* window = windows.FirstWindow(); window != NULL; 3177 window = window->NextWindow(kWorkingList), i++) { 3178 BRegion* region = (BRegion*)previousRegions.ItemAt(i); 3179 region->ExclusiveInclude(&window->VisibleRegion()); 3180 dirty.Include(region); 3181 delete region; 3182 } 3183 3184 // Set new focus to the front window, but keep focus to a floating 3185 // window if still visible 3186 if (!_Windows(index).HasWindow(FocusWindow()) 3187 || !FocusWindow()->IsFloating()) 3188 SetFocusWindow(FrontWindow()); 3189 3190 _WindowChanged(NULL); 3191 MarkDirty(dirty); 3192 3193 #if 0 3194 // Show the dirty regions of this workspace switch 3195 if (GetDrawingEngine()->LockParallelAccess()) { 3196 GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0}); 3197 GetDrawingEngine()->UnlockParallelAccess(); 3198 snooze(100000); 3199 } 3200 #endif 3201 3202 if (previousColor != fWorkspaces[fCurrentWorkspace].Color()) 3203 RedrawBackground(); 3204 } 3205