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_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. 1624 1625 Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both 1626 prevent it from getting focus. 1627 1628 In any case, this method makes sure that there is a focus window, if there 1629 is any window at all, that is. 1630 */ 1631 void 1632 Desktop::SetFocusWindow(Window* focus) 1633 { 1634 if (!LockAllWindows()) 1635 return; 1636 1637 // test for B_LOCK_WINDOW_FOCUS 1638 if (fLockedFocusWindow && focus != fLockedFocusWindow) { 1639 UnlockAllWindows(); 1640 return; 1641 } 1642 1643 bool hasModal = _WindowHasModal(focus); 1644 bool hasWindowScreen = false; 1645 1646 if (!hasModal && focus != NULL) { 1647 // Check whether or not a window screen is in front of the window 1648 // (if it has a modal, the right thing is done, anyway) 1649 Window* window = focus; 1650 while (true) { 1651 window = window->NextWindow(fCurrentWorkspace); 1652 if (window == NULL || window->Feel() == kWindowScreenFeel) 1653 break; 1654 } 1655 if (window != NULL) 1656 hasWindowScreen = true; 1657 } 1658 1659 if (focus == fFocus && focus != NULL && !focus->IsHidden() 1660 && (focus->Flags() & B_AVOID_FOCUS) == 0 1661 && !hasModal && !hasWindowScreen) { 1662 // the window that is supposed to get focus already has focus 1663 UnlockAllWindows(); 1664 return; 1665 } 1666 1667 uint32 list = fCurrentWorkspace; 1668 if (fSettings->FocusFollowsMouse()) 1669 list = kFocusList; 1670 1671 if (focus == NULL || hasModal || hasWindowScreen) { 1672 if (!fSettings->FocusFollowsMouse()) 1673 focus = _CurrentWindows().LastWindow(); 1674 else 1675 focus = fFocusList.LastWindow(); 1676 } 1677 1678 // make sure no window is chosen that doesn't want focus or cannot have it 1679 while (focus != NULL 1680 && (!focus->InWorkspace(fCurrentWorkspace) 1681 || (focus->Flags() & B_AVOID_FOCUS) != 0 1682 || _WindowHasModal(focus) 1683 || focus->IsHidden())) { 1684 focus = focus->PreviousWindow(list); 1685 } 1686 1687 if (fFocus == focus) { 1688 // turns out the window that is supposed to get focus now already has it 1689 UnlockAllWindows(); 1690 return; 1691 } 1692 1693 team_id oldActiveApp = -1; 1694 team_id newActiveApp = -1; 1695 1696 if (fFocus != NULL) { 1697 fFocus->SetFocus(false); 1698 oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 1699 } 1700 1701 fFocus = focus; 1702 1703 if (fFocus != NULL) { 1704 fFocus->SetFocus(true); 1705 newActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 1706 1707 // move current focus to the end of the focus list 1708 fFocusList.RemoveWindow(fFocus); 1709 fFocusList.AddWindow(fFocus); 1710 } 1711 1712 if (newActiveApp == -1) { 1713 // make sure the cursor is visible 1714 HWInterface()->SetCursorVisible(true); 1715 } 1716 1717 UnlockAllWindows(); 1718 1719 // change the "active" app if appropriate 1720 if (oldActiveApp == newActiveApp) 1721 return; 1722 1723 BAutolock locker(fApplicationsLock); 1724 1725 for (int32 i = 0; i < fApplications.CountItems(); i++) { 1726 ServerApp* app = fApplications.ItemAt(i); 1727 1728 if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp) 1729 app->Activate(false); 1730 else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp) 1731 app->Activate(true); 1732 } 1733 } 1734 1735 1736 void 1737 Desktop::SetFocusLocked(const Window* window) 1738 { 1739 AutoWriteLocker _(fWindowLock); 1740 1741 if (window != NULL) { 1742 // Don't allow this to be set when no mouse buttons 1743 // are pressed. (BView::SetMouseEventMask() should only be called 1744 // from mouse hooks.) 1745 if (fLastMouseButtons == 0) 1746 return; 1747 } 1748 1749 fLockedFocusWindow = window; 1750 } 1751 1752 1753 Window* 1754 Desktop::FindWindowByClientToken(int32 token, team_id teamID) 1755 { 1756 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1757 window = window->NextWindow(kAllWindowList)) { 1758 if (window->ServerWindow()->ClientToken() == token 1759 && window->ServerWindow()->ClientTeam() == teamID) { 1760 return window; 1761 } 1762 } 1763 1764 return NULL; 1765 } 1766 1767 1768 ::EventTarget* 1769 Desktop::FindTarget(BMessenger& messenger) 1770 { 1771 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1772 window = window->NextWindow(kAllWindowList)) { 1773 if (window->EventTarget().Messenger() == messenger) 1774 return &window->EventTarget(); 1775 } 1776 1777 return NULL; 1778 } 1779 1780 1781 void 1782 Desktop::MarkDirty(BRegion& region) 1783 { 1784 if (region.CountRects() == 0) 1785 return; 1786 1787 if (LockAllWindows()) { 1788 // send redraw messages to all windows intersecting the dirty region 1789 _TriggerWindowRedrawing(region); 1790 1791 UnlockAllWindows(); 1792 } 1793 } 1794 1795 1796 void 1797 Desktop::Redraw() 1798 { 1799 BRegion dirty(fVirtualScreen.Frame()); 1800 MarkDirty(dirty); 1801 } 1802 1803 1804 /*! \brief Redraws the background (ie. the desktop window, if any). 1805 */ 1806 void 1807 Desktop::RedrawBackground() 1808 { 1809 LockAllWindows(); 1810 1811 BRegion redraw; 1812 1813 Window* window = _CurrentWindows().FirstWindow(); 1814 if (window->Feel() == kDesktopWindowFeel) { 1815 redraw = window->VisibleContentRegion(); 1816 1817 // look for desktop background view, and update its background color 1818 // TODO: is there a better way to do this? 1819 View* view = window->TopView(); 1820 if (view != NULL) 1821 view = view->FirstChild(); 1822 1823 while (view) { 1824 if (view->IsDesktopBackground()) { 1825 view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color()); 1826 break; 1827 } 1828 view = view->NextSibling(); 1829 } 1830 1831 window->ProcessDirtyRegion(redraw); 1832 } else { 1833 redraw = BackgroundRegion(); 1834 fBackgroundRegion.MakeEmpty(); 1835 _SetBackground(redraw); 1836 } 1837 1838 _WindowChanged(NULL); 1839 // update workspaces view as well 1840 1841 UnlockAllWindows(); 1842 } 1843 1844 1845 void 1846 Desktop::MinimizeApplication(team_id team) 1847 { 1848 AutoWriteLocker locker(fWindowLock); 1849 1850 // Just minimize all windows of that application 1851 1852 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1853 window = window->NextWindow(kAllWindowList)) { 1854 if (window->ServerWindow()->ClientTeam() != team) 1855 continue; 1856 1857 window->ServerWindow()->NotifyMinimize(true); 1858 } 1859 } 1860 1861 1862 void 1863 Desktop::BringApplicationToFront(team_id team) 1864 { 1865 AutoWriteLocker locker(fWindowLock); 1866 1867 // TODO: for now, just maximize all windows of that application 1868 // TODO: have the ability to lock the current workspace 1869 1870 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1871 window = window->NextWindow(kAllWindowList)) { 1872 if (window->ServerWindow()->ClientTeam() != team) 1873 continue; 1874 1875 window->ServerWindow()->NotifyMinimize(false); 1876 } 1877 } 1878 1879 1880 void 1881 Desktop::WindowAction(int32 windowToken, int32 action) 1882 { 1883 if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT) 1884 return; 1885 1886 LockAllWindows(); 1887 1888 ::ServerWindow* serverWindow; 1889 Window* window; 1890 if (BPrivate::gDefaultTokens.GetToken(windowToken, 1891 B_SERVER_TOKEN, (void**)&serverWindow) != B_OK 1892 || (window = serverWindow->Window()) == NULL) { 1893 UnlockAllWindows(); 1894 return; 1895 } 1896 1897 if (action == B_BRING_TO_FRONT && !window->IsMinimized()) { 1898 // the window is visible, we just need to make it the front window 1899 ActivateWindow(window); 1900 } else { 1901 // if not, ask the window if it wants to be unminimized 1902 serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW); 1903 } 1904 1905 UnlockAllWindows(); 1906 } 1907 1908 1909 void 1910 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 1911 { 1912 AutoWriteLocker locker(fWindowLock); 1913 1914 // compute the number of windows 1915 1916 int32 count = 0; 1917 1918 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1919 window = window->NextWindow(kAllWindowList)) { 1920 if (team < B_OK || window->ServerWindow()->ClientTeam() == team) 1921 count++; 1922 } 1923 1924 // write list 1925 1926 sender.StartMessage(B_OK); 1927 sender.Attach<int32>(count); 1928 1929 // first write the windows of the current workspace correctly ordered 1930 for (Window *window = _CurrentWindows().LastWindow(); window != NULL; 1931 window = window->PreviousWindow(fCurrentWorkspace)) { 1932 if (team >= B_OK && window->ServerWindow()->ClientTeam() != team) 1933 continue; 1934 1935 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 1936 } 1937 1938 // then write all the other windows 1939 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1940 window = window->NextWindow(kAllWindowList)) { 1941 if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team) 1942 || window->InWorkspace(fCurrentWorkspace)) 1943 continue; 1944 1945 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 1946 } 1947 1948 sender.Flush(); 1949 } 1950 1951 1952 void 1953 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 1954 { 1955 AutoWriteLocker locker(fWindowLock); 1956 BAutolock tokenLocker(BPrivate::gDefaultTokens); 1957 1958 ::ServerWindow* window; 1959 if (BPrivate::gDefaultTokens.GetToken(serverToken, 1960 B_SERVER_TOKEN, (void**)&window) != B_OK) { 1961 sender.StartMessage(B_ENTRY_NOT_FOUND); 1962 sender.Flush(); 1963 return; 1964 } 1965 1966 window_info info; 1967 window->GetInfo(info); 1968 1969 float tabSize = 0.0; 1970 float borderSize = 0.0; 1971 ::Window* tmp = window->Window(); 1972 if (tmp) { 1973 BMessage message; 1974 if (tmp->GetDecoratorSettings(&message)) { 1975 BRect tabFrame; 1976 message.FindRect("tab frame", &tabFrame); 1977 tabSize = tabFrame.bottom - tabFrame.top; 1978 message.FindFloat("border width", &borderSize); 1979 } 1980 } 1981 1982 int32 length = window->Title() ? strlen(window->Title()) : 0; 1983 1984 sender.StartMessage(B_OK); 1985 sender.Attach<int32>(sizeof(client_window_info) + length); 1986 sender.Attach(&info, sizeof(window_info)); 1987 sender.Attach<float>(tabSize); 1988 sender.Attach<float>(borderSize); 1989 1990 if (length > 0) 1991 sender.Attach(window->Title(), length + 1); 1992 else 1993 sender.Attach<char>('\0'); 1994 1995 sender.Flush(); 1996 } 1997 1998 1999 void 2000 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender) 2001 { 2002 LockSingleWindow(); 2003 2004 if (workspace < 0) 2005 workspace = fCurrentWorkspace; 2006 else if (workspace >= kMaxWorkspaces) { 2007 sender.StartMessage(B_BAD_VALUE); 2008 sender.Flush(); 2009 UnlockSingleWindow(); 2010 return; 2011 } 2012 2013 int32 count = _Windows(workspace).Count(); 2014 2015 // write list 2016 2017 sender.StartMessage(B_OK); 2018 sender.Attach<int32>(count); 2019 2020 for (Window *window = _Windows(workspace).LastWindow(); window != NULL; 2021 window = window->PreviousWindow(workspace)) { 2022 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2023 } 2024 2025 sender.Flush(); 2026 2027 UnlockSingleWindow(); 2028 } 2029 2030 2031 void 2032 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender) 2033 { 2034 fApplicationsLock.Lock(); 2035 LockSingleWindow(); 2036 2037 int32 maxCount = fApplications.CountItems(); 2038 2039 fApplicationsLock.Unlock(); 2040 // as long as we hold the window lock, no new window can appear 2041 2042 if (workspace < 0) 2043 workspace = fCurrentWorkspace; 2044 else if (workspace >= kMaxWorkspaces) { 2045 sender.StartMessage(B_BAD_VALUE); 2046 sender.Flush(); 2047 UnlockSingleWindow(); 2048 return; 2049 } 2050 2051 // compute the list of applications on this workspace 2052 2053 team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id)); 2054 if (teams == NULL) { 2055 sender.StartMessage(B_NO_MEMORY); 2056 sender.Flush(); 2057 UnlockSingleWindow(); 2058 return; 2059 } 2060 2061 int32 count = 0; 2062 2063 for (Window *window = _Windows(workspace).LastWindow(); window != NULL; 2064 window = window->PreviousWindow(workspace)) { 2065 team_id team = window->ServerWindow()->ClientTeam(); 2066 if (count > 1) { 2067 // see if we already have this team 2068 bool found = false; 2069 for (int32 i = 0; i < count; i++) { 2070 if (teams[i] == team) { 2071 found = true; 2072 break; 2073 } 2074 } 2075 if (found) 2076 continue; 2077 } 2078 2079 ASSERT(count < maxCount); 2080 teams[count++] = team; 2081 } 2082 2083 UnlockSingleWindow(); 2084 2085 // write list 2086 2087 sender.StartMessage(B_OK); 2088 sender.Attach<int32>(count); 2089 2090 for (int32 i = 0; i < count; i++) { 2091 sender.Attach<int32>(teams[i]); 2092 } 2093 2094 sender.Flush(); 2095 free(teams); 2096 } 2097 2098 2099 void 2100 Desktop::_LaunchInputServer() 2101 { 2102 BRoster roster; 2103 status_t status = roster.Launch("application/x-vnd.Be-input_server"); 2104 if (status == B_OK || status == B_ALREADY_RUNNING) 2105 return; 2106 2107 // Could not load input_server by signature, try well-known location 2108 2109 BEntry entry("/system/servers/input_server"); 2110 entry_ref ref; 2111 status_t entryStatus = entry.GetRef(&ref); 2112 if (entryStatus == B_OK) 2113 entryStatus = roster.Launch(&ref); 2114 if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) { 2115 syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n", 2116 strerror(status)); 2117 return; 2118 } 2119 2120 syslog(LOG_ERR, "Failed to launch the input server: %s!\n", 2121 strerror(entryStatus)); 2122 } 2123 2124 2125 void 2126 Desktop::_GetLooperName(char* name, size_t length) 2127 { 2128 snprintf(name, length, "d:%d:%s", fUserID, 2129 fTargetScreen == NULL ? "baron" : fTargetScreen); 2130 } 2131 2132 2133 void 2134 Desktop::_PrepareQuit() 2135 { 2136 // let's kill all remaining applications 2137 2138 fApplicationsLock.Lock(); 2139 2140 int32 count = fApplications.CountItems(); 2141 for (int32 i = 0; i < count; i++) { 2142 ServerApp *app = fApplications.ItemAt(i); 2143 team_id clientTeam = app->ClientTeam(); 2144 2145 app->Quit(); 2146 kill_team(clientTeam); 2147 } 2148 2149 // wait for the last app to die 2150 if (count > 0) { 2151 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 2152 250000); 2153 } 2154 2155 fApplicationsLock.Unlock(); 2156 } 2157 2158 2159 void 2160 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) 2161 { 2162 switch (code) { 2163 case AS_CREATE_APP: 2164 { 2165 // Create the ServerApp to node monitor a new BApplication 2166 2167 // Attached data: 2168 // 1) port_id - receiver port of a regular app 2169 // 2) port_id - client looper port - for sending messages to the 2170 // client 2171 // 2) team_id - app's team ID 2172 // 3) int32 - handler token of the regular app 2173 // 4) char * - signature of the regular app 2174 2175 // Find the necessary data 2176 team_id clientTeamID = -1; 2177 port_id clientLooperPort = -1; 2178 port_id clientReplyPort = -1; 2179 int32 htoken = B_NULL_TOKEN; 2180 char *appSignature = NULL; 2181 2182 link.Read<port_id>(&clientReplyPort); 2183 link.Read<port_id>(&clientLooperPort); 2184 link.Read<team_id>(&clientTeamID); 2185 link.Read<int32>(&htoken); 2186 if (link.ReadString(&appSignature) != B_OK) 2187 break; 2188 2189 ServerApp *app = new ServerApp(this, clientReplyPort, 2190 clientLooperPort, clientTeamID, htoken, appSignature); 2191 if (app->InitCheck() == B_OK 2192 && app->Run()) { 2193 // add the new ServerApp to the known list of ServerApps 2194 fApplicationsLock.Lock(); 2195 fApplications.AddItem(app); 2196 fApplicationsLock.Unlock(); 2197 } else { 2198 delete app; 2199 2200 // if everything went well, ServerApp::Run() will notify 2201 // the client - but since it didn't, we do it here 2202 BPrivate::LinkSender reply(clientReplyPort); 2203 reply.StartMessage(B_ERROR); 2204 reply.Flush(); 2205 } 2206 2207 // This is necessary because BPortLink::ReadString allocates memory 2208 free(appSignature); 2209 break; 2210 } 2211 2212 case AS_DELETE_APP: 2213 { 2214 // Delete a ServerApp. Received only from the respective ServerApp 2215 // when a BApplication asks it to quit. 2216 2217 // Attached Data: 2218 // 1) thread_id - thread ID of the ServerApp to be deleted 2219 2220 thread_id thread = -1; 2221 if (link.Read<thread_id>(&thread) < B_OK) 2222 break; 2223 2224 fApplicationsLock.Lock(); 2225 2226 // Run through the list of apps and nuke the proper one 2227 2228 int32 count = fApplications.CountItems(); 2229 ServerApp *removeApp = NULL; 2230 2231 for (int32 i = 0; i < count; i++) { 2232 ServerApp *app = fApplications.ItemAt(i); 2233 2234 if (app->Thread() == thread) { 2235 fApplications.RemoveItemAt(i); 2236 removeApp = app; 2237 break; 2238 } 2239 } 2240 2241 fApplicationsLock.Unlock(); 2242 2243 if (removeApp != NULL) 2244 removeApp->Quit(fShutdownSemaphore); 2245 2246 if (fQuitting && count <= 1) { 2247 // wait for the last app to die 2248 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, 2249 B_RELATIVE_TIMEOUT, 500000); 2250 PostMessage(kMsgQuitLooper); 2251 } 2252 break; 2253 } 2254 2255 case AS_ACTIVATE_APP: 2256 { 2257 // Someone is requesting to activation of a certain app. 2258 2259 // Attached data: 2260 // 1) port_id reply port 2261 // 2) team_id team 2262 2263 status_t status; 2264 2265 // get the parameters 2266 port_id replyPort; 2267 team_id team; 2268 if (link.Read(&replyPort) == B_OK 2269 && link.Read(&team) == B_OK) 2270 status = _ActivateApp(team); 2271 else 2272 status = B_ERROR; 2273 2274 // send the reply 2275 BPrivate::PortLink replyLink(replyPort); 2276 replyLink.StartMessage(status); 2277 replyLink.Flush(); 2278 break; 2279 } 2280 2281 case AS_APP_CRASHED: 2282 { 2283 BAutolock locker(fApplicationsLock); 2284 2285 team_id team; 2286 if (link.Read(&team) != B_OK) 2287 break; 2288 2289 for (int32 i = 0; i < fApplications.CountItems(); i++) { 2290 ServerApp* app = fApplications.ItemAt(i); 2291 2292 if (app->ClientTeam() == team) 2293 app->PostMessage(AS_APP_CRASHED); 2294 } 2295 break; 2296 } 2297 2298 case AS_EVENT_STREAM_CLOSED: 2299 _LaunchInputServer(); 2300 break; 2301 2302 case B_QUIT_REQUESTED: 2303 // We've been asked to quit, so (for now) broadcast to all 2304 // test apps to quit. This situation will occur only when the 2305 // server is compiled as a regular Be application. 2306 2307 fApplicationsLock.Lock(); 2308 fShutdownSemaphore = create_sem(0, "desktop shutdown"); 2309 fShutdownCount = fApplications.CountItems(); 2310 fApplicationsLock.Unlock(); 2311 2312 fQuitting = true; 2313 BroadcastToAllApps(AS_QUIT_APP); 2314 2315 // We now need to process the remaining AS_DELETE_APP messages and 2316 // wait for the kMsgShutdownServer message. 2317 // If an application does not quit as asked, the picasso thread 2318 // will send us this message in 2-3 seconds. 2319 2320 // if there are no apps to quit, shutdown directly 2321 if (fShutdownCount == 0) 2322 PostMessage(kMsgQuitLooper); 2323 break; 2324 2325 case AS_ACTIVATE_WORKSPACE: 2326 { 2327 int32 index; 2328 link.Read<int32>(&index); 2329 if (index == -1) 2330 index = fPreviousWorkspace; 2331 2332 bool moveFocusWindow; 2333 link.Read<bool>(&moveFocusWindow); 2334 2335 SetWorkspace(index, moveFocusWindow); 2336 break; 2337 } 2338 2339 // ToDo: Remove this again. It is a message sent by the 2340 // invalidate_on_exit kernel debugger add-on to trigger a redraw 2341 // after exiting a kernel debugger session. 2342 case 'KDLE': 2343 { 2344 BRegion dirty; 2345 dirty.Include(fVirtualScreen.Frame()); 2346 MarkDirty(dirty); 2347 break; 2348 } 2349 2350 default: 2351 printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", 2352 code); 2353 2354 if (link.NeedsReply()) { 2355 // the client is now blocking and waiting for a reply! 2356 fLink.StartMessage(B_ERROR); 2357 fLink.Flush(); 2358 } 2359 break; 2360 } 2361 } 2362 2363 2364 WindowList& 2365 Desktop::_CurrentWindows() 2366 { 2367 return fWorkspaces[fCurrentWorkspace].Windows(); 2368 } 2369 2370 2371 WindowList& 2372 Desktop::_Windows(int32 index) 2373 { 2374 return fWorkspaces[index].Windows(); 2375 } 2376 2377 2378 void 2379 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace, 2380 Window* mouseEventWindow) 2381 { 2382 if (previousWorkspace == -1) 2383 previousWorkspace = fCurrentWorkspace; 2384 if (nextWorkspace == -1) 2385 nextWorkspace = previousWorkspace; 2386 2387 for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL; 2388 floating = floating->NextWindow(kSubsetList)) { 2389 // we only care about app/subset floating windows 2390 if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL 2391 && floating->Feel() != B_FLOATING_APP_WINDOW_FEEL) 2392 continue; 2393 2394 if (fFront != NULL && fFront->IsNormal() 2395 && floating->HasInSubset(fFront)) { 2396 // is now visible 2397 if (_Windows(previousWorkspace).HasWindow(floating) 2398 && previousWorkspace != nextWorkspace 2399 && !floating->InSubsetWorkspace(previousWorkspace)) { 2400 // but no longer on the previous workspace 2401 _Windows(previousWorkspace).RemoveWindow(floating); 2402 floating->SetCurrentWorkspace(-1); 2403 } 2404 2405 if (!_Windows(nextWorkspace).HasWindow(floating)) { 2406 // but wasn't before 2407 _Windows(nextWorkspace).AddWindow(floating, 2408 floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), 2409 nextWorkspace)); 2410 floating->SetCurrentWorkspace(nextWorkspace); 2411 if (mouseEventWindow != fFront) 2412 _ShowWindow(floating); 2413 2414 // TODO: put the floating last in the floating window list to 2415 // preserve the on screen window order 2416 } 2417 } else if (_Windows(previousWorkspace).HasWindow(floating) 2418 && !floating->InSubsetWorkspace(previousWorkspace)) { 2419 // was visible, but is no longer 2420 2421 _Windows(previousWorkspace).RemoveWindow(floating); 2422 floating->SetCurrentWorkspace(-1); 2423 _HideWindow(floating); 2424 2425 if (FocusWindow() == floating) 2426 SetFocusWindow(); 2427 } 2428 } 2429 } 2430 2431 2432 /*! Search the visible windows for a valid back window 2433 (only desktop windows can't be back windows) 2434 */ 2435 void 2436 Desktop::_UpdateBack() 2437 { 2438 fBack = NULL; 2439 2440 for (Window* window = _CurrentWindows().FirstWindow(); window != NULL; 2441 window = window->NextWindow(fCurrentWorkspace)) { 2442 if (window->IsHidden() || window->Feel() == kDesktopWindowFeel) 2443 continue; 2444 2445 fBack = window; 2446 break; 2447 } 2448 } 2449 2450 2451 /*! Search the visible windows for a valid front window 2452 (only normal and modal windows can be front windows) 2453 2454 The only place where you don't want to update floating windows is 2455 during a workspace change - because then you'll call _UpdateFloating() 2456 yourself. 2457 */ 2458 void 2459 Desktop::_UpdateFront(bool updateFloating) 2460 { 2461 fFront = NULL; 2462 2463 for (Window* window = _CurrentWindows().LastWindow(); window != NULL; 2464 window = window->PreviousWindow(fCurrentWorkspace)) { 2465 if (window->IsHidden() || window->IsFloating() 2466 || !window->SupportsFront()) 2467 continue; 2468 2469 fFront = window; 2470 break; 2471 } 2472 2473 if (updateFloating) 2474 _UpdateFloating(); 2475 } 2476 2477 2478 void 2479 Desktop::_UpdateFronts(bool updateFloating) 2480 { 2481 _UpdateBack(); 2482 _UpdateFront(updateFloating); 2483 } 2484 2485 2486 bool 2487 Desktop::_WindowHasModal(Window* window) 2488 { 2489 if (window == NULL) 2490 return false; 2491 2492 for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL; 2493 modal = modal->NextWindow(kSubsetList)) { 2494 // only visible modal windows count 2495 if (!modal->IsModal() || modal->IsHidden()) 2496 continue; 2497 2498 if (modal->HasInSubset(window)) 2499 return true; 2500 } 2501 2502 return false; 2503 } 2504 2505 2506 /*! You must at least hold a single window lock when calling this method. 2507 */ 2508 void 2509 Desktop::_WindowChanged(Window* window) 2510 { 2511 ASSERT_MULTI_LOCKED(fWindowLock); 2512 2513 BAutolock _(fWorkspacesLock); 2514 2515 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 2516 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 2517 view->WindowChanged(window); 2518 } 2519 } 2520 2521 2522 /*! You must at least hold a single window lock when calling this method. 2523 */ 2524 void 2525 Desktop::_WindowRemoved(Window* window) 2526 { 2527 ASSERT_MULTI_LOCKED(fWindowLock); 2528 2529 BAutolock _(fWorkspacesLock); 2530 2531 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 2532 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 2533 view->WindowRemoved(window); 2534 } 2535 } 2536 2537 2538 /*! Shows the window on the screen - it does this independently of the 2539 Window::IsHidden() state. 2540 */ 2541 void 2542 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows) 2543 { 2544 BRegion background; 2545 _RebuildClippingForAllWindows(background); 2546 _SetBackground(background); 2547 _WindowChanged(window); 2548 2549 BRegion dirty(window->VisibleRegion()); 2550 2551 if (!affectsOtherWindows) { 2552 // everything that is now visible in the 2553 // window needs a redraw, but other windows 2554 // are not affected, we can call ProcessDirtyRegion() 2555 // of the window, and don't have to use MarkDirty() 2556 window->ProcessDirtyRegion(dirty); 2557 } else 2558 MarkDirty(dirty); 2559 2560 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 2561 window->ServerWindow()->HandleDirectConnection( 2562 B_DIRECT_START | B_BUFFER_RESET); 2563 } 2564 } 2565 2566 2567 /*! Hides the window from the screen - it does this independently of the 2568 Window::IsHidden() state. 2569 */ 2570 void 2571 Desktop::_HideWindow(Window* window) 2572 { 2573 if (window->ServerWindow()->IsDirectlyAccessing()) 2574 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 2575 2576 // after rebuilding the clipping, 2577 // this window will not have a visible 2578 // region anymore, so we need to remember 2579 // it now 2580 // (actually that's not true, since 2581 // hidden windows are excluded from the 2582 // clipping calculation, but anyways) 2583 BRegion dirty(window->VisibleRegion()); 2584 2585 BRegion background; 2586 _RebuildClippingForAllWindows(background); 2587 _SetBackground(background); 2588 _WindowChanged(window); 2589 2590 MarkDirty(dirty); 2591 } 2592 2593 2594 /*! Updates the workspaces of all subset windows with regard to the 2595 specifed window. 2596 If newIndex is not -1, it will move all subset windows that belong to 2597 the specifed window to the new workspace; this form is only called by 2598 SetWorkspace(). 2599 */ 2600 void 2601 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex, 2602 int32 newIndex) 2603 { 2604 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, 2605 window->Title())); 2606 2607 // if the window is hidden, the subset windows are up-to-date already 2608 if (!window->IsNormal() || window->IsHidden()) 2609 return; 2610 2611 for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL; 2612 subset = subset->NextWindow(kSubsetList)) { 2613 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL 2614 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) { 2615 // These windows are always visible on all workspaces, 2616 // no need to update them. 2617 continue; 2618 } 2619 2620 if (subset->IsFloating()) { 2621 // Floating windows are inserted and removed to the current 2622 // workspace as the need arises - they are not handled here 2623 // but in _UpdateFront() 2624 continue; 2625 } 2626 2627 if (subset->HasInSubset(window)) { 2628 // adopt the workspace change 2629 SetWindowWorkspaces(subset, subset->SubsetWorkspaces()); 2630 } 2631 } 2632 } 2633 2634 2635 /*! \brief Adds or removes the window to or from the workspaces it's on. 2636 */ 2637 void 2638 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces, 2639 uint32 newWorkspaces) 2640 { 2641 if (oldWorkspaces == newWorkspaces) 2642 return; 2643 2644 // apply changes to the workspaces' window lists 2645 2646 LockAllWindows(); 2647 2648 // NOTE: we bypass the anchor-mechanism by intention when switching 2649 // the workspace programmatically. 2650 2651 for (int32 i = 0; i < kMaxWorkspaces; i++) { 2652 if (workspace_in_workspaces(i, oldWorkspaces)) { 2653 // window is on this workspace, is it anymore? 2654 if (!workspace_in_workspaces(i, newWorkspaces)) { 2655 _Windows(i).RemoveWindow(window); 2656 if (fLastWorkspaceFocus[i] == window) 2657 fLastWorkspaceFocus[i] = NULL; 2658 2659 if (i == CurrentWorkspace()) { 2660 // remove its appearance from the current workspace 2661 window->SetCurrentWorkspace(-1); 2662 2663 if (!window->IsHidden()) 2664 _HideWindow(window); 2665 } 2666 } 2667 } else { 2668 // window was not on this workspace, is it now? 2669 if (workspace_in_workspaces(i, newWorkspaces)) { 2670 _Windows(i).AddWindow(window, 2671 window->Frontmost(_Windows(i).FirstWindow(), i)); 2672 2673 if (i == CurrentWorkspace()) { 2674 // make the window visible in current workspace 2675 window->SetCurrentWorkspace(fCurrentWorkspace); 2676 2677 if (!window->IsHidden()) { 2678 // This only affects other windows if this window has 2679 // floating or modal windows that need to be shown as 2680 // well 2681 // TODO: take care of this 2682 _ShowWindow(window, FrontWindow() == window); 2683 } 2684 } 2685 } 2686 } 2687 } 2688 2689 // If the window is visible only on one workspace, we set it's current 2690 // position in that workspace (so that WorkspacesView will find us). 2691 int32 firstWorkspace = -1; 2692 for (int32 i = 0; i < kMaxWorkspaces; i++) { 2693 if ((newWorkspaces & (1L << i)) != 0) { 2694 if (firstWorkspace != -1) { 2695 firstWorkspace = -1; 2696 break; 2697 } 2698 firstWorkspace = i; 2699 } 2700 } 2701 if (firstWorkspace >= 0) 2702 window->Anchor(firstWorkspace).position = window->Frame().LeftTop(); 2703 2704 // take care about modals and floating windows 2705 _UpdateSubsetWorkspaces(window); 2706 2707 UnlockAllWindows(); 2708 } 2709 2710 2711 void 2712 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, 2713 bool wereVisible) 2714 { 2715 // we don't need to redraw what is currently 2716 // visible of the window 2717 BRegion clean; 2718 2719 for (Window* window = windows.FirstWindow(); window != NULL; 2720 window = window->NextWindow(list)) { 2721 if (wereVisible) 2722 clean.Include(&window->VisibleRegion()); 2723 2724 _CurrentWindows().AddWindow(window, 2725 window->Frontmost(_CurrentWindows().FirstWindow(), 2726 fCurrentWorkspace)); 2727 2728 _WindowChanged(window); 2729 } 2730 2731 BRegion dummy; 2732 _RebuildClippingForAllWindows(dummy); 2733 2734 // redraw what became visible of the window(s) 2735 2736 BRegion dirty; 2737 for (Window* window = windows.FirstWindow(); window != NULL; 2738 window = window->NextWindow(list)) { 2739 dirty.Include(&window->VisibleRegion()); 2740 } 2741 2742 dirty.Exclude(&clean); 2743 MarkDirty(dirty); 2744 2745 _UpdateFront(); 2746 2747 if (windows.FirstWindow() == fBack || fBack == NULL) 2748 _UpdateBack(); 2749 } 2750 2751 2752 /*! Returns the last focussed non-hidden subset window belonging to the 2753 specified \a window. 2754 */ 2755 Window* 2756 Desktop::_LastFocusSubsetWindow(Window* window) 2757 { 2758 if (window == NULL) 2759 return NULL; 2760 2761 for (Window* front = fFocusList.LastWindow(); front != NULL; 2762 front = front->PreviousWindow(kFocusList)) { 2763 if (front != window && !front->IsHidden() 2764 && window->HasInSubset(front)) 2765 return front; 2766 } 2767 2768 return NULL; 2769 } 2770 2771 2772 /*! \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse, 2773 and also updates the current view under the mouse. 2774 2775 This has only to be done in case the view changed without user interaction, 2776 ie. because of a workspace change or a closing window. 2777 */ 2778 void 2779 Desktop::_SendFakeMouseMoved(Window* window) 2780 { 2781 int32 viewToken = B_NULL_TOKEN; 2782 EventTarget* target = NULL; 2783 2784 LockAllWindows(); 2785 2786 if (window == NULL) 2787 window = MouseEventWindow(); 2788 if (window == NULL) 2789 window = WindowAt(fLastMousePosition); 2790 2791 if (window != NULL) { 2792 BMessage message; 2793 window->MouseMoved(&message, fLastMousePosition, &viewToken, true, 2794 true); 2795 2796 if (viewToken != B_NULL_TOKEN) 2797 target = &window->EventTarget(); 2798 } 2799 2800 if (viewToken != B_NULL_TOKEN) 2801 SetViewUnderMouse(window, viewToken); 2802 else { 2803 SetViewUnderMouse(NULL, B_NULL_TOKEN); 2804 SetCursor(NULL); 2805 } 2806 2807 UnlockAllWindows(); 2808 2809 if (target != NULL) 2810 EventDispatcher().SendFakeMouseMoved(*target, viewToken); 2811 } 2812 2813 2814 Screen* 2815 Desktop::_DetermineScreenFor(BRect frame) 2816 { 2817 AutoReadLocker _(fScreenLock); 2818 2819 // TODO: choose the screen depending on where most of the area is 2820 return fVirtualScreen.ScreenAt(0); 2821 } 2822 2823 2824 void 2825 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen) 2826 { 2827 // the available region on screen starts with the entire screen area 2828 // each window on the screen will take a portion from that area 2829 2830 // figure out what the entire screen area is 2831 stillAvailableOnScreen = fScreenRegion; 2832 2833 // set clipping of each window 2834 for (Window* window = _CurrentWindows().LastWindow(); window != NULL; 2835 window = window->PreviousWindow(fCurrentWorkspace)) { 2836 if (!window->IsHidden()) { 2837 window->SetClipping(&stillAvailableOnScreen); 2838 window->SetScreen(_DetermineScreenFor(window->Frame())); 2839 2840 if (window->ServerWindow()->IsDirectlyAccessing()) { 2841 window->ServerWindow()->HandleDirectConnection( 2842 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 2843 } 2844 2845 // that windows region is not available on screen anymore 2846 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 2847 } 2848 } 2849 } 2850 2851 2852 void 2853 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion) 2854 { 2855 // send redraw messages to all windows intersecting the dirty region 2856 for (Window* window = _CurrentWindows().LastWindow(); window != NULL; 2857 window = window->PreviousWindow(fCurrentWorkspace)) { 2858 if (!window->IsHidden() 2859 && newDirtyRegion.Intersects(window->VisibleRegion().Frame())) 2860 window->ProcessDirtyRegion(newDirtyRegion); 2861 } 2862 } 2863 2864 2865 void 2866 Desktop::_SetBackground(BRegion& background) 2867 { 2868 // NOTE: the drawing operation is caried out 2869 // in the clipping region rebuild, but it is 2870 // ok actually, because it also avoids trails on 2871 // moving windows 2872 2873 // remember the region not covered by any windows 2874 // and redraw the dirty background 2875 BRegion dirtyBackground(background); 2876 dirtyBackground.Exclude(&fBackgroundRegion); 2877 dirtyBackground.IntersectWith(&background); 2878 fBackgroundRegion = background; 2879 if (dirtyBackground.Frame().IsValid()) { 2880 if (GetDrawingEngine()->LockParallelAccess()) { 2881 GetDrawingEngine()->FillRegion(dirtyBackground, 2882 fWorkspaces[fCurrentWorkspace].Color()); 2883 2884 GetDrawingEngine()->UnlockParallelAccess(); 2885 } 2886 } 2887 } 2888 2889 2890 //! The all window lock must be held when calling this function. 2891 void 2892 Desktop::_RebuildAndRedrawAfterWindowChange(Window* changedWindow, 2893 BRegion& dirty) 2894 { 2895 if (!changedWindow->IsVisible() || dirty.CountRects() == 0) 2896 return; 2897 2898 // The following loop is pretty much a copy of 2899 // _RebuildClippingForAllWindows(), but will also 2900 // take care about restricting our dirty region. 2901 2902 // figure out what the entire screen area is 2903 BRegion stillAvailableOnScreen(fScreenRegion); 2904 2905 // set clipping of each window 2906 for (Window* window = _CurrentWindows().LastWindow(); window != NULL; 2907 window = window->PreviousWindow(fCurrentWorkspace)) { 2908 if (!window->IsHidden()) { 2909 if (window == changedWindow) 2910 dirty.IntersectWith(&stillAvailableOnScreen); 2911 2912 window->SetClipping(&stillAvailableOnScreen); 2913 window->SetScreen(_DetermineScreenFor(window->Frame())); 2914 2915 if (window->ServerWindow()->IsDirectlyAccessing()) { 2916 window->ServerWindow()->HandleDirectConnection( 2917 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 2918 } 2919 2920 // that windows region is not available on screen anymore 2921 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 2922 } 2923 } 2924 2925 _SetBackground(stillAvailableOnScreen); 2926 _WindowChanged(changedWindow); 2927 2928 _TriggerWindowRedrawing(dirty); 2929 } 2930 2931 2932 //! Suspend all windows with direct access to the frame buffer 2933 void 2934 Desktop::_SuspendDirectFrameBufferAccess() 2935 { 2936 ASSERT_MULTI_LOCKED(fWindowLock); 2937 2938 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2939 window = window->NextWindow(kAllWindowList)) { 2940 if (window->ServerWindow()->IsDirectlyAccessing()) 2941 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 2942 } 2943 } 2944 2945 2946 //! Resume all windows with direct access to the frame buffer 2947 void 2948 Desktop::_ResumeDirectFrameBufferAccess() 2949 { 2950 ASSERT_MULTI_LOCKED(fWindowLock); 2951 2952 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2953 window = window->NextWindow(kAllWindowList)) { 2954 if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace)) 2955 continue; 2956 2957 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 2958 window->ServerWindow()->HandleDirectConnection( 2959 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 2960 } 2961 } 2962 } 2963 2964 2965 void 2966 Desktop::_ScreenChanged(Screen* screen) 2967 { 2968 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 2969 2970 // the entire screen is dirty, because we're actually 2971 // operating on an all new buffer in memory 2972 BRegion dirty(screen->Frame()); 2973 2974 // update our cached screen region 2975 fScreenRegion.Set(screen->Frame()); 2976 gInputManager->UpdateScreenBounds(screen->Frame()); 2977 2978 BRegion background; 2979 _RebuildClippingForAllWindows(background); 2980 2981 fBackgroundRegion.MakeEmpty(); 2982 // makes sure that the complete background is redrawn 2983 _SetBackground(background); 2984 2985 // figure out dirty region 2986 dirty.Exclude(&background); 2987 _TriggerWindowRedrawing(dirty); 2988 2989 // send B_SCREEN_CHANGED to windows on that screen 2990 BMessage update(B_SCREEN_CHANGED); 2991 update.AddInt64("when", real_time_clock_usecs()); 2992 update.AddRect("frame", screen->Frame()); 2993 update.AddInt32("mode", screen->ColorSpace()); 2994 2995 fVirtualScreen.UpdateFrame(); 2996 2997 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2998 window = window->NextWindow(kAllWindowList)) { 2999 if (window->Screen() == screen) 3000 window->ServerWindow()->ScreenChanged(&update); 3001 } 3002 } 3003 3004 3005 /*! \brief activate one of the app's windows. 3006 */ 3007 status_t 3008 Desktop::_ActivateApp(team_id team) 3009 { 3010 // search for an unhidden window in the current workspace 3011 3012 AutoWriteLocker locker(fWindowLock); 3013 3014 for (Window* window = _CurrentWindows().LastWindow(); window != NULL; 3015 window = window->PreviousWindow(fCurrentWorkspace)) { 3016 if (!window->IsHidden() && window->IsNormal() 3017 && window->ServerWindow()->ClientTeam() == team) { 3018 ActivateWindow(window); 3019 return B_OK; 3020 } 3021 } 3022 3023 // search for an unhidden window to give focus to 3024 3025 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3026 window = window->NextWindow(kAllWindowList)) { 3027 // if window is a normal window of the team, and not hidden, 3028 // we've found our target 3029 if (!window->IsHidden() && window->IsNormal() 3030 && window->ServerWindow()->ClientTeam() == team) { 3031 ActivateWindow(window); 3032 return B_OK; 3033 } 3034 } 3035 3036 // TODO: we cannot maximize minimized windows here (with the window lock 3037 // write locked). To work-around this, we could forward the request to 3038 // the ServerApp of this team - it maintains its own window list, and can 3039 // therefore call ActivateWindow() without holding the window lock. 3040 return B_BAD_VALUE; 3041 } 3042 3043 3044 void 3045 Desktop::_SetCurrentWorkspaceConfiguration() 3046 { 3047 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3048 3049 status_t status = fDirectScreenLock.LockWithTimeout(1000000L); 3050 if (status != B_OK) { 3051 // The application having the direct screen lock didn't give it up in 3052 // time, make it crash 3053 syslog(LOG_ERR, "Team %ld did not give up its direct screen lock.\n", 3054 fDirectScreenTeam); 3055 3056 debug_thread(fDirectScreenTeam); 3057 fDirectScreenTeam = -1; 3058 } else 3059 fDirectScreenLock.Unlock(); 3060 3061 AutoWriteLocker _(fScreenLock); 3062 3063 uint32 changedScreens; 3064 fVirtualScreen.SetConfiguration(*this, 3065 fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(), 3066 &changedScreens); 3067 3068 for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) { 3069 if ((changedScreens & (1 << i)) != 0) 3070 _ScreenChanged(fVirtualScreen.ScreenAt(i)); 3071 } 3072 } 3073 3074 3075 /*! Changes the current workspace to the one specified by \a index. 3076 You must hold the all window lock when calling this method. 3077 */ 3078 void 3079 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow) 3080 { 3081 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3082 3083 int32 previousIndex = fCurrentWorkspace; 3084 rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color(); 3085 bool movedMouseEventWindow = false; 3086 Window* movedWindow = fMouseEventWindow; 3087 if (movedWindow == NULL && moveFocusWindow) 3088 movedWindow = FocusWindow(); 3089 3090 if (movedWindow != NULL) { 3091 if (movedWindow->IsNormal()) { 3092 if (!movedWindow->InWorkspace(index)) { 3093 // The window currently being dragged will follow us to this 3094 // workspace if it's not already on it. 3095 // But only normal windows are following 3096 uint32 oldWorkspaces = movedWindow->Workspaces(); 3097 3098 _Windows(previousIndex).RemoveWindow(movedWindow); 3099 _Windows(index).AddWindow(movedWindow, 3100 movedWindow->Frontmost(_Windows(index).FirstWindow(), 3101 index)); 3102 3103 // TODO: subset windows will always flicker this way 3104 3105 movedMouseEventWindow = true; 3106 3107 // send B_WORKSPACES_CHANGED message 3108 movedWindow->WorkspacesChanged(oldWorkspaces, 3109 movedWindow->Workspaces()); 3110 } else { 3111 // make sure it's frontmost 3112 _Windows(index).RemoveWindow(movedWindow); 3113 _Windows(index).AddWindow(movedWindow, 3114 movedWindow->Frontmost(_Windows(index).FirstWindow(), 3115 index)); 3116 } 3117 } 3118 3119 movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop(); 3120 } 3121 3122 fLastWorkspaceFocus[previousIndex] = FocusWindow(); 3123 3124 // build region of windows that are no longer visible in the new workspace 3125 3126 BRegion dirty; 3127 3128 for (Window* window = _CurrentWindows().FirstWindow(); 3129 window != NULL; window = window->NextWindow(previousIndex)) { 3130 // store current position in Workspace anchor 3131 window->Anchor(previousIndex).position = window->Frame().LeftTop(); 3132 3133 if (!window->IsHidden() 3134 && window->ServerWindow()->IsDirectlyAccessing()) 3135 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3136 3137 window->WorkspaceActivated(previousIndex, false); 3138 3139 if (window->InWorkspace(index)) 3140 continue; 3141 3142 if (!window->IsHidden()) { 3143 // this window will no longer be visible 3144 dirty.Include(&window->VisibleRegion()); 3145 } 3146 3147 window->SetCurrentWorkspace(-1); 3148 } 3149 3150 fPreviousWorkspace = fCurrentWorkspace; 3151 fCurrentWorkspace = index; 3152 3153 // Change the display modes, if needed 3154 _SetCurrentWorkspaceConfiguration(); 3155 3156 // Show windows, and include them in the changed region - but only 3157 // those that were not visible before (or whose position changed) 3158 3159 WindowList windows(kWorkingList); 3160 BList previousRegions; 3161 3162 for (Window* window = _Windows(index).FirstWindow(); 3163 window != NULL; window = window->NextWindow(index)) { 3164 BPoint position = window->Anchor(index).position; 3165 3166 window->SetCurrentWorkspace(index); 3167 3168 if (window->IsHidden()) 3169 continue; 3170 3171 if (position == kInvalidWindowPosition) { 3172 // if you enter a workspace for the first time, the position 3173 // of the window in the previous workspace is adopted 3174 position = window->Frame().LeftTop(); 3175 // TODO: make sure the window is still on-screen if it 3176 // was before! 3177 } 3178 3179 if (!window->InWorkspace(previousIndex)) { 3180 // This window was not visible before, make sure its frame 3181 // is up-to-date 3182 if (window->Frame().LeftTop() != position) { 3183 BPoint offset = position - window->Frame().LeftTop(); 3184 window->MoveBy((int32)offset.x, (int32)offset.y); 3185 } 3186 continue; 3187 } 3188 3189 if (window->Frame().LeftTop() != position) { 3190 // the window was visible before, but its on-screen location changed 3191 BPoint offset = position - window->Frame().LeftTop(); 3192 MoveWindowBy(window, offset.x, offset.y); 3193 // TODO: be a bit smarter than this... 3194 } else { 3195 // We need to remember the previous visible region of the 3196 // window if they changed their order 3197 BRegion* region = new (std::nothrow) 3198 BRegion(window->VisibleRegion()); 3199 if (region != NULL) { 3200 if (previousRegions.AddItem(region)) 3201 windows.AddWindow(window); 3202 else 3203 delete region; 3204 } 3205 } 3206 } 3207 3208 _UpdateFronts(false); 3209 _UpdateFloating(previousIndex, index, 3210 movedMouseEventWindow ? movedWindow : NULL); 3211 3212 BRegion stillAvailableOnScreen; 3213 _RebuildClippingForAllWindows(stillAvailableOnScreen); 3214 _SetBackground(stillAvailableOnScreen); 3215 3216 for (Window* window = _Windows(index).FirstWindow(); window != NULL; 3217 window = window->NextWindow(index)) { 3218 // send B_WORKSPACE_ACTIVATED message 3219 window->WorkspaceActivated(index, true); 3220 3221 if (!window->IsHidden() 3222 && window->ServerWindow()->HasDirectFrameBufferAccess()) { 3223 window->ServerWindow()->HandleDirectConnection( 3224 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 3225 } 3226 3227 if (window->InWorkspace(previousIndex) || window->IsHidden() 3228 || (window == movedWindow && movedWindow->IsNormal()) 3229 || (!window->IsNormal() 3230 && window->HasInSubset(movedWindow))) { 3231 // This window was visible before, and is already handled in the 3232 // above loop 3233 continue; 3234 } 3235 3236 dirty.Include(&window->VisibleRegion()); 3237 } 3238 3239 // Catch order changes in the new workspaces window list 3240 int32 i = 0; 3241 for (Window* window = windows.FirstWindow(); window != NULL; 3242 window = window->NextWindow(kWorkingList), i++) { 3243 BRegion* region = (BRegion*)previousRegions.ItemAt(i); 3244 region->ExclusiveInclude(&window->VisibleRegion()); 3245 dirty.Include(region); 3246 delete region; 3247 } 3248 3249 // Set new focus, but keep focus to a floating window if still visible 3250 if (!_Windows(index).HasWindow(FocusWindow()) 3251 || !FocusWindow()->IsFloating()) 3252 SetFocusWindow(fLastWorkspaceFocus[index]); 3253 3254 _WindowChanged(NULL); 3255 MarkDirty(dirty); 3256 3257 #if 0 3258 // Show the dirty regions of this workspace switch 3259 if (GetDrawingEngine()->LockParallelAccess()) { 3260 GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0}); 3261 GetDrawingEngine()->UnlockParallelAccess(); 3262 snooze(100000); 3263 } 3264 #endif 3265 3266 if (previousColor != fWorkspaces[fCurrentWorkspace].Color()) 3267 RedrawBackground(); 3268 } 3269