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