1 /* 2 * Copyright 2001-2013, 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->ClickToFocusMouse()) { 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->FocusFollowsMouse()) 1205 SetFocusWindow(WindowAt(fLastMousePosition)); 1206 else if (fSettings->NormalMouse()) 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* nextFocus) 1872 { 1873 if (!LockAllWindows()) 1874 return; 1875 1876 // test for B_LOCK_WINDOW_FOCUS 1877 if (fLockedFocusWindow && nextFocus != fLockedFocusWindow) { 1878 UnlockAllWindows(); 1879 return; 1880 } 1881 1882 bool hasModal = _WindowHasModal(nextFocus); 1883 bool hasWindowScreen = false; 1884 1885 if (!hasModal && nextFocus != 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 = nextFocus; 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 (nextFocus == fFocus && nextFocus != NULL && !nextFocus->IsHidden() 1899 && (nextFocus->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 listIndex = fCurrentWorkspace; 1907 WindowList* list = &_Windows(fCurrentWorkspace); 1908 if (!fSettings->NormalMouse()) { 1909 listIndex = kFocusList; 1910 list = &fFocusList; 1911 } 1912 1913 if (nextFocus == NULL || hasModal || hasWindowScreen) { 1914 nextFocus = list->LastWindow(); 1915 1916 if (fSettings->NormalMouse()) { 1917 // If the last window having focus is a window that cannot make it 1918 // to the front, we use that as the next focus 1919 Window* lastFocus = fFocusList.LastWindow(); 1920 if (lastFocus != NULL && !lastFocus->SupportsFront() 1921 && _WindowCanHaveFocus(lastFocus)) { 1922 nextFocus = lastFocus; 1923 } 1924 } 1925 } 1926 1927 // make sure no window is chosen that doesn't want focus or cannot have it 1928 while (nextFocus != NULL && !_WindowCanHaveFocus(nextFocus)) { 1929 nextFocus = nextFocus->PreviousWindow(listIndex); 1930 } 1931 1932 if (fFocus == nextFocus) { 1933 // turns out the window that is supposed to get focus now already has it 1934 UnlockAllWindows(); 1935 return; 1936 } 1937 1938 team_id oldActiveApp = -1; 1939 team_id newActiveApp = -1; 1940 1941 if (fFocus != NULL) { 1942 fFocus->SetFocus(false); 1943 oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 1944 } 1945 1946 fFocus = nextFocus; 1947 1948 if (fFocus != NULL) { 1949 fFocus->SetFocus(true); 1950 newActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 1951 1952 // move current focus to the end of the focus list 1953 fFocusList.RemoveWindow(fFocus); 1954 fFocusList.AddWindow(fFocus); 1955 } 1956 1957 if (newActiveApp == -1) { 1958 // make sure the cursor is visible 1959 HWInterface()->SetCursorVisible(true); 1960 } 1961 1962 UnlockAllWindows(); 1963 1964 // change the "active" app if appropriate 1965 if (oldActiveApp == newActiveApp) 1966 return; 1967 1968 BAutolock locker(fApplicationsLock); 1969 1970 for (int32 i = 0; i < fApplications.CountItems(); i++) { 1971 ServerApp* app = fApplications.ItemAt(i); 1972 1973 if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp) 1974 app->Activate(false); 1975 else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp) 1976 app->Activate(true); 1977 } 1978 } 1979 1980 1981 void 1982 Desktop::SetFocusLocked(const Window* window) 1983 { 1984 AutoWriteLocker _(fWindowLock); 1985 1986 if (window != NULL) { 1987 // Don't allow this to be set when no mouse buttons 1988 // are pressed. (BView::SetMouseEventMask() should only be called 1989 // from mouse hooks.) 1990 if (fLastMouseButtons == 0) 1991 return; 1992 } 1993 1994 fLockedFocusWindow = window; 1995 } 1996 1997 1998 Window* 1999 Desktop::FindWindowByClientToken(int32 token, team_id teamID) 2000 { 2001 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2002 window = window->NextWindow(kAllWindowList)) { 2003 if (window->ServerWindow()->ClientToken() == token 2004 && window->ServerWindow()->ClientTeam() == teamID) { 2005 return window; 2006 } 2007 } 2008 2009 return NULL; 2010 } 2011 2012 2013 ::EventTarget* 2014 Desktop::FindTarget(BMessenger& messenger) 2015 { 2016 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2017 window = window->NextWindow(kAllWindowList)) { 2018 if (window->EventTarget().Messenger() == messenger) 2019 return &window->EventTarget(); 2020 } 2021 2022 return NULL; 2023 } 2024 2025 2026 void 2027 Desktop::MarkDirty(BRegion& region) 2028 { 2029 if (region.CountRects() == 0) 2030 return; 2031 2032 if (LockAllWindows()) { 2033 // send redraw messages to all windows intersecting the dirty region 2034 _TriggerWindowRedrawing(region); 2035 2036 UnlockAllWindows(); 2037 } 2038 } 2039 2040 2041 void 2042 Desktop::Redraw() 2043 { 2044 BRegion dirty(fVirtualScreen.Frame()); 2045 MarkDirty(dirty); 2046 } 2047 2048 2049 /*! \brief Redraws the background (ie. the desktop window, if any). 2050 */ 2051 void 2052 Desktop::RedrawBackground() 2053 { 2054 LockAllWindows(); 2055 2056 BRegion redraw; 2057 2058 Window* window = CurrentWindows().FirstWindow(); 2059 if (window->Feel() == kDesktopWindowFeel) { 2060 redraw = window->VisibleContentRegion(); 2061 2062 // look for desktop background view, and update its background color 2063 // TODO: is there a better way to do this? 2064 View* view = window->TopView(); 2065 if (view != NULL) 2066 view = view->FirstChild(); 2067 2068 while (view) { 2069 if (view->IsDesktopBackground()) { 2070 view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color()); 2071 break; 2072 } 2073 view = view->NextSibling(); 2074 } 2075 2076 window->ProcessDirtyRegion(redraw); 2077 } else { 2078 redraw = BackgroundRegion(); 2079 fBackgroundRegion.MakeEmpty(); 2080 _SetBackground(redraw); 2081 } 2082 2083 _WindowChanged(NULL); 2084 // update workspaces view as well 2085 2086 UnlockAllWindows(); 2087 } 2088 2089 2090 bool 2091 Desktop::ReloadDecor(DecorAddOn* oldDecor) 2092 { 2093 AutoWriteLocker _(fWindowLock); 2094 2095 bool returnValue = true; 2096 2097 if (oldDecor != NULL) { 2098 const DesktopListenerList* oldListeners 2099 = &oldDecor->GetDesktopListeners(); 2100 for (int i = 0; i < oldListeners->CountItems(); i++) 2101 UnregisterListener(oldListeners->ItemAt(i)); 2102 } 2103 2104 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2105 window = window->NextWindow(kAllWindowList)) { 2106 BRegion oldBorder; 2107 window->GetBorderRegion(&oldBorder); 2108 2109 if (!window->ReloadDecor()) { 2110 // prevent unloading previous add-on 2111 returnValue = false; 2112 } 2113 2114 BRegion border; 2115 window->GetBorderRegion(&border); 2116 2117 border.Include(&oldBorder); 2118 RebuildAndRedrawAfterWindowChange(window, border); 2119 } 2120 2121 // register new listeners 2122 const DesktopListenerList& newListeners 2123 = gDecorManager.GetDesktopListeners(); 2124 for (int i = 0; i < newListeners.CountItems(); i++) 2125 RegisterListener(newListeners.ItemAt(i)); 2126 2127 return returnValue; 2128 } 2129 2130 2131 void 2132 Desktop::MinimizeApplication(team_id team) 2133 { 2134 AutoWriteLocker locker(fWindowLock); 2135 2136 // Just minimize all windows of that application 2137 2138 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2139 window = window->NextWindow(kAllWindowList)) { 2140 if (window->ServerWindow()->ClientTeam() != team) 2141 continue; 2142 2143 window->ServerWindow()->NotifyMinimize(true); 2144 } 2145 } 2146 2147 2148 void 2149 Desktop::BringApplicationToFront(team_id team) 2150 { 2151 AutoWriteLocker locker(fWindowLock); 2152 2153 // TODO: for now, just maximize all windows of that application 2154 // TODO: have the ability to lock the current workspace 2155 2156 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2157 window = window->NextWindow(kAllWindowList)) { 2158 if (window->ServerWindow()->ClientTeam() != team) 2159 continue; 2160 2161 window->ServerWindow()->NotifyMinimize(false); 2162 } 2163 } 2164 2165 2166 void 2167 Desktop::WindowAction(int32 windowToken, int32 action) 2168 { 2169 if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT) 2170 return; 2171 2172 LockAllWindows(); 2173 2174 ::ServerWindow* serverWindow; 2175 Window* window; 2176 if (BPrivate::gDefaultTokens.GetToken(windowToken, 2177 B_SERVER_TOKEN, (void**)&serverWindow) != B_OK 2178 || (window = serverWindow->Window()) == NULL) { 2179 UnlockAllWindows(); 2180 return; 2181 } 2182 2183 if (action == B_BRING_TO_FRONT && !window->IsMinimized()) { 2184 // the window is visible, we just need to make it the front window 2185 ActivateWindow(window); 2186 } else { 2187 // if not, ask the window if it wants to be unminimized 2188 serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW); 2189 } 2190 2191 UnlockAllWindows(); 2192 } 2193 2194 2195 void 2196 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 2197 { 2198 AutoWriteLocker locker(fWindowLock); 2199 2200 // compute the number of windows 2201 2202 int32 count = 0; 2203 2204 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2205 window = window->NextWindow(kAllWindowList)) { 2206 if (team < B_OK || window->ServerWindow()->ClientTeam() == team) 2207 count++; 2208 } 2209 2210 // write list 2211 2212 sender.StartMessage(B_OK); 2213 sender.Attach<int32>(count); 2214 2215 // first write the windows of the current workspace correctly ordered 2216 for (Window *window = CurrentWindows().LastWindow(); window != NULL; 2217 window = window->PreviousWindow(fCurrentWorkspace)) { 2218 if (team >= B_OK && window->ServerWindow()->ClientTeam() != team) 2219 continue; 2220 2221 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2222 } 2223 2224 // then write all the other windows 2225 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2226 window = window->NextWindow(kAllWindowList)) { 2227 if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team) 2228 || window->InWorkspace(fCurrentWorkspace)) 2229 continue; 2230 2231 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2232 } 2233 2234 sender.Flush(); 2235 } 2236 2237 2238 void 2239 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 2240 { 2241 AutoWriteLocker locker(fWindowLock); 2242 BAutolock tokenLocker(BPrivate::gDefaultTokens); 2243 2244 ::ServerWindow* window; 2245 if (BPrivate::gDefaultTokens.GetToken(serverToken, 2246 B_SERVER_TOKEN, (void**)&window) != B_OK) { 2247 sender.StartMessage(B_ENTRY_NOT_FOUND); 2248 sender.Flush(); 2249 return; 2250 } 2251 2252 window_info info; 2253 window->GetInfo(info); 2254 2255 float tabSize = 0.0; 2256 float borderSize = 0.0; 2257 ::Window* tmp = window->Window(); 2258 if (tmp) { 2259 BMessage message; 2260 if (tmp->GetDecoratorSettings(&message)) { 2261 BRect tabFrame; 2262 message.FindRect("tab frame", &tabFrame); 2263 tabSize = tabFrame.bottom - tabFrame.top; 2264 message.FindFloat("border width", &borderSize); 2265 } 2266 } 2267 2268 int32 length = window->Title() ? strlen(window->Title()) : 0; 2269 2270 sender.StartMessage(B_OK); 2271 sender.Attach<int32>(sizeof(client_window_info) + length); 2272 sender.Attach(&info, sizeof(window_info)); 2273 sender.Attach<float>(tabSize); 2274 sender.Attach<float>(borderSize); 2275 2276 if (length > 0) 2277 sender.Attach(window->Title(), length + 1); 2278 else 2279 sender.Attach<char>('\0'); 2280 2281 sender.Flush(); 2282 } 2283 2284 2285 void 2286 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender) 2287 { 2288 LockSingleWindow(); 2289 2290 if (workspace < 0) 2291 workspace = fCurrentWorkspace; 2292 else if (workspace >= kMaxWorkspaces) { 2293 sender.StartMessage(B_BAD_VALUE); 2294 sender.Flush(); 2295 UnlockSingleWindow(); 2296 return; 2297 } 2298 2299 int32 count = _Windows(workspace).Count(); 2300 2301 // write list 2302 2303 sender.StartMessage(B_OK); 2304 sender.Attach<int32>(count); 2305 2306 for (Window *window = _Windows(workspace).LastWindow(); window != NULL; 2307 window = window->PreviousWindow(workspace)) { 2308 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2309 } 2310 2311 sender.Flush(); 2312 2313 UnlockSingleWindow(); 2314 } 2315 2316 2317 void 2318 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender) 2319 { 2320 fApplicationsLock.Lock(); 2321 LockSingleWindow(); 2322 2323 int32 maxCount = fApplications.CountItems(); 2324 2325 fApplicationsLock.Unlock(); 2326 // as long as we hold the window lock, no new window can appear 2327 2328 if (workspace < 0) 2329 workspace = fCurrentWorkspace; 2330 else if (workspace >= kMaxWorkspaces) { 2331 sender.StartMessage(B_BAD_VALUE); 2332 sender.Flush(); 2333 UnlockSingleWindow(); 2334 return; 2335 } 2336 2337 // compute the list of applications on this workspace 2338 2339 team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id)); 2340 if (teams == NULL) { 2341 sender.StartMessage(B_NO_MEMORY); 2342 sender.Flush(); 2343 UnlockSingleWindow(); 2344 return; 2345 } 2346 2347 int32 count = 0; 2348 2349 for (Window *window = _Windows(workspace).LastWindow(); window != NULL; 2350 window = window->PreviousWindow(workspace)) { 2351 team_id team = window->ServerWindow()->ClientTeam(); 2352 if (count > 1) { 2353 // see if we already have this team 2354 bool found = false; 2355 for (int32 i = 0; i < count; i++) { 2356 if (teams[i] == team) { 2357 found = true; 2358 break; 2359 } 2360 } 2361 if (found) 2362 continue; 2363 } 2364 2365 ASSERT(count < maxCount); 2366 teams[count++] = team; 2367 } 2368 2369 UnlockSingleWindow(); 2370 2371 // write list 2372 2373 sender.StartMessage(B_OK); 2374 sender.Attach<int32>(count); 2375 2376 for (int32 i = 0; i < count; i++) { 2377 sender.Attach<int32>(teams[i]); 2378 } 2379 2380 sender.Flush(); 2381 free(teams); 2382 } 2383 2384 2385 void 2386 Desktop::_LaunchInputServer() 2387 { 2388 BRoster roster; 2389 status_t status = roster.Launch("application/x-vnd.Be-input_server"); 2390 if (status == B_OK || status == B_ALREADY_RUNNING) 2391 return; 2392 2393 // Could not load input_server by signature, try well-known location 2394 2395 BEntry entry; 2396 BPath inputServerPath; 2397 if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, &inputServerPath) == B_OK 2398 && inputServerPath.Append("input_server") == B_OK) { 2399 entry.SetTo(inputServerPath.Path()); 2400 } else 2401 entry.SetTo("/system/servers/input_server"); 2402 entry_ref ref; 2403 status_t entryStatus = entry.GetRef(&ref); 2404 if (entryStatus == B_OK) 2405 entryStatus = roster.Launch(&ref); 2406 if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) { 2407 syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n", 2408 strerror(status)); 2409 return; 2410 } 2411 2412 syslog(LOG_ERR, "Failed to launch the input server: %s!\n", 2413 strerror(entryStatus)); 2414 } 2415 2416 2417 void 2418 Desktop::_GetLooperName(char* name, size_t length) 2419 { 2420 snprintf(name, length, "d:%d:%s", fUserID, 2421 fTargetScreen == NULL ? "baron" : fTargetScreen); 2422 } 2423 2424 2425 void 2426 Desktop::_PrepareQuit() 2427 { 2428 // let's kill all remaining applications 2429 2430 fApplicationsLock.Lock(); 2431 2432 int32 count = fApplications.CountItems(); 2433 for (int32 i = 0; i < count; i++) { 2434 ServerApp *app = fApplications.ItemAt(i); 2435 team_id clientTeam = app->ClientTeam(); 2436 2437 app->Quit(); 2438 kill_team(clientTeam); 2439 } 2440 2441 // wait for the last app to die 2442 if (count > 0) { 2443 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 2444 250000); 2445 } 2446 2447 fApplicationsLock.Unlock(); 2448 } 2449 2450 2451 void 2452 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) 2453 { 2454 switch (code) { 2455 case AS_CREATE_APP: 2456 { 2457 // Create the ServerApp to node monitor a new BApplication 2458 2459 // Attached data: 2460 // 1) port_id - receiver port of a regular app 2461 // 2) port_id - client looper port - for sending messages to the 2462 // client 2463 // 2) team_id - app's team ID 2464 // 3) int32 - handler token of the regular app 2465 // 4) char * - signature of the regular app 2466 2467 // Find the necessary data 2468 team_id clientTeamID = -1; 2469 port_id clientLooperPort = -1; 2470 port_id clientReplyPort = -1; 2471 int32 htoken = B_NULL_TOKEN; 2472 char* appSignature = NULL; 2473 2474 link.Read<port_id>(&clientReplyPort); 2475 link.Read<port_id>(&clientLooperPort); 2476 link.Read<team_id>(&clientTeamID); 2477 link.Read<int32>(&htoken); 2478 if (link.ReadString(&appSignature) != B_OK) 2479 break; 2480 2481 ServerApp* app = new ServerApp(this, clientReplyPort, 2482 clientLooperPort, clientTeamID, htoken, appSignature); 2483 if (app->InitCheck() == B_OK 2484 && app->Run()) { 2485 // add the new ServerApp to the known list of ServerApps 2486 fApplicationsLock.Lock(); 2487 fApplications.AddItem(app); 2488 fApplicationsLock.Unlock(); 2489 } else { 2490 delete app; 2491 2492 // if everything went well, ServerApp::Run() will notify 2493 // the client - but since it didn't, we do it here 2494 BPrivate::LinkSender reply(clientReplyPort); 2495 reply.StartMessage(B_ERROR); 2496 reply.Flush(); 2497 } 2498 2499 // This is necessary because BPortLink::ReadString allocates memory 2500 free(appSignature); 2501 break; 2502 } 2503 2504 case AS_DELETE_APP: 2505 { 2506 // Delete a ServerApp. Received only from the respective ServerApp 2507 // when a BApplication asks it to quit. 2508 2509 // Attached Data: 2510 // 1) thread_id - thread ID of the ServerApp to be deleted 2511 2512 thread_id thread = -1; 2513 if (link.Read<thread_id>(&thread) < B_OK) 2514 break; 2515 2516 fApplicationsLock.Lock(); 2517 2518 // Run through the list of apps and nuke the proper one 2519 2520 int32 count = fApplications.CountItems(); 2521 ServerApp* removeApp = NULL; 2522 2523 for (int32 i = 0; i < count; i++) { 2524 ServerApp* app = fApplications.ItemAt(i); 2525 2526 if (app->Thread() == thread) { 2527 fApplications.RemoveItemAt(i); 2528 removeApp = app; 2529 break; 2530 } 2531 } 2532 2533 fApplicationsLock.Unlock(); 2534 2535 if (removeApp != NULL) 2536 removeApp->Quit(fShutdownSemaphore); 2537 2538 if (fQuitting && count <= 1) { 2539 // wait for the last app to die 2540 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, 2541 B_RELATIVE_TIMEOUT, 500000); 2542 PostMessage(kMsgQuitLooper); 2543 } 2544 break; 2545 } 2546 2547 case AS_ACTIVATE_APP: 2548 { 2549 // Someone is requesting to activation of a certain app. 2550 2551 // Attached data: 2552 // 1) port_id reply port 2553 // 2) team_id team 2554 2555 status_t status; 2556 2557 // get the parameters 2558 port_id replyPort; 2559 team_id team; 2560 if (link.Read(&replyPort) == B_OK 2561 && link.Read(&team) == B_OK) 2562 status = _ActivateApp(team); 2563 else 2564 status = B_ERROR; 2565 2566 // send the reply 2567 BPrivate::PortLink replyLink(replyPort); 2568 replyLink.StartMessage(status); 2569 replyLink.Flush(); 2570 break; 2571 } 2572 2573 case AS_APP_CRASHED: 2574 case AS_DUMP_ALLOCATOR: 2575 case AS_DUMP_BITMAPS: 2576 { 2577 BAutolock locker(fApplicationsLock); 2578 2579 team_id team; 2580 if (link.Read(&team) != B_OK) 2581 break; 2582 2583 for (int32 i = 0; i < fApplications.CountItems(); i++) { 2584 ServerApp* app = fApplications.ItemAt(i); 2585 2586 if (app->ClientTeam() == team) 2587 app->PostMessage(code); 2588 } 2589 break; 2590 } 2591 2592 case AS_EVENT_STREAM_CLOSED: 2593 _LaunchInputServer(); 2594 break; 2595 2596 case B_QUIT_REQUESTED: 2597 // We've been asked to quit, so (for now) broadcast to all 2598 // test apps to quit. This situation will occur only when the 2599 // server is compiled as a regular Be application. 2600 2601 fApplicationsLock.Lock(); 2602 fShutdownSemaphore = create_sem(0, "desktop shutdown"); 2603 fShutdownCount = fApplications.CountItems(); 2604 fApplicationsLock.Unlock(); 2605 2606 fQuitting = true; 2607 BroadcastToAllApps(AS_QUIT_APP); 2608 2609 // We now need to process the remaining AS_DELETE_APP messages and 2610 // wait for the kMsgShutdownServer message. 2611 // If an application does not quit as asked, the picasso thread 2612 // will send us this message in 2-3 seconds. 2613 2614 // if there are no apps to quit, shutdown directly 2615 if (fShutdownCount == 0) 2616 PostMessage(kMsgQuitLooper); 2617 break; 2618 2619 case AS_ACTIVATE_WORKSPACE: 2620 { 2621 int32 index; 2622 link.Read<int32>(&index); 2623 if (index == -1) 2624 index = fPreviousWorkspace; 2625 2626 bool moveFocusWindow; 2627 link.Read<bool>(&moveFocusWindow); 2628 2629 SetWorkspace(index, moveFocusWindow); 2630 break; 2631 } 2632 2633 case AS_TALK_TO_DESKTOP_LISTENER: 2634 { 2635 port_id clientReplyPort; 2636 if (link.Read<port_id>(&clientReplyPort) != B_OK) 2637 break; 2638 2639 BPrivate::LinkSender reply(clientReplyPort); 2640 AutoWriteLocker locker(fWindowLock); 2641 if (MessageForListener(NULL, link, reply) != true) { 2642 // unhandled message, at least send an error if needed 2643 if (link.NeedsReply()) { 2644 reply.StartMessage(B_ERROR); 2645 reply.Flush(); 2646 } 2647 } 2648 break; 2649 } 2650 2651 // ToDo: Remove this again. It is a message sent by the 2652 // invalidate_on_exit kernel debugger add-on to trigger a redraw 2653 // after exiting a kernel debugger session. 2654 case 'KDLE': 2655 { 2656 BRegion dirty; 2657 dirty.Include(fVirtualScreen.Frame()); 2658 MarkDirty(dirty); 2659 break; 2660 } 2661 2662 default: 2663 printf("Desktop %d:%s received unexpected code %" B_PRId32 "\n", 0, 2664 "baron", code); 2665 2666 if (link.NeedsReply()) { 2667 // the client is now blocking and waiting for a reply! 2668 fLink.StartMessage(B_ERROR); 2669 fLink.Flush(); 2670 } 2671 break; 2672 } 2673 } 2674 2675 2676 WindowList& 2677 Desktop::CurrentWindows() 2678 { 2679 return fWorkspaces[fCurrentWorkspace].Windows(); 2680 } 2681 2682 2683 WindowList& 2684 Desktop::AllWindows() 2685 { 2686 return fAllWindows; 2687 } 2688 2689 2690 Window* 2691 Desktop::WindowForClientLooperPort(port_id port) 2692 { 2693 ASSERT_MULTI_LOCKED(fWindowLock); 2694 2695 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2696 window = window->NextWindow(kAllWindowList)) { 2697 if (window->ServerWindow()->ClientLooperPort() == port) 2698 return window; 2699 } 2700 return NULL; 2701 } 2702 2703 2704 WindowList& 2705 Desktop::_Windows(int32 index) 2706 { 2707 ASSERT(index >= 0 && index < kMaxWorkspaces); 2708 return fWorkspaces[index].Windows(); 2709 } 2710 2711 2712 void 2713 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace, 2714 Window* mouseEventWindow) 2715 { 2716 if (previousWorkspace == -1) 2717 previousWorkspace = fCurrentWorkspace; 2718 if (nextWorkspace == -1) 2719 nextWorkspace = previousWorkspace; 2720 2721 for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL; 2722 floating = floating->NextWindow(kSubsetList)) { 2723 // we only care about app/subset floating windows 2724 if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL 2725 && floating->Feel() != B_FLOATING_APP_WINDOW_FEEL) 2726 continue; 2727 2728 if (fFront != NULL && fFront->IsNormal() 2729 && floating->HasInSubset(fFront)) { 2730 // is now visible 2731 if (_Windows(previousWorkspace).HasWindow(floating) 2732 && previousWorkspace != nextWorkspace 2733 && !floating->InSubsetWorkspace(previousWorkspace)) { 2734 // but no longer on the previous workspace 2735 _Windows(previousWorkspace).RemoveWindow(floating); 2736 floating->SetCurrentWorkspace(-1); 2737 } 2738 2739 if (!_Windows(nextWorkspace).HasWindow(floating)) { 2740 // but wasn't before 2741 _Windows(nextWorkspace).AddWindow(floating, 2742 floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), 2743 nextWorkspace)); 2744 floating->SetCurrentWorkspace(nextWorkspace); 2745 if (mouseEventWindow != fFront) 2746 _ShowWindow(floating); 2747 2748 // TODO: put the floating last in the floating window list to 2749 // preserve the on screen window order 2750 } 2751 } else if (_Windows(previousWorkspace).HasWindow(floating) 2752 && !floating->InSubsetWorkspace(previousWorkspace)) { 2753 // was visible, but is no longer 2754 2755 _Windows(previousWorkspace).RemoveWindow(floating); 2756 floating->SetCurrentWorkspace(-1); 2757 _HideWindow(floating); 2758 2759 if (FocusWindow() == floating) 2760 SetFocusWindow(); 2761 } 2762 } 2763 } 2764 2765 2766 /*! Search the visible windows for a valid back window 2767 (only desktop windows can't be back windows) 2768 */ 2769 void 2770 Desktop::_UpdateBack() 2771 { 2772 fBack = NULL; 2773 2774 for (Window* window = CurrentWindows().FirstWindow(); window != NULL; 2775 window = window->NextWindow(fCurrentWorkspace)) { 2776 if (window->IsHidden() || window->Feel() == kDesktopWindowFeel) 2777 continue; 2778 2779 fBack = window; 2780 break; 2781 } 2782 } 2783 2784 2785 /*! Search the visible windows for a valid front window 2786 (only normal and modal windows can be front windows) 2787 2788 The only place where you don't want to update floating windows is 2789 during a workspace change - because then you'll call _UpdateFloating() 2790 yourself. 2791 */ 2792 void 2793 Desktop::_UpdateFront(bool updateFloating) 2794 { 2795 fFront = NULL; 2796 2797 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 2798 window = window->PreviousWindow(fCurrentWorkspace)) { 2799 if (window->IsHidden() || window->IsFloating() 2800 || !window->SupportsFront()) 2801 continue; 2802 2803 fFront = window; 2804 break; 2805 } 2806 2807 if (updateFloating) 2808 _UpdateFloating(); 2809 } 2810 2811 2812 void 2813 Desktop::_UpdateFronts(bool updateFloating) 2814 { 2815 _UpdateBack(); 2816 _UpdateFront(updateFloating); 2817 } 2818 2819 2820 bool 2821 Desktop::_WindowHasModal(Window* window) const 2822 { 2823 if (window == NULL) 2824 return false; 2825 2826 for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL; 2827 modal = modal->NextWindow(kSubsetList)) { 2828 // only visible modal windows count 2829 if (!modal->IsModal() || modal->IsHidden()) 2830 continue; 2831 2832 if (modal->HasInSubset(window)) 2833 return true; 2834 } 2835 2836 return false; 2837 } 2838 2839 2840 /*! Determines whether or not the specified \a window can have focus at all. 2841 */ 2842 bool 2843 Desktop::_WindowCanHaveFocus(Window* window) const 2844 { 2845 return window != NULL 2846 && window->InWorkspace(fCurrentWorkspace) 2847 && (window->Flags() & B_AVOID_FOCUS) == 0 2848 && !_WindowHasModal(window) 2849 && !window->IsHidden(); 2850 } 2851 2852 2853 /*! You must at least hold a single window lock when calling this method. 2854 */ 2855 void 2856 Desktop::_WindowChanged(Window* window) 2857 { 2858 ASSERT_MULTI_LOCKED(fWindowLock); 2859 2860 BAutolock _(fWorkspacesLock); 2861 2862 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 2863 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 2864 view->WindowChanged(window); 2865 } 2866 } 2867 2868 2869 /*! You must at least hold a single window lock when calling this method. 2870 */ 2871 void 2872 Desktop::_WindowRemoved(Window* window) 2873 { 2874 ASSERT_MULTI_LOCKED(fWindowLock); 2875 2876 BAutolock _(fWorkspacesLock); 2877 2878 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 2879 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 2880 view->WindowRemoved(window); 2881 } 2882 } 2883 2884 2885 /*! Shows the window on the screen - it does this independently of the 2886 Window::IsHidden() state. 2887 */ 2888 void 2889 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows) 2890 { 2891 BRegion background; 2892 _RebuildClippingForAllWindows(background); 2893 _SetBackground(background); 2894 _WindowChanged(window); 2895 2896 BRegion dirty(window->VisibleRegion()); 2897 2898 if (!affectsOtherWindows) { 2899 // everything that is now visible in the 2900 // window needs a redraw, but other windows 2901 // are not affected, we can call ProcessDirtyRegion() 2902 // of the window, and don't have to use MarkDirty() 2903 window->ProcessDirtyRegion(dirty); 2904 } else 2905 MarkDirty(dirty); 2906 2907 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 2908 window->ServerWindow()->HandleDirectConnection( 2909 B_DIRECT_START | B_BUFFER_RESET); 2910 } 2911 } 2912 2913 2914 /*! Hides the window from the screen - it does this independently of the 2915 Window::IsHidden() state. 2916 */ 2917 void 2918 Desktop::_HideWindow(Window* window) 2919 { 2920 if (window->ServerWindow()->IsDirectlyAccessing()) 2921 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 2922 2923 // after rebuilding the clipping, 2924 // this window will not have a visible 2925 // region anymore, so we need to remember 2926 // it now 2927 // (actually that's not true, since 2928 // hidden windows are excluded from the 2929 // clipping calculation, but anyways) 2930 BRegion dirty(window->VisibleRegion()); 2931 2932 BRegion background; 2933 _RebuildClippingForAllWindows(background); 2934 _SetBackground(background); 2935 _WindowChanged(window); 2936 2937 MarkDirty(dirty); 2938 } 2939 2940 2941 /*! Updates the workspaces of all subset windows with regard to the 2942 specifed window. 2943 If newIndex is not -1, it will move all subset windows that belong to 2944 the specifed window to the new workspace; this form is only called by 2945 SetWorkspace(). 2946 */ 2947 void 2948 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex, 2949 int32 newIndex) 2950 { 2951 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, 2952 window->Title())); 2953 2954 // if the window is hidden, the subset windows are up-to-date already 2955 if (!window->IsNormal() || window->IsHidden()) 2956 return; 2957 2958 for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL; 2959 subset = subset->NextWindow(kSubsetList)) { 2960 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL 2961 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) { 2962 // These windows are always visible on all workspaces, 2963 // no need to update them. 2964 continue; 2965 } 2966 2967 if (subset->IsFloating()) { 2968 // Floating windows are inserted and removed to the current 2969 // workspace as the need arises - they are not handled here 2970 // but in _UpdateFront() 2971 continue; 2972 } 2973 2974 if (subset->HasInSubset(window)) { 2975 // adopt the workspace change 2976 SetWindowWorkspaces(subset, subset->SubsetWorkspaces()); 2977 } 2978 } 2979 } 2980 2981 2982 /*! \brief Adds or removes the window to or from the workspaces it's on. 2983 */ 2984 void 2985 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces, 2986 uint32 newWorkspaces) 2987 { 2988 if (oldWorkspaces == newWorkspaces) 2989 return; 2990 2991 // apply changes to the workspaces' window lists 2992 2993 LockAllWindows(); 2994 2995 // NOTE: we bypass the anchor-mechanism by intention when switching 2996 // the workspace programmatically. 2997 2998 for (int32 i = 0; i < kMaxWorkspaces; i++) { 2999 if (workspace_in_workspaces(i, oldWorkspaces)) { 3000 // window is on this workspace, is it anymore? 3001 if (!workspace_in_workspaces(i, newWorkspaces)) { 3002 _Windows(i).RemoveWindow(window); 3003 if (fLastWorkspaceFocus[i] == window) 3004 fLastWorkspaceFocus[i] = NULL; 3005 3006 if (i == CurrentWorkspace()) { 3007 // remove its appearance from the current workspace 3008 window->SetCurrentWorkspace(-1); 3009 3010 if (!window->IsHidden()) 3011 _HideWindow(window); 3012 } 3013 } 3014 } else { 3015 // window was not on this workspace, is it now? 3016 if (workspace_in_workspaces(i, newWorkspaces)) { 3017 _Windows(i).AddWindow(window, 3018 window->Frontmost(_Windows(i).FirstWindow(), i)); 3019 3020 if (i == CurrentWorkspace()) { 3021 // make the window visible in current workspace 3022 window->SetCurrentWorkspace(fCurrentWorkspace); 3023 3024 if (!window->IsHidden()) { 3025 // This only affects other windows if this window has 3026 // floating or modal windows that need to be shown as 3027 // well 3028 // TODO: take care of this 3029 _ShowWindow(window, FrontWindow() == window); 3030 } 3031 } 3032 } 3033 } 3034 } 3035 3036 // If the window is visible only on one workspace, we set it's current 3037 // position in that workspace (so that WorkspacesView will find us). 3038 int32 firstWorkspace = -1; 3039 for (int32 i = 0; i < kMaxWorkspaces; i++) { 3040 if ((newWorkspaces & (1L << i)) != 0) { 3041 if (firstWorkspace != -1) { 3042 firstWorkspace = -1; 3043 break; 3044 } 3045 firstWorkspace = i; 3046 } 3047 } 3048 if (firstWorkspace >= 0) 3049 window->Anchor(firstWorkspace).position = window->Frame().LeftTop(); 3050 3051 // take care about modals and floating windows 3052 _UpdateSubsetWorkspaces(window); 3053 3054 NotifyWindowWorkspacesChanged(window, newWorkspaces); 3055 3056 UnlockAllWindows(); 3057 } 3058 3059 3060 void 3061 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, 3062 bool wereVisible) 3063 { 3064 // we don't need to redraw what is currently 3065 // visible of the window 3066 BRegion clean; 3067 3068 for (Window* window = windows.FirstWindow(); window != NULL; 3069 window = window->NextWindow(list)) { 3070 if (wereVisible) 3071 clean.Include(&window->VisibleRegion()); 3072 3073 CurrentWindows().AddWindow(window, 3074 window->Frontmost(CurrentWindows().FirstWindow(), 3075 fCurrentWorkspace)); 3076 3077 _WindowChanged(window); 3078 } 3079 3080 BRegion dummy; 3081 _RebuildClippingForAllWindows(dummy); 3082 3083 // redraw what became visible of the window(s) 3084 3085 BRegion dirty; 3086 for (Window* window = windows.FirstWindow(); window != NULL; 3087 window = window->NextWindow(list)) { 3088 dirty.Include(&window->VisibleRegion()); 3089 } 3090 3091 dirty.Exclude(&clean); 3092 MarkDirty(dirty); 3093 3094 _UpdateFront(); 3095 3096 if (windows.FirstWindow() == fBack || fBack == NULL) 3097 _UpdateBack(); 3098 } 3099 3100 3101 /*! Returns the last focussed non-hidden subset window belonging to the 3102 specified \a window. 3103 */ 3104 Window* 3105 Desktop::_LastFocusSubsetWindow(Window* window) 3106 { 3107 if (window == NULL) 3108 return NULL; 3109 3110 for (Window* front = fFocusList.LastWindow(); front != NULL; 3111 front = front->PreviousWindow(kFocusList)) { 3112 if (front != window && !front->IsHidden() 3113 && window->HasInSubset(front)) 3114 return front; 3115 } 3116 3117 return NULL; 3118 } 3119 3120 3121 /*! \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse, 3122 and also updates the current view under the mouse. 3123 3124 This has only to be done in case the view changed without user interaction, 3125 ie. because of a workspace change or a closing window. 3126 */ 3127 void 3128 Desktop::_SendFakeMouseMoved(Window* window) 3129 { 3130 int32 viewToken = B_NULL_TOKEN; 3131 EventTarget* target = NULL; 3132 3133 LockAllWindows(); 3134 3135 if (window == NULL) 3136 window = MouseEventWindow(); 3137 if (window == NULL) 3138 window = WindowAt(fLastMousePosition); 3139 3140 if (window != NULL) { 3141 BMessage message; 3142 window->MouseMoved(&message, fLastMousePosition, &viewToken, true, 3143 true); 3144 3145 if (viewToken != B_NULL_TOKEN) 3146 target = &window->EventTarget(); 3147 } 3148 3149 if (viewToken != B_NULL_TOKEN) 3150 SetViewUnderMouse(window, viewToken); 3151 else { 3152 SetViewUnderMouse(NULL, B_NULL_TOKEN); 3153 SetCursor(NULL); 3154 } 3155 3156 UnlockAllWindows(); 3157 3158 if (target != NULL) 3159 EventDispatcher().SendFakeMouseMoved(*target, viewToken); 3160 } 3161 3162 3163 Screen* 3164 Desktop::_DetermineScreenFor(BRect frame) 3165 { 3166 AutoReadLocker _(fScreenLock); 3167 3168 // TODO: choose the screen depending on where most of the area is 3169 return fVirtualScreen.ScreenAt(0); 3170 } 3171 3172 3173 void 3174 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen) 3175 { 3176 // the available region on screen starts with the entire screen area 3177 // each window on the screen will take a portion from that area 3178 3179 // figure out what the entire screen area is 3180 stillAvailableOnScreen = fScreenRegion; 3181 3182 // set clipping of each window 3183 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3184 window = window->PreviousWindow(fCurrentWorkspace)) { 3185 if (!window->IsHidden()) { 3186 window->SetClipping(&stillAvailableOnScreen); 3187 window->SetScreen(_DetermineScreenFor(window->Frame())); 3188 3189 if (window->ServerWindow()->IsDirectlyAccessing()) { 3190 window->ServerWindow()->HandleDirectConnection( 3191 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 3192 } 3193 3194 // that windows region is not available on screen anymore 3195 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 3196 } 3197 } 3198 } 3199 3200 3201 void 3202 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion) 3203 { 3204 // send redraw messages to all windows intersecting the dirty region 3205 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3206 window = window->PreviousWindow(fCurrentWorkspace)) { 3207 if (!window->IsHidden() 3208 && newDirtyRegion.Intersects(window->VisibleRegion().Frame())) 3209 window->ProcessDirtyRegion(newDirtyRegion); 3210 } 3211 } 3212 3213 3214 void 3215 Desktop::_SetBackground(BRegion& background) 3216 { 3217 // NOTE: the drawing operation is caried out 3218 // in the clipping region rebuild, but it is 3219 // ok actually, because it also avoids trails on 3220 // moving windows 3221 3222 // remember the region not covered by any windows 3223 // and redraw the dirty background 3224 BRegion dirtyBackground(background); 3225 dirtyBackground.Exclude(&fBackgroundRegion); 3226 dirtyBackground.IntersectWith(&background); 3227 fBackgroundRegion = background; 3228 if (dirtyBackground.Frame().IsValid()) { 3229 if (GetDrawingEngine()->LockParallelAccess()) { 3230 GetDrawingEngine()->FillRegion(dirtyBackground, 3231 fWorkspaces[fCurrentWorkspace].Color()); 3232 3233 GetDrawingEngine()->UnlockParallelAccess(); 3234 } 3235 } 3236 } 3237 3238 3239 //! The all window lock must be held when calling this function. 3240 void 3241 Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow, 3242 BRegion& dirty) 3243 { 3244 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3245 if (!changedWindow->IsVisible() || dirty.CountRects() == 0) 3246 return; 3247 3248 // The following loop is pretty much a copy of 3249 // _RebuildClippingForAllWindows(), but will also 3250 // take care about restricting our dirty region. 3251 3252 // figure out what the entire screen area is 3253 BRegion stillAvailableOnScreen(fScreenRegion); 3254 3255 // set clipping of each window 3256 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3257 window = window->PreviousWindow(fCurrentWorkspace)) { 3258 if (!window->IsHidden()) { 3259 if (window == changedWindow) 3260 dirty.IntersectWith(&stillAvailableOnScreen); 3261 3262 window->SetClipping(&stillAvailableOnScreen); 3263 window->SetScreen(_DetermineScreenFor(window->Frame())); 3264 3265 if (window->ServerWindow()->IsDirectlyAccessing()) { 3266 window->ServerWindow()->HandleDirectConnection( 3267 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 3268 } 3269 3270 // that windows region is not available on screen anymore 3271 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 3272 } 3273 } 3274 3275 _SetBackground(stillAvailableOnScreen); 3276 _WindowChanged(changedWindow); 3277 3278 _TriggerWindowRedrawing(dirty); 3279 } 3280 3281 3282 //! Suspend all windows with direct access to the frame buffer 3283 void 3284 Desktop::_SuspendDirectFrameBufferAccess() 3285 { 3286 ASSERT_MULTI_LOCKED(fWindowLock); 3287 3288 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3289 window = window->NextWindow(kAllWindowList)) { 3290 if (window->ServerWindow()->IsDirectlyAccessing()) 3291 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3292 } 3293 } 3294 3295 3296 //! Resume all windows with direct access to the frame buffer 3297 void 3298 Desktop::_ResumeDirectFrameBufferAccess() 3299 { 3300 ASSERT_MULTI_LOCKED(fWindowLock); 3301 3302 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3303 window = window->NextWindow(kAllWindowList)) { 3304 if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace)) 3305 continue; 3306 3307 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 3308 window->ServerWindow()->HandleDirectConnection( 3309 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 3310 } 3311 } 3312 } 3313 3314 3315 void 3316 Desktop::_ScreenChanged(Screen* screen) 3317 { 3318 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3319 3320 // the entire screen is dirty, because we're actually 3321 // operating on an all new buffer in memory 3322 BRegion dirty(screen->Frame()); 3323 3324 // update our cached screen region 3325 fScreenRegion.Set(screen->Frame()); 3326 gInputManager->UpdateScreenBounds(screen->Frame()); 3327 3328 BRegion background; 3329 _RebuildClippingForAllWindows(background); 3330 3331 fBackgroundRegion.MakeEmpty(); 3332 // makes sure that the complete background is redrawn 3333 _SetBackground(background); 3334 3335 // figure out dirty region 3336 dirty.Exclude(&background); 3337 _TriggerWindowRedrawing(dirty); 3338 3339 // send B_SCREEN_CHANGED to windows on that screen 3340 BMessage update(B_SCREEN_CHANGED); 3341 update.AddInt64("when", real_time_clock_usecs()); 3342 update.AddRect("frame", screen->Frame()); 3343 update.AddInt32("mode", screen->ColorSpace()); 3344 3345 fVirtualScreen.UpdateFrame(); 3346 3347 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3348 window = window->NextWindow(kAllWindowList)) { 3349 if (window->Screen() == screen) 3350 window->ServerWindow()->ScreenChanged(&update); 3351 } 3352 } 3353 3354 3355 /*! \brief activate one of the app's windows. 3356 */ 3357 status_t 3358 Desktop::_ActivateApp(team_id team) 3359 { 3360 // search for an unhidden window in the current workspace 3361 3362 AutoWriteLocker locker(fWindowLock); 3363 3364 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3365 window = window->PreviousWindow(fCurrentWorkspace)) { 3366 if (!window->IsHidden() && window->IsNormal() 3367 && window->ServerWindow()->ClientTeam() == team) { 3368 ActivateWindow(window); 3369 return B_OK; 3370 } 3371 } 3372 3373 // search for an unhidden window to give focus to 3374 3375 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3376 window = window->NextWindow(kAllWindowList)) { 3377 // if window is a normal window of the team, and not hidden, 3378 // we've found our target 3379 if (!window->IsHidden() && window->IsNormal() 3380 && window->ServerWindow()->ClientTeam() == team) { 3381 ActivateWindow(window); 3382 return B_OK; 3383 } 3384 } 3385 3386 // TODO: we cannot maximize minimized windows here (with the window lock 3387 // write locked). To work-around this, we could forward the request to 3388 // the ServerApp of this team - it maintains its own window list, and can 3389 // therefore call ActivateWindow() without holding the window lock. 3390 return B_BAD_VALUE; 3391 } 3392 3393 3394 void 3395 Desktop::_SetCurrentWorkspaceConfiguration() 3396 { 3397 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3398 3399 status_t status = fDirectScreenLock.LockWithTimeout(1000000L); 3400 if (status != B_OK) { 3401 // The application having the direct screen lock didn't give it up in 3402 // time, make it crash 3403 syslog(LOG_ERR, "Team %" B_PRId32 " did not give up its direct screen " 3404 "lock.\n", fDirectScreenTeam); 3405 3406 debug_thread(fDirectScreenTeam); 3407 fDirectScreenTeam = -1; 3408 } else 3409 fDirectScreenLock.Unlock(); 3410 3411 AutoWriteLocker _(fScreenLock); 3412 3413 uint32 changedScreens; 3414 fVirtualScreen.SetConfiguration(*this, 3415 fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(), 3416 &changedScreens); 3417 3418 for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) { 3419 if ((changedScreens & (1 << i)) != 0) 3420 _ScreenChanged(fVirtualScreen.ScreenAt(i)); 3421 } 3422 } 3423 3424 3425 /*! Changes the current workspace to the one specified by \a index. 3426 You must hold the all window lock when calling this method. 3427 */ 3428 void 3429 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow) 3430 { 3431 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3432 3433 int32 previousIndex = fCurrentWorkspace; 3434 rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color(); 3435 bool movedMouseEventWindow = false; 3436 Window* movedWindow = NULL; 3437 if (moveFocusWindow) { 3438 if (fMouseEventWindow != NULL) 3439 movedWindow = fMouseEventWindow; 3440 else 3441 movedWindow = FocusWindow(); 3442 } 3443 3444 if (movedWindow != NULL) { 3445 if (movedWindow->IsNormal()) { 3446 if (!movedWindow->InWorkspace(index)) { 3447 // The window currently being dragged will follow us to this 3448 // workspace if it's not already on it. 3449 // But only normal windows are following 3450 uint32 oldWorkspaces = movedWindow->Workspaces(); 3451 3452 WindowStack* stack = movedWindow->GetWindowStack(); 3453 if (stack != NULL) { 3454 for (int32 s = 0; s < stack->CountWindows(); s++) { 3455 Window* stackWindow = stack->LayerOrder().ItemAt(s); 3456 3457 _Windows(previousIndex).RemoveWindow(stackWindow); 3458 _Windows(index).AddWindow(stackWindow, 3459 stackWindow->Frontmost( 3460 _Windows(index).FirstWindow(), index)); 3461 3462 // send B_WORKSPACES_CHANGED message 3463 stackWindow->WorkspacesChanged(oldWorkspaces, 3464 stackWindow->Workspaces()); 3465 } 3466 } 3467 // TODO: subset windows will always flicker this way 3468 3469 movedMouseEventWindow = true; 3470 3471 NotifyWindowWorkspacesChanged(movedWindow, 3472 movedWindow->Workspaces()); 3473 } else { 3474 // make sure it's frontmost 3475 _Windows(index).RemoveWindow(movedWindow); 3476 _Windows(index).AddWindow(movedWindow, 3477 movedWindow->Frontmost(_Windows(index).FirstWindow(), 3478 index)); 3479 } 3480 } 3481 3482 movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop(); 3483 } 3484 3485 if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex)) 3486 fLastWorkspaceFocus[previousIndex] = FocusWindow(); 3487 else 3488 fLastWorkspaceFocus[previousIndex] = NULL; 3489 3490 // build region of windows that are no longer visible in the new workspace 3491 3492 BRegion dirty; 3493 3494 for (Window* window = CurrentWindows().FirstWindow(); 3495 window != NULL; window = window->NextWindow(previousIndex)) { 3496 // store current position in Workspace anchor 3497 window->Anchor(previousIndex).position = window->Frame().LeftTop(); 3498 3499 if (!window->IsHidden() 3500 && window->ServerWindow()->IsDirectlyAccessing()) 3501 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3502 3503 window->WorkspaceActivated(previousIndex, false); 3504 3505 if (window->InWorkspace(index)) 3506 continue; 3507 3508 if (!window->IsHidden()) { 3509 // this window will no longer be visible 3510 dirty.Include(&window->VisibleRegion()); 3511 } 3512 3513 window->SetCurrentWorkspace(-1); 3514 } 3515 3516 fPreviousWorkspace = fCurrentWorkspace; 3517 fCurrentWorkspace = index; 3518 3519 // Change the display modes, if needed 3520 _SetCurrentWorkspaceConfiguration(); 3521 3522 // Show windows, and include them in the changed region - but only 3523 // those that were not visible before (or whose position changed) 3524 3525 WindowList windows(kWorkingList); 3526 BList previousRegions; 3527 3528 for (Window* window = _Windows(index).FirstWindow(); 3529 window != NULL; window = window->NextWindow(index)) { 3530 BPoint position = window->Anchor(index).position; 3531 3532 window->SetCurrentWorkspace(index); 3533 3534 if (window->IsHidden()) 3535 continue; 3536 3537 if (position == kInvalidWindowPosition) { 3538 // if you enter a workspace for the first time, the position 3539 // of the window in the previous workspace is adopted 3540 position = window->Frame().LeftTop(); 3541 // TODO: make sure the window is still on-screen if it 3542 // was before! 3543 } 3544 3545 if (!window->InWorkspace(previousIndex)) { 3546 // This window was not visible before, make sure its frame 3547 // is up-to-date 3548 if (window->Frame().LeftTop() != position) { 3549 BPoint offset = position - window->Frame().LeftTop(); 3550 window->MoveBy((int32)offset.x, (int32)offset.y); 3551 } 3552 continue; 3553 } 3554 3555 if (window->Frame().LeftTop() != position) { 3556 // the window was visible before, but its on-screen location changed 3557 BPoint offset = position - window->Frame().LeftTop(); 3558 MoveWindowBy(window, offset.x, offset.y); 3559 // TODO: be a bit smarter than this... 3560 } else { 3561 // We need to remember the previous visible region of the 3562 // window if they changed their order 3563 BRegion* region = new (std::nothrow) 3564 BRegion(window->VisibleRegion()); 3565 if (region != NULL) { 3566 if (previousRegions.AddItem(region)) 3567 windows.AddWindow(window); 3568 else 3569 delete region; 3570 } 3571 } 3572 } 3573 3574 _UpdateFronts(false); 3575 _UpdateFloating(previousIndex, index, 3576 movedMouseEventWindow ? movedWindow : NULL); 3577 3578 BRegion stillAvailableOnScreen; 3579 _RebuildClippingForAllWindows(stillAvailableOnScreen); 3580 _SetBackground(stillAvailableOnScreen); 3581 3582 for (Window* window = _Windows(index).FirstWindow(); window != NULL; 3583 window = window->NextWindow(index)) { 3584 // send B_WORKSPACE_ACTIVATED message 3585 window->WorkspaceActivated(index, true); 3586 3587 if (!window->IsHidden() 3588 && window->ServerWindow()->HasDirectFrameBufferAccess()) { 3589 window->ServerWindow()->HandleDirectConnection( 3590 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 3591 } 3592 3593 if (window->InWorkspace(previousIndex) || window->IsHidden() 3594 || (window == movedWindow && movedWindow->IsNormal()) 3595 || (!window->IsNormal() 3596 && window->HasInSubset(movedWindow))) { 3597 // This window was visible before, and is already handled in the 3598 // above loop 3599 continue; 3600 } 3601 3602 dirty.Include(&window->VisibleRegion()); 3603 } 3604 3605 // Catch order changes in the new workspaces window list 3606 int32 i = 0; 3607 for (Window* window = windows.FirstWindow(); window != NULL; 3608 window = window->NextWindow(kWorkingList), i++) { 3609 BRegion* region = (BRegion*)previousRegions.ItemAt(i); 3610 region->ExclusiveInclude(&window->VisibleRegion()); 3611 dirty.Include(region); 3612 delete region; 3613 } 3614 3615 // Set new focus, but keep focus to a floating window if still visible 3616 if (movedWindow != NULL) 3617 SetFocusWindow(movedWindow); 3618 else if (!_Windows(index).HasWindow(FocusWindow()) 3619 || (FocusWindow() != NULL && !FocusWindow()->IsFloating())) 3620 SetFocusWindow(fLastWorkspaceFocus[index]); 3621 3622 _WindowChanged(NULL); 3623 MarkDirty(dirty); 3624 3625 #if 0 3626 // Show the dirty regions of this workspace switch 3627 if (GetDrawingEngine()->LockParallelAccess()) { 3628 GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0}); 3629 GetDrawingEngine()->UnlockParallelAccess(); 3630 snooze(100000); 3631 } 3632 #endif 3633 3634 if (previousColor != fWorkspaces[fCurrentWorkspace].Color()) 3635 RedrawBackground(); 3636 } 3637