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