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