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 constructed by the window itself in 1562 // ResizeBy() 1563 BRegion newDirtyRegion; 1564 // Track the dirty region outside the window in case it is shrunk in "previouslyOccupiedRegion" 1565 BRegion previouslyOccupiedRegion(window->VisibleRegion()); 1566 // Track the region that was drawn in previous update sessions, so we can compute the newly 1567 // exposed areas by excluding this from the update region. 1568 BRegion previousVisibleContentRegion(window->VisibleContentRegion()); 1569 1570 // stop direct frame buffer access 1571 bool direct = false; 1572 if (window->ServerWindow()->IsDirectlyAccessing()) { 1573 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 1574 direct = true; 1575 } 1576 1577 window->ResizeBy((int32)x, (int32)y, &newDirtyRegion); 1578 1579 BRegion background; 1580 _RebuildClippingForAllWindows(background); 1581 1582 // we just care for the region outside the window 1583 previouslyOccupiedRegion.Exclude(&window->VisibleRegion()); 1584 1585 // make sure the window cannot mark stuff dirty outside 1586 // its visible region... 1587 newDirtyRegion.IntersectWith(&window->VisibleRegion()); 1588 // ...because we do this ourselves 1589 newDirtyRegion.Include(&previouslyOccupiedRegion); 1590 1591 // calculate old expose region as window visible region difference 1592 BRegion exposeRegion(previouslyOccupiedRegion); 1593 exposeRegion.Exclude(&window->VisibleRegion()); 1594 // ...and new expose region as window content visible region difference 1595 BRegion tmp(window->VisibleContentRegion()); 1596 tmp.Exclude(&previousVisibleContentRegion); 1597 exposeRegion.Include(&tmp); 1598 1599 MarkDirty(newDirtyRegion, exposeRegion); 1600 _SetBackground(background); 1601 _WindowChanged(window); 1602 1603 // resume direct frame buffer access 1604 if (direct) { 1605 window->ServerWindow()->HandleDirectConnection( 1606 B_DIRECT_START | B_BUFFER_RESIZED | B_CLIPPING_MODIFIED); 1607 } 1608 1609 NotifyWindowResized(window); 1610 } 1611 1612 1613 void 1614 Desktop::SetWindowOutlinesDelta(Window* window, BPoint delta) 1615 { 1616 AutoWriteLocker _(fWindowLock); 1617 1618 if (!window->IsVisible()) 1619 return; 1620 1621 BRegion newDirtyRegion; 1622 window->SetOutlinesDelta(delta, &newDirtyRegion); 1623 1624 BRegion background; 1625 _RebuildClippingForAllWindows(background); 1626 1627 MarkDirty(newDirtyRegion); 1628 _SetBackground(background); 1629 } 1630 1631 1632 bool 1633 Desktop::SetWindowTabLocation(Window* window, float location, bool isShifting) 1634 { 1635 AutoWriteLocker _(fWindowLock); 1636 1637 BRegion dirty; 1638 bool changed = window->SetTabLocation(location, isShifting, dirty); 1639 if (changed) 1640 RebuildAndRedrawAfterWindowChange(window, dirty); 1641 1642 NotifyWindowTabLocationChanged(window, location, isShifting); 1643 1644 return changed; 1645 } 1646 1647 1648 bool 1649 Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings) 1650 { 1651 AutoWriteLocker _(fWindowLock); 1652 1653 BRegion dirty; 1654 bool changed = window->SetDecoratorSettings(settings, dirty); 1655 bool listenerChanged = SetDecoratorSettings(window, settings); 1656 if (changed || listenerChanged) 1657 RebuildAndRedrawAfterWindowChange(window, dirty); 1658 1659 return changed; 1660 } 1661 1662 1663 void 1664 Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces) 1665 { 1666 LockAllWindows(); 1667 1668 if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE) 1669 workspaces = workspace_to_workspaces(CurrentWorkspace()); 1670 1671 WindowStack* stack = window->GetWindowStack(); 1672 if (stack != NULL) { 1673 for (int32 s = 0; s < stack->CountWindows(); s++) { 1674 window = stack->LayerOrder().ItemAt(s); 1675 1676 uint32 oldWorkspaces = window->Workspaces(); 1677 window->WorkspacesChanged(oldWorkspaces, workspaces); 1678 _ChangeWindowWorkspaces(window, oldWorkspaces, workspaces); 1679 } 1680 } 1681 UnlockAllWindows(); 1682 } 1683 1684 1685 /*! \brief Adds the window to the desktop. 1686 At this point, the window is still hidden and must be shown explicitly 1687 via ShowWindow(). 1688 */ 1689 void 1690 Desktop::AddWindow(Window *window) 1691 { 1692 LockAllWindows(); 1693 1694 fAllWindows.AddWindow(window); 1695 if (!window->IsNormal()) 1696 fSubsetWindows.AddWindow(window); 1697 1698 if (window->IsNormal()) { 1699 if (window->Workspaces() == B_CURRENT_WORKSPACE) 1700 window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace())); 1701 } else { 1702 // subset windows are visible on all workspaces their subset is on 1703 window->SetWorkspaces(window->SubsetWorkspaces()); 1704 } 1705 1706 _ChangeWindowWorkspaces(window, 0, window->Workspaces()); 1707 1708 NotifyWindowAdded(window); 1709 1710 UnlockAllWindows(); 1711 } 1712 1713 1714 void 1715 Desktop::RemoveWindow(Window *window) 1716 { 1717 LockAllWindows(); 1718 1719 if (!window->IsHidden()) 1720 HideWindow(window); 1721 1722 fAllWindows.RemoveWindow(window); 1723 if (!window->IsNormal()) 1724 fSubsetWindows.RemoveWindow(window); 1725 1726 _ChangeWindowWorkspaces(window, window->Workspaces(), 0); 1727 1728 NotifyWindowRemoved(window); 1729 1730 UnlockAllWindows(); 1731 1732 // make sure this window won't get any events anymore 1733 1734 EventDispatcher().RemoveTarget(window->EventTarget()); 1735 } 1736 1737 1738 bool 1739 Desktop::AddWindowToSubset(Window* subset, Window* window) 1740 { 1741 if (!subset->AddToSubset(window)) 1742 return false; 1743 1744 _ChangeWindowWorkspaces(subset, subset->Workspaces(), 1745 subset->SubsetWorkspaces()); 1746 return true; 1747 } 1748 1749 1750 void 1751 Desktop::RemoveWindowFromSubset(Window* subset, Window* window) 1752 { 1753 subset->RemoveFromSubset(window); 1754 _ChangeWindowWorkspaces(subset, subset->Workspaces(), 1755 subset->SubsetWorkspaces()); 1756 } 1757 1758 1759 void 1760 Desktop::FontsChanged(Window* window) 1761 { 1762 AutoWriteLocker _(fWindowLock); 1763 1764 BRegion dirty; 1765 window->FontsChanged(&dirty); 1766 1767 RebuildAndRedrawAfterWindowChange(window, dirty); 1768 } 1769 1770 1771 void 1772 Desktop::ColorUpdated(Window* window, color_which which, rgb_color color) 1773 { 1774 AutoWriteLocker _(fWindowLock); 1775 1776 window->TopView()->ColorUpdated(which, color); 1777 1778 switch (which) { 1779 case B_WINDOW_TAB_COLOR: 1780 case B_WINDOW_TEXT_COLOR: 1781 case B_WINDOW_INACTIVE_TAB_COLOR: 1782 case B_WINDOW_INACTIVE_TEXT_COLOR: 1783 case B_WINDOW_BORDER_COLOR: 1784 case B_WINDOW_INACTIVE_BORDER_COLOR: 1785 break; 1786 default: 1787 return; 1788 } 1789 1790 BRegion dirty; 1791 window->ColorsChanged(&dirty); 1792 RebuildAndRedrawAfterWindowChange(window, dirty); 1793 } 1794 1795 1796 void 1797 Desktop::SetWindowLook(Window* window, window_look newLook) 1798 { 1799 if (window->Look() == newLook) 1800 return; 1801 1802 AutoWriteLocker _(fWindowLock); 1803 1804 BRegion dirty; 1805 window->SetLook(newLook, &dirty); 1806 // TODO: test what happens when the window 1807 // finds out it needs to resize itself... 1808 1809 RebuildAndRedrawAfterWindowChange(window, dirty); 1810 1811 NotifyWindowLookChanged(window, newLook); 1812 } 1813 1814 1815 void 1816 Desktop::SetWindowFeel(Window* window, window_feel newFeel) 1817 { 1818 if (window->Feel() == newFeel) 1819 return; 1820 1821 LockAllWindows(); 1822 1823 bool wasNormal = window->IsNormal(); 1824 1825 window->SetFeel(newFeel); 1826 1827 // move the window out of or into the subset window list as needed 1828 if (window->IsNormal() && !wasNormal) 1829 fSubsetWindows.RemoveWindow(window); 1830 else if (!window->IsNormal() && wasNormal) 1831 fSubsetWindows.AddWindow(window); 1832 1833 // A normal window that was once a floating or modal window will 1834 // adopt the window's current workspaces 1835 1836 if (!window->IsNormal()) { 1837 _ChangeWindowWorkspaces(window, window->Workspaces(), 1838 window->SubsetWorkspaces()); 1839 } 1840 1841 // make sure the window has the correct position in the window lists 1842 // (ie. all floating windows have to be on the top, ...) 1843 1844 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1845 if (!workspace_in_workspaces(i, window->Workspaces())) 1846 continue; 1847 1848 bool changed = false; 1849 BRegion visibleBefore; 1850 if (i == fCurrentWorkspace && window->IsVisible()) 1851 visibleBefore = window->VisibleRegion(); 1852 1853 Window* backmost = window->Backmost(_Windows(i).LastWindow(), i); 1854 if (backmost != NULL) { 1855 // check if the backmost window is really behind it 1856 Window* previous = window->PreviousWindow(i); 1857 while (previous != NULL) { 1858 if (previous == backmost) 1859 break; 1860 1861 previous = previous->PreviousWindow(i); 1862 } 1863 1864 if (previous == NULL) { 1865 // need to reinsert window before its backmost window 1866 _Windows(i).RemoveWindow(window); 1867 _Windows(i).AddWindow(window, backmost->NextWindow(i)); 1868 changed = true; 1869 } 1870 } 1871 1872 Window* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i); 1873 if (frontmost != NULL) { 1874 // check if the frontmost window is really in front of it 1875 Window* next = window->NextWindow(i); 1876 while (next != NULL) { 1877 if (next == frontmost) 1878 break; 1879 1880 next = next->NextWindow(i); 1881 } 1882 1883 if (next == NULL) { 1884 // need to reinsert window behind its frontmost window 1885 _Windows(i).RemoveWindow(window); 1886 _Windows(i).AddWindow(window, frontmost); 1887 changed = true; 1888 } 1889 } 1890 1891 if (i == fCurrentWorkspace && changed) { 1892 BRegion dummy; 1893 _RebuildClippingForAllWindows(dummy); 1894 1895 // mark everything dirty that is no longer visible, or 1896 // is now visible and wasn't before 1897 BRegion visibleAfter(window->VisibleRegion()); 1898 BRegion dirty(visibleAfter); 1899 dirty.Exclude(&visibleBefore); 1900 visibleBefore.Exclude(&visibleAfter); 1901 dirty.Include(&visibleBefore); 1902 1903 MarkDirty(dirty); 1904 } 1905 } 1906 1907 _UpdateFronts(); 1908 1909 if (window == FocusWindow() && !window->IsVisible()) 1910 SetFocusWindow(); 1911 1912 NotifyWindowFeelChanged(window, newFeel); 1913 1914 UnlockAllWindows(); 1915 } 1916 1917 1918 void 1919 Desktop::SetWindowFlags(Window *window, uint32 newFlags) 1920 { 1921 if (window->Flags() == newFlags) 1922 return; 1923 1924 AutoWriteLocker _(fWindowLock); 1925 1926 BRegion dirty; 1927 window->SetFlags(newFlags, &dirty); 1928 // TODO: test what happens when the window 1929 // finds out it needs to resize itself... 1930 1931 RebuildAndRedrawAfterWindowChange(window, dirty); 1932 } 1933 1934 1935 void 1936 Desktop::SetWindowTitle(Window *window, const char* title) 1937 { 1938 AutoWriteLocker _(fWindowLock); 1939 1940 BRegion dirty; 1941 window->SetTitle(title, dirty); 1942 1943 RebuildAndRedrawAfterWindowChange(window, dirty); 1944 } 1945 1946 1947 /*! Returns the window under the mouse cursor. 1948 You need to have acquired the All Windows lock when calling this method. 1949 */ 1950 Window* 1951 Desktop::WindowAt(BPoint where) 1952 { 1953 for (Window* window = CurrentWindows().LastWindow(); window; 1954 window = window->PreviousWindow(fCurrentWorkspace)) { 1955 if (window->IsVisible() && window->VisibleRegion().Contains(where)) 1956 return window->StackedWindowAt(where); 1957 } 1958 1959 return NULL; 1960 } 1961 1962 1963 void 1964 Desktop::SetMouseEventWindow(Window* window) 1965 { 1966 fMouseEventWindow = window; 1967 } 1968 1969 1970 void 1971 Desktop::SetViewUnderMouse(const Window* window, int32 viewToken) 1972 { 1973 fWindowUnderMouse = window; 1974 fViewUnderMouse = viewToken; 1975 } 1976 1977 1978 int32 1979 Desktop::ViewUnderMouse(const Window* window) 1980 { 1981 if (window != NULL && fWindowUnderMouse == window) 1982 return fViewUnderMouse; 1983 1984 return B_NULL_TOKEN; 1985 } 1986 1987 1988 /*! Returns the current keyboard event target candidate - which is either the 1989 top-most window (in case it has the kAcceptKeyboardFocusFlag flag set), or 1990 the one having focus. 1991 The window lock must be held when calling this function. 1992 */ 1993 EventTarget* 1994 Desktop::KeyboardEventTarget() 1995 { 1996 // Get the top most non-hidden window 1997 Window* window = CurrentWindows().LastWindow(); 1998 while (window != NULL && window->IsHidden()) { 1999 window = window->PreviousWindow(fCurrentWorkspace); 2000 } 2001 2002 if (window != NULL && (window->Flags() & kAcceptKeyboardFocusFlag) != 0) 2003 return &window->EventTarget(); 2004 2005 if (FocusWindow() != NULL) 2006 return &FocusWindow()->EventTarget(); 2007 2008 return NULL; 2009 } 2010 2011 2012 /*! Tries to set the focus to the specified \a focus window. It will make sure, 2013 however, that the window actually can have focus. You are allowed to pass 2014 in a NULL pointer for \a focus. 2015 2016 Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both 2017 prevent it from getting focus. 2018 2019 In any case, this method makes sure that there is a focus window, if there 2020 is any window at all, that is. 2021 */ 2022 void 2023 Desktop::SetFocusWindow(Window* nextFocus) 2024 { 2025 if (!LockAllWindows()) 2026 return; 2027 2028 // test for B_LOCK_WINDOW_FOCUS 2029 if (fLockedFocusWindow && nextFocus != fLockedFocusWindow) { 2030 UnlockAllWindows(); 2031 return; 2032 } 2033 2034 bool hasModal = _WindowHasModal(nextFocus); 2035 bool hasWindowScreen = false; 2036 2037 if (!hasModal && nextFocus != NULL) { 2038 // Check whether or not a window screen is in front of the window 2039 // (if it has a modal, the right thing is done, anyway) 2040 Window* window = nextFocus; 2041 while (true) { 2042 window = window->NextWindow(fCurrentWorkspace); 2043 if (window == NULL || window->Feel() == kWindowScreenFeel) 2044 break; 2045 } 2046 if (window != NULL) 2047 hasWindowScreen = true; 2048 } 2049 2050 if (nextFocus == fFocus && nextFocus != NULL && !nextFocus->IsHidden() 2051 && (nextFocus->Flags() & B_AVOID_FOCUS) == 0 2052 && !hasModal && !hasWindowScreen) { 2053 // the window that is supposed to get focus already has focus 2054 UnlockAllWindows(); 2055 return; 2056 } 2057 2058 uint32 listIndex = fCurrentWorkspace; 2059 WindowList* list = &_Windows(fCurrentWorkspace); 2060 if (!fSettings->NormalMouse()) { 2061 listIndex = kFocusList; 2062 list = &fFocusList; 2063 } 2064 2065 if (nextFocus == NULL || hasModal || hasWindowScreen) { 2066 nextFocus = list->LastWindow(); 2067 2068 if (fSettings->NormalMouse()) { 2069 // If the last window having focus is a window that cannot make it 2070 // to the front, we use that as the next focus 2071 Window* lastFocus = fFocusList.LastWindow(); 2072 if (lastFocus != NULL && !lastFocus->SupportsFront() 2073 && _WindowCanHaveFocus(lastFocus)) { 2074 nextFocus = lastFocus; 2075 } 2076 } 2077 } 2078 2079 // make sure no window is chosen that doesn't want focus or cannot have it 2080 while (nextFocus != NULL && !_WindowCanHaveFocus(nextFocus)) { 2081 nextFocus = nextFocus->PreviousWindow(listIndex); 2082 } 2083 2084 if (fFocus == nextFocus) { 2085 // turns out the window that is supposed to get focus now already has it 2086 UnlockAllWindows(); 2087 return; 2088 } 2089 2090 team_id oldActiveApp = -1; 2091 team_id newActiveApp = -1; 2092 2093 if (fFocus != NULL) { 2094 fFocus->SetFocus(false); 2095 oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 2096 } 2097 2098 fFocus = nextFocus; 2099 2100 if (fFocus != NULL) { 2101 fFocus->SetFocus(true); 2102 newActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 2103 2104 // move current focus to the end of the focus list 2105 fFocusList.RemoveWindow(fFocus); 2106 fFocusList.AddWindow(fFocus); 2107 } 2108 2109 if (newActiveApp == -1) { 2110 // make sure the cursor is visible 2111 HWInterface()->SetCursorVisible(true); 2112 } 2113 2114 UnlockAllWindows(); 2115 2116 // change the "active" app if appropriate 2117 if (oldActiveApp == newActiveApp) 2118 return; 2119 2120 BAutolock locker(fApplicationsLock); 2121 2122 for (int32 i = 0; i < fApplications.CountItems(); i++) { 2123 ServerApp* app = fApplications.ItemAt(i); 2124 2125 if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp) 2126 app->Activate(false); 2127 else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp) 2128 app->Activate(true); 2129 } 2130 } 2131 2132 2133 void 2134 Desktop::SetFocusLocked(const Window* window) 2135 { 2136 AutoWriteLocker _(fWindowLock); 2137 2138 if (window != NULL) { 2139 // Don't allow this to be set when no mouse buttons 2140 // are pressed. (BView::SetMouseEventMask() should only be called 2141 // from mouse hooks.) 2142 if (fLastMouseButtons == 0) 2143 return; 2144 } 2145 2146 fLockedFocusWindow = window; 2147 } 2148 2149 2150 Window* 2151 Desktop::FindWindowByClientToken(int32 token, team_id teamID) 2152 { 2153 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2154 window = window->NextWindow(kAllWindowList)) { 2155 if (window->ServerWindow()->ClientToken() == token 2156 && window->ServerWindow()->ClientTeam() == teamID) { 2157 return window; 2158 } 2159 } 2160 2161 return NULL; 2162 } 2163 2164 2165 ::EventTarget* 2166 Desktop::FindTarget(BMessenger& messenger) 2167 { 2168 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2169 window = window->NextWindow(kAllWindowList)) { 2170 if (window->EventTarget().Messenger() == messenger) 2171 return &window->EventTarget(); 2172 } 2173 2174 return NULL; 2175 } 2176 2177 2178 void 2179 Desktop::MarkDirty(BRegion& dirtyRegion, BRegion& exposeRegion) 2180 { 2181 if (dirtyRegion.CountRects() == 0) 2182 return; 2183 2184 if (LockAllWindows()) { 2185 // send redraw messages to all windows intersecting the dirty region 2186 _TriggerWindowRedrawing(dirtyRegion, exposeRegion); 2187 2188 UnlockAllWindows(); 2189 } 2190 } 2191 2192 2193 void 2194 Desktop::Redraw() 2195 { 2196 BRegion dirty(fVirtualScreen.Frame()); 2197 MarkDirty(dirty); 2198 } 2199 2200 2201 /*! \brief Redraws the background (ie. the desktop window, if any). 2202 */ 2203 void 2204 Desktop::RedrawBackground() 2205 { 2206 LockAllWindows(); 2207 2208 BRegion redraw; 2209 2210 Window* window = CurrentWindows().FirstWindow(); 2211 if (window != NULL && window->Feel() == kDesktopWindowFeel) { 2212 redraw = window->VisibleContentRegion(); 2213 2214 // look for desktop background view, and update its background color 2215 // TODO: is there a better way to do this? 2216 View* view = window->TopView(); 2217 if (view != NULL) 2218 view = view->FirstChild(); 2219 2220 while (view != NULL) { 2221 if (view->IsDesktopBackground()) { 2222 view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color()); 2223 break; 2224 } 2225 view = view->NextSibling(); 2226 } 2227 2228 window->ProcessDirtyRegion(redraw); 2229 } else { 2230 redraw = BackgroundRegion(); 2231 fBackgroundRegion.MakeEmpty(); 2232 _SetBackground(redraw); 2233 } 2234 2235 _WindowChanged(NULL); 2236 // update workspaces view as well 2237 2238 UnlockAllWindows(); 2239 } 2240 2241 2242 bool 2243 Desktop::ReloadDecor(DecorAddOn* oldDecor) 2244 { 2245 AutoWriteLocker _(fWindowLock); 2246 2247 bool returnValue = true; 2248 2249 if (oldDecor != NULL) { 2250 const DesktopListenerList* oldListeners 2251 = &oldDecor->GetDesktopListeners(); 2252 for (int i = 0; i < oldListeners->CountItems(); i++) 2253 UnregisterListener(oldListeners->ItemAt(i)); 2254 } 2255 2256 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2257 window = window->NextWindow(kAllWindowList)) { 2258 BRegion oldBorder; 2259 window->GetBorderRegion(&oldBorder); 2260 2261 if (!window->ReloadDecor()) { 2262 // prevent unloading previous add-on 2263 returnValue = false; 2264 } 2265 2266 BRegion border; 2267 window->GetBorderRegion(&border); 2268 2269 border.Include(&oldBorder); 2270 RebuildAndRedrawAfterWindowChange(window, border); 2271 } 2272 2273 // register new listeners 2274 const DesktopListenerList& newListeners 2275 = gDecorManager.GetDesktopListeners(); 2276 for (int i = 0; i < newListeners.CountItems(); i++) 2277 RegisterListener(newListeners.ItemAt(i)); 2278 2279 return returnValue; 2280 } 2281 2282 2283 void 2284 Desktop::MinimizeApplication(team_id team) 2285 { 2286 AutoWriteLocker locker(fWindowLock); 2287 2288 // Just minimize all windows of that application 2289 2290 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2291 window = window->NextWindow(kAllWindowList)) { 2292 if (window->ServerWindow()->ClientTeam() != team) 2293 continue; 2294 2295 window->ServerWindow()->NotifyMinimize(true); 2296 } 2297 } 2298 2299 2300 void 2301 Desktop::BringApplicationToFront(team_id team) 2302 { 2303 AutoWriteLocker locker(fWindowLock); 2304 2305 // TODO: for now, just maximize all windows of that application 2306 // TODO: have the ability to lock the current workspace 2307 2308 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2309 window = window->NextWindow(kAllWindowList)) { 2310 if (window->ServerWindow()->ClientTeam() != team) 2311 continue; 2312 2313 window->ServerWindow()->NotifyMinimize(false); 2314 } 2315 } 2316 2317 2318 void 2319 Desktop::WindowAction(int32 windowToken, int32 action) 2320 { 2321 if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT) 2322 return; 2323 2324 LockAllWindows(); 2325 2326 ::ServerWindow* serverWindow; 2327 Window* window; 2328 if (BPrivate::gDefaultTokens.GetToken(windowToken, 2329 B_SERVER_TOKEN, (void**)&serverWindow) != B_OK 2330 || (window = serverWindow->Window()) == NULL) { 2331 UnlockAllWindows(); 2332 return; 2333 } 2334 2335 if (action == B_BRING_TO_FRONT && !window->IsMinimized()) { 2336 // the window is visible, we just need to make it the front window 2337 ActivateWindow(window); 2338 } else { 2339 // if not, ask the window if it wants to be unminimized 2340 serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW); 2341 } 2342 2343 UnlockAllWindows(); 2344 } 2345 2346 2347 void 2348 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 2349 { 2350 AutoWriteLocker locker(fWindowLock); 2351 2352 // compute the number of windows 2353 2354 int32 count = 0; 2355 2356 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2357 window = window->NextWindow(kAllWindowList)) { 2358 if (team < B_OK || window->ServerWindow()->ClientTeam() == team) 2359 count++; 2360 } 2361 2362 // write list 2363 2364 sender.StartMessage(B_OK); 2365 sender.Attach<int32>(count); 2366 2367 // first write the windows of the current workspace correctly ordered 2368 for (Window *window = CurrentWindows().LastWindow(); window != NULL; 2369 window = window->PreviousWindow(fCurrentWorkspace)) { 2370 if (team >= B_OK && window->ServerWindow()->ClientTeam() != team) 2371 continue; 2372 2373 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2374 } 2375 2376 // then write all the other windows 2377 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2378 window = window->NextWindow(kAllWindowList)) { 2379 if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team) 2380 || window->InWorkspace(fCurrentWorkspace)) 2381 continue; 2382 2383 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2384 } 2385 2386 sender.Flush(); 2387 } 2388 2389 2390 void 2391 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 2392 { 2393 AutoWriteLocker locker(fWindowLock); 2394 BAutolock tokenLocker(BPrivate::gDefaultTokens); 2395 2396 ::ServerWindow* window; 2397 if (BPrivate::gDefaultTokens.GetToken(serverToken, 2398 B_SERVER_TOKEN, (void**)&window) != B_OK) { 2399 sender.StartMessage(B_ENTRY_NOT_FOUND); 2400 sender.Flush(); 2401 return; 2402 } 2403 2404 window_info info; 2405 window->GetInfo(info); 2406 2407 float tabSize = 0.0; 2408 float borderSize = 0.0; 2409 ::Window* tmp = window->Window(); 2410 if (tmp) { 2411 BMessage message; 2412 if (tmp->GetDecoratorSettings(&message)) { 2413 BRect tabFrame; 2414 message.FindRect("tab frame", &tabFrame); 2415 tabSize = tabFrame.bottom - tabFrame.top; 2416 message.FindFloat("border width", &borderSize); 2417 } 2418 } 2419 2420 int32 length = window->Title() ? strlen(window->Title()) : 0; 2421 2422 sender.StartMessage(B_OK); 2423 sender.Attach<int32>(sizeof(client_window_info) + length + 1); 2424 sender.Attach(&info, sizeof(window_info)); 2425 sender.Attach<float>(tabSize); 2426 sender.Attach<float>(borderSize); 2427 2428 if (length > 0) 2429 sender.Attach(window->Title(), length + 1); 2430 else 2431 sender.Attach<char>('\0'); 2432 2433 sender.Flush(); 2434 } 2435 2436 2437 void 2438 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender) 2439 { 2440 LockSingleWindow(); 2441 2442 if (workspace < 0) 2443 workspace = fCurrentWorkspace; 2444 else if (workspace >= kMaxWorkspaces) { 2445 sender.StartMessage(B_BAD_VALUE); 2446 sender.Flush(); 2447 UnlockSingleWindow(); 2448 return; 2449 } 2450 2451 int32 count = _Windows(workspace).Count(); 2452 2453 // write list 2454 2455 sender.StartMessage(B_OK); 2456 sender.Attach<int32>(count); 2457 2458 for (Window *window = _Windows(workspace).LastWindow(); window != NULL; 2459 window = window->PreviousWindow(workspace)) { 2460 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2461 } 2462 2463 sender.Flush(); 2464 2465 UnlockSingleWindow(); 2466 } 2467 2468 2469 void 2470 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender) 2471 { 2472 fApplicationsLock.Lock(); 2473 LockSingleWindow(); 2474 2475 int32 maxCount = fApplications.CountItems(); 2476 2477 fApplicationsLock.Unlock(); 2478 // as long as we hold the window lock, no new window can appear 2479 2480 if (workspace < 0) 2481 workspace = fCurrentWorkspace; 2482 else if (workspace >= kMaxWorkspaces) { 2483 sender.StartMessage(B_BAD_VALUE); 2484 sender.Flush(); 2485 UnlockSingleWindow(); 2486 return; 2487 } 2488 2489 // compute the list of applications on this workspace 2490 2491 team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id)); 2492 if (teams == NULL) { 2493 sender.StartMessage(B_NO_MEMORY); 2494 sender.Flush(); 2495 UnlockSingleWindow(); 2496 return; 2497 } 2498 2499 int32 count = 0; 2500 2501 for (Window *window = _Windows(workspace).LastWindow(); window != NULL; 2502 window = window->PreviousWindow(workspace)) { 2503 team_id team = window->ServerWindow()->ClientTeam(); 2504 if (count > 1) { 2505 // see if we already have this team 2506 bool found = false; 2507 for (int32 i = 0; i < count; i++) { 2508 if (teams[i] == team) { 2509 found = true; 2510 break; 2511 } 2512 } 2513 if (found) 2514 continue; 2515 } 2516 2517 ASSERT(count < maxCount); 2518 teams[count++] = team; 2519 } 2520 2521 UnlockSingleWindow(); 2522 2523 // write list 2524 2525 sender.StartMessage(B_OK); 2526 sender.Attach<int32>(count); 2527 2528 for (int32 i = 0; i < count; i++) { 2529 sender.Attach<int32>(teams[i]); 2530 } 2531 2532 sender.Flush(); 2533 free(teams); 2534 } 2535 2536 2537 void 2538 Desktop::_LaunchInputServer() 2539 { 2540 BRoster roster; 2541 status_t status = roster.Launch("application/x-vnd.Be-input_server"); 2542 if (status == B_OK || status == B_ALREADY_RUNNING) 2543 return; 2544 2545 // Could not load input_server by signature, try well-known location 2546 2547 BEntry entry; 2548 BPath inputServerPath; 2549 if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, &inputServerPath) == B_OK 2550 && inputServerPath.Append("input_server") == B_OK) { 2551 entry.SetTo(inputServerPath.Path()); 2552 } else 2553 entry.SetTo("/system/servers/input_server"); 2554 entry_ref ref; 2555 status_t entryStatus = entry.GetRef(&ref); 2556 if (entryStatus == B_OK) 2557 entryStatus = roster.Launch(&ref); 2558 if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) { 2559 syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n", 2560 strerror(status)); 2561 return; 2562 } 2563 2564 syslog(LOG_ERR, "Failed to launch the input server: %s!\n", 2565 strerror(entryStatus)); 2566 } 2567 2568 2569 void 2570 Desktop::_GetLooperName(char* name, size_t length) 2571 { 2572 snprintf(name, length, "d:%d:%s", fUserID, 2573 fTargetScreen == NULL ? "baron" : fTargetScreen); 2574 } 2575 2576 2577 void 2578 Desktop::_PrepareQuit() 2579 { 2580 // let's kill all remaining applications 2581 2582 fApplicationsLock.Lock(); 2583 2584 int32 count = fApplications.CountItems(); 2585 for (int32 i = 0; i < count; i++) { 2586 ServerApp *app = fApplications.ItemAt(i); 2587 team_id clientTeam = app->ClientTeam(); 2588 2589 app->Quit(); 2590 kill_team(clientTeam); 2591 } 2592 2593 // wait for the last app to die 2594 if (count > 0) { 2595 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 2596 250000); 2597 } 2598 2599 fApplicationsLock.Unlock(); 2600 } 2601 2602 2603 void 2604 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) 2605 { 2606 switch (code) { 2607 case AS_CREATE_APP: 2608 { 2609 // Create the ServerApp to node monitor a new BApplication 2610 2611 // Attached data: 2612 // 1) port_id - receiver port of a regular app 2613 // 2) port_id - client looper port - for sending messages to the 2614 // client 2615 // 2) team_id - app's team ID 2616 // 3) int32 - handler token of the regular app 2617 // 4) char * - signature of the regular app 2618 2619 // Find the necessary data 2620 team_id clientTeamID = -1; 2621 port_id clientLooperPort = -1; 2622 port_id clientReplyPort = -1; 2623 int32 htoken = B_NULL_TOKEN; 2624 char* appSignature = NULL; 2625 2626 link.Read<port_id>(&clientReplyPort); 2627 link.Read<port_id>(&clientLooperPort); 2628 link.Read<team_id>(&clientTeamID); 2629 link.Read<int32>(&htoken); 2630 if (link.ReadString(&appSignature) != B_OK) 2631 break; 2632 2633 ObjectDeleter<ServerApp> app(new (std::nothrow) ServerApp(this, clientReplyPort, 2634 clientLooperPort, clientTeamID, htoken, appSignature)); 2635 status_t status = B_OK; 2636 if (!app.IsSet()) 2637 status = B_NO_MEMORY; 2638 if (status == B_OK) 2639 status = app->InitCheck(); 2640 if (status == B_OK) 2641 status = app->Run(); 2642 if (status == B_OK) { 2643 // add the new ServerApp to the known list of ServerApps 2644 fApplicationsLock.Lock(); 2645 fApplications.AddItem(app.Detach()); 2646 fApplicationsLock.Unlock(); 2647 } else { 2648 // if everything went well, ServerApp::Run() will notify 2649 // the client - but since it didn't, we do it here 2650 BPrivate::LinkSender reply(clientReplyPort); 2651 reply.StartMessage(status); 2652 reply.Flush(); 2653 } 2654 2655 // This is necessary because BPortLink::ReadString allocates memory 2656 free(appSignature); 2657 break; 2658 } 2659 2660 case AS_DELETE_APP: 2661 { 2662 // Delete a ServerApp. Received only from the respective ServerApp 2663 // when a BApplication asks it to quit. 2664 2665 // Attached Data: 2666 // 1) thread_id - thread ID of the ServerApp to be deleted 2667 2668 thread_id thread = -1; 2669 if (link.Read<thread_id>(&thread) < B_OK) 2670 break; 2671 2672 fApplicationsLock.Lock(); 2673 2674 // Run through the list of apps and nuke the proper one 2675 2676 int32 count = fApplications.CountItems(); 2677 ServerApp* removeApp = NULL; 2678 2679 for (int32 i = 0; i < count; i++) { 2680 ServerApp* app = fApplications.ItemAt(i); 2681 2682 if (app->Thread() == thread) { 2683 fApplications.RemoveItemAt(i); 2684 removeApp = app; 2685 break; 2686 } 2687 } 2688 2689 fApplicationsLock.Unlock(); 2690 2691 if (removeApp != NULL) 2692 removeApp->Quit(fShutdownSemaphore); 2693 2694 if (fQuitting && count <= 1) { 2695 // wait for the last app to die 2696 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, 2697 B_RELATIVE_TIMEOUT, 500000); 2698 PostMessage(kMsgQuitLooper); 2699 } 2700 break; 2701 } 2702 2703 case AS_ACTIVATE_APP: 2704 { 2705 // Someone is requesting to activation of a certain app. 2706 2707 // Attached data: 2708 // 1) port_id reply port 2709 // 2) team_id team 2710 2711 status_t status; 2712 2713 // get the parameters 2714 port_id replyPort; 2715 team_id team; 2716 if (link.Read(&replyPort) == B_OK 2717 && link.Read(&team) == B_OK) 2718 status = _ActivateApp(team); 2719 else 2720 status = B_ERROR; 2721 2722 // send the reply 2723 BPrivate::PortLink replyLink(replyPort); 2724 replyLink.StartMessage(status); 2725 replyLink.Flush(); 2726 break; 2727 } 2728 2729 case AS_APP_CRASHED: 2730 case AS_DUMP_ALLOCATOR: 2731 case AS_DUMP_BITMAPS: 2732 { 2733 BAutolock locker(fApplicationsLock); 2734 2735 team_id team; 2736 if (link.Read(&team) != B_OK) 2737 break; 2738 2739 for (int32 i = 0; i < fApplications.CountItems(); i++) { 2740 ServerApp* app = fApplications.ItemAt(i); 2741 2742 if (app->ClientTeam() == team) 2743 app->PostMessage(code); 2744 } 2745 break; 2746 } 2747 2748 case AS_EVENT_STREAM_CLOSED: 2749 _LaunchInputServer(); 2750 break; 2751 2752 case B_QUIT_REQUESTED: 2753 // We've been asked to quit, so (for now) broadcast to all 2754 // test apps to quit. This situation will occur only when the 2755 // server is compiled as a regular Be application. 2756 2757 fApplicationsLock.Lock(); 2758 fShutdownSemaphore = create_sem(0, "desktop shutdown"); 2759 fShutdownCount = fApplications.CountItems(); 2760 fApplicationsLock.Unlock(); 2761 2762 fQuitting = true; 2763 BroadcastToAllApps(AS_QUIT_APP); 2764 2765 // We now need to process the remaining AS_DELETE_APP messages. 2766 // We quit the looper when the last app is deleted. 2767 2768 // if there are no apps to quit, shutdown directly 2769 if (fShutdownCount == 0) 2770 PostMessage(kMsgQuitLooper); 2771 2772 break; 2773 2774 case AS_ACTIVATE_WORKSPACE: 2775 { 2776 int32 index; 2777 link.Read<int32>(&index); 2778 if (index == -1) 2779 index = fPreviousWorkspace; 2780 2781 bool moveFocusWindow; 2782 link.Read<bool>(&moveFocusWindow); 2783 2784 SetWorkspace(index, moveFocusWindow); 2785 break; 2786 } 2787 2788 case AS_TALK_TO_DESKTOP_LISTENER: 2789 { 2790 port_id clientReplyPort; 2791 if (link.Read<port_id>(&clientReplyPort) != B_OK) 2792 break; 2793 2794 BPrivate::LinkSender reply(clientReplyPort); 2795 AutoWriteLocker locker(fWindowLock); 2796 if (MessageForListener(NULL, link, reply) != true) { 2797 // unhandled message, at least send an error if needed 2798 if (link.NeedsReply()) { 2799 reply.StartMessage(B_ERROR); 2800 reply.Flush(); 2801 } 2802 } 2803 break; 2804 } 2805 2806 case AS_SET_UI_COLOR: 2807 { 2808 color_which which; 2809 rgb_color color; 2810 2811 if (link.Read<color_which>(&which) == B_OK 2812 && link.Read<rgb_color>(&color) == B_OK) { 2813 2814 const char* colorName = ui_color_name(which); 2815 fPendingColors.SetColor(colorName, color); 2816 2817 DelayedMessage delayed(AS_SET_UI_COLORS, DM_60HZ_DELAY); 2818 delayed.AddTarget(MessagePort()); 2819 delayed.SetMerge(DM_MERGE_CANCEL); 2820 2821 delayed.Attach<bool>(true); 2822 delayed.Flush(); 2823 } 2824 2825 break; 2826 } 2827 2828 case AS_SET_UI_COLORS: 2829 { 2830 bool flushPendingOnly = false; 2831 2832 if (link.Read<bool>(&flushPendingOnly) != B_OK 2833 || (flushPendingOnly && 2834 fPendingColors.CountNames(B_RGB_32_BIT_TYPE) == 0)) { 2835 break; 2836 } 2837 2838 if (!flushPendingOnly) { 2839 // Client wants to set a color map 2840 color_which which = B_NO_COLOR; 2841 rgb_color color; 2842 2843 do { 2844 if (link.Read<color_which>(&which) != B_OK 2845 || link.Read<rgb_color>(&color) != B_OK) 2846 break; 2847 2848 fPendingColors.SetColor(ui_color_name(which), color); 2849 } while (which != B_NO_COLOR); 2850 } 2851 2852 _FlushPendingColors(); 2853 break; 2854 } 2855 2856 // ToDo: Remove this again. It is a message sent by the 2857 // invalidate_on_exit kernel debugger add-on to trigger a redraw 2858 // after exiting a kernel debugger session. 2859 case 'KDLE': 2860 { 2861 BRegion dirty; 2862 dirty.Include(fVirtualScreen.Frame()); 2863 MarkDirty(dirty); 2864 break; 2865 } 2866 2867 default: 2868 printf("Desktop %d:%s received unexpected code %" B_PRId32 "\n", 0, 2869 "baron", code); 2870 2871 if (link.NeedsReply()) { 2872 // the client is now blocking and waiting for a reply! 2873 fLink.StartMessage(B_ERROR); 2874 fLink.Flush(); 2875 } 2876 break; 2877 } 2878 } 2879 2880 2881 WindowList& 2882 Desktop::CurrentWindows() 2883 { 2884 return fWorkspaces[fCurrentWorkspace].Windows(); 2885 } 2886 2887 2888 WindowList& 2889 Desktop::AllWindows() 2890 { 2891 return fAllWindows; 2892 } 2893 2894 2895 Window* 2896 Desktop::WindowForClientLooperPort(port_id port) 2897 { 2898 ASSERT_MULTI_LOCKED(fWindowLock); 2899 2900 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2901 window = window->NextWindow(kAllWindowList)) { 2902 if (window->ServerWindow()->ClientLooperPort() == port) 2903 return window; 2904 } 2905 return NULL; 2906 } 2907 2908 2909 WindowList& 2910 Desktop::_Windows(int32 index) 2911 { 2912 ASSERT(index >= 0 && index < kMaxWorkspaces); 2913 return fWorkspaces[index].Windows(); 2914 } 2915 2916 2917 void 2918 Desktop::_FlushPendingColors() 2919 { 2920 // Update all windows while we are holding the write lock. 2921 2922 int32 count = fPendingColors.CountNames(B_RGB_32_BIT_TYPE); 2923 if (count == 0) 2924 return; 2925 2926 bool changed[count]; 2927 LockedDesktopSettings settings(this); 2928 settings.SetUIColors(fPendingColors, &changed[0]); 2929 2930 int32 index = 0; 2931 char* name = NULL; 2932 type_code type = B_RGB_32_BIT_TYPE; 2933 rgb_color color; 2934 color_which which = B_NO_COLOR; 2935 BMessage clientMessage(B_COLORS_UPDATED); 2936 2937 while (fPendingColors.GetInfo(type, index, &name, &type) == B_OK) { 2938 which = which_ui_color(name); 2939 if (which == B_NO_COLOR || fPendingColors.FindColor(name, 2940 &color) != B_OK || !changed[index]) { 2941 ++index; 2942 continue; 2943 } 2944 2945 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2946 window = window->NextWindow(kAllWindowList)) { 2947 ColorUpdated(window, which, color); 2948 } 2949 2950 // Ensure client only gets list of changed colors 2951 clientMessage.AddColor(name, color); 2952 ++index; 2953 } 2954 2955 // Notify client applications 2956 BAutolock appListLock(fApplicationsLock); 2957 for (int32 index = 0; index < fApplications.CountItems(); ++index) { 2958 fApplications.ItemAt(index)->SendMessageToClient(&clientMessage); 2959 } 2960 2961 fPendingColors.MakeEmpty(); 2962 } 2963 2964 2965 void 2966 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace, 2967 Window* mouseEventWindow) 2968 { 2969 if (previousWorkspace == -1) 2970 previousWorkspace = fCurrentWorkspace; 2971 if (nextWorkspace == -1) 2972 nextWorkspace = previousWorkspace; 2973 2974 for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL; 2975 floating = floating->NextWindow(kSubsetList)) { 2976 // we only care about app/subset floating windows 2977 if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL 2978 && floating->Feel() != B_FLOATING_APP_WINDOW_FEEL) 2979 continue; 2980 2981 if (fFront != NULL && fFront->IsNormal() 2982 && floating->HasInSubset(fFront)) { 2983 // is now visible 2984 if (_Windows(previousWorkspace).HasWindow(floating) 2985 && previousWorkspace != nextWorkspace 2986 && !floating->InSubsetWorkspace(previousWorkspace)) { 2987 // but no longer on the previous workspace 2988 _Windows(previousWorkspace).RemoveWindow(floating); 2989 floating->SetCurrentWorkspace(-1); 2990 } 2991 2992 if (!_Windows(nextWorkspace).HasWindow(floating)) { 2993 // but wasn't before 2994 _Windows(nextWorkspace).AddWindow(floating, 2995 floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), 2996 nextWorkspace)); 2997 floating->SetCurrentWorkspace(nextWorkspace); 2998 if (mouseEventWindow != fFront) 2999 _ShowWindow(floating); 3000 3001 // TODO: put the floating last in the floating window list to 3002 // preserve the on screen window order 3003 } 3004 } else if (_Windows(previousWorkspace).HasWindow(floating) 3005 && !floating->InSubsetWorkspace(previousWorkspace)) { 3006 // was visible, but is no longer 3007 3008 _Windows(previousWorkspace).RemoveWindow(floating); 3009 floating->SetCurrentWorkspace(-1); 3010 _HideWindow(floating); 3011 3012 if (FocusWindow() == floating) 3013 SetFocusWindow(); 3014 } 3015 } 3016 } 3017 3018 3019 /*! Search the visible windows for a valid back window 3020 (only desktop windows can't be back windows) 3021 */ 3022 void 3023 Desktop::_UpdateBack() 3024 { 3025 fBack = NULL; 3026 3027 for (Window* window = CurrentWindows().FirstWindow(); window != NULL; 3028 window = window->NextWindow(fCurrentWorkspace)) { 3029 if (window->IsHidden() || window->Feel() == kDesktopWindowFeel) 3030 continue; 3031 3032 fBack = window; 3033 break; 3034 } 3035 } 3036 3037 3038 /*! Search the visible windows for a valid front window 3039 (only normal and modal windows can be front windows) 3040 3041 The only place where you don't want to update floating windows is 3042 during a workspace change - because then you'll call _UpdateFloating() 3043 yourself. 3044 */ 3045 void 3046 Desktop::_UpdateFront(bool updateFloating) 3047 { 3048 fFront = NULL; 3049 3050 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3051 window = window->PreviousWindow(fCurrentWorkspace)) { 3052 if (window->IsHidden() || window->IsFloating() 3053 || !window->SupportsFront()) 3054 continue; 3055 3056 fFront = window; 3057 break; 3058 } 3059 3060 if (updateFloating) 3061 _UpdateFloating(); 3062 } 3063 3064 3065 void 3066 Desktop::_UpdateFronts(bool updateFloating) 3067 { 3068 _UpdateBack(); 3069 _UpdateFront(updateFloating); 3070 } 3071 3072 3073 bool 3074 Desktop::_WindowHasModal(Window* window) const 3075 { 3076 if (window == NULL) 3077 return false; 3078 3079 for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL; 3080 modal = modal->NextWindow(kSubsetList)) { 3081 // only visible modal windows count 3082 if (!modal->IsModal() || modal->IsHidden()) 3083 continue; 3084 3085 if (modal->HasInSubset(window)) 3086 return true; 3087 } 3088 3089 return false; 3090 } 3091 3092 3093 /*! Determines whether or not the specified \a window can have focus at all. 3094 */ 3095 bool 3096 Desktop::_WindowCanHaveFocus(Window* window) const 3097 { 3098 return window != NULL 3099 && window->InWorkspace(fCurrentWorkspace) 3100 && (window->Flags() & B_AVOID_FOCUS) == 0 3101 && !_WindowHasModal(window) 3102 && !window->IsHidden(); 3103 } 3104 3105 3106 /*! You must at least hold a single window lock when calling this method. 3107 */ 3108 void 3109 Desktop::_WindowChanged(Window* window) 3110 { 3111 ASSERT_MULTI_LOCKED(fWindowLock); 3112 3113 BAutolock _(fWorkspacesLock); 3114 3115 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 3116 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 3117 view->WindowChanged(window); 3118 } 3119 } 3120 3121 3122 /*! You must at least hold a single window lock when calling this method. 3123 */ 3124 void 3125 Desktop::_WindowRemoved(Window* window) 3126 { 3127 ASSERT_MULTI_LOCKED(fWindowLock); 3128 3129 BAutolock _(fWorkspacesLock); 3130 3131 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 3132 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 3133 view->WindowRemoved(window); 3134 } 3135 } 3136 3137 3138 /*! Shows the window on the screen - it does this independently of the 3139 Window::IsHidden() state. 3140 */ 3141 void 3142 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows) 3143 { 3144 BRegion background; 3145 _RebuildClippingForAllWindows(background); 3146 _SetBackground(background); 3147 _WindowChanged(window); 3148 3149 BRegion dirty(window->VisibleRegion()); 3150 3151 if (!affectsOtherWindows) { 3152 // everything that is now visible in the 3153 // window needs a redraw, but other windows 3154 // are not affected, we can call ProcessDirtyRegion() 3155 // of the window, and don't have to use MarkDirty() 3156 window->ProcessDirtyRegion(dirty); 3157 } else 3158 MarkDirty(dirty); 3159 3160 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 3161 window->ServerWindow()->HandleDirectConnection( 3162 B_DIRECT_START | B_BUFFER_RESET); 3163 } 3164 } 3165 3166 3167 /*! Hides the window from the screen - it does this independently of the 3168 Window::IsHidden() state. 3169 */ 3170 void 3171 Desktop::_HideWindow(Window* window) 3172 { 3173 if (window->ServerWindow()->IsDirectlyAccessing()) 3174 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3175 3176 // after rebuilding the clipping, 3177 // this window will not have a visible 3178 // region anymore, so we need to remember 3179 // it now 3180 // (actually that's not true, since 3181 // hidden windows are excluded from the 3182 // clipping calculation, but anyways) 3183 BRegion dirty(window->VisibleRegion()); 3184 3185 BRegion background; 3186 _RebuildClippingForAllWindows(background); 3187 _SetBackground(background); 3188 _WindowChanged(window); 3189 3190 MarkDirty(dirty); 3191 } 3192 3193 3194 /*! Updates the workspaces of all subset windows with regard to the 3195 specifed window. 3196 If newIndex is not -1, it will move all subset windows that belong to 3197 the specifed window to the new workspace; this form is only called by 3198 SetWorkspace(). 3199 */ 3200 void 3201 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex, 3202 int32 newIndex) 3203 { 3204 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, 3205 window->Title())); 3206 3207 // if the window is hidden, the subset windows are up-to-date already 3208 if (!window->IsNormal() || window->IsHidden()) 3209 return; 3210 3211 for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL; 3212 subset = subset->NextWindow(kSubsetList)) { 3213 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL 3214 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) { 3215 // These windows are always visible on all workspaces, 3216 // no need to update them. 3217 continue; 3218 } 3219 3220 if (subset->IsFloating()) { 3221 // Floating windows are inserted and removed to the current 3222 // workspace as the need arises - they are not handled here 3223 // but in _UpdateFront() 3224 continue; 3225 } 3226 3227 if (subset->HasInSubset(window)) { 3228 // adopt the workspace change 3229 SetWindowWorkspaces(subset, subset->SubsetWorkspaces()); 3230 } 3231 } 3232 } 3233 3234 3235 /*! \brief Adds or removes the window to or from the workspaces it's on. 3236 */ 3237 void 3238 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces, 3239 uint32 newWorkspaces) 3240 { 3241 if (oldWorkspaces == newWorkspaces) 3242 return; 3243 3244 // apply changes to the workspaces' window lists 3245 3246 LockAllWindows(); 3247 3248 // NOTE: we bypass the anchor-mechanism by intention when switching 3249 // the workspace programmatically. 3250 3251 for (int32 i = 0; i < kMaxWorkspaces; i++) { 3252 if (workspace_in_workspaces(i, oldWorkspaces)) { 3253 // window is on this workspace, is it anymore? 3254 if (!workspace_in_workspaces(i, newWorkspaces)) { 3255 _Windows(i).RemoveWindow(window); 3256 if (fLastWorkspaceFocus[i] == window) 3257 fLastWorkspaceFocus[i] = NULL; 3258 3259 if (i == CurrentWorkspace()) { 3260 // remove its appearance from the current workspace 3261 window->SetCurrentWorkspace(-1); 3262 3263 if (!window->IsHidden()) 3264 _HideWindow(window); 3265 } 3266 } 3267 } else { 3268 // window was not on this workspace, is it now? 3269 if (workspace_in_workspaces(i, newWorkspaces)) { 3270 _Windows(i).AddWindow(window, 3271 window->Frontmost(_Windows(i).FirstWindow(), i)); 3272 3273 if (i == CurrentWorkspace()) { 3274 // make the window visible in current workspace 3275 window->SetCurrentWorkspace(fCurrentWorkspace); 3276 3277 if (!window->IsHidden()) { 3278 // This only affects other windows if this window has 3279 // floating or modal windows that need to be shown as 3280 // well 3281 // TODO: take care of this 3282 _ShowWindow(window, FrontWindow() == window); 3283 } 3284 } 3285 } 3286 } 3287 } 3288 3289 // If the window is visible only on one workspace, we set it's current 3290 // position in that workspace (so that WorkspacesView will find us). 3291 int32 firstWorkspace = -1; 3292 for (int32 i = 0; i < kMaxWorkspaces; i++) { 3293 if ((newWorkspaces & (1L << i)) != 0) { 3294 if (firstWorkspace != -1) { 3295 firstWorkspace = -1; 3296 break; 3297 } 3298 firstWorkspace = i; 3299 } 3300 } 3301 if (firstWorkspace >= 0) 3302 window->Anchor(firstWorkspace).position = window->Frame().LeftTop(); 3303 3304 // take care about modals and floating windows 3305 _UpdateSubsetWorkspaces(window); 3306 3307 NotifyWindowWorkspacesChanged(window, newWorkspaces); 3308 3309 UnlockAllWindows(); 3310 } 3311 3312 3313 void 3314 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, bool wereVisible) 3315 { 3316 // we don't need to redraw what is currently 3317 // visible of the window 3318 BRegion clean; 3319 3320 for (Window* window = windows.FirstWindow(); window != NULL; 3321 window = window->NextWindow(list)) { 3322 if (wereVisible) 3323 clean.Include(&window->VisibleRegion()); 3324 3325 CurrentWindows().AddWindow(window, 3326 window->Frontmost(CurrentWindows().FirstWindow(), 3327 fCurrentWorkspace)); 3328 3329 _WindowChanged(window); 3330 } 3331 3332 BRegion dummy; 3333 _RebuildClippingForAllWindows(dummy); 3334 3335 // redraw what became visible of the window(s) 3336 3337 BRegion dirty; 3338 for (Window* window = windows.FirstWindow(); window != NULL; 3339 window = window->NextWindow(list)) { 3340 dirty.Include(&window->VisibleRegion()); 3341 } 3342 3343 dirty.Exclude(&clean); 3344 MarkDirty(dirty); 3345 3346 _UpdateFront(); 3347 3348 if (windows.FirstWindow() == fBack || fBack == NULL) 3349 _UpdateBack(); 3350 } 3351 3352 3353 /*! Returns the last focussed non-hidden subset window belonging to the 3354 specified \a window. 3355 */ 3356 Window* 3357 Desktop::_LastFocusSubsetWindow(Window* window) 3358 { 3359 if (window == NULL) 3360 return NULL; 3361 3362 for (Window* front = fFocusList.LastWindow(); front != NULL; 3363 front = front->PreviousWindow(kFocusList)) { 3364 if (front != window && !front->IsHidden() 3365 && window->HasInSubset(front)) 3366 return front; 3367 } 3368 3369 return NULL; 3370 } 3371 3372 3373 /*! \brief Checks whether or not a fake mouse moved message needs to be sent 3374 to the previous mouse window. 3375 3376 You need to have the all window lock held when calling this method. 3377 */ 3378 bool 3379 Desktop::_CheckSendFakeMouseMoved(const Window* lastWindowUnderMouse) 3380 { 3381 Window* window = WindowAt(fLastMousePosition); 3382 return window != lastWindowUnderMouse; 3383 } 3384 3385 3386 /*! \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse, 3387 and also updates the current view under the mouse. 3388 3389 This has only to be done in case the view changed without mouse movement, 3390 ie. because of a workspace change, a closing window, or programmatic window 3391 movement. 3392 3393 You must not have locked any windows when calling this method. 3394 */ 3395 void 3396 Desktop::_SendFakeMouseMoved(Window* window) 3397 { 3398 int32 viewToken = B_NULL_TOKEN; 3399 EventTarget* target = NULL; 3400 3401 LockAllWindows(); 3402 3403 if (window == NULL) 3404 window = WindowAt(fLastMousePosition); 3405 3406 if (window != NULL) { 3407 BMessage message; 3408 window->MouseMoved(&message, fLastMousePosition, &viewToken, true, 3409 true); 3410 3411 if (viewToken != B_NULL_TOKEN) 3412 target = &window->EventTarget(); 3413 } 3414 3415 if (viewToken != B_NULL_TOKEN) 3416 SetViewUnderMouse(window, viewToken); 3417 else { 3418 SetViewUnderMouse(NULL, B_NULL_TOKEN); 3419 SetCursor(NULL); 3420 } 3421 3422 UnlockAllWindows(); 3423 3424 if (target != NULL) 3425 EventDispatcher().SendFakeMouseMoved(*target, viewToken); 3426 } 3427 3428 3429 Screen* 3430 Desktop::_DetermineScreenFor(BRect frame) 3431 { 3432 AutoReadLocker _(fScreenLock); 3433 3434 // TODO: choose the screen depending on where most of the area is 3435 return fVirtualScreen.ScreenAt(0); 3436 } 3437 3438 3439 void 3440 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen) 3441 { 3442 // the available region on screen starts with the entire screen area 3443 // each window on the screen will take a portion from that area 3444 3445 // figure out what the entire screen area is 3446 stillAvailableOnScreen = fScreenRegion; 3447 3448 // set clipping of each window 3449 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3450 window = window->PreviousWindow(fCurrentWorkspace)) { 3451 if (!window->IsHidden()) { 3452 window->SetClipping(&stillAvailableOnScreen); 3453 window->SetScreen(_DetermineScreenFor(window->Frame())); 3454 3455 if (window->ServerWindow()->IsDirectlyAccessing()) { 3456 window->ServerWindow()->HandleDirectConnection( 3457 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 3458 } 3459 3460 // that windows region is not available on screen anymore 3461 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 3462 } 3463 } 3464 } 3465 3466 3467 void 3468 Desktop::_TriggerWindowRedrawing(BRegion& dirtyRegion, BRegion& exposeRegion) 3469 { 3470 // send redraw messages to all windows intersecting the dirty region 3471 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3472 window = window->PreviousWindow(fCurrentWorkspace)) { 3473 if (!window->IsHidden() 3474 && dirtyRegion.Intersects(window->VisibleRegion().Frame())) 3475 window->ProcessDirtyRegion(dirtyRegion, exposeRegion); 3476 } 3477 } 3478 3479 3480 void 3481 Desktop::_SetBackground(BRegion& background) 3482 { 3483 // NOTE: the drawing operation is caried out 3484 // in the clipping region rebuild, but it is 3485 // ok actually, because it also avoids trails on 3486 // moving windows 3487 3488 // remember the region not covered by any windows 3489 // and redraw the dirty background 3490 BRegion dirtyBackground(background); 3491 dirtyBackground.Exclude(&fBackgroundRegion); 3492 dirtyBackground.IntersectWith(&background); 3493 fBackgroundRegion = background; 3494 if (dirtyBackground.Frame().IsValid()) { 3495 if (GetDrawingEngine()->LockParallelAccess()) { 3496 GetDrawingEngine()->FillRegion(dirtyBackground, 3497 fWorkspaces[fCurrentWorkspace].Color()); 3498 3499 GetDrawingEngine()->UnlockParallelAccess(); 3500 } 3501 } 3502 } 3503 3504 3505 //! The all window lock must be held when calling this function. 3506 void 3507 Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow, 3508 BRegion& dirty) 3509 { 3510 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3511 if (!changedWindow->IsVisible() || dirty.CountRects() == 0) 3512 return; 3513 3514 // The following loop is pretty much a copy of 3515 // _RebuildClippingForAllWindows(), but will also 3516 // take care about restricting our dirty region. 3517 3518 // figure out what the entire screen area is 3519 BRegion stillAvailableOnScreen(fScreenRegion); 3520 3521 // set clipping of each window 3522 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3523 window = window->PreviousWindow(fCurrentWorkspace)) { 3524 if (!window->IsHidden()) { 3525 if (window == changedWindow) 3526 dirty.IntersectWith(&stillAvailableOnScreen); 3527 3528 window->SetClipping(&stillAvailableOnScreen); 3529 window->SetScreen(_DetermineScreenFor(window->Frame())); 3530 3531 if (window->ServerWindow()->IsDirectlyAccessing()) { 3532 window->ServerWindow()->HandleDirectConnection( 3533 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 3534 } 3535 3536 // that windows region is not available on screen anymore 3537 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 3538 } 3539 } 3540 3541 _SetBackground(stillAvailableOnScreen); 3542 _WindowChanged(changedWindow); 3543 3544 _TriggerWindowRedrawing(dirty, dirty); 3545 } 3546 3547 3548 //! Suspend all windows with direct access to the frame buffer 3549 void 3550 Desktop::_SuspendDirectFrameBufferAccess() 3551 { 3552 ASSERT_MULTI_LOCKED(fWindowLock); 3553 3554 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3555 window = window->NextWindow(kAllWindowList)) { 3556 if (window->ServerWindow()->IsDirectlyAccessing()) 3557 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3558 } 3559 } 3560 3561 3562 //! Resume all windows with direct access to the frame buffer 3563 void 3564 Desktop::_ResumeDirectFrameBufferAccess() 3565 { 3566 ASSERT_MULTI_LOCKED(fWindowLock); 3567 3568 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3569 window = window->NextWindow(kAllWindowList)) { 3570 if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace)) 3571 continue; 3572 3573 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 3574 window->ServerWindow()->HandleDirectConnection( 3575 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 3576 } 3577 } 3578 } 3579 3580 3581 void 3582 Desktop::ScreenChanged(Screen* screen) 3583 { 3584 AutoWriteLocker windowLocker(fWindowLock); 3585 3586 AutoWriteLocker screenLocker(fScreenLock); 3587 screen->SetPreferredMode(); 3588 screenLocker.Unlock(); 3589 3590 _ScreenChanged(screen); 3591 } 3592 3593 3594 void 3595 Desktop::_ScreenChanged(Screen* screen) 3596 { 3597 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3598 3599 // the entire screen is dirty, because we're actually 3600 // operating on an all new buffer in memory 3601 BRegion dirty(screen->Frame()); 3602 3603 // update our cached screen region 3604 fScreenRegion.Set(screen->Frame()); 3605 gInputManager->UpdateScreenBounds(screen->Frame()); 3606 3607 BRegion background; 3608 _RebuildClippingForAllWindows(background); 3609 3610 fBackgroundRegion.MakeEmpty(); 3611 // makes sure that the complete background is redrawn 3612 _SetBackground(background); 3613 3614 // figure out dirty region 3615 dirty.Exclude(&background); 3616 _TriggerWindowRedrawing(dirty, dirty); 3617 3618 // send B_SCREEN_CHANGED to windows on that screen 3619 BMessage update(B_SCREEN_CHANGED); 3620 update.AddInt64("when", real_time_clock_usecs()); 3621 update.AddRect("frame", screen->Frame()); 3622 update.AddInt32("mode", screen->ColorSpace()); 3623 3624 fVirtualScreen.UpdateFrame(); 3625 3626 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3627 window = window->NextWindow(kAllWindowList)) { 3628 if (window->Screen() == screen) 3629 window->ServerWindow()->ScreenChanged(&update); 3630 } 3631 } 3632 3633 3634 /*! \brief activate one of the app's windows. 3635 */ 3636 status_t 3637 Desktop::_ActivateApp(team_id team) 3638 { 3639 // search for an unhidden window in the current workspace 3640 3641 AutoWriteLocker locker(fWindowLock); 3642 3643 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3644 window = window->PreviousWindow(fCurrentWorkspace)) { 3645 if (!window->IsHidden() && window->IsNormal() 3646 && window->ServerWindow()->ClientTeam() == team) { 3647 ActivateWindow(window); 3648 return B_OK; 3649 } 3650 } 3651 3652 // search for an unhidden window to give focus to 3653 3654 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3655 window = window->NextWindow(kAllWindowList)) { 3656 // if window is a normal window of the team, and not hidden, 3657 // we've found our target 3658 if (!window->IsHidden() && window->IsNormal() 3659 && window->ServerWindow()->ClientTeam() == team) { 3660 ActivateWindow(window); 3661 return B_OK; 3662 } 3663 } 3664 3665 // TODO: we cannot maximize minimized windows here (with the window lock 3666 // write locked). To work-around this, we could forward the request to 3667 // the ServerApp of this team - it maintains its own window list, and can 3668 // therefore call ActivateWindow() without holding the window lock. 3669 return B_BAD_VALUE; 3670 } 3671 3672 3673 void 3674 Desktop::_SetCurrentWorkspaceConfiguration() 3675 { 3676 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3677 3678 status_t status = fDirectScreenLock.LockWithTimeout(1000000L); 3679 if (status != B_OK) { 3680 // The application having the direct screen lock didn't give it up in 3681 // time, make it crash 3682 syslog(LOG_ERR, "Team %" B_PRId32 " did not give up its direct screen " 3683 "lock.\n", fDirectScreenTeam); 3684 3685 debug_thread(fDirectScreenTeam); 3686 fDirectScreenTeam = -1; 3687 } else 3688 fDirectScreenLock.Unlock(); 3689 3690 AutoWriteLocker _(fScreenLock); 3691 3692 uint32 changedScreens; 3693 fVirtualScreen.SetConfiguration(*this, 3694 fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(), 3695 &changedScreens); 3696 3697 for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) { 3698 if ((changedScreens & (1 << i)) != 0) 3699 _ScreenChanged(fVirtualScreen.ScreenAt(i)); 3700 } 3701 } 3702 3703 3704 /*! Changes the current workspace to the one specified by \a index. 3705 You must hold the all window lock when calling this method. 3706 */ 3707 void 3708 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow) 3709 { 3710 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3711 3712 int32 previousIndex = fCurrentWorkspace; 3713 rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color(); 3714 bool movedMouseEventWindow = false; 3715 Window* movedWindow = NULL; 3716 if (moveFocusWindow) { 3717 if (fMouseEventWindow != NULL) 3718 movedWindow = fMouseEventWindow; 3719 else 3720 movedWindow = FocusWindow(); 3721 } 3722 3723 if (movedWindow != NULL) { 3724 if (movedWindow->IsNormal()) { 3725 if (!movedWindow->InWorkspace(index)) { 3726 // The window currently being dragged will follow us to this 3727 // workspace if it's not already on it. 3728 // But only normal windows are following 3729 uint32 oldWorkspaces = movedWindow->Workspaces(); 3730 3731 WindowStack* stack = movedWindow->GetWindowStack(); 3732 if (stack != NULL) { 3733 for (int32 s = 0; s < stack->CountWindows(); s++) { 3734 Window* stackWindow = stack->LayerOrder().ItemAt(s); 3735 3736 _Windows(previousIndex).RemoveWindow(stackWindow); 3737 _Windows(index).AddWindow(stackWindow, 3738 stackWindow->Frontmost( 3739 _Windows(index).FirstWindow(), index)); 3740 3741 // send B_WORKSPACES_CHANGED message 3742 stackWindow->WorkspacesChanged(oldWorkspaces, 3743 stackWindow->Workspaces()); 3744 } 3745 } 3746 // TODO: subset windows will always flicker this way 3747 3748 movedMouseEventWindow = true; 3749 3750 NotifyWindowWorkspacesChanged(movedWindow, 3751 movedWindow->Workspaces()); 3752 } else { 3753 // make sure it's frontmost 3754 _Windows(index).RemoveWindow(movedWindow); 3755 _Windows(index).AddWindow(movedWindow, 3756 movedWindow->Frontmost(_Windows(index).FirstWindow(), 3757 index)); 3758 } 3759 } 3760 3761 movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop(); 3762 } 3763 3764 if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex)) 3765 fLastWorkspaceFocus[previousIndex] = FocusWindow(); 3766 else 3767 fLastWorkspaceFocus[previousIndex] = NULL; 3768 3769 // build region of windows that are no longer visible in the new workspace 3770 3771 BRegion dirty; 3772 3773 for (Window* window = CurrentWindows().FirstWindow(); 3774 window != NULL; window = window->NextWindow(previousIndex)) { 3775 // store current position in Workspace anchor 3776 window->Anchor(previousIndex).position = window->Frame().LeftTop(); 3777 3778 if (!window->IsHidden() 3779 && window->ServerWindow()->IsDirectlyAccessing()) 3780 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3781 3782 window->WorkspaceActivated(previousIndex, false); 3783 3784 if (window->InWorkspace(index)) 3785 continue; 3786 3787 if (!window->IsHidden()) { 3788 // this window will no longer be visible 3789 dirty.Include(&window->VisibleRegion()); 3790 } 3791 3792 window->SetCurrentWorkspace(-1); 3793 } 3794 3795 fPreviousWorkspace = fCurrentWorkspace; 3796 fCurrentWorkspace = index; 3797 3798 // Change the display modes, if needed 3799 _SetCurrentWorkspaceConfiguration(); 3800 3801 // Show windows, and include them in the changed region - but only 3802 // those that were not visible before (or whose position changed) 3803 3804 WindowList windows(kWorkingList); 3805 BList previousRegions; 3806 3807 for (Window* window = _Windows(index).FirstWindow(); 3808 window != NULL; window = window->NextWindow(index)) { 3809 BPoint position = window->Anchor(index).position; 3810 3811 window->SetCurrentWorkspace(index); 3812 3813 if (window->IsHidden()) 3814 continue; 3815 3816 if (position == kInvalidWindowPosition) { 3817 // if you enter a workspace for the first time, the position 3818 // of the window in the previous workspace is adopted 3819 position = window->Frame().LeftTop(); 3820 // TODO: make sure the window is still on-screen if it 3821 // was before! 3822 } 3823 3824 if (!window->InWorkspace(previousIndex)) { 3825 // This window was not visible before, make sure its frame 3826 // is up-to-date 3827 if (window->Frame().LeftTop() != position) { 3828 BPoint offset = position - window->Frame().LeftTop(); 3829 window->MoveBy((int32)offset.x, (int32)offset.y); 3830 } 3831 continue; 3832 } 3833 3834 if (window->Frame().LeftTop() != position) { 3835 // the window was visible before, but its on-screen location changed 3836 BPoint offset = position - window->Frame().LeftTop(); 3837 MoveWindowBy(window, offset.x, offset.y); 3838 // TODO: be a bit smarter than this... 3839 } else { 3840 // We need to remember the previous visible region of the 3841 // window if they changed their order 3842 ObjectDeleter<BRegion> region(new (std::nothrow) 3843 BRegion(window->VisibleRegion())); 3844 if (region.IsSet()) { 3845 if (previousRegions.AddItem(region.Detach())) 3846 windows.AddWindow(window); 3847 } 3848 } 3849 } 3850 3851 _UpdateFronts(false); 3852 _UpdateFloating(previousIndex, index, 3853 movedMouseEventWindow ? movedWindow : NULL); 3854 3855 BRegion stillAvailableOnScreen; 3856 _RebuildClippingForAllWindows(stillAvailableOnScreen); 3857 _SetBackground(stillAvailableOnScreen); 3858 3859 for (Window* window = _Windows(index).FirstWindow(); window != NULL; 3860 window = window->NextWindow(index)) { 3861 // send B_WORKSPACE_ACTIVATED message 3862 window->WorkspaceActivated(index, true); 3863 3864 if (!window->IsHidden() 3865 && window->ServerWindow()->HasDirectFrameBufferAccess()) { 3866 window->ServerWindow()->HandleDirectConnection( 3867 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 3868 } 3869 3870 if (window->InWorkspace(previousIndex) || window->IsHidden() 3871 || (window == movedWindow && movedWindow->IsNormal()) 3872 || (!window->IsNormal() 3873 && window->HasInSubset(movedWindow))) { 3874 // This window was visible before, and is already handled in the 3875 // above loop 3876 continue; 3877 } 3878 3879 dirty.Include(&window->VisibleRegion()); 3880 } 3881 3882 // Catch order changes in the new workspaces window list 3883 int32 i = 0; 3884 for (Window* window = windows.FirstWindow(); window != NULL; 3885 window = window->NextWindow(kWorkingList), i++) { 3886 BRegion* region = (BRegion*)previousRegions.ItemAt(i); 3887 region->ExclusiveInclude(&window->VisibleRegion()); 3888 dirty.Include(region); 3889 delete region; 3890 } 3891 3892 // Set new focus, but keep focus to a floating window if still visible 3893 if (movedWindow != NULL) 3894 SetFocusWindow(movedWindow); 3895 else if (!_Windows(index).HasWindow(FocusWindow()) 3896 || (FocusWindow() != NULL && !FocusWindow()->IsFloating())) 3897 SetFocusWindow(fLastWorkspaceFocus[index]); 3898 3899 _WindowChanged(NULL); 3900 MarkDirty(dirty); 3901 3902 #if 0 3903 // Show the dirty regions of this workspace switch 3904 if (GetDrawingEngine()->LockParallelAccess()) { 3905 GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0}); 3906 GetDrawingEngine()->UnlockParallelAccess(); 3907 snooze(100000); 3908 } 3909 #endif 3910 3911 if (previousColor != fWorkspaces[fCurrentWorkspace].Color()) 3912 RedrawBackground(); 3913 } 3914