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 inputServerPath; 2377 if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, &inputServerPath) == B_OK 2378 && inputServerPath.Append("input_server") == B_OK) { 2379 entry.SetTo(inputServerPath.Path()); 2380 } else 2381 entry.SetTo("/system/servers/input_server"); 2382 entry_ref ref; 2383 status_t entryStatus = entry.GetRef(&ref); 2384 if (entryStatus == B_OK) 2385 entryStatus = roster.Launch(&ref); 2386 if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) { 2387 syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n", 2388 strerror(status)); 2389 return; 2390 } 2391 2392 syslog(LOG_ERR, "Failed to launch the input server: %s!\n", 2393 strerror(entryStatus)); 2394 } 2395 2396 2397 void 2398 Desktop::_GetLooperName(char* name, size_t length) 2399 { 2400 snprintf(name, length, "d:%d:%s", fUserID, 2401 fTargetScreen == NULL ? "baron" : fTargetScreen); 2402 } 2403 2404 2405 void 2406 Desktop::_PrepareQuit() 2407 { 2408 // let's kill all remaining applications 2409 2410 fApplicationsLock.Lock(); 2411 2412 int32 count = fApplications.CountItems(); 2413 for (int32 i = 0; i < count; i++) { 2414 ServerApp *app = fApplications.ItemAt(i); 2415 team_id clientTeam = app->ClientTeam(); 2416 2417 app->Quit(); 2418 kill_team(clientTeam); 2419 } 2420 2421 // wait for the last app to die 2422 if (count > 0) { 2423 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 2424 250000); 2425 } 2426 2427 fApplicationsLock.Unlock(); 2428 } 2429 2430 2431 void 2432 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) 2433 { 2434 switch (code) { 2435 case AS_CREATE_APP: 2436 { 2437 // Create the ServerApp to node monitor a new BApplication 2438 2439 // Attached data: 2440 // 1) port_id - receiver port of a regular app 2441 // 2) port_id - client looper port - for sending messages to the 2442 // client 2443 // 2) team_id - app's team ID 2444 // 3) int32 - handler token of the regular app 2445 // 4) char * - signature of the regular app 2446 2447 // Find the necessary data 2448 team_id clientTeamID = -1; 2449 port_id clientLooperPort = -1; 2450 port_id clientReplyPort = -1; 2451 int32 htoken = B_NULL_TOKEN; 2452 char* appSignature = NULL; 2453 2454 link.Read<port_id>(&clientReplyPort); 2455 link.Read<port_id>(&clientLooperPort); 2456 link.Read<team_id>(&clientTeamID); 2457 link.Read<int32>(&htoken); 2458 if (link.ReadString(&appSignature) != B_OK) 2459 break; 2460 2461 ServerApp* app = new ServerApp(this, clientReplyPort, 2462 clientLooperPort, clientTeamID, htoken, appSignature); 2463 if (app->InitCheck() == B_OK 2464 && app->Run()) { 2465 // add the new ServerApp to the known list of ServerApps 2466 fApplicationsLock.Lock(); 2467 fApplications.AddItem(app); 2468 fApplicationsLock.Unlock(); 2469 } else { 2470 delete app; 2471 2472 // if everything went well, ServerApp::Run() will notify 2473 // the client - but since it didn't, we do it here 2474 BPrivate::LinkSender reply(clientReplyPort); 2475 reply.StartMessage(B_ERROR); 2476 reply.Flush(); 2477 } 2478 2479 // This is necessary because BPortLink::ReadString allocates memory 2480 free(appSignature); 2481 break; 2482 } 2483 2484 case AS_DELETE_APP: 2485 { 2486 // Delete a ServerApp. Received only from the respective ServerApp 2487 // when a BApplication asks it to quit. 2488 2489 // Attached Data: 2490 // 1) thread_id - thread ID of the ServerApp to be deleted 2491 2492 thread_id thread = -1; 2493 if (link.Read<thread_id>(&thread) < B_OK) 2494 break; 2495 2496 fApplicationsLock.Lock(); 2497 2498 // Run through the list of apps and nuke the proper one 2499 2500 int32 count = fApplications.CountItems(); 2501 ServerApp* removeApp = NULL; 2502 2503 for (int32 i = 0; i < count; i++) { 2504 ServerApp* app = fApplications.ItemAt(i); 2505 2506 if (app->Thread() == thread) { 2507 fApplications.RemoveItemAt(i); 2508 removeApp = app; 2509 break; 2510 } 2511 } 2512 2513 fApplicationsLock.Unlock(); 2514 2515 if (removeApp != NULL) 2516 removeApp->Quit(fShutdownSemaphore); 2517 2518 if (fQuitting && count <= 1) { 2519 // wait for the last app to die 2520 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, 2521 B_RELATIVE_TIMEOUT, 500000); 2522 PostMessage(kMsgQuitLooper); 2523 } 2524 break; 2525 } 2526 2527 case AS_ACTIVATE_APP: 2528 { 2529 // Someone is requesting to activation of a certain app. 2530 2531 // Attached data: 2532 // 1) port_id reply port 2533 // 2) team_id team 2534 2535 status_t status; 2536 2537 // get the parameters 2538 port_id replyPort; 2539 team_id team; 2540 if (link.Read(&replyPort) == B_OK 2541 && link.Read(&team) == B_OK) 2542 status = _ActivateApp(team); 2543 else 2544 status = B_ERROR; 2545 2546 // send the reply 2547 BPrivate::PortLink replyLink(replyPort); 2548 replyLink.StartMessage(status); 2549 replyLink.Flush(); 2550 break; 2551 } 2552 2553 case AS_APP_CRASHED: 2554 case AS_DUMP_ALLOCATOR: 2555 case AS_DUMP_BITMAPS: 2556 { 2557 BAutolock locker(fApplicationsLock); 2558 2559 team_id team; 2560 if (link.Read(&team) != B_OK) 2561 break; 2562 2563 for (int32 i = 0; i < fApplications.CountItems(); i++) { 2564 ServerApp* app = fApplications.ItemAt(i); 2565 2566 if (app->ClientTeam() == team) 2567 app->PostMessage(code); 2568 } 2569 break; 2570 } 2571 2572 case AS_EVENT_STREAM_CLOSED: 2573 _LaunchInputServer(); 2574 break; 2575 2576 case B_QUIT_REQUESTED: 2577 // We've been asked to quit, so (for now) broadcast to all 2578 // test apps to quit. This situation will occur only when the 2579 // server is compiled as a regular Be application. 2580 2581 fApplicationsLock.Lock(); 2582 fShutdownSemaphore = create_sem(0, "desktop shutdown"); 2583 fShutdownCount = fApplications.CountItems(); 2584 fApplicationsLock.Unlock(); 2585 2586 fQuitting = true; 2587 BroadcastToAllApps(AS_QUIT_APP); 2588 2589 // We now need to process the remaining AS_DELETE_APP messages and 2590 // wait for the kMsgShutdownServer message. 2591 // If an application does not quit as asked, the picasso thread 2592 // will send us this message in 2-3 seconds. 2593 2594 // if there are no apps to quit, shutdown directly 2595 if (fShutdownCount == 0) 2596 PostMessage(kMsgQuitLooper); 2597 break; 2598 2599 case AS_ACTIVATE_WORKSPACE: 2600 { 2601 int32 index; 2602 link.Read<int32>(&index); 2603 if (index == -1) 2604 index = fPreviousWorkspace; 2605 2606 bool moveFocusWindow; 2607 link.Read<bool>(&moveFocusWindow); 2608 2609 SetWorkspace(index, moveFocusWindow); 2610 break; 2611 } 2612 2613 case AS_TALK_TO_DESKTOP_LISTENER: 2614 { 2615 port_id clientReplyPort; 2616 if (link.Read<port_id>(&clientReplyPort) != B_OK) 2617 break; 2618 2619 BPrivate::LinkSender reply(clientReplyPort); 2620 AutoWriteLocker locker(fWindowLock); 2621 if (MessageForListener(NULL, link, reply) != true) { 2622 // unhandled message, at least send an error if needed 2623 if (link.NeedsReply()) { 2624 reply.StartMessage(B_ERROR); 2625 reply.Flush(); 2626 } 2627 } 2628 break; 2629 } 2630 2631 // ToDo: Remove this again. It is a message sent by the 2632 // invalidate_on_exit kernel debugger add-on to trigger a redraw 2633 // after exiting a kernel debugger session. 2634 case 'KDLE': 2635 { 2636 BRegion dirty; 2637 dirty.Include(fVirtualScreen.Frame()); 2638 MarkDirty(dirty); 2639 break; 2640 } 2641 2642 default: 2643 printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", 2644 code); 2645 2646 if (link.NeedsReply()) { 2647 // the client is now blocking and waiting for a reply! 2648 fLink.StartMessage(B_ERROR); 2649 fLink.Flush(); 2650 } 2651 break; 2652 } 2653 } 2654 2655 2656 WindowList& 2657 Desktop::CurrentWindows() 2658 { 2659 return fWorkspaces[fCurrentWorkspace].Windows(); 2660 } 2661 2662 2663 WindowList& 2664 Desktop::AllWindows() 2665 { 2666 return fAllWindows; 2667 } 2668 2669 2670 Window* 2671 Desktop::WindowForClientLooperPort(port_id port) 2672 { 2673 ASSERT_MULTI_LOCKED(fWindowLock); 2674 2675 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 2676 window = window->NextWindow(kAllWindowList)) { 2677 if (window->ServerWindow()->ClientLooperPort() == port) 2678 return window; 2679 } 2680 return NULL; 2681 } 2682 2683 2684 WindowList& 2685 Desktop::_Windows(int32 index) 2686 { 2687 return fWorkspaces[index].Windows(); 2688 } 2689 2690 2691 void 2692 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace, 2693 Window* mouseEventWindow) 2694 { 2695 if (previousWorkspace == -1) 2696 previousWorkspace = fCurrentWorkspace; 2697 if (nextWorkspace == -1) 2698 nextWorkspace = previousWorkspace; 2699 2700 for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL; 2701 floating = floating->NextWindow(kSubsetList)) { 2702 // we only care about app/subset floating windows 2703 if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL 2704 && floating->Feel() != B_FLOATING_APP_WINDOW_FEEL) 2705 continue; 2706 2707 if (fFront != NULL && fFront->IsNormal() 2708 && floating->HasInSubset(fFront)) { 2709 // is now visible 2710 if (_Windows(previousWorkspace).HasWindow(floating) 2711 && previousWorkspace != nextWorkspace 2712 && !floating->InSubsetWorkspace(previousWorkspace)) { 2713 // but no longer on the previous workspace 2714 _Windows(previousWorkspace).RemoveWindow(floating); 2715 floating->SetCurrentWorkspace(-1); 2716 } 2717 2718 if (!_Windows(nextWorkspace).HasWindow(floating)) { 2719 // but wasn't before 2720 _Windows(nextWorkspace).AddWindow(floating, 2721 floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), 2722 nextWorkspace)); 2723 floating->SetCurrentWorkspace(nextWorkspace); 2724 if (mouseEventWindow != fFront) 2725 _ShowWindow(floating); 2726 2727 // TODO: put the floating last in the floating window list to 2728 // preserve the on screen window order 2729 } 2730 } else if (_Windows(previousWorkspace).HasWindow(floating) 2731 && !floating->InSubsetWorkspace(previousWorkspace)) { 2732 // was visible, but is no longer 2733 2734 _Windows(previousWorkspace).RemoveWindow(floating); 2735 floating->SetCurrentWorkspace(-1); 2736 _HideWindow(floating); 2737 2738 if (FocusWindow() == floating) 2739 SetFocusWindow(); 2740 } 2741 } 2742 } 2743 2744 2745 /*! Search the visible windows for a valid back window 2746 (only desktop windows can't be back windows) 2747 */ 2748 void 2749 Desktop::_UpdateBack() 2750 { 2751 fBack = NULL; 2752 2753 for (Window* window = CurrentWindows().FirstWindow(); window != NULL; 2754 window = window->NextWindow(fCurrentWorkspace)) { 2755 if (window->IsHidden() || window->Feel() == kDesktopWindowFeel) 2756 continue; 2757 2758 fBack = window; 2759 break; 2760 } 2761 } 2762 2763 2764 /*! Search the visible windows for a valid front window 2765 (only normal and modal windows can be front windows) 2766 2767 The only place where you don't want to update floating windows is 2768 during a workspace change - because then you'll call _UpdateFloating() 2769 yourself. 2770 */ 2771 void 2772 Desktop::_UpdateFront(bool updateFloating) 2773 { 2774 fFront = NULL; 2775 2776 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 2777 window = window->PreviousWindow(fCurrentWorkspace)) { 2778 if (window->IsHidden() || window->IsFloating() 2779 || !window->SupportsFront()) 2780 continue; 2781 2782 fFront = window; 2783 break; 2784 } 2785 2786 if (updateFloating) 2787 _UpdateFloating(); 2788 } 2789 2790 2791 void 2792 Desktop::_UpdateFronts(bool updateFloating) 2793 { 2794 _UpdateBack(); 2795 _UpdateFront(updateFloating); 2796 } 2797 2798 2799 bool 2800 Desktop::_WindowHasModal(Window* window) 2801 { 2802 if (window == NULL) 2803 return false; 2804 2805 for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL; 2806 modal = modal->NextWindow(kSubsetList)) { 2807 // only visible modal windows count 2808 if (!modal->IsModal() || modal->IsHidden()) 2809 continue; 2810 2811 if (modal->HasInSubset(window)) 2812 return true; 2813 } 2814 2815 return false; 2816 } 2817 2818 2819 /*! You must at least hold a single window lock when calling this method. 2820 */ 2821 void 2822 Desktop::_WindowChanged(Window* window) 2823 { 2824 ASSERT_MULTI_LOCKED(fWindowLock); 2825 2826 BAutolock _(fWorkspacesLock); 2827 2828 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 2829 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 2830 view->WindowChanged(window); 2831 } 2832 } 2833 2834 2835 /*! You must at least hold a single window lock when calling this method. 2836 */ 2837 void 2838 Desktop::_WindowRemoved(Window* window) 2839 { 2840 ASSERT_MULTI_LOCKED(fWindowLock); 2841 2842 BAutolock _(fWorkspacesLock); 2843 2844 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 2845 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 2846 view->WindowRemoved(window); 2847 } 2848 } 2849 2850 2851 /*! Shows the window on the screen - it does this independently of the 2852 Window::IsHidden() state. 2853 */ 2854 void 2855 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows) 2856 { 2857 BRegion background; 2858 _RebuildClippingForAllWindows(background); 2859 _SetBackground(background); 2860 _WindowChanged(window); 2861 2862 BRegion dirty(window->VisibleRegion()); 2863 2864 if (!affectsOtherWindows) { 2865 // everything that is now visible in the 2866 // window needs a redraw, but other windows 2867 // are not affected, we can call ProcessDirtyRegion() 2868 // of the window, and don't have to use MarkDirty() 2869 window->ProcessDirtyRegion(dirty); 2870 } else 2871 MarkDirty(dirty); 2872 2873 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 2874 window->ServerWindow()->HandleDirectConnection( 2875 B_DIRECT_START | B_BUFFER_RESET); 2876 } 2877 } 2878 2879 2880 /*! Hides the window from the screen - it does this independently of the 2881 Window::IsHidden() state. 2882 */ 2883 void 2884 Desktop::_HideWindow(Window* window) 2885 { 2886 if (window->ServerWindow()->IsDirectlyAccessing()) 2887 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 2888 2889 // after rebuilding the clipping, 2890 // this window will not have a visible 2891 // region anymore, so we need to remember 2892 // it now 2893 // (actually that's not true, since 2894 // hidden windows are excluded from the 2895 // clipping calculation, but anyways) 2896 BRegion dirty(window->VisibleRegion()); 2897 2898 BRegion background; 2899 _RebuildClippingForAllWindows(background); 2900 _SetBackground(background); 2901 _WindowChanged(window); 2902 2903 MarkDirty(dirty); 2904 } 2905 2906 2907 /*! Updates the workspaces of all subset windows with regard to the 2908 specifed window. 2909 If newIndex is not -1, it will move all subset windows that belong to 2910 the specifed window to the new workspace; this form is only called by 2911 SetWorkspace(). 2912 */ 2913 void 2914 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex, 2915 int32 newIndex) 2916 { 2917 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, 2918 window->Title())); 2919 2920 // if the window is hidden, the subset windows are up-to-date already 2921 if (!window->IsNormal() || window->IsHidden()) 2922 return; 2923 2924 for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL; 2925 subset = subset->NextWindow(kSubsetList)) { 2926 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL 2927 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) { 2928 // These windows are always visible on all workspaces, 2929 // no need to update them. 2930 continue; 2931 } 2932 2933 if (subset->IsFloating()) { 2934 // Floating windows are inserted and removed to the current 2935 // workspace as the need arises - they are not handled here 2936 // but in _UpdateFront() 2937 continue; 2938 } 2939 2940 if (subset->HasInSubset(window)) { 2941 // adopt the workspace change 2942 SetWindowWorkspaces(subset, subset->SubsetWorkspaces()); 2943 } 2944 } 2945 } 2946 2947 2948 /*! \brief Adds or removes the window to or from the workspaces it's on. 2949 */ 2950 void 2951 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces, 2952 uint32 newWorkspaces) 2953 { 2954 if (oldWorkspaces == newWorkspaces) 2955 return; 2956 2957 // apply changes to the workspaces' window lists 2958 2959 LockAllWindows(); 2960 2961 // NOTE: we bypass the anchor-mechanism by intention when switching 2962 // the workspace programmatically. 2963 2964 for (int32 i = 0; i < kMaxWorkspaces; i++) { 2965 if (workspace_in_workspaces(i, oldWorkspaces)) { 2966 // window is on this workspace, is it anymore? 2967 if (!workspace_in_workspaces(i, newWorkspaces)) { 2968 _Windows(i).RemoveWindow(window); 2969 if (fLastWorkspaceFocus[i] == window) 2970 fLastWorkspaceFocus[i] = NULL; 2971 2972 if (i == CurrentWorkspace()) { 2973 // remove its appearance from the current workspace 2974 window->SetCurrentWorkspace(-1); 2975 2976 if (!window->IsHidden()) 2977 _HideWindow(window); 2978 } 2979 } 2980 } else { 2981 // window was not on this workspace, is it now? 2982 if (workspace_in_workspaces(i, newWorkspaces)) { 2983 _Windows(i).AddWindow(window, 2984 window->Frontmost(_Windows(i).FirstWindow(), i)); 2985 2986 if (i == CurrentWorkspace()) { 2987 // make the window visible in current workspace 2988 window->SetCurrentWorkspace(fCurrentWorkspace); 2989 2990 if (!window->IsHidden()) { 2991 // This only affects other windows if this window has 2992 // floating or modal windows that need to be shown as 2993 // well 2994 // TODO: take care of this 2995 _ShowWindow(window, FrontWindow() == window); 2996 } 2997 } 2998 } 2999 } 3000 } 3001 3002 // If the window is visible only on one workspace, we set it's current 3003 // position in that workspace (so that WorkspacesView will find us). 3004 int32 firstWorkspace = -1; 3005 for (int32 i = 0; i < kMaxWorkspaces; i++) { 3006 if ((newWorkspaces & (1L << i)) != 0) { 3007 if (firstWorkspace != -1) { 3008 firstWorkspace = -1; 3009 break; 3010 } 3011 firstWorkspace = i; 3012 } 3013 } 3014 if (firstWorkspace >= 0) 3015 window->Anchor(firstWorkspace).position = window->Frame().LeftTop(); 3016 3017 // take care about modals and floating windows 3018 _UpdateSubsetWorkspaces(window); 3019 3020 NotifyWindowWorkspacesChanged(window, newWorkspaces); 3021 3022 UnlockAllWindows(); 3023 } 3024 3025 3026 void 3027 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, 3028 bool wereVisible) 3029 { 3030 // we don't need to redraw what is currently 3031 // visible of the window 3032 BRegion clean; 3033 3034 for (Window* window = windows.FirstWindow(); window != NULL; 3035 window = window->NextWindow(list)) { 3036 if (wereVisible) 3037 clean.Include(&window->VisibleRegion()); 3038 3039 CurrentWindows().AddWindow(window, 3040 window->Frontmost(CurrentWindows().FirstWindow(), 3041 fCurrentWorkspace)); 3042 3043 _WindowChanged(window); 3044 } 3045 3046 BRegion dummy; 3047 _RebuildClippingForAllWindows(dummy); 3048 3049 // redraw what became visible of the window(s) 3050 3051 BRegion dirty; 3052 for (Window* window = windows.FirstWindow(); window != NULL; 3053 window = window->NextWindow(list)) { 3054 dirty.Include(&window->VisibleRegion()); 3055 } 3056 3057 dirty.Exclude(&clean); 3058 MarkDirty(dirty); 3059 3060 _UpdateFront(); 3061 3062 if (windows.FirstWindow() == fBack || fBack == NULL) 3063 _UpdateBack(); 3064 } 3065 3066 3067 /*! Returns the last focussed non-hidden subset window belonging to the 3068 specified \a window. 3069 */ 3070 Window* 3071 Desktop::_LastFocusSubsetWindow(Window* window) 3072 { 3073 if (window == NULL) 3074 return NULL; 3075 3076 for (Window* front = fFocusList.LastWindow(); front != NULL; 3077 front = front->PreviousWindow(kFocusList)) { 3078 if (front != window && !front->IsHidden() 3079 && window->HasInSubset(front)) 3080 return front; 3081 } 3082 3083 return NULL; 3084 } 3085 3086 3087 /*! \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse, 3088 and also updates the current view under the mouse. 3089 3090 This has only to be done in case the view changed without user interaction, 3091 ie. because of a workspace change or a closing window. 3092 */ 3093 void 3094 Desktop::_SendFakeMouseMoved(Window* window) 3095 { 3096 int32 viewToken = B_NULL_TOKEN; 3097 EventTarget* target = NULL; 3098 3099 LockAllWindows(); 3100 3101 if (window == NULL) 3102 window = MouseEventWindow(); 3103 if (window == NULL) 3104 window = WindowAt(fLastMousePosition); 3105 3106 if (window != NULL) { 3107 BMessage message; 3108 window->MouseMoved(&message, fLastMousePosition, &viewToken, true, 3109 true); 3110 3111 if (viewToken != B_NULL_TOKEN) 3112 target = &window->EventTarget(); 3113 } 3114 3115 if (viewToken != B_NULL_TOKEN) 3116 SetViewUnderMouse(window, viewToken); 3117 else { 3118 SetViewUnderMouse(NULL, B_NULL_TOKEN); 3119 SetCursor(NULL); 3120 } 3121 3122 UnlockAllWindows(); 3123 3124 if (target != NULL) 3125 EventDispatcher().SendFakeMouseMoved(*target, viewToken); 3126 } 3127 3128 3129 Screen* 3130 Desktop::_DetermineScreenFor(BRect frame) 3131 { 3132 AutoReadLocker _(fScreenLock); 3133 3134 // TODO: choose the screen depending on where most of the area is 3135 return fVirtualScreen.ScreenAt(0); 3136 } 3137 3138 3139 void 3140 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen) 3141 { 3142 // the available region on screen starts with the entire screen area 3143 // each window on the screen will take a portion from that area 3144 3145 // figure out what the entire screen area is 3146 stillAvailableOnScreen = fScreenRegion; 3147 3148 // set clipping of each window 3149 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3150 window = window->PreviousWindow(fCurrentWorkspace)) { 3151 if (!window->IsHidden()) { 3152 window->SetClipping(&stillAvailableOnScreen); 3153 window->SetScreen(_DetermineScreenFor(window->Frame())); 3154 3155 if (window->ServerWindow()->IsDirectlyAccessing()) { 3156 window->ServerWindow()->HandleDirectConnection( 3157 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 3158 } 3159 3160 // that windows region is not available on screen anymore 3161 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 3162 } 3163 } 3164 } 3165 3166 3167 void 3168 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion) 3169 { 3170 // send redraw messages to all windows intersecting the dirty region 3171 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3172 window = window->PreviousWindow(fCurrentWorkspace)) { 3173 if (!window->IsHidden() 3174 && newDirtyRegion.Intersects(window->VisibleRegion().Frame())) 3175 window->ProcessDirtyRegion(newDirtyRegion); 3176 } 3177 } 3178 3179 3180 void 3181 Desktop::_SetBackground(BRegion& background) 3182 { 3183 // NOTE: the drawing operation is caried out 3184 // in the clipping region rebuild, but it is 3185 // ok actually, because it also avoids trails on 3186 // moving windows 3187 3188 // remember the region not covered by any windows 3189 // and redraw the dirty background 3190 BRegion dirtyBackground(background); 3191 dirtyBackground.Exclude(&fBackgroundRegion); 3192 dirtyBackground.IntersectWith(&background); 3193 fBackgroundRegion = background; 3194 if (dirtyBackground.Frame().IsValid()) { 3195 if (GetDrawingEngine()->LockParallelAccess()) { 3196 GetDrawingEngine()->FillRegion(dirtyBackground, 3197 fWorkspaces[fCurrentWorkspace].Color()); 3198 3199 GetDrawingEngine()->UnlockParallelAccess(); 3200 } 3201 } 3202 } 3203 3204 3205 //! The all window lock must be held when calling this function. 3206 void 3207 Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow, 3208 BRegion& dirty) 3209 { 3210 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3211 if (!changedWindow->IsVisible() || dirty.CountRects() == 0) 3212 return; 3213 3214 // The following loop is pretty much a copy of 3215 // _RebuildClippingForAllWindows(), but will also 3216 // take care about restricting our dirty region. 3217 3218 // figure out what the entire screen area is 3219 BRegion stillAvailableOnScreen(fScreenRegion); 3220 3221 // set clipping of each window 3222 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3223 window = window->PreviousWindow(fCurrentWorkspace)) { 3224 if (!window->IsHidden()) { 3225 if (window == changedWindow) 3226 dirty.IntersectWith(&stillAvailableOnScreen); 3227 3228 window->SetClipping(&stillAvailableOnScreen); 3229 window->SetScreen(_DetermineScreenFor(window->Frame())); 3230 3231 if (window->ServerWindow()->IsDirectlyAccessing()) { 3232 window->ServerWindow()->HandleDirectConnection( 3233 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 3234 } 3235 3236 // that windows region is not available on screen anymore 3237 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 3238 } 3239 } 3240 3241 _SetBackground(stillAvailableOnScreen); 3242 _WindowChanged(changedWindow); 3243 3244 _TriggerWindowRedrawing(dirty); 3245 } 3246 3247 3248 //! Suspend all windows with direct access to the frame buffer 3249 void 3250 Desktop::_SuspendDirectFrameBufferAccess() 3251 { 3252 ASSERT_MULTI_LOCKED(fWindowLock); 3253 3254 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3255 window = window->NextWindow(kAllWindowList)) { 3256 if (window->ServerWindow()->IsDirectlyAccessing()) 3257 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3258 } 3259 } 3260 3261 3262 //! Resume all windows with direct access to the frame buffer 3263 void 3264 Desktop::_ResumeDirectFrameBufferAccess() 3265 { 3266 ASSERT_MULTI_LOCKED(fWindowLock); 3267 3268 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3269 window = window->NextWindow(kAllWindowList)) { 3270 if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace)) 3271 continue; 3272 3273 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 3274 window->ServerWindow()->HandleDirectConnection( 3275 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 3276 } 3277 } 3278 } 3279 3280 3281 void 3282 Desktop::_ScreenChanged(Screen* screen) 3283 { 3284 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3285 3286 // the entire screen is dirty, because we're actually 3287 // operating on an all new buffer in memory 3288 BRegion dirty(screen->Frame()); 3289 3290 // update our cached screen region 3291 fScreenRegion.Set(screen->Frame()); 3292 gInputManager->UpdateScreenBounds(screen->Frame()); 3293 3294 BRegion background; 3295 _RebuildClippingForAllWindows(background); 3296 3297 fBackgroundRegion.MakeEmpty(); 3298 // makes sure that the complete background is redrawn 3299 _SetBackground(background); 3300 3301 // figure out dirty region 3302 dirty.Exclude(&background); 3303 _TriggerWindowRedrawing(dirty); 3304 3305 // send B_SCREEN_CHANGED to windows on that screen 3306 BMessage update(B_SCREEN_CHANGED); 3307 update.AddInt64("when", real_time_clock_usecs()); 3308 update.AddRect("frame", screen->Frame()); 3309 update.AddInt32("mode", screen->ColorSpace()); 3310 3311 fVirtualScreen.UpdateFrame(); 3312 3313 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3314 window = window->NextWindow(kAllWindowList)) { 3315 if (window->Screen() == screen) 3316 window->ServerWindow()->ScreenChanged(&update); 3317 } 3318 } 3319 3320 3321 /*! \brief activate one of the app's windows. 3322 */ 3323 status_t 3324 Desktop::_ActivateApp(team_id team) 3325 { 3326 // search for an unhidden window in the current workspace 3327 3328 AutoWriteLocker locker(fWindowLock); 3329 3330 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3331 window = window->PreviousWindow(fCurrentWorkspace)) { 3332 if (!window->IsHidden() && window->IsNormal() 3333 && window->ServerWindow()->ClientTeam() == team) { 3334 ActivateWindow(window); 3335 return B_OK; 3336 } 3337 } 3338 3339 // search for an unhidden window to give focus to 3340 3341 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3342 window = window->NextWindow(kAllWindowList)) { 3343 // if window is a normal window of the team, and not hidden, 3344 // we've found our target 3345 if (!window->IsHidden() && window->IsNormal() 3346 && window->ServerWindow()->ClientTeam() == team) { 3347 ActivateWindow(window); 3348 return B_OK; 3349 } 3350 } 3351 3352 // TODO: we cannot maximize minimized windows here (with the window lock 3353 // write locked). To work-around this, we could forward the request to 3354 // the ServerApp of this team - it maintains its own window list, and can 3355 // therefore call ActivateWindow() without holding the window lock. 3356 return B_BAD_VALUE; 3357 } 3358 3359 3360 void 3361 Desktop::_SetCurrentWorkspaceConfiguration() 3362 { 3363 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3364 3365 status_t status = fDirectScreenLock.LockWithTimeout(1000000L); 3366 if (status != B_OK) { 3367 // The application having the direct screen lock didn't give it up in 3368 // time, make it crash 3369 syslog(LOG_ERR, "Team %ld did not give up its direct screen lock.\n", 3370 fDirectScreenTeam); 3371 3372 debug_thread(fDirectScreenTeam); 3373 fDirectScreenTeam = -1; 3374 } else 3375 fDirectScreenLock.Unlock(); 3376 3377 AutoWriteLocker _(fScreenLock); 3378 3379 uint32 changedScreens; 3380 fVirtualScreen.SetConfiguration(*this, 3381 fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(), 3382 &changedScreens); 3383 3384 for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) { 3385 if ((changedScreens & (1 << i)) != 0) 3386 _ScreenChanged(fVirtualScreen.ScreenAt(i)); 3387 } 3388 } 3389 3390 3391 /*! Changes the current workspace to the one specified by \a index. 3392 You must hold the all window lock when calling this method. 3393 */ 3394 void 3395 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow) 3396 { 3397 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3398 3399 int32 previousIndex = fCurrentWorkspace; 3400 rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color(); 3401 bool movedMouseEventWindow = false; 3402 Window* movedWindow = NULL; 3403 if (moveFocusWindow) { 3404 if (fMouseEventWindow != NULL) 3405 movedWindow = fMouseEventWindow; 3406 else 3407 movedWindow = FocusWindow(); 3408 } 3409 3410 if (movedWindow != NULL) { 3411 if (movedWindow->IsNormal()) { 3412 if (!movedWindow->InWorkspace(index)) { 3413 // The window currently being dragged will follow us to this 3414 // workspace if it's not already on it. 3415 // But only normal windows are following 3416 uint32 oldWorkspaces = movedWindow->Workspaces(); 3417 3418 _Windows(previousIndex).RemoveWindow(movedWindow); 3419 _Windows(index).AddWindow(movedWindow, 3420 movedWindow->Frontmost(_Windows(index).FirstWindow(), 3421 index)); 3422 3423 // TODO: subset windows will always flicker this way 3424 3425 movedMouseEventWindow = true; 3426 3427 // send B_WORKSPACES_CHANGED message 3428 movedWindow->WorkspacesChanged(oldWorkspaces, 3429 movedWindow->Workspaces()); 3430 NotifyWindowWorkspacesChanged(movedWindow, 3431 movedWindow->Workspaces()); 3432 } else { 3433 // make sure it's frontmost 3434 _Windows(index).RemoveWindow(movedWindow); 3435 _Windows(index).AddWindow(movedWindow, 3436 movedWindow->Frontmost(_Windows(index).FirstWindow(), 3437 index)); 3438 } 3439 } 3440 3441 movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop(); 3442 } 3443 3444 if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex)) 3445 fLastWorkspaceFocus[previousIndex] = FocusWindow(); 3446 else 3447 fLastWorkspaceFocus[previousIndex] = NULL; 3448 3449 // build region of windows that are no longer visible in the new workspace 3450 3451 BRegion dirty; 3452 3453 for (Window* window = CurrentWindows().FirstWindow(); 3454 window != NULL; window = window->NextWindow(previousIndex)) { 3455 // store current position in Workspace anchor 3456 window->Anchor(previousIndex).position = window->Frame().LeftTop(); 3457 3458 if (!window->IsHidden() 3459 && window->ServerWindow()->IsDirectlyAccessing()) 3460 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3461 3462 window->WorkspaceActivated(previousIndex, false); 3463 3464 if (window->InWorkspace(index)) 3465 continue; 3466 3467 if (!window->IsHidden()) { 3468 // this window will no longer be visible 3469 dirty.Include(&window->VisibleRegion()); 3470 } 3471 3472 window->SetCurrentWorkspace(-1); 3473 } 3474 3475 fPreviousWorkspace = fCurrentWorkspace; 3476 fCurrentWorkspace = index; 3477 3478 // Change the display modes, if needed 3479 _SetCurrentWorkspaceConfiguration(); 3480 3481 // Show windows, and include them in the changed region - but only 3482 // those that were not visible before (or whose position changed) 3483 3484 WindowList windows(kWorkingList); 3485 BList previousRegions; 3486 3487 for (Window* window = _Windows(index).FirstWindow(); 3488 window != NULL; window = window->NextWindow(index)) { 3489 BPoint position = window->Anchor(index).position; 3490 3491 window->SetCurrentWorkspace(index); 3492 3493 if (window->IsHidden()) 3494 continue; 3495 3496 if (position == kInvalidWindowPosition) { 3497 // if you enter a workspace for the first time, the position 3498 // of the window in the previous workspace is adopted 3499 position = window->Frame().LeftTop(); 3500 // TODO: make sure the window is still on-screen if it 3501 // was before! 3502 } 3503 3504 if (!window->InWorkspace(previousIndex)) { 3505 // This window was not visible before, make sure its frame 3506 // is up-to-date 3507 if (window->Frame().LeftTop() != position) { 3508 BPoint offset = position - window->Frame().LeftTop(); 3509 window->MoveBy((int32)offset.x, (int32)offset.y); 3510 } 3511 continue; 3512 } 3513 3514 if (window->Frame().LeftTop() != position) { 3515 // the window was visible before, but its on-screen location changed 3516 BPoint offset = position - window->Frame().LeftTop(); 3517 MoveWindowBy(window, offset.x, offset.y); 3518 // TODO: be a bit smarter than this... 3519 } else { 3520 // We need to remember the previous visible region of the 3521 // window if they changed their order 3522 BRegion* region = new (std::nothrow) 3523 BRegion(window->VisibleRegion()); 3524 if (region != NULL) { 3525 if (previousRegions.AddItem(region)) 3526 windows.AddWindow(window); 3527 else 3528 delete region; 3529 } 3530 } 3531 } 3532 3533 _UpdateFronts(false); 3534 _UpdateFloating(previousIndex, index, 3535 movedMouseEventWindow ? movedWindow : NULL); 3536 3537 BRegion stillAvailableOnScreen; 3538 _RebuildClippingForAllWindows(stillAvailableOnScreen); 3539 _SetBackground(stillAvailableOnScreen); 3540 3541 for (Window* window = _Windows(index).FirstWindow(); window != NULL; 3542 window = window->NextWindow(index)) { 3543 // send B_WORKSPACE_ACTIVATED message 3544 window->WorkspaceActivated(index, true); 3545 3546 if (!window->IsHidden() 3547 && window->ServerWindow()->HasDirectFrameBufferAccess()) { 3548 window->ServerWindow()->HandleDirectConnection( 3549 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 3550 } 3551 3552 if (window->InWorkspace(previousIndex) || window->IsHidden() 3553 || (window == movedWindow && movedWindow->IsNormal()) 3554 || (!window->IsNormal() 3555 && window->HasInSubset(movedWindow))) { 3556 // This window was visible before, and is already handled in the 3557 // above loop 3558 continue; 3559 } 3560 3561 dirty.Include(&window->VisibleRegion()); 3562 } 3563 3564 // Catch order changes in the new workspaces window list 3565 int32 i = 0; 3566 for (Window* window = windows.FirstWindow(); window != NULL; 3567 window = window->NextWindow(kWorkingList), i++) { 3568 BRegion* region = (BRegion*)previousRegions.ItemAt(i); 3569 region->ExclusiveInclude(&window->VisibleRegion()); 3570 dirty.Include(region); 3571 delete region; 3572 } 3573 3574 // Set new focus, but keep focus to a floating window if still visible 3575 if (movedWindow != NULL) 3576 SetFocusWindow(movedWindow); 3577 else if (!_Windows(index).HasWindow(FocusWindow()) 3578 || (FocusWindow() != NULL && !FocusWindow()->IsFloating())) 3579 SetFocusWindow(fLastWorkspaceFocus[index]); 3580 3581 _WindowChanged(NULL); 3582 MarkDirty(dirty); 3583 3584 #if 0 3585 // Show the dirty regions of this workspace switch 3586 if (GetDrawingEngine()->LockParallelAccess()) { 3587 GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0}); 3588 GetDrawingEngine()->UnlockParallelAccess(); 3589 snooze(100000); 3590 } 3591 #endif 3592 3593 if (previousColor != fWorkspaces[fCurrentWorkspace].Color()) 3594 RedrawBackground(); 3595 } 3596