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