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