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