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