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