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