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