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