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