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