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