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