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