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