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