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