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