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 if (!LockAllWindows()) 1033 return; 1034 1035 // TODO: should the "not in current workspace" be handled anyway? 1036 // (the code below would have to be changed then, though) 1037 if (window == BackWindow() 1038 || !window->InWorkspace(fCurrentWorkspace) 1039 || (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace))) { 1040 UnlockAllWindows(); 1041 return; 1042 } 1043 1044 // Is this a valid behindOf window? 1045 if (behindOf != NULL && window->HasInSubset(behindOf)) 1046 behindOf = NULL; 1047 1048 // what is currently visible of the window 1049 // might be dirty after the window is send to back 1050 BRegion dirty(window->VisibleRegion()); 1051 1052 // detach window and re-attach at desired position 1053 Window* backmost = window->Backmost(behindOf); 1054 1055 CurrentWindows().RemoveWindow(window); 1056 CurrentWindows().AddWindow(window, backmost 1057 ? backmost->NextWindow(fCurrentWorkspace) : BackWindow()); 1058 1059 BRegion dummy; 1060 _RebuildClippingForAllWindows(dummy); 1061 1062 // mark everything dirty that is no longer visible 1063 BRegion clean(window->VisibleRegion()); 1064 dirty.Exclude(&clean); 1065 MarkDirty(dirty); 1066 1067 _UpdateFronts(); 1068 if (fSettings->MouseMode() == B_FOCUS_FOLLOWS_MOUSE) 1069 SetFocusWindow(WindowAt(fLastMousePosition)); 1070 else if (fSettings->MouseMode() == B_NORMAL_MOUSE) 1071 SetFocusWindow(NULL); 1072 1073 bool sendFakeMouseMoved = false; 1074 if (FocusWindow() != window) 1075 sendFakeMouseMoved = true; 1076 1077 _WindowChanged(window); 1078 1079 InvokeSendWindowBehind(window, behindOf); 1080 1081 UnlockAllWindows(); 1082 1083 if (sendFakeMouseMoved) 1084 _SendFakeMouseMoved(); 1085 } 1086 1087 1088 void 1089 Desktop::ShowWindow(Window* window) 1090 { 1091 if (!window->IsHidden()) 1092 return; 1093 1094 AutoWriteLocker locker(fWindowLock); 1095 1096 window->SetHidden(false); 1097 fFocusList.AddWindow(window); 1098 1099 // If the window is on the current workspace, we'll show it. Special 1100 // handling for floating windows, as they can only be shown if their 1101 // subset is. 1102 if (window->InWorkspace(fCurrentWorkspace) 1103 || (window->IsFloating() && _LastFocusSubsetWindow(window) != NULL)) { 1104 _ShowWindow(window, true); 1105 _UpdateSubsetWorkspaces(window); 1106 ActivateWindow(window); 1107 } else { 1108 // then we don't need to send the fake mouse event either 1109 _WindowChanged(window); 1110 return; 1111 } 1112 1113 if (window->HasWorkspacesViews()) { 1114 // find workspaces views in view hierarchy 1115 BAutolock _(fWorkspacesLock); 1116 window->FindWorkspacesViews(fWorkspacesViews); 1117 } 1118 1119 // If the mouse cursor is directly over the newly visible window, 1120 // we'll send a fake mouse moved message to the window, so that 1121 // it knows the mouse is over it. 1122 1123 _SendFakeMouseMoved(window); 1124 1125 InvokeShowWindow(window); 1126 } 1127 1128 1129 void 1130 Desktop::HideWindow(Window* window) 1131 { 1132 if (window->IsHidden()) 1133 return; 1134 1135 if (!LockAllWindows()) 1136 return; 1137 1138 window->SetHidden(true); 1139 fFocusList.RemoveWindow(window); 1140 1141 if (fMouseEventWindow == window) { 1142 // Make its decorator lose the current mouse action 1143 BMessage message; 1144 int32 viewToken; 1145 window->MouseUp(&message, fLastMousePosition, &viewToken); 1146 1147 fMouseEventWindow = NULL; 1148 } 1149 1150 if (fLockedFocusWindow == window) { 1151 // Remove the focus lock so the focus can be changed below 1152 fLockedFocusWindow = NULL; 1153 } 1154 1155 if (window->InWorkspace(fCurrentWorkspace)) { 1156 _UpdateSubsetWorkspaces(window); 1157 _HideWindow(window); 1158 _UpdateFronts(); 1159 } else 1160 _WindowChanged(window); 1161 1162 if (FocusWindow() == window) 1163 SetFocusWindow(); 1164 1165 _WindowRemoved(window); 1166 1167 if (window->HasWorkspacesViews()) { 1168 // remove workspaces views from this window 1169 BObjectList<WorkspacesView> list(false); 1170 window->FindWorkspacesViews(list); 1171 1172 BAutolock _(fWorkspacesLock); 1173 1174 while (WorkspacesView* view = list.RemoveItemAt(0)) { 1175 fWorkspacesViews.RemoveItem(view); 1176 } 1177 } 1178 1179 InvokeHideWindow(window); 1180 1181 UnlockAllWindows(); 1182 1183 if (window == fWindowUnderMouse) 1184 _SendFakeMouseMoved(); 1185 } 1186 1187 1188 void 1189 Desktop::MinimizeWindow(Window* window, bool minimize) 1190 { 1191 if (!LockAllWindows()) 1192 return; 1193 1194 if (minimize && !window->IsHidden()) { 1195 HideWindow(window); 1196 window->SetMinimized(minimize); 1197 InvokeMinimizeWindow(window, minimize); 1198 } else if (!minimize && window->IsHidden()) { 1199 ActivateWindow(window); 1200 // this will unminimize the window for us 1201 InvokeMinimizeWindow(window, minimize); 1202 } 1203 1204 UnlockAllWindows(); 1205 } 1206 1207 1208 void 1209 Desktop::MoveWindowBy(Window* window, float x, float y, int32 workspace) 1210 { 1211 if (x == 0 && y == 0) 1212 return; 1213 1214 if (!LockAllWindows()) 1215 return; 1216 1217 if (workspace == -1) 1218 workspace = fCurrentWorkspace; 1219 if (!window->IsVisible() || workspace != fCurrentWorkspace) { 1220 if (workspace != fCurrentWorkspace) { 1221 // move the window on another workspace - this doesn't change it's 1222 // current position 1223 if (window->Anchor(workspace).position == kInvalidWindowPosition) 1224 window->Anchor(workspace).position = window->Frame().LeftTop(); 1225 1226 window->Anchor(workspace).position += BPoint(x, y); 1227 window->SetCurrentWorkspace(workspace); 1228 _WindowChanged(window); 1229 } else 1230 window->MoveBy((int32)x, (int32)y); 1231 1232 InvokeMoveWindow(window); 1233 UnlockAllWindows(); 1234 return; 1235 } 1236 1237 // the dirty region starts with the visible area of the window being moved 1238 BRegion newDirtyRegion(window->VisibleRegion()); 1239 1240 // stop direct frame buffer access 1241 bool direct = false; 1242 if (window->ServerWindow()->IsDirectlyAccessing()) { 1243 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 1244 direct = true; 1245 } 1246 1247 window->MoveBy((int32)x, (int32)y); 1248 1249 BRegion background; 1250 _RebuildClippingForAllWindows(background); 1251 1252 // construct the region that is possible to be blitted 1253 // to move the contents of the window 1254 BRegion copyRegion(window->VisibleRegion()); 1255 copyRegion.OffsetBy((int32)-x, (int32)-y); 1256 copyRegion.IntersectWith(&newDirtyRegion); 1257 // newDirtyRegion == the windows old visible region 1258 1259 // include the the new visible region of the window being 1260 // moved into the dirty region (for now) 1261 newDirtyRegion.Include(&window->VisibleRegion()); 1262 1263 // NOTE: Having all windows locked should prevent any 1264 // problems with locking the drawing engine here. 1265 if (GetDrawingEngine()->LockParallelAccess()) { 1266 GetDrawingEngine()->CopyRegion(©Region, (int32)x, (int32)y); 1267 GetDrawingEngine()->UnlockParallelAccess(); 1268 } 1269 1270 // in the dirty region, exclude the parts that we 1271 // could move by blitting 1272 copyRegion.OffsetBy((int32)x, (int32)y); 1273 newDirtyRegion.Exclude(©Region); 1274 1275 MarkDirty(newDirtyRegion); 1276 _SetBackground(background); 1277 _WindowChanged(window); 1278 1279 // resume direct frame buffer access 1280 if (direct) { 1281 // TODO: the clipping actually only changes when we move our window 1282 // off screen, or behind some other window 1283 window->ServerWindow()->HandleDirectConnection( 1284 B_DIRECT_START | B_BUFFER_MOVED | B_CLIPPING_MODIFIED); 1285 } 1286 1287 InvokeMoveWindow(window); 1288 1289 UnlockAllWindows(); 1290 } 1291 1292 1293 void 1294 Desktop::ResizeWindowBy(Window* window, float x, float y) 1295 { 1296 if (x == 0 && y == 0) 1297 return; 1298 1299 if (!LockAllWindows()) 1300 return; 1301 1302 if (!window->IsVisible()) { 1303 window->ResizeBy((int32)x, (int32)y, NULL); 1304 InvokeResizeWindow(window); 1305 UnlockAllWindows(); 1306 return; 1307 } 1308 1309 // the dirty region for the inside of the window is 1310 // constructed by the window itself in ResizeBy() 1311 BRegion newDirtyRegion; 1312 // track the dirty region outside the window in case 1313 // it is shrunk in "previouslyOccupiedRegion" 1314 BRegion previouslyOccupiedRegion(window->VisibleRegion()); 1315 1316 // stop direct frame buffer access 1317 bool direct = false; 1318 if (window->ServerWindow()->IsDirectlyAccessing()) { 1319 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 1320 direct = true; 1321 } 1322 1323 window->ResizeBy((int32)x, (int32)y, &newDirtyRegion); 1324 1325 BRegion background; 1326 _RebuildClippingForAllWindows(background); 1327 1328 // we just care for the region outside the window 1329 previouslyOccupiedRegion.Exclude(&window->VisibleRegion()); 1330 1331 // make sure the window cannot mark stuff dirty outside 1332 // its visible region... 1333 newDirtyRegion.IntersectWith(&window->VisibleRegion()); 1334 // ...because we do this outself 1335 newDirtyRegion.Include(&previouslyOccupiedRegion); 1336 1337 MarkDirty(newDirtyRegion); 1338 _SetBackground(background); 1339 _WindowChanged(window); 1340 1341 // resume direct frame buffer access 1342 if (direct) { 1343 window->ServerWindow()->HandleDirectConnection( 1344 B_DIRECT_START | B_BUFFER_RESIZED | B_CLIPPING_MODIFIED); 1345 } 1346 1347 InvokeResizeWindow(window); 1348 1349 UnlockAllWindows(); 1350 } 1351 1352 1353 bool 1354 Desktop::SetWindowTabLocation(Window* window, float location) 1355 { 1356 AutoWriteLocker _(fWindowLock); 1357 1358 BRegion dirty; 1359 bool changed = window->SetTabLocation(location, dirty); 1360 if (changed) 1361 _RebuildAndRedrawAfterWindowChange(window, dirty); 1362 1363 return changed; 1364 } 1365 1366 1367 bool 1368 Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings) 1369 { 1370 AutoWriteLocker _(fWindowLock); 1371 1372 BRegion dirty; 1373 bool changed = window->SetDecoratorSettings(settings, dirty); 1374 bool listenerChanged = InvokeSetDecoratorSettings(window, settings); 1375 if (changed || listenerChanged) 1376 _RebuildAndRedrawAfterWindowChange(window, dirty); 1377 1378 return changed; 1379 } 1380 1381 1382 void 1383 Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces) 1384 { 1385 LockAllWindows(); 1386 1387 if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE) 1388 workspaces = workspace_to_workspaces(CurrentWorkspace()); 1389 1390 uint32 oldWorkspaces = window->Workspaces(); 1391 1392 window->WorkspacesChanged(oldWorkspaces, workspaces); 1393 _ChangeWindowWorkspaces(window, oldWorkspaces, workspaces); 1394 1395 UnlockAllWindows(); 1396 } 1397 1398 1399 /*! \brief Adds the window to the desktop. 1400 At this point, the window is still hidden and must be shown explicetly 1401 via ShowWindow(). 1402 */ 1403 void 1404 Desktop::AddWindow(Window *window) 1405 { 1406 LockAllWindows(); 1407 1408 fAllWindows.AddWindow(window); 1409 if (!window->IsNormal()) 1410 fSubsetWindows.AddWindow(window); 1411 1412 if (window->IsNormal()) { 1413 if (window->Workspaces() == B_CURRENT_WORKSPACE) 1414 window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace())); 1415 } else { 1416 // subset windows are visible on all workspaces their subset is on 1417 window->SetWorkspaces(window->SubsetWorkspaces()); 1418 } 1419 1420 _ChangeWindowWorkspaces(window, 0, window->Workspaces()); 1421 1422 InvokeAddWindow(window); 1423 1424 UnlockAllWindows(); 1425 } 1426 1427 1428 void 1429 Desktop::RemoveWindow(Window *window) 1430 { 1431 LockAllWindows(); 1432 1433 if (!window->IsHidden()) 1434 HideWindow(window); 1435 1436 fAllWindows.RemoveWindow(window); 1437 if (!window->IsNormal()) 1438 fSubsetWindows.RemoveWindow(window); 1439 1440 _ChangeWindowWorkspaces(window, window->Workspaces(), 0); 1441 1442 InvokeRemoveWindow(window); 1443 1444 UnlockAllWindows(); 1445 1446 // make sure this window won't get any events anymore 1447 1448 EventDispatcher().RemoveTarget(window->EventTarget()); 1449 } 1450 1451 1452 bool 1453 Desktop::AddWindowToSubset(Window* subset, Window* window) 1454 { 1455 if (!subset->AddToSubset(window)) 1456 return false; 1457 1458 _ChangeWindowWorkspaces(subset, subset->Workspaces(), 1459 subset->SubsetWorkspaces()); 1460 return true; 1461 } 1462 1463 1464 void 1465 Desktop::RemoveWindowFromSubset(Window* subset, Window* window) 1466 { 1467 subset->RemoveFromSubset(window); 1468 _ChangeWindowWorkspaces(subset, subset->Workspaces(), 1469 subset->SubsetWorkspaces()); 1470 } 1471 1472 1473 void 1474 Desktop::FontsChanged(Window* window) 1475 { 1476 AutoWriteLocker _(fWindowLock); 1477 1478 BRegion dirty; 1479 window->FontsChanged(&dirty); 1480 1481 _RebuildAndRedrawAfterWindowChange(window, dirty); 1482 } 1483 1484 1485 void 1486 Desktop::SetWindowLook(Window* window, window_look newLook) 1487 { 1488 if (window->Look() == newLook) 1489 return; 1490 1491 AutoWriteLocker _(fWindowLock); 1492 1493 BRegion dirty; 1494 window->SetLook(newLook, &dirty); 1495 // TODO: test what happens when the window 1496 // finds out it needs to resize itself... 1497 1498 _RebuildAndRedrawAfterWindowChange(window, dirty); 1499 } 1500 1501 1502 void 1503 Desktop::SetWindowFeel(Window* window, window_feel newFeel) 1504 { 1505 if (window->Feel() == newFeel) 1506 return; 1507 1508 LockAllWindows(); 1509 1510 bool wasNormal = window->IsNormal(); 1511 1512 window->SetFeel(newFeel); 1513 1514 // move the window out of or into the subset window list as needed 1515 if (window->IsNormal() && !wasNormal) 1516 fSubsetWindows.RemoveWindow(window); 1517 else if (!window->IsNormal() && wasNormal) 1518 fSubsetWindows.AddWindow(window); 1519 1520 // A normal window that was once a floating or modal window will 1521 // adopt the window's current workspaces 1522 1523 if (!window->IsNormal()) { 1524 _ChangeWindowWorkspaces(window, window->Workspaces(), 1525 window->SubsetWorkspaces()); 1526 } 1527 1528 // make sure the window has the correct position in the window lists 1529 // (ie. all floating windows have to be on the top, ...) 1530 1531 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1532 if (!workspace_in_workspaces(i, window->Workspaces())) 1533 continue; 1534 1535 bool changed = false; 1536 BRegion visibleBefore; 1537 if (i == fCurrentWorkspace && window->IsVisible()) 1538 visibleBefore = window->VisibleRegion(); 1539 1540 Window* backmost = window->Backmost(_Windows(i).LastWindow(), i); 1541 if (backmost != NULL) { 1542 // check if the backmost window is really behind it 1543 Window* previous = window->PreviousWindow(i); 1544 while (previous != NULL) { 1545 if (previous == backmost) 1546 break; 1547 1548 previous = previous->PreviousWindow(i); 1549 } 1550 1551 if (previous == NULL) { 1552 // need to reinsert window before its backmost window 1553 _Windows(i).RemoveWindow(window); 1554 _Windows(i).AddWindow(window, backmost->NextWindow(i)); 1555 changed = true; 1556 } 1557 } 1558 1559 Window* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i); 1560 if (frontmost != NULL) { 1561 // check if the frontmost window is really in front of it 1562 Window* next = window->NextWindow(i); 1563 while (next != NULL) { 1564 if (next == frontmost) 1565 break; 1566 1567 next = next->NextWindow(i); 1568 } 1569 1570 if (next == NULL) { 1571 // need to reinsert window behind its frontmost window 1572 _Windows(i).RemoveWindow(window); 1573 _Windows(i).AddWindow(window, frontmost); 1574 changed = true; 1575 } 1576 } 1577 1578 if (i == fCurrentWorkspace && changed) { 1579 BRegion dummy; 1580 _RebuildClippingForAllWindows(dummy); 1581 1582 // mark everything dirty that is no longer visible, or 1583 // is now visible and wasn't before 1584 BRegion visibleAfter(window->VisibleRegion()); 1585 BRegion dirty(visibleAfter); 1586 dirty.Exclude(&visibleBefore); 1587 visibleBefore.Exclude(&visibleAfter); 1588 dirty.Include(&visibleBefore); 1589 1590 MarkDirty(dirty); 1591 } 1592 } 1593 1594 _UpdateFronts(); 1595 1596 if (window == FocusWindow() && !window->IsVisible()) 1597 SetFocusWindow(); 1598 1599 UnlockAllWindows(); 1600 } 1601 1602 1603 void 1604 Desktop::SetWindowFlags(Window *window, uint32 newFlags) 1605 { 1606 if (window->Flags() == newFlags) 1607 return; 1608 1609 AutoWriteLocker _(fWindowLock); 1610 1611 BRegion dirty; 1612 window->SetFlags(newFlags, &dirty); 1613 // TODO: test what happens when the window 1614 // finds out it needs to resize itself... 1615 1616 _RebuildAndRedrawAfterWindowChange(window, dirty); 1617 } 1618 1619 1620 void 1621 Desktop::SetWindowTitle(Window *window, const char* title) 1622 { 1623 AutoWriteLocker _(fWindowLock); 1624 1625 BRegion dirty; 1626 window->SetTitle(title, dirty); 1627 1628 _RebuildAndRedrawAfterWindowChange(window, dirty); 1629 } 1630 1631 1632 /*! Returns the window under the mouse cursor. 1633 You need to have acquired the All Windows lock when calling this method. 1634 */ 1635 Window* 1636 Desktop::WindowAt(BPoint where) 1637 { 1638 for (Window* window = CurrentWindows().LastWindow(); window; 1639 window = window->PreviousWindow(fCurrentWorkspace)) { 1640 if (window->IsVisible() && window->VisibleRegion().Contains(where)) 1641 return window; 1642 } 1643 1644 return NULL; 1645 } 1646 1647 1648 void 1649 Desktop::SetMouseEventWindow(Window* window) 1650 { 1651 fMouseEventWindow = window; 1652 } 1653 1654 1655 void 1656 Desktop::SetViewUnderMouse(const Window* window, int32 viewToken) 1657 { 1658 fWindowUnderMouse = window; 1659 fViewUnderMouse = viewToken; 1660 } 1661 1662 1663 int32 1664 Desktop::ViewUnderMouse(const Window* window) 1665 { 1666 if (window != NULL && fWindowUnderMouse == window) 1667 return fViewUnderMouse; 1668 1669 return B_NULL_TOKEN; 1670 } 1671 1672 1673 /*! Returns the current keyboard event target candidate - which is either the 1674 top-most window (in case it has the kAcceptKeyboardFocusFlag flag set), or 1675 the one having focus. 1676 The window lock must be held when calling this function. 1677 */ 1678 EventTarget* 1679 Desktop::KeyboardEventTarget() 1680 { 1681 // Get the top most non-hidden window 1682 Window* window = CurrentWindows().LastWindow(); 1683 while (window != NULL && window->IsHidden()) { 1684 window = window->PreviousWindow(fCurrentWorkspace); 1685 } 1686 1687 if (window != NULL && (window->Flags() & kAcceptKeyboardFocusFlag) != 0) 1688 return &window->EventTarget(); 1689 1690 if (FocusWindow() != NULL) 1691 return &FocusWindow()->EventTarget(); 1692 1693 return NULL; 1694 } 1695 1696 1697 /*! Tries to set the focus to the specified \a focus window. It will make sure, 1698 however, that the window actually can have focus. You are allowed to pass 1699 in a NULL pointer for \a focus. 1700 1701 Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both 1702 prevent it from getting focus. 1703 1704 In any case, this method makes sure that there is a focus window, if there 1705 is any window at all, that is. 1706 */ 1707 void 1708 Desktop::SetFocusWindow(Window* focus) 1709 { 1710 if (!LockAllWindows()) 1711 return; 1712 1713 // test for B_LOCK_WINDOW_FOCUS 1714 if (fLockedFocusWindow && focus != fLockedFocusWindow) { 1715 UnlockAllWindows(); 1716 return; 1717 } 1718 1719 bool hasModal = _WindowHasModal(focus); 1720 bool hasWindowScreen = false; 1721 1722 if (!hasModal && focus != NULL) { 1723 // Check whether or not a window screen is in front of the window 1724 // (if it has a modal, the right thing is done, anyway) 1725 Window* window = focus; 1726 while (true) { 1727 window = window->NextWindow(fCurrentWorkspace); 1728 if (window == NULL || window->Feel() == kWindowScreenFeel) 1729 break; 1730 } 1731 if (window != NULL) 1732 hasWindowScreen = true; 1733 } 1734 1735 if (focus == fFocus && focus != NULL && !focus->IsHidden() 1736 && (focus->Flags() & B_AVOID_FOCUS) == 0 1737 && !hasModal && !hasWindowScreen) { 1738 // the window that is supposed to get focus already has focus 1739 UnlockAllWindows(); 1740 return; 1741 } 1742 1743 uint32 list = fCurrentWorkspace; 1744 if (fSettings->FocusFollowsMouse()) 1745 list = kFocusList; 1746 1747 if (focus == NULL || hasModal || hasWindowScreen) { 1748 if (!fSettings->FocusFollowsMouse()) 1749 focus = CurrentWindows().LastWindow(); 1750 else 1751 focus = fFocusList.LastWindow(); 1752 } 1753 1754 // make sure no window is chosen that doesn't want focus or cannot have it 1755 while (focus != NULL 1756 && (!focus->InWorkspace(fCurrentWorkspace) 1757 || (focus->Flags() & B_AVOID_FOCUS) != 0 1758 || _WindowHasModal(focus) 1759 || focus->IsHidden())) { 1760 focus = focus->PreviousWindow(list); 1761 } 1762 1763 if (fFocus == focus) { 1764 // turns out the window that is supposed to get focus now already has it 1765 UnlockAllWindows(); 1766 return; 1767 } 1768 1769 team_id oldActiveApp = -1; 1770 team_id newActiveApp = -1; 1771 1772 if (fFocus != NULL) { 1773 fFocus->SetFocus(false); 1774 oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 1775 } 1776 1777 fFocus = focus; 1778 1779 if (fFocus != NULL) { 1780 fFocus->SetFocus(true); 1781 newActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 1782 1783 // move current focus to the end of the focus list 1784 fFocusList.RemoveWindow(fFocus); 1785 fFocusList.AddWindow(fFocus); 1786 } 1787 1788 if (newActiveApp == -1) { 1789 // make sure the cursor is visible 1790 HWInterface()->SetCursorVisible(true); 1791 } 1792 1793 UnlockAllWindows(); 1794 1795 // change the "active" app if appropriate 1796 if (oldActiveApp == newActiveApp) 1797 return; 1798 1799 BAutolock locker(fApplicationsLock); 1800 1801 for (int32 i = 0; i < fApplications.CountItems(); i++) { 1802 ServerApp* app = fApplications.ItemAt(i); 1803 1804 if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp) 1805 app->Activate(false); 1806 else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp) 1807 app->Activate(true); 1808 } 1809 } 1810 1811 1812 void 1813 Desktop::SetFocusLocked(const Window* window) 1814 { 1815 AutoWriteLocker _(fWindowLock); 1816 1817 if (window != NULL) { 1818 // Don't allow this to be set when no mouse buttons 1819 // are pressed. (BView::SetMouseEventMask() should only be called 1820 // from mouse hooks.) 1821 if (fLastMouseButtons == 0) 1822 return; 1823 } 1824 1825 fLockedFocusWindow = window; 1826 } 1827 1828 1829 Window* 1830 Desktop::FindWindowByClientToken(int32 token, team_id teamID) 1831 { 1832 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1833 window = window->NextWindow(kAllWindowList)) { 1834 if (window->ServerWindow()->ClientToken() == token 1835 && window->ServerWindow()->ClientTeam() == teamID) { 1836 return window; 1837 } 1838 } 1839 1840 return NULL; 1841 } 1842 1843 1844 ::EventTarget* 1845 Desktop::FindTarget(BMessenger& messenger) 1846 { 1847 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1848 window = window->NextWindow(kAllWindowList)) { 1849 if (window->EventTarget().Messenger() == messenger) 1850 return &window->EventTarget(); 1851 } 1852 1853 return NULL; 1854 } 1855 1856 1857 void 1858 Desktop::MarkDirty(BRegion& region) 1859 { 1860 if (region.CountRects() == 0) 1861 return; 1862 1863 if (LockAllWindows()) { 1864 // send redraw messages to all windows intersecting the dirty region 1865 _TriggerWindowRedrawing(region); 1866 1867 UnlockAllWindows(); 1868 } 1869 } 1870 1871 1872 void 1873 Desktop::Redraw() 1874 { 1875 BRegion dirty(fVirtualScreen.Frame()); 1876 MarkDirty(dirty); 1877 } 1878 1879 1880 /*! \brief Redraws the background (ie. the desktop window, if any). 1881 */ 1882 void 1883 Desktop::RedrawBackground() 1884 { 1885 LockAllWindows(); 1886 1887 BRegion redraw; 1888 1889 Window* window = CurrentWindows().FirstWindow(); 1890 if (window->Feel() == kDesktopWindowFeel) { 1891 redraw = window->VisibleContentRegion(); 1892 1893 // look for desktop background view, and update its background color 1894 // TODO: is there a better way to do this? 1895 View* view = window->TopView(); 1896 if (view != NULL) 1897 view = view->FirstChild(); 1898 1899 while (view) { 1900 if (view->IsDesktopBackground()) { 1901 view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color()); 1902 break; 1903 } 1904 view = view->NextSibling(); 1905 } 1906 1907 window->ProcessDirtyRegion(redraw); 1908 } else { 1909 redraw = BackgroundRegion(); 1910 fBackgroundRegion.MakeEmpty(); 1911 _SetBackground(redraw); 1912 } 1913 1914 _WindowChanged(NULL); 1915 // update workspaces view as well 1916 1917 UnlockAllWindows(); 1918 } 1919 1920 1921 void 1922 Desktop::ReloadDecor() 1923 { 1924 AutoWriteLocker _(fWindowLock); 1925 1926 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 1927 window = window->NextWindow(kAllWindowList)) { 1928 BRegion oldBorder; 1929 window->GetBorderRegion(&oldBorder); 1930 1931 window->ReloadDecor(); 1932 1933 BRegion border; 1934 window->GetBorderRegion(&border); 1935 1936 border.Include(&oldBorder); 1937 _RebuildAndRedrawAfterWindowChange(window, border); 1938 } 1939 1940 // TODO it is assumed all listeners are registered by one decor 1941 // unregister old listeners 1942 const DesktopListenerDLList& currentListeners = GetDesktopListenerList(); 1943 for (DesktopListener* listener = currentListeners.First(); 1944 listener != NULL; listener = currentListeners.GetNext(listener)) 1945 UnregisterListener(listener); 1946 // register new listeners 1947 const DesktopListenerList& newListeners 1948 = gDecorManager.GetDesktopListeners(); 1949 for (int i = 0; i < newListeners.CountItems(); i++) 1950 RegisterListener(newListeners.ItemAt(i)); 1951 } 1952 1953 1954 void 1955 Desktop::MinimizeApplication(team_id team) 1956 { 1957 AutoWriteLocker locker(fWindowLock); 1958 1959 // Just minimize all windows of that application 1960 1961 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1962 window = window->NextWindow(kAllWindowList)) { 1963 if (window->ServerWindow()->ClientTeam() != team) 1964 continue; 1965 1966 window->ServerWindow()->NotifyMinimize(true); 1967 } 1968 } 1969 1970 1971 void 1972 Desktop::BringApplicationToFront(team_id team) 1973 { 1974 AutoWriteLocker locker(fWindowLock); 1975 1976 // TODO: for now, just maximize all windows of that application 1977 // TODO: have the ability to lock the current workspace 1978 1979 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 1980 window = window->NextWindow(kAllWindowList)) { 1981 if (window->ServerWindow()->ClientTeam() != team) 1982 continue; 1983 1984 window->ServerWindow()->NotifyMinimize(false); 1985 } 1986 } 1987 1988 1989 void 1990 Desktop::WindowAction(int32 windowToken, int32 action) 1991 { 1992 if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT) 1993 return; 1994 1995 LockAllWindows(); 1996 1997 ::ServerWindow* serverWindow; 1998 Window* window; 1999 if (BPrivate::gDefaultTokens.GetToken(windowToken, 2000 B_SERVER_TOKEN, (void**)&serverWindow) != B_OK 2001 || (window = serverWindow->Window()) == NULL) { 2002 UnlockAllWindows(); 2003 return; 2004 } 2005 2006 if (action == B_BRING_TO_FRONT && !window->IsMinimized()) { 2007 // the window is visible, we just need to make it the front window 2008 ActivateWindow(window); 2009 } else { 2010 // if not, ask the window if it wants to be unminimized 2011 serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW); 2012 } 2013 2014 UnlockAllWindows(); 2015 } 2016 2017 2018 void 2019 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 2020 { 2021 AutoWriteLocker locker(fWindowLock); 2022 2023 // compute the number of windows 2024 2025 int32 count = 0; 2026 2027 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2028 window = window->NextWindow(kAllWindowList)) { 2029 if (team < B_OK || window->ServerWindow()->ClientTeam() == team) 2030 count++; 2031 } 2032 2033 // write list 2034 2035 sender.StartMessage(B_OK); 2036 sender.Attach<int32>(count); 2037 2038 // first write the windows of the current workspace correctly ordered 2039 for (Window *window = CurrentWindows().LastWindow(); window != NULL; 2040 window = window->PreviousWindow(fCurrentWorkspace)) { 2041 if (team >= B_OK && window->ServerWindow()->ClientTeam() != team) 2042 continue; 2043 2044 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2045 } 2046 2047 // then write all the other windows 2048 for (Window *window = fAllWindows.FirstWindow(); window != NULL; 2049 window = window->NextWindow(kAllWindowList)) { 2050 if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team) 2051 || window->InWorkspace(fCurrentWorkspace)) 2052 continue; 2053 2054 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2055 } 2056 2057 sender.Flush(); 2058 } 2059 2060 2061 void 2062 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 2063 { 2064 AutoWriteLocker locker(fWindowLock); 2065 BAutolock tokenLocker(BPrivate::gDefaultTokens); 2066 2067 ::ServerWindow* window; 2068 if (BPrivate::gDefaultTokens.GetToken(serverToken, 2069 B_SERVER_TOKEN, (void**)&window) != B_OK) { 2070 sender.StartMessage(B_ENTRY_NOT_FOUND); 2071 sender.Flush(); 2072 return; 2073 } 2074 2075 window_info info; 2076 window->GetInfo(info); 2077 2078 float tabSize = 0.0; 2079 float borderSize = 0.0; 2080 ::Window* tmp = window->Window(); 2081 if (tmp) { 2082 BMessage message; 2083 InvokeGetDecoratorSettings(tmp, message); 2084 if (tmp->GetDecoratorSettings(&message)) { 2085 BRect tabFrame; 2086 message.FindRect("tab frame", &tabFrame); 2087 tabSize = tabFrame.bottom - tabFrame.top; 2088 message.FindFloat("border width", &borderSize); 2089 } 2090 } 2091 2092 int32 length = window->Title() ? strlen(window->Title()) : 0; 2093 2094 sender.StartMessage(B_OK); 2095 sender.Attach<int32>(sizeof(client_window_info) + length); 2096 sender.Attach(&info, sizeof(window_info)); 2097 sender.Attach<float>(tabSize); 2098 sender.Attach<float>(borderSize); 2099 2100 if (length > 0) 2101 sender.Attach(window->Title(), length + 1); 2102 else 2103 sender.Attach<char>('\0'); 2104 2105 sender.Flush(); 2106 } 2107 2108 2109 void 2110 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender) 2111 { 2112 LockSingleWindow(); 2113 2114 if (workspace < 0) 2115 workspace = fCurrentWorkspace; 2116 else if (workspace >= kMaxWorkspaces) { 2117 sender.StartMessage(B_BAD_VALUE); 2118 sender.Flush(); 2119 UnlockSingleWindow(); 2120 return; 2121 } 2122 2123 int32 count = _Windows(workspace).Count(); 2124 2125 // write list 2126 2127 sender.StartMessage(B_OK); 2128 sender.Attach<int32>(count); 2129 2130 for (Window *window = _Windows(workspace).LastWindow(); window != NULL; 2131 window = window->PreviousWindow(workspace)) { 2132 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2133 } 2134 2135 sender.Flush(); 2136 2137 UnlockSingleWindow(); 2138 } 2139 2140 2141 void 2142 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender) 2143 { 2144 fApplicationsLock.Lock(); 2145 LockSingleWindow(); 2146 2147 int32 maxCount = fApplications.CountItems(); 2148 2149 fApplicationsLock.Unlock(); 2150 // as long as we hold the window lock, no new window can appear 2151 2152 if (workspace < 0) 2153 workspace = fCurrentWorkspace; 2154 else if (workspace >= kMaxWorkspaces) { 2155 sender.StartMessage(B_BAD_VALUE); 2156 sender.Flush(); 2157 UnlockSingleWindow(); 2158 return; 2159 } 2160 2161 // compute the list of applications on this workspace 2162 2163 team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id)); 2164 if (teams == NULL) { 2165 sender.StartMessage(B_NO_MEMORY); 2166 sender.Flush(); 2167 UnlockSingleWindow(); 2168 return; 2169 } 2170 2171 int32 count = 0; 2172 2173 for (Window *window = _Windows(workspace).LastWindow(); window != NULL; 2174 window = window->PreviousWindow(workspace)) { 2175 team_id team = window->ServerWindow()->ClientTeam(); 2176 if (count > 1) { 2177 // see if we already have this team 2178 bool found = false; 2179 for (int32 i = 0; i < count; i++) { 2180 if (teams[i] == team) { 2181 found = true; 2182 break; 2183 } 2184 } 2185 if (found) 2186 continue; 2187 } 2188 2189 ASSERT(count < maxCount); 2190 teams[count++] = team; 2191 } 2192 2193 UnlockSingleWindow(); 2194 2195 // write list 2196 2197 sender.StartMessage(B_OK); 2198 sender.Attach<int32>(count); 2199 2200 for (int32 i = 0; i < count; i++) { 2201 sender.Attach<int32>(teams[i]); 2202 } 2203 2204 sender.Flush(); 2205 free(teams); 2206 } 2207 2208 2209 void 2210 Desktop::_LaunchInputServer() 2211 { 2212 BRoster roster; 2213 status_t status = roster.Launch("application/x-vnd.Be-input_server"); 2214 if (status == B_OK || status == B_ALREADY_RUNNING) 2215 return; 2216 2217 // Could not load input_server by signature, try well-known location 2218 2219 BEntry entry("/system/servers/input_server"); 2220 entry_ref ref; 2221 status_t entryStatus = entry.GetRef(&ref); 2222 if (entryStatus == B_OK) 2223 entryStatus = roster.Launch(&ref); 2224 if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) { 2225 syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n", 2226 strerror(status)); 2227 return; 2228 } 2229 2230 syslog(LOG_ERR, "Failed to launch the input server: %s!\n", 2231 strerror(entryStatus)); 2232 } 2233 2234 2235 void 2236 Desktop::_GetLooperName(char* name, size_t length) 2237 { 2238 snprintf(name, length, "d:%d:%s", fUserID, 2239 fTargetScreen == NULL ? "baron" : fTargetScreen); 2240 } 2241 2242 2243 void 2244 Desktop::_PrepareQuit() 2245 { 2246 // let's kill all remaining applications 2247 2248 fApplicationsLock.Lock(); 2249 2250 int32 count = fApplications.CountItems(); 2251 for (int32 i = 0; i < count; i++) { 2252 ServerApp *app = fApplications.ItemAt(i); 2253 team_id clientTeam = app->ClientTeam(); 2254 2255 app->Quit(); 2256 kill_team(clientTeam); 2257 } 2258 2259 // wait for the last app to die 2260 if (count > 0) { 2261 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 2262 250000); 2263 } 2264 2265 fApplicationsLock.Unlock(); 2266 } 2267 2268 2269 void 2270 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) 2271 { 2272 switch (code) { 2273 case AS_CREATE_APP: 2274 { 2275 // Create the ServerApp to node monitor a new BApplication 2276 2277 // Attached data: 2278 // 1) port_id - receiver port of a regular app 2279 // 2) port_id - client looper port - for sending messages to the 2280 // client 2281 // 2) team_id - app's team ID 2282 // 3) int32 - handler token of the regular app 2283 // 4) char * - signature of the regular app 2284 2285 // Find the necessary data 2286 team_id clientTeamID = -1; 2287 port_id clientLooperPort = -1; 2288 port_id clientReplyPort = -1; 2289 int32 htoken = B_NULL_TOKEN; 2290 char *appSignature = NULL; 2291 2292 link.Read<port_id>(&clientReplyPort); 2293 link.Read<port_id>(&clientLooperPort); 2294 link.Read<team_id>(&clientTeamID); 2295 link.Read<int32>(&htoken); 2296 if (link.ReadString(&appSignature) != B_OK) 2297 break; 2298 2299 ServerApp *app = new ServerApp(this, clientReplyPort, 2300 clientLooperPort, clientTeamID, htoken, appSignature); 2301 if (app->InitCheck() == B_OK 2302 && app->Run()) { 2303 // add the new ServerApp to the known list of ServerApps 2304 fApplicationsLock.Lock(); 2305 fApplications.AddItem(app); 2306 fApplicationsLock.Unlock(); 2307 } else { 2308 delete app; 2309 2310 // if everything went well, ServerApp::Run() will notify 2311 // the client - but since it didn't, we do it here 2312 BPrivate::LinkSender reply(clientReplyPort); 2313 reply.StartMessage(B_ERROR); 2314 reply.Flush(); 2315 } 2316 2317 // This is necessary because BPortLink::ReadString allocates memory 2318 free(appSignature); 2319 break; 2320 } 2321 2322 case AS_DELETE_APP: 2323 { 2324 // Delete a ServerApp. Received only from the respective ServerApp 2325 // when a BApplication asks it to quit. 2326 2327 // Attached Data: 2328 // 1) thread_id - thread ID of the ServerApp to be deleted 2329 2330 thread_id thread = -1; 2331 if (link.Read<thread_id>(&thread) < B_OK) 2332 break; 2333 2334 fApplicationsLock.Lock(); 2335 2336 // Run through the list of apps and nuke the proper one 2337 2338 int32 count = fApplications.CountItems(); 2339 ServerApp *removeApp = NULL; 2340 2341 for (int32 i = 0; i < count; i++) { 2342 ServerApp *app = fApplications.ItemAt(i); 2343 2344 if (app->Thread() == thread) { 2345 fApplications.RemoveItemAt(i); 2346 removeApp = app; 2347 break; 2348 } 2349 } 2350 2351 fApplicationsLock.Unlock(); 2352 2353 if (removeApp != NULL) 2354 removeApp->Quit(fShutdownSemaphore); 2355 2356 if (fQuitting && count <= 1) { 2357 // wait for the last app to die 2358 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, 2359 B_RELATIVE_TIMEOUT, 500000); 2360 PostMessage(kMsgQuitLooper); 2361 } 2362 break; 2363 } 2364 2365 case AS_ACTIVATE_APP: 2366 { 2367 // Someone is requesting to activation of a certain app. 2368 2369 // Attached data: 2370 // 1) port_id reply port 2371 // 2) team_id team 2372 2373 status_t status; 2374 2375 // get the parameters 2376 port_id replyPort; 2377 team_id team; 2378 if (link.Read(&replyPort) == B_OK 2379 && link.Read(&team) == B_OK) 2380 status = _ActivateApp(team); 2381 else 2382 status = B_ERROR; 2383 2384 // send the reply 2385 BPrivate::PortLink replyLink(replyPort); 2386 replyLink.StartMessage(status); 2387 replyLink.Flush(); 2388 break; 2389 } 2390 2391 case AS_APP_CRASHED: 2392 { 2393 BAutolock locker(fApplicationsLock); 2394 2395 team_id team; 2396 if (link.Read(&team) != B_OK) 2397 break; 2398 2399 for (int32 i = 0; i < fApplications.CountItems(); i++) { 2400 ServerApp* app = fApplications.ItemAt(i); 2401 2402 if (app->ClientTeam() == team) 2403 app->PostMessage(AS_APP_CRASHED); 2404 } 2405 break; 2406 } 2407 2408 case AS_EVENT_STREAM_CLOSED: 2409 _LaunchInputServer(); 2410 break; 2411 2412 case B_QUIT_REQUESTED: 2413 // We've been asked to quit, so (for now) broadcast to all 2414 // test apps to quit. This situation will occur only when the 2415 // server is compiled as a regular Be application. 2416 2417 fApplicationsLock.Lock(); 2418 fShutdownSemaphore = create_sem(0, "desktop shutdown"); 2419 fShutdownCount = fApplications.CountItems(); 2420 fApplicationsLock.Unlock(); 2421 2422 fQuitting = true; 2423 BroadcastToAllApps(AS_QUIT_APP); 2424 2425 // We now need to process the remaining AS_DELETE_APP messages and 2426 // wait for the kMsgShutdownServer message. 2427 // If an application does not quit as asked, the picasso thread 2428 // will send us this message in 2-3 seconds. 2429 2430 // if there are no apps to quit, shutdown directly 2431 if (fShutdownCount == 0) 2432 PostMessage(kMsgQuitLooper); 2433 break; 2434 2435 case AS_ACTIVATE_WORKSPACE: 2436 { 2437 int32 index; 2438 link.Read<int32>(&index); 2439 if (index == -1) 2440 index = fPreviousWorkspace; 2441 2442 bool moveFocusWindow; 2443 link.Read<bool>(&moveFocusWindow); 2444 2445 SetWorkspace(index, moveFocusWindow); 2446 break; 2447 } 2448 2449 // ToDo: Remove this again. It is a message sent by the 2450 // invalidate_on_exit kernel debugger add-on to trigger a redraw 2451 // after exiting a kernel debugger session. 2452 case 'KDLE': 2453 { 2454 BRegion dirty; 2455 dirty.Include(fVirtualScreen.Frame()); 2456 MarkDirty(dirty); 2457 break; 2458 } 2459 2460 default: 2461 printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", 2462 code); 2463 2464 if (link.NeedsReply()) { 2465 // the client is now blocking and waiting for a reply! 2466 fLink.StartMessage(B_ERROR); 2467 fLink.Flush(); 2468 } 2469 break; 2470 } 2471 } 2472 2473 2474 WindowList& 2475 Desktop::CurrentWindows() 2476 { 2477 return fWorkspaces[fCurrentWorkspace].Windows(); 2478 } 2479 2480 2481 WindowList& 2482 Desktop::AllWindows() 2483 { 2484 return fAllWindows; 2485 } 2486 2487 2488 WindowList& 2489 Desktop::_Windows(int32 index) 2490 { 2491 return fWorkspaces[index].Windows(); 2492 } 2493 2494 2495 void 2496 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace, 2497 Window* mouseEventWindow) 2498 { 2499 if (previousWorkspace == -1) 2500 previousWorkspace = fCurrentWorkspace; 2501 if (nextWorkspace == -1) 2502 nextWorkspace = previousWorkspace; 2503 2504 for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL; 2505 floating = floating->NextWindow(kSubsetList)) { 2506 // we only care about app/subset floating windows 2507 if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL 2508 && floating->Feel() != B_FLOATING_APP_WINDOW_FEEL) 2509 continue; 2510 2511 if (fFront != NULL && fFront->IsNormal() 2512 && floating->HasInSubset(fFront)) { 2513 // is now visible 2514 if (_Windows(previousWorkspace).HasWindow(floating) 2515 && previousWorkspace != nextWorkspace 2516 && !floating->InSubsetWorkspace(previousWorkspace)) { 2517 // but no longer on the previous workspace 2518 _Windows(previousWorkspace).RemoveWindow(floating); 2519 floating->SetCurrentWorkspace(-1); 2520 } 2521 2522 if (!_Windows(nextWorkspace).HasWindow(floating)) { 2523 // but wasn't before 2524 _Windows(nextWorkspace).AddWindow(floating, 2525 floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), 2526 nextWorkspace)); 2527 floating->SetCurrentWorkspace(nextWorkspace); 2528 if (mouseEventWindow != fFront) 2529 _ShowWindow(floating); 2530 2531 // TODO: put the floating last in the floating window list to 2532 // preserve the on screen window order 2533 } 2534 } else if (_Windows(previousWorkspace).HasWindow(floating) 2535 && !floating->InSubsetWorkspace(previousWorkspace)) { 2536 // was visible, but is no longer 2537 2538 _Windows(previousWorkspace).RemoveWindow(floating); 2539 floating->SetCurrentWorkspace(-1); 2540 _HideWindow(floating); 2541 2542 if (FocusWindow() == floating) 2543 SetFocusWindow(); 2544 } 2545 } 2546 } 2547 2548 2549 /*! Search the visible windows for a valid back window 2550 (only desktop windows can't be back windows) 2551 */ 2552 void 2553 Desktop::_UpdateBack() 2554 { 2555 fBack = NULL; 2556 2557 for (Window* window = CurrentWindows().FirstWindow(); window != NULL; 2558 window = window->NextWindow(fCurrentWorkspace)) { 2559 if (window->IsHidden() || window->Feel() == kDesktopWindowFeel) 2560 continue; 2561 2562 fBack = window; 2563 break; 2564 } 2565 } 2566 2567 2568 /*! Search the visible windows for a valid front window 2569 (only normal and modal windows can be front windows) 2570 2571 The only place where you don't want to update floating windows is 2572 during a workspace change - because then you'll call _UpdateFloating() 2573 yourself. 2574 */ 2575 void 2576 Desktop::_UpdateFront(bool updateFloating) 2577 { 2578 fFront = NULL; 2579 2580 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 2581 window = window->PreviousWindow(fCurrentWorkspace)) { 2582 if (window->IsHidden() || window->IsFloating() 2583 || !window->SupportsFront()) 2584 continue; 2585 2586 fFront = window; 2587 break; 2588 } 2589 2590 if (updateFloating) 2591 _UpdateFloating(); 2592 } 2593 2594 2595 void 2596 Desktop::_UpdateFronts(bool updateFloating) 2597 { 2598 _UpdateBack(); 2599 _UpdateFront(updateFloating); 2600 } 2601 2602 2603 bool 2604 Desktop::_WindowHasModal(Window* window) 2605 { 2606 if (window == NULL) 2607 return false; 2608 2609 for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL; 2610 modal = modal->NextWindow(kSubsetList)) { 2611 // only visible modal windows count 2612 if (!modal->IsModal() || modal->IsHidden()) 2613 continue; 2614 2615 if (modal->HasInSubset(window)) 2616 return true; 2617 } 2618 2619 return false; 2620 } 2621 2622 2623 /*! You must at least hold a single window lock when calling this method. 2624 */ 2625 void 2626 Desktop::_WindowChanged(Window* window) 2627 { 2628 ASSERT_MULTI_LOCKED(fWindowLock); 2629 2630 BAutolock _(fWorkspacesLock); 2631 2632 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 2633 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 2634 view->WindowChanged(window); 2635 } 2636 } 2637 2638 2639 /*! You must at least hold a single window lock when calling this method. 2640 */ 2641 void 2642 Desktop::_WindowRemoved(Window* window) 2643 { 2644 ASSERT_MULTI_LOCKED(fWindowLock); 2645 2646 BAutolock _(fWorkspacesLock); 2647 2648 for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) { 2649 WorkspacesView* view = fWorkspacesViews.ItemAt(i); 2650 view->WindowRemoved(window); 2651 } 2652 } 2653 2654 2655 /*! Shows the window on the screen - it does this independently of the 2656 Window::IsHidden() state. 2657 */ 2658 void 2659 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows) 2660 { 2661 BRegion background; 2662 _RebuildClippingForAllWindows(background); 2663 _SetBackground(background); 2664 _WindowChanged(window); 2665 2666 BRegion dirty(window->VisibleRegion()); 2667 2668 if (!affectsOtherWindows) { 2669 // everything that is now visible in the 2670 // window needs a redraw, but other windows 2671 // are not affected, we can call ProcessDirtyRegion() 2672 // of the window, and don't have to use MarkDirty() 2673 window->ProcessDirtyRegion(dirty); 2674 } else 2675 MarkDirty(dirty); 2676 2677 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 2678 window->ServerWindow()->HandleDirectConnection( 2679 B_DIRECT_START | B_BUFFER_RESET); 2680 } 2681 } 2682 2683 2684 /*! Hides the window from the screen - it does this independently of the 2685 Window::IsHidden() state. 2686 */ 2687 void 2688 Desktop::_HideWindow(Window* window) 2689 { 2690 if (window->ServerWindow()->IsDirectlyAccessing()) 2691 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 2692 2693 // after rebuilding the clipping, 2694 // this window will not have a visible 2695 // region anymore, so we need to remember 2696 // it now 2697 // (actually that's not true, since 2698 // hidden windows are excluded from the 2699 // clipping calculation, but anyways) 2700 BRegion dirty(window->VisibleRegion()); 2701 2702 BRegion background; 2703 _RebuildClippingForAllWindows(background); 2704 _SetBackground(background); 2705 _WindowChanged(window); 2706 2707 MarkDirty(dirty); 2708 } 2709 2710 2711 /*! Updates the workspaces of all subset windows with regard to the 2712 specifed window. 2713 If newIndex is not -1, it will move all subset windows that belong to 2714 the specifed window to the new workspace; this form is only called by 2715 SetWorkspace(). 2716 */ 2717 void 2718 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex, 2719 int32 newIndex) 2720 { 2721 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, 2722 window->Title())); 2723 2724 // if the window is hidden, the subset windows are up-to-date already 2725 if (!window->IsNormal() || window->IsHidden()) 2726 return; 2727 2728 for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL; 2729 subset = subset->NextWindow(kSubsetList)) { 2730 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL 2731 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) { 2732 // These windows are always visible on all workspaces, 2733 // no need to update them. 2734 continue; 2735 } 2736 2737 if (subset->IsFloating()) { 2738 // Floating windows are inserted and removed to the current 2739 // workspace as the need arises - they are not handled here 2740 // but in _UpdateFront() 2741 continue; 2742 } 2743 2744 if (subset->HasInSubset(window)) { 2745 // adopt the workspace change 2746 SetWindowWorkspaces(subset, subset->SubsetWorkspaces()); 2747 } 2748 } 2749 } 2750 2751 2752 /*! \brief Adds or removes the window to or from the workspaces it's on. 2753 */ 2754 void 2755 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces, 2756 uint32 newWorkspaces) 2757 { 2758 if (oldWorkspaces == newWorkspaces) 2759 return; 2760 2761 // apply changes to the workspaces' window lists 2762 2763 LockAllWindows(); 2764 2765 // NOTE: we bypass the anchor-mechanism by intention when switching 2766 // the workspace programmatically. 2767 2768 for (int32 i = 0; i < kMaxWorkspaces; i++) { 2769 if (workspace_in_workspaces(i, oldWorkspaces)) { 2770 // window is on this workspace, is it anymore? 2771 if (!workspace_in_workspaces(i, newWorkspaces)) { 2772 _Windows(i).RemoveWindow(window); 2773 if (fLastWorkspaceFocus[i] == window) 2774 fLastWorkspaceFocus[i] = NULL; 2775 2776 if (i == CurrentWorkspace()) { 2777 // remove its appearance from the current workspace 2778 window->SetCurrentWorkspace(-1); 2779 2780 if (!window->IsHidden()) 2781 _HideWindow(window); 2782 } 2783 } 2784 } else { 2785 // window was not on this workspace, is it now? 2786 if (workspace_in_workspaces(i, newWorkspaces)) { 2787 _Windows(i).AddWindow(window, 2788 window->Frontmost(_Windows(i).FirstWindow(), i)); 2789 2790 if (i == CurrentWorkspace()) { 2791 // make the window visible in current workspace 2792 window->SetCurrentWorkspace(fCurrentWorkspace); 2793 2794 if (!window->IsHidden()) { 2795 // This only affects other windows if this window has 2796 // floating or modal windows that need to be shown as 2797 // well 2798 // TODO: take care of this 2799 _ShowWindow(window, FrontWindow() == window); 2800 } 2801 } 2802 } 2803 } 2804 } 2805 2806 // If the window is visible only on one workspace, we set it's current 2807 // position in that workspace (so that WorkspacesView will find us). 2808 int32 firstWorkspace = -1; 2809 for (int32 i = 0; i < kMaxWorkspaces; i++) { 2810 if ((newWorkspaces & (1L << i)) != 0) { 2811 if (firstWorkspace != -1) { 2812 firstWorkspace = -1; 2813 break; 2814 } 2815 firstWorkspace = i; 2816 } 2817 } 2818 if (firstWorkspace >= 0) 2819 window->Anchor(firstWorkspace).position = window->Frame().LeftTop(); 2820 2821 // take care about modals and floating windows 2822 _UpdateSubsetWorkspaces(window); 2823 2824 InvokeSetWindowWorkspaces(window, newWorkspaces); 2825 2826 UnlockAllWindows(); 2827 } 2828 2829 2830 void 2831 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, 2832 bool wereVisible) 2833 { 2834 // we don't need to redraw what is currently 2835 // visible of the window 2836 BRegion clean; 2837 2838 for (Window* window = windows.FirstWindow(); window != NULL; 2839 window = window->NextWindow(list)) { 2840 if (wereVisible) 2841 clean.Include(&window->VisibleRegion()); 2842 2843 CurrentWindows().AddWindow(window, 2844 window->Frontmost(CurrentWindows().FirstWindow(), 2845 fCurrentWorkspace)); 2846 2847 _WindowChanged(window); 2848 } 2849 2850 BRegion dummy; 2851 _RebuildClippingForAllWindows(dummy); 2852 2853 // redraw what became visible of the window(s) 2854 2855 BRegion dirty; 2856 for (Window* window = windows.FirstWindow(); window != NULL; 2857 window = window->NextWindow(list)) { 2858 dirty.Include(&window->VisibleRegion()); 2859 } 2860 2861 dirty.Exclude(&clean); 2862 MarkDirty(dirty); 2863 2864 _UpdateFront(); 2865 2866 if (windows.FirstWindow() == fBack || fBack == NULL) 2867 _UpdateBack(); 2868 } 2869 2870 2871 /*! Returns the last focussed non-hidden subset window belonging to the 2872 specified \a window. 2873 */ 2874 Window* 2875 Desktop::_LastFocusSubsetWindow(Window* window) 2876 { 2877 if (window == NULL) 2878 return NULL; 2879 2880 for (Window* front = fFocusList.LastWindow(); front != NULL; 2881 front = front->PreviousWindow(kFocusList)) { 2882 if (front != window && !front->IsHidden() 2883 && window->HasInSubset(front)) 2884 return front; 2885 } 2886 2887 return NULL; 2888 } 2889 2890 2891 /*! \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse, 2892 and also updates the current view under the mouse. 2893 2894 This has only to be done in case the view changed without user interaction, 2895 ie. because of a workspace change or a closing window. 2896 */ 2897 void 2898 Desktop::_SendFakeMouseMoved(Window* window) 2899 { 2900 int32 viewToken = B_NULL_TOKEN; 2901 EventTarget* target = NULL; 2902 2903 LockAllWindows(); 2904 2905 if (window == NULL) 2906 window = MouseEventWindow(); 2907 if (window == NULL) 2908 window = WindowAt(fLastMousePosition); 2909 2910 if (window != NULL) { 2911 BMessage message; 2912 window->MouseMoved(&message, fLastMousePosition, &viewToken, true, 2913 true); 2914 2915 if (viewToken != B_NULL_TOKEN) 2916 target = &window->EventTarget(); 2917 } 2918 2919 if (viewToken != B_NULL_TOKEN) 2920 SetViewUnderMouse(window, viewToken); 2921 else { 2922 SetViewUnderMouse(NULL, B_NULL_TOKEN); 2923 SetCursor(NULL); 2924 } 2925 2926 UnlockAllWindows(); 2927 2928 if (target != NULL) 2929 EventDispatcher().SendFakeMouseMoved(*target, viewToken); 2930 } 2931 2932 2933 Screen* 2934 Desktop::_DetermineScreenFor(BRect frame) 2935 { 2936 AutoReadLocker _(fScreenLock); 2937 2938 // TODO: choose the screen depending on where most of the area is 2939 return fVirtualScreen.ScreenAt(0); 2940 } 2941 2942 2943 void 2944 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen) 2945 { 2946 // the available region on screen starts with the entire screen area 2947 // each window on the screen will take a portion from that area 2948 2949 // figure out what the entire screen area is 2950 stillAvailableOnScreen = fScreenRegion; 2951 2952 // set clipping of each window 2953 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 2954 window = window->PreviousWindow(fCurrentWorkspace)) { 2955 if (!window->IsHidden()) { 2956 window->SetClipping(&stillAvailableOnScreen); 2957 window->SetScreen(_DetermineScreenFor(window->Frame())); 2958 2959 if (window->ServerWindow()->IsDirectlyAccessing()) { 2960 window->ServerWindow()->HandleDirectConnection( 2961 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 2962 } 2963 2964 // that windows region is not available on screen anymore 2965 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 2966 } 2967 } 2968 } 2969 2970 2971 void 2972 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion) 2973 { 2974 // send redraw messages to all windows intersecting the dirty region 2975 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 2976 window = window->PreviousWindow(fCurrentWorkspace)) { 2977 if (!window->IsHidden() 2978 && newDirtyRegion.Intersects(window->VisibleRegion().Frame())) 2979 window->ProcessDirtyRegion(newDirtyRegion); 2980 } 2981 } 2982 2983 2984 void 2985 Desktop::_SetBackground(BRegion& background) 2986 { 2987 // NOTE: the drawing operation is caried out 2988 // in the clipping region rebuild, but it is 2989 // ok actually, because it also avoids trails on 2990 // moving windows 2991 2992 // remember the region not covered by any windows 2993 // and redraw the dirty background 2994 BRegion dirtyBackground(background); 2995 dirtyBackground.Exclude(&fBackgroundRegion); 2996 dirtyBackground.IntersectWith(&background); 2997 fBackgroundRegion = background; 2998 if (dirtyBackground.Frame().IsValid()) { 2999 if (GetDrawingEngine()->LockParallelAccess()) { 3000 GetDrawingEngine()->FillRegion(dirtyBackground, 3001 fWorkspaces[fCurrentWorkspace].Color()); 3002 3003 GetDrawingEngine()->UnlockParallelAccess(); 3004 } 3005 } 3006 } 3007 3008 3009 //! The all window lock must be held when calling this function. 3010 void 3011 Desktop::_RebuildAndRedrawAfterWindowChange(Window* changedWindow, 3012 BRegion& dirty) 3013 { 3014 if (!changedWindow->IsVisible() || dirty.CountRects() == 0) 3015 return; 3016 3017 // The following loop is pretty much a copy of 3018 // _RebuildClippingForAllWindows(), but will also 3019 // take care about restricting our dirty region. 3020 3021 // figure out what the entire screen area is 3022 BRegion stillAvailableOnScreen(fScreenRegion); 3023 3024 // set clipping of each window 3025 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3026 window = window->PreviousWindow(fCurrentWorkspace)) { 3027 if (!window->IsHidden()) { 3028 if (window == changedWindow) 3029 dirty.IntersectWith(&stillAvailableOnScreen); 3030 3031 window->SetClipping(&stillAvailableOnScreen); 3032 window->SetScreen(_DetermineScreenFor(window->Frame())); 3033 3034 if (window->ServerWindow()->IsDirectlyAccessing()) { 3035 window->ServerWindow()->HandleDirectConnection( 3036 B_DIRECT_MODIFY | B_CLIPPING_MODIFIED); 3037 } 3038 3039 // that windows region is not available on screen anymore 3040 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 3041 } 3042 } 3043 3044 _SetBackground(stillAvailableOnScreen); 3045 _WindowChanged(changedWindow); 3046 3047 _TriggerWindowRedrawing(dirty); 3048 } 3049 3050 3051 //! Suspend all windows with direct access to the frame buffer 3052 void 3053 Desktop::_SuspendDirectFrameBufferAccess() 3054 { 3055 ASSERT_MULTI_LOCKED(fWindowLock); 3056 3057 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3058 window = window->NextWindow(kAllWindowList)) { 3059 if (window->ServerWindow()->IsDirectlyAccessing()) 3060 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3061 } 3062 } 3063 3064 3065 //! Resume all windows with direct access to the frame buffer 3066 void 3067 Desktop::_ResumeDirectFrameBufferAccess() 3068 { 3069 ASSERT_MULTI_LOCKED(fWindowLock); 3070 3071 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3072 window = window->NextWindow(kAllWindowList)) { 3073 if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace)) 3074 continue; 3075 3076 if (window->ServerWindow()->HasDirectFrameBufferAccess()) { 3077 window->ServerWindow()->HandleDirectConnection( 3078 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 3079 } 3080 } 3081 } 3082 3083 3084 void 3085 Desktop::_ScreenChanged(Screen* screen) 3086 { 3087 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3088 3089 // the entire screen is dirty, because we're actually 3090 // operating on an all new buffer in memory 3091 BRegion dirty(screen->Frame()); 3092 3093 // update our cached screen region 3094 fScreenRegion.Set(screen->Frame()); 3095 gInputManager->UpdateScreenBounds(screen->Frame()); 3096 3097 BRegion background; 3098 _RebuildClippingForAllWindows(background); 3099 3100 fBackgroundRegion.MakeEmpty(); 3101 // makes sure that the complete background is redrawn 3102 _SetBackground(background); 3103 3104 // figure out dirty region 3105 dirty.Exclude(&background); 3106 _TriggerWindowRedrawing(dirty); 3107 3108 // send B_SCREEN_CHANGED to windows on that screen 3109 BMessage update(B_SCREEN_CHANGED); 3110 update.AddInt64("when", real_time_clock_usecs()); 3111 update.AddRect("frame", screen->Frame()); 3112 update.AddInt32("mode", screen->ColorSpace()); 3113 3114 fVirtualScreen.UpdateFrame(); 3115 3116 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3117 window = window->NextWindow(kAllWindowList)) { 3118 if (window->Screen() == screen) 3119 window->ServerWindow()->ScreenChanged(&update); 3120 } 3121 } 3122 3123 3124 /*! \brief activate one of the app's windows. 3125 */ 3126 status_t 3127 Desktop::_ActivateApp(team_id team) 3128 { 3129 // search for an unhidden window in the current workspace 3130 3131 AutoWriteLocker locker(fWindowLock); 3132 3133 for (Window* window = CurrentWindows().LastWindow(); window != NULL; 3134 window = window->PreviousWindow(fCurrentWorkspace)) { 3135 if (!window->IsHidden() && window->IsNormal() 3136 && window->ServerWindow()->ClientTeam() == team) { 3137 ActivateWindow(window); 3138 return B_OK; 3139 } 3140 } 3141 3142 // search for an unhidden window to give focus to 3143 3144 for (Window* window = fAllWindows.FirstWindow(); window != NULL; 3145 window = window->NextWindow(kAllWindowList)) { 3146 // if window is a normal window of the team, and not hidden, 3147 // we've found our target 3148 if (!window->IsHidden() && window->IsNormal() 3149 && window->ServerWindow()->ClientTeam() == team) { 3150 ActivateWindow(window); 3151 return B_OK; 3152 } 3153 } 3154 3155 // TODO: we cannot maximize minimized windows here (with the window lock 3156 // write locked). To work-around this, we could forward the request to 3157 // the ServerApp of this team - it maintains its own window list, and can 3158 // therefore call ActivateWindow() without holding the window lock. 3159 return B_BAD_VALUE; 3160 } 3161 3162 3163 void 3164 Desktop::_SetCurrentWorkspaceConfiguration() 3165 { 3166 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3167 3168 status_t status = fDirectScreenLock.LockWithTimeout(1000000L); 3169 if (status != B_OK) { 3170 // The application having the direct screen lock didn't give it up in 3171 // time, make it crash 3172 syslog(LOG_ERR, "Team %ld did not give up its direct screen lock.\n", 3173 fDirectScreenTeam); 3174 3175 debug_thread(fDirectScreenTeam); 3176 fDirectScreenTeam = -1; 3177 } else 3178 fDirectScreenLock.Unlock(); 3179 3180 AutoWriteLocker _(fScreenLock); 3181 3182 uint32 changedScreens; 3183 fVirtualScreen.SetConfiguration(*this, 3184 fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(), 3185 &changedScreens); 3186 3187 for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) { 3188 if ((changedScreens & (1 << i)) != 0) 3189 _ScreenChanged(fVirtualScreen.ScreenAt(i)); 3190 } 3191 } 3192 3193 3194 /*! Changes the current workspace to the one specified by \a index. 3195 You must hold the all window lock when calling this method. 3196 */ 3197 void 3198 Desktop::_SetWorkspace(int32 index, bool moveFocusWindow) 3199 { 3200 ASSERT_MULTI_WRITE_LOCKED(fWindowLock); 3201 3202 int32 previousIndex = fCurrentWorkspace; 3203 rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color(); 3204 bool movedMouseEventWindow = false; 3205 Window* movedWindow = fMouseEventWindow; 3206 if (movedWindow == NULL && moveFocusWindow) 3207 movedWindow = FocusWindow(); 3208 3209 if (movedWindow != NULL) { 3210 if (movedWindow->IsNormal()) { 3211 if (!movedWindow->InWorkspace(index)) { 3212 // The window currently being dragged will follow us to this 3213 // workspace if it's not already on it. 3214 // But only normal windows are following 3215 uint32 oldWorkspaces = movedWindow->Workspaces(); 3216 3217 _Windows(previousIndex).RemoveWindow(movedWindow); 3218 _Windows(index).AddWindow(movedWindow, 3219 movedWindow->Frontmost(_Windows(index).FirstWindow(), 3220 index)); 3221 3222 // TODO: subset windows will always flicker this way 3223 3224 movedMouseEventWindow = true; 3225 3226 // send B_WORKSPACES_CHANGED message 3227 movedWindow->WorkspacesChanged(oldWorkspaces, 3228 movedWindow->Workspaces()); 3229 InvokeSetWindowWorkspaces(movedWindow, movedWindow->Workspaces()); 3230 } else { 3231 // make sure it's frontmost 3232 _Windows(index).RemoveWindow(movedWindow); 3233 _Windows(index).AddWindow(movedWindow, 3234 movedWindow->Frontmost(_Windows(index).FirstWindow(), 3235 index)); 3236 } 3237 } 3238 3239 movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop(); 3240 } 3241 3242 if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex)) 3243 fLastWorkspaceFocus[previousIndex] = FocusWindow(); 3244 else 3245 fLastWorkspaceFocus[previousIndex] = NULL; 3246 3247 // build region of windows that are no longer visible in the new workspace 3248 3249 BRegion dirty; 3250 3251 for (Window* window = CurrentWindows().FirstWindow(); 3252 window != NULL; window = window->NextWindow(previousIndex)) { 3253 // store current position in Workspace anchor 3254 window->Anchor(previousIndex).position = window->Frame().LeftTop(); 3255 3256 if (!window->IsHidden() 3257 && window->ServerWindow()->IsDirectlyAccessing()) 3258 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 3259 3260 window->WorkspaceActivated(previousIndex, false); 3261 3262 if (window->InWorkspace(index)) 3263 continue; 3264 3265 if (!window->IsHidden()) { 3266 // this window will no longer be visible 3267 dirty.Include(&window->VisibleRegion()); 3268 } 3269 3270 window->SetCurrentWorkspace(-1); 3271 } 3272 3273 fPreviousWorkspace = fCurrentWorkspace; 3274 fCurrentWorkspace = index; 3275 3276 // Change the display modes, if needed 3277 _SetCurrentWorkspaceConfiguration(); 3278 3279 // Show windows, and include them in the changed region - but only 3280 // those that were not visible before (or whose position changed) 3281 3282 WindowList windows(kWorkingList); 3283 BList previousRegions; 3284 3285 for (Window* window = _Windows(index).FirstWindow(); 3286 window != NULL; window = window->NextWindow(index)) { 3287 BPoint position = window->Anchor(index).position; 3288 3289 window->SetCurrentWorkspace(index); 3290 3291 if (window->IsHidden()) 3292 continue; 3293 3294 if (position == kInvalidWindowPosition) { 3295 // if you enter a workspace for the first time, the position 3296 // of the window in the previous workspace is adopted 3297 position = window->Frame().LeftTop(); 3298 // TODO: make sure the window is still on-screen if it 3299 // was before! 3300 } 3301 3302 if (!window->InWorkspace(previousIndex)) { 3303 // This window was not visible before, make sure its frame 3304 // is up-to-date 3305 if (window->Frame().LeftTop() != position) { 3306 BPoint offset = position - window->Frame().LeftTop(); 3307 window->MoveBy((int32)offset.x, (int32)offset.y); 3308 } 3309 continue; 3310 } 3311 3312 if (window->Frame().LeftTop() != position) { 3313 // the window was visible before, but its on-screen location changed 3314 BPoint offset = position - window->Frame().LeftTop(); 3315 MoveWindowBy(window, offset.x, offset.y); 3316 // TODO: be a bit smarter than this... 3317 } else { 3318 // We need to remember the previous visible region of the 3319 // window if they changed their order 3320 BRegion* region = new (std::nothrow) 3321 BRegion(window->VisibleRegion()); 3322 if (region != NULL) { 3323 if (previousRegions.AddItem(region)) 3324 windows.AddWindow(window); 3325 else 3326 delete region; 3327 } 3328 } 3329 } 3330 3331 _UpdateFronts(false); 3332 _UpdateFloating(previousIndex, index, 3333 movedMouseEventWindow ? movedWindow : NULL); 3334 3335 BRegion stillAvailableOnScreen; 3336 _RebuildClippingForAllWindows(stillAvailableOnScreen); 3337 _SetBackground(stillAvailableOnScreen); 3338 3339 for (Window* window = _Windows(index).FirstWindow(); window != NULL; 3340 window = window->NextWindow(index)) { 3341 // send B_WORKSPACE_ACTIVATED message 3342 window->WorkspaceActivated(index, true); 3343 3344 if (!window->IsHidden() 3345 && window->ServerWindow()->HasDirectFrameBufferAccess()) { 3346 window->ServerWindow()->HandleDirectConnection( 3347 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED); 3348 } 3349 3350 if (window->InWorkspace(previousIndex) || window->IsHidden() 3351 || (window == movedWindow && movedWindow->IsNormal()) 3352 || (!window->IsNormal() 3353 && window->HasInSubset(movedWindow))) { 3354 // This window was visible before, and is already handled in the 3355 // above loop 3356 continue; 3357 } 3358 3359 dirty.Include(&window->VisibleRegion()); 3360 } 3361 3362 // Catch order changes in the new workspaces window list 3363 int32 i = 0; 3364 for (Window* window = windows.FirstWindow(); window != NULL; 3365 window = window->NextWindow(kWorkingList), i++) { 3366 BRegion* region = (BRegion*)previousRegions.ItemAt(i); 3367 region->ExclusiveInclude(&window->VisibleRegion()); 3368 dirty.Include(region); 3369 delete region; 3370 } 3371 3372 // Set new focus, but keep focus to a floating window if still visible 3373 if (movedWindow != NULL) 3374 SetFocusWindow(movedWindow); 3375 else if (!_Windows(index).HasWindow(FocusWindow()) 3376 || (FocusWindow() != NULL && !FocusWindow()->IsFloating())) 3377 SetFocusWindow(fLastWorkspaceFocus[index]); 3378 3379 _WindowChanged(NULL); 3380 MarkDirty(dirty); 3381 3382 #if 0 3383 // Show the dirty regions of this workspace switch 3384 if (GetDrawingEngine()->LockParallelAccess()) { 3385 GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0}); 3386 GetDrawingEngine()->UnlockParallelAccess(); 3387 snooze(100000); 3388 } 3389 #endif 3390 3391 if (previousColor != fWorkspaces[fCurrentWorkspace].Color()) 3392 RedrawBackground(); 3393 } 3394