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