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