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