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