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