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