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