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 // TODO: Not yet useful, and prevents a crashing application 1109 // which has opened menu windows to be debugged (ticket #1152) 1110 #if 0 1111 WindowLayer* window = _CurrentWindows().LastWindow(); 1112 if (window != NULL && window->Feel() == kMenuWindowFeel) 1113 return &window->EventTarget(); 1114 #endif 1115 if (FocusWindow() != NULL) 1116 return &FocusWindow()->EventTarget(); 1117 1118 return NULL; 1119 } 1120 1121 1122 bool 1123 Desktop::_WindowHasModal(WindowLayer* window) 1124 { 1125 if (window == NULL) 1126 return false; 1127 1128 for (WindowLayer* modal = fSubsetWindows.FirstWindow(); modal != NULL; 1129 modal = modal->NextWindow(kSubsetList)) { 1130 // only visible modal windows count 1131 if (!modal->IsModal() || modal->IsHidden()) 1132 continue; 1133 1134 if (modal->HasInSubset(window)) 1135 return true; 1136 } 1137 1138 return false; 1139 } 1140 1141 1142 /*! 1143 You must at least hold a single window lock when calling this method. 1144 */ 1145 void 1146 Desktop::_WindowChanged(WindowLayer* window) 1147 { 1148 if (fWorkspacesLayer == NULL) 1149 return; 1150 1151 fWorkspacesLayer->WindowChanged(window); 1152 } 1153 1154 1155 /*! 1156 \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse, 1157 and also updates the current view under the mouse. 1158 1159 This has only to be done in case the view changed without user interaction, 1160 ie. because of a workspace change or a closing window. 1161 1162 Windows must not be locked when calling this method. 1163 */ 1164 void 1165 Desktop::_SendFakeMouseMoved(WindowLayer* window) 1166 { 1167 BPoint where; 1168 int32 buttons; 1169 EventDispatcher().GetMouse(where, buttons); 1170 1171 int32 viewToken = B_NULL_TOKEN; 1172 EventTarget* target = NULL; 1173 1174 LockAllWindows(); 1175 1176 if (window == NULL) 1177 window = MouseEventWindow(); 1178 if (window == NULL) 1179 window = WindowAt(where); 1180 1181 if (window != NULL) { 1182 BMessage message; 1183 window->MouseMoved(&message, where, &viewToken, true); 1184 1185 if (viewToken != B_NULL_TOKEN) 1186 target = &window->EventTarget(); 1187 } 1188 1189 if (viewToken != B_NULL_TOKEN) 1190 SetViewUnderMouse(window, viewToken); 1191 else { 1192 SetViewUnderMouse(NULL, B_NULL_TOKEN); 1193 SetCursor(NULL); 1194 } 1195 1196 UnlockAllWindows(); 1197 1198 if (target != NULL) 1199 EventDispatcher().SendFakeMouseMoved(*target, viewToken); 1200 } 1201 1202 1203 void 1204 Desktop::SetFocusWindow(WindowLayer* focus) 1205 { 1206 if (!LockAllWindows()) 1207 return; 1208 1209 bool hasModal = _WindowHasModal(focus); 1210 1211 // TODO: test for FFM and B_LOCK_WINDOW_FOCUS 1212 1213 if (focus == fFocus && focus != NULL && !focus->IsHidden() 1214 && (focus->Flags() & B_AVOID_FOCUS) == 0 && !hasModal) { 1215 // the window that is supposed to get focus already has focus 1216 UnlockAllWindows(); 1217 return; 1218 } 1219 1220 if (focus == NULL || hasModal) { 1221 focus = FrontWindow(); 1222 if (focus == NULL) { 1223 // there might be no front window in case of only a single 1224 // window with B_FLOATING_ALL_WINDOW_FEEL 1225 focus = _CurrentWindows().LastWindow(); 1226 } 1227 } 1228 1229 // make sure no window is chosen that doesn't want focus or cannot have it 1230 while (focus != NULL 1231 && ((focus->Flags() & B_AVOID_FOCUS) != 0 1232 || _WindowHasModal(focus) 1233 || focus->IsHidden())) { 1234 focus = focus->PreviousWindow(fCurrentWorkspace); 1235 } 1236 1237 if (fFocus == focus) { 1238 // turns out the window that is supposed to get focus now already has it 1239 UnlockAllWindows(); 1240 return; 1241 } 1242 1243 team_id oldActiveApp = -1; 1244 team_id newActiveApp = -1; 1245 1246 if (fFocus != NULL) { 1247 fFocus->SetFocus(false); 1248 oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 1249 } 1250 1251 fFocus = focus; 1252 1253 if (fFocus != NULL) { 1254 fFocus->SetFocus(true); 1255 newActiveApp = fFocus->ServerWindow()->App()->ClientTeam(); 1256 } 1257 1258 if (newActiveApp == -1) { 1259 // make sure the cursor is visible 1260 HWInterface()->SetCursorVisible(true); 1261 } 1262 1263 UnlockAllWindows(); 1264 1265 // change the "active" app if appropriate 1266 if (oldActiveApp == newActiveApp) 1267 return; 1268 1269 BAutolock locker(fApplicationsLock); 1270 1271 for (int32 i = 0; i < fApplications.CountItems(); i++) { 1272 ServerApp *app = fApplications.ItemAt(i); 1273 1274 if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp) 1275 app->Activate(false); 1276 else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp) 1277 app->Activate(true); 1278 } 1279 } 1280 1281 1282 void 1283 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, 1284 bool wereVisible) 1285 { 1286 // we don't need to redraw what is currently 1287 // visible of the window 1288 BRegion clean; 1289 1290 for (WindowLayer* window = windows.FirstWindow(); window != NULL; 1291 window = window->NextWindow(list)) { 1292 if (wereVisible) 1293 clean.Include(&window->VisibleRegion()); 1294 1295 _CurrentWindows().AddWindow(window, 1296 window->Frontmost(_CurrentWindows().FirstWindow(), 1297 fCurrentWorkspace)); 1298 1299 _WindowChanged(window); 1300 } 1301 1302 BRegion dummy; 1303 _RebuildClippingForAllWindows(dummy); 1304 1305 // redraw what became visible of the window(s) 1306 1307 BRegion dirty; 1308 for (WindowLayer* window = windows.FirstWindow(); window != NULL; 1309 window = window->NextWindow(list)) { 1310 dirty.Include(&window->VisibleRegion()); 1311 } 1312 1313 dirty.Exclude(&clean); 1314 MarkDirty(dirty); 1315 1316 _UpdateFront(); 1317 1318 if (windows.FirstWindow() == fBack || fBack == NULL) 1319 _UpdateBack(); 1320 } 1321 1322 1323 /*! 1324 \brief Tries to move the specified window to the front of the screen, 1325 and make it the focus window. 1326 1327 If there are any modal windows on this screen, it might not actually 1328 become the frontmost window, though, as modal windows stay in front 1329 of their subset. 1330 */ 1331 void 1332 Desktop::ActivateWindow(WindowLayer* window) 1333 { 1334 STRACE(("ActivateWindow(%p, %s)\n", window, window ? window->Title() : "<none>")); 1335 1336 if (window == NULL) { 1337 fBack = NULL; 1338 fFront = NULL; 1339 return; 1340 } 1341 1342 bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace); 1343 1344 if (windowOnOtherWorkspace 1345 && (window->Flags() & B_NO_WORKSPACE_ACTIVATION) 1346 && !(window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE)) 1347 return; 1348 1349 // TODO: take care about floating windows 1350 1351 if (!LockAllWindows()) 1352 return; 1353 1354 if (windowOnOtherWorkspace) { 1355 // if the window wants to come to the current workspace, 1356 // do so here - else activate the workspace on which this 1357 // window is 1358 if (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) { 1359 // bring the window to the current workspace 1360 // TODO: what if this window is on multiple workspaces?!? 1361 uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace); 1362 SetWindowWorkspaces(window, workspaces); 1363 } else { 1364 // switch to the workspace on which this window is 1365 // (we'll take the first one that the window is on) 1366 uint32 workspaces = window->Workspaces(); 1367 for (int32 i = 0; i < 32; i++) { 1368 uint32 workspace = workspace_to_workspaces(i); 1369 if (workspaces & workspace) { 1370 SetWorkspace(i); 1371 break; 1372 } 1373 } 1374 } 1375 } 1376 1377 if (window == FrontWindow()) { 1378 // see if there is a normal B_AVOID_FRONT window still in front of us 1379 WindowLayer* avoidsFront = window->NextWindow(fCurrentWorkspace); 1380 while (avoidsFront && avoidsFront->IsNormal() 1381 && (avoidsFront->Flags() & B_AVOID_FRONT) == 0) { 1382 avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace); 1383 } 1384 1385 if (avoidsFront == NULL) { 1386 // we're already the frontmost window, we might just not have focus yet 1387 if ((window->Flags() & B_AVOID_FOCUS) == 0) 1388 SetFocusWindow(window); 1389 1390 UnlockAllWindows(); 1391 return; 1392 } 1393 } 1394 1395 // we don't need to redraw what is currently 1396 // visible of the window 1397 BRegion clean(window->VisibleRegion()); 1398 WindowList windows(kWorkingList); 1399 1400 WindowLayer* frontmost = window->Frontmost(); 1401 1402 _CurrentWindows().RemoveWindow(window); 1403 windows.AddWindow(window); 1404 1405 if (frontmost != NULL && frontmost->IsModal()) { 1406 // all modal windows follow their subsets to the front 1407 // (ie. they are staying in front of them, but they are 1408 // not supposed to change their order because of that) 1409 1410 WindowLayer* nextModal; 1411 for (WindowLayer* modal = frontmost; modal != NULL; modal = nextModal) { 1412 // get the next modal window 1413 nextModal = modal->NextWindow(fCurrentWorkspace); 1414 while (nextModal != NULL && !nextModal->IsModal()) { 1415 nextModal = nextModal->NextWindow(fCurrentWorkspace); 1416 } 1417 if (nextModal != NULL && !nextModal->HasInSubset(window)) 1418 nextModal = NULL; 1419 1420 _CurrentWindows().RemoveWindow(modal); 1421 windows.AddWindow(modal); 1422 } 1423 } 1424 1425 _BringWindowsToFront(windows, kWorkingList, true); 1426 if ((window->Flags() & B_AVOID_FOCUS) == 0) 1427 SetFocusWindow(window); 1428 1429 UnlockAllWindows(); 1430 } 1431 1432 1433 void 1434 Desktop::SendWindowBehind(WindowLayer* window, WindowLayer* behindOf) 1435 { 1436 // TODO: should the "not in current workspace" be handled anyway? 1437 // (the code below would have to be changed then, though) 1438 if (window == BackWindow() 1439 || !window->InWorkspace(fCurrentWorkspace) 1440 || (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace)) 1441 || !LockAllWindows()) 1442 return; 1443 1444 // Is this a valid behindOf window? 1445 if (behindOf != NULL && window->HasInSubset(behindOf)) 1446 behindOf = NULL; 1447 1448 // what is currently visible of the window 1449 // might be dirty after the window is send to back 1450 BRegion dirty(window->VisibleRegion()); 1451 1452 // detach window and re-attach at desired position 1453 WindowLayer* backmost = window->Backmost(behindOf); 1454 1455 _CurrentWindows().RemoveWindow(window); 1456 _CurrentWindows().AddWindow(window, backmost 1457 ? backmost->NextWindow(fCurrentWorkspace) : BackWindow()); 1458 1459 BRegion dummy; 1460 _RebuildClippingForAllWindows(dummy); 1461 1462 // mark everything dirty that is no longer visible 1463 BRegion clean(window->VisibleRegion()); 1464 dirty.Exclude(&clean); 1465 MarkDirty(dirty); 1466 1467 _UpdateFronts(); 1468 SetFocusWindow(_CurrentWindows().LastWindow()); 1469 _WindowChanged(window); 1470 1471 UnlockAllWindows(); 1472 } 1473 1474 1475 void 1476 Desktop::ShowWindow(WindowLayer* window) 1477 { 1478 if (!window->IsHidden()) 1479 return; 1480 1481 LockAllWindows(); 1482 1483 window->SetHidden(false); 1484 1485 if (window->InWorkspace(fCurrentWorkspace)) { 1486 _ShowWindow(window, true); 1487 _UpdateSubsetWorkspaces(window); 1488 ActivateWindow(window); 1489 } else { 1490 // then we don't need to send the fake mouse event either 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 } 1529 1530 if (fWorkspacesLayer != NULL) 1531 fWorkspacesLayer->WindowRemoved(window); 1532 1533 if (dynamic_cast<WorkspacesLayer*>(window->TopLayer()) != NULL) 1534 fWorkspacesLayer = NULL; 1535 1536 UnlockAllWindows(); 1537 1538 if (window == fWindowUnderMouse) 1539 _SendFakeMouseMoved(); 1540 } 1541 1542 1543 /*! 1544 Shows the window on the screen - it does this independently of the 1545 WindowLayer::IsHidden() state. 1546 */ 1547 void 1548 Desktop::_ShowWindow(WindowLayer* window, bool affectsOtherWindows) 1549 { 1550 BRegion background; 1551 _RebuildClippingForAllWindows(background); 1552 _SetBackground(background); 1553 _WindowChanged(window); 1554 1555 BRegion dirty(window->VisibleRegion()); 1556 1557 if (!affectsOtherWindows) { 1558 // everything that is now visible in the 1559 // window needs a redraw, but other windows 1560 // are not affected, we can call ProcessDirtyRegion() 1561 // of the window, and don't have to use MarkDirty() 1562 window->ProcessDirtyRegion(dirty); 1563 } else 1564 MarkDirty(dirty); 1565 } 1566 1567 1568 /*! 1569 Hides the window from the screen - it does this independently of the 1570 WindowLayer::IsHidden() state. 1571 */ 1572 void 1573 Desktop::_HideWindow(WindowLayer* window) 1574 { 1575 // after rebuilding the clipping, 1576 // this window will not have a visible 1577 // region anymore, so we need to remember 1578 // it now 1579 // (actually that's not true, since 1580 // hidden windows are excluded from the 1581 // clipping calculation, but anyways) 1582 BRegion dirty(window->VisibleRegion()); 1583 1584 BRegion background; 1585 _RebuildClippingForAllWindows(background); 1586 _SetBackground(background); 1587 _WindowChanged(window); 1588 1589 MarkDirty(dirty); 1590 } 1591 1592 1593 void 1594 Desktop::MoveWindowBy(WindowLayer* window, float x, float y, int32 workspace) 1595 { 1596 if (!LockAllWindows()) 1597 return; 1598 1599 if (workspace == -1) 1600 workspace = fCurrentWorkspace; 1601 1602 if (!window->IsVisible() || workspace != fCurrentWorkspace) { 1603 if (workspace != fCurrentWorkspace) { 1604 // move the window on another workspace - this doesn't change it's 1605 // current position 1606 if (window->Anchor(workspace).position == kInvalidWindowPosition) 1607 window->Anchor(workspace).position = window->Frame().LeftTop(); 1608 1609 window->Anchor(workspace).position += BPoint(x, y); 1610 _WindowChanged(window); 1611 } else 1612 window->MoveBy(x, y); 1613 1614 UnlockAllWindows(); 1615 return; 1616 } 1617 1618 // the dirty region starts with the visible area of the window being moved 1619 BRegion newDirtyRegion(window->VisibleRegion()); 1620 1621 // no more drawing for DirectWindows 1622 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 1623 1624 window->MoveBy(x, y); 1625 1626 BRegion background; 1627 _RebuildClippingForAllWindows(background); 1628 1629 // construct the region that is possible to be blitted 1630 // to move the contents of the window 1631 BRegion copyRegion(window->VisibleRegion()); 1632 copyRegion.OffsetBy(-x, -y); 1633 copyRegion.IntersectWith(&newDirtyRegion); 1634 // newDirtyRegion == the windows old visible region 1635 1636 // include the the new visible region of the window being 1637 // moved into the dirty region (for now) 1638 newDirtyRegion.Include(&window->VisibleRegion()); 1639 1640 GetDrawingEngine()->CopyRegion(©Region, x, y); 1641 1642 // allow DirectWindows to draw again after the visual 1643 // content is at the new location 1644 window->ServerWindow()->HandleDirectConnection(B_DIRECT_START | B_BUFFER_MOVED); 1645 1646 // in the dirty region, exclude the parts that we 1647 // could move by blitting 1648 copyRegion.OffsetBy(x, y); 1649 newDirtyRegion.Exclude(©Region); 1650 1651 MarkDirty(newDirtyRegion); 1652 _SetBackground(background); 1653 _WindowChanged(window); 1654 1655 UnlockAllWindows(); 1656 } 1657 1658 1659 void 1660 Desktop::ResizeWindowBy(WindowLayer* window, float x, float y) 1661 { 1662 if (!LockAllWindows()) 1663 return; 1664 1665 if (!window->IsVisible()) { 1666 window->ResizeBy(x, y, NULL); 1667 UnlockAllWindows(); 1668 return; 1669 } 1670 1671 // the dirty region for the inside of the window is 1672 // constructed by the window itself in ResizeBy() 1673 BRegion newDirtyRegion; 1674 // track the dirty region outside the window in case 1675 // it is shrunk in "previouslyOccupiedRegion" 1676 BRegion previouslyOccupiedRegion(window->VisibleRegion()); 1677 1678 window->ResizeBy(x, y, &newDirtyRegion); 1679 1680 BRegion background; 1681 _RebuildClippingForAllWindows(background); 1682 1683 // we just care for the region outside the window 1684 previouslyOccupiedRegion.Exclude(&window->VisibleRegion()); 1685 1686 // make sure the window cannot mark stuff dirty outside 1687 // its visible region... 1688 newDirtyRegion.IntersectWith(&window->VisibleRegion()); 1689 // ...because we do this outself 1690 newDirtyRegion.Include(&previouslyOccupiedRegion); 1691 1692 MarkDirty(newDirtyRegion); 1693 _SetBackground(background); 1694 _WindowChanged(window); 1695 1696 UnlockAllWindows(); 1697 } 1698 1699 1700 bool 1701 Desktop::SetWindowTabLocation(WindowLayer* window, float location) 1702 { 1703 if (!LockAllWindows()) 1704 return false; 1705 1706 BRegion dirty; 1707 bool changed = window->SetTabLocation(location, dirty); 1708 1709 if (changed && window->IsVisible() && dirty.CountRects() > 0) { 1710 BRegion stillAvailableOnScreen; 1711 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1712 _SetBackground(stillAvailableOnScreen); 1713 1714 _WindowChanged(window); 1715 _TriggerWindowRedrawing(dirty); 1716 } 1717 1718 UnlockAllWindows(); 1719 1720 return changed; 1721 } 1722 1723 1724 bool 1725 Desktop::SetWindowDecoratorSettings(WindowLayer* window, 1726 const BMessage& settings) 1727 { 1728 // TODO: almost exact code duplication to above function... 1729 1730 if (!LockAllWindows()) 1731 return false; 1732 1733 BRegion dirty; 1734 bool changed = window->SetDecoratorSettings(settings, dirty); 1735 1736 if (changed && window->IsVisible() && dirty.CountRects() > 0) { 1737 BRegion stillAvailableOnScreen; 1738 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1739 _SetBackground(stillAvailableOnScreen); 1740 1741 _TriggerWindowRedrawing(dirty); 1742 } 1743 1744 UnlockAllWindows(); 1745 1746 return changed; 1747 } 1748 1749 1750 /*! 1751 Updates the workspaces of all subset windows with regard to the 1752 specifed window. 1753 If newIndex is not -1, it will move all subset windows that belong to 1754 the specifed window to the new workspace; this form is only called by 1755 SetWorkspace(). 1756 */ 1757 void 1758 Desktop::_UpdateSubsetWorkspaces(WindowLayer* window, int32 previousIndex, 1759 int32 newIndex) 1760 { 1761 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, window->Title())); 1762 1763 // if the window is hidden, the subset windows are up-to-date already 1764 if (!window->IsNormal() || window->IsHidden()) 1765 return; 1766 1767 for (WindowLayer* subset = fSubsetWindows.FirstWindow(); subset != NULL; 1768 subset = subset->NextWindow(kSubsetList)) { 1769 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL 1770 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) { 1771 // These windows are always visible on all workspaces, 1772 // no need to update them. 1773 continue; 1774 } 1775 1776 if (subset->IsFloating()) { 1777 // Floating windows are inserted and removed to the current 1778 // workspace as the need arises - they are not handled here 1779 // but in _UpdateFront() 1780 continue; 1781 } 1782 1783 if (subset->HasInSubset(window)) { 1784 // adopt the workspace change 1785 if (newIndex != -1) { 1786 _Windows(newIndex).AddWindow(subset); 1787 _Windows(previousIndex).RemoveWindow(subset); 1788 } else 1789 SetWindowWorkspaces(subset, subset->SubsetWorkspaces()); 1790 } 1791 } 1792 } 1793 1794 1795 /*! 1796 \brief Adds or removes the window to or from the workspaces it's on. 1797 */ 1798 void 1799 Desktop::_ChangeWindowWorkspaces(WindowLayer* window, uint32 oldWorkspaces, 1800 uint32 newWorkspaces) 1801 { 1802 // apply changes to the workspaces' window lists 1803 1804 LockAllWindows(); 1805 1806 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1807 if (workspace_in_workspaces(i, oldWorkspaces)) { 1808 // window is on this workspace, is it anymore? 1809 if (!workspace_in_workspaces(i, newWorkspaces)) { 1810 _Windows(i).RemoveWindow(window); 1811 1812 if (i == CurrentWorkspace()) { 1813 // remove its appearance from the current workspace 1814 window->SetCurrentWorkspace(-1); 1815 1816 if (!window->IsHidden()) 1817 _HideWindow(window); 1818 } 1819 } 1820 } else { 1821 // window was not on this workspace, is it now? 1822 if (workspace_in_workspaces(i, newWorkspaces)) { 1823 _Windows(i).AddWindow(window, 1824 window->Frontmost(_Windows(i).FirstWindow(), i)); 1825 1826 if (i == CurrentWorkspace()) { 1827 // make the window visible in current workspace 1828 window->SetCurrentWorkspace(fCurrentWorkspace); 1829 1830 if (!window->IsHidden()) { 1831 // this only affects other windows if this windows has floating or 1832 // modal windows that need to be shown as well 1833 // TODO: take care of this 1834 _ShowWindow(window, FrontWindow() == window); 1835 } 1836 } 1837 } 1838 } 1839 } 1840 1841 // take care about modals and floating windows 1842 _UpdateSubsetWorkspaces(window); 1843 1844 UnlockAllWindows(); 1845 } 1846 1847 1848 void 1849 Desktop::SetWindowWorkspaces(WindowLayer* window, uint32 workspaces) 1850 { 1851 LockAllWindows(); 1852 1853 if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE) 1854 workspaces = workspace_to_workspaces(CurrentWorkspace()); 1855 1856 uint32 oldWorkspaces = window->Workspaces(); 1857 1858 window->WorkspacesChanged(oldWorkspaces, workspaces); 1859 _ChangeWindowWorkspaces(window, oldWorkspaces, workspaces); 1860 1861 UnlockAllWindows(); 1862 } 1863 1864 1865 /*! \brief Adds the window to the desktop. 1866 At this point, the window is still hidden and must be shown explicetly 1867 via ShowWindow(). 1868 */ 1869 void 1870 Desktop::AddWindow(WindowLayer *window) 1871 { 1872 LockAllWindows(); 1873 1874 fAllWindows.AddWindow(window); 1875 if (!window->IsNormal()) 1876 fSubsetWindows.AddWindow(window); 1877 1878 if (window->IsNormal()) { 1879 if (window->Workspaces() == B_CURRENT_WORKSPACE) 1880 window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace())); 1881 } else { 1882 // subset windows are visible on all workspaces their subset is on 1883 window->SetWorkspaces(window->SubsetWorkspaces()); 1884 } 1885 1886 _ChangeWindowWorkspaces(window, 0, window->Workspaces()); 1887 UnlockAllWindows(); 1888 } 1889 1890 1891 void 1892 Desktop::RemoveWindow(WindowLayer *window) 1893 { 1894 LockAllWindows(); 1895 1896 if (!window->IsHidden()) 1897 HideWindow(window); 1898 1899 fAllWindows.RemoveWindow(window); 1900 if (!window->IsNormal()) 1901 fSubsetWindows.RemoveWindow(window); 1902 1903 _ChangeWindowWorkspaces(window, window->Workspaces(), 0); 1904 UnlockAllWindows(); 1905 1906 // make sure this window won't get any events anymore 1907 1908 EventDispatcher().RemoveTarget(window->EventTarget()); 1909 } 1910 1911 1912 bool 1913 Desktop::AddWindowToSubset(WindowLayer* subset, WindowLayer* window) 1914 { 1915 if (!subset->AddToSubset(window)) 1916 return false; 1917 1918 _ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces()); 1919 return true; 1920 } 1921 1922 1923 void 1924 Desktop::RemoveWindowFromSubset(WindowLayer* subset, WindowLayer* window) 1925 { 1926 subset->RemoveFromSubset(window); 1927 _ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces()); 1928 } 1929 1930 1931 void 1932 Desktop::SetWindowLook(WindowLayer *window, window_look newLook) 1933 { 1934 if (window->Look() == newLook) 1935 return; 1936 1937 if (!LockAllWindows()) 1938 return; 1939 1940 BRegion dirty; 1941 window->SetLook(newLook, &dirty); 1942 // TODO: test what happens when the window 1943 // finds out it needs to resize itself... 1944 1945 BRegion stillAvailableOnScreen; 1946 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1947 _SetBackground(stillAvailableOnScreen); 1948 _WindowChanged(window); 1949 1950 _TriggerWindowRedrawing(dirty); 1951 1952 UnlockAllWindows(); 1953 } 1954 1955 1956 void 1957 Desktop::SetWindowFeel(WindowLayer *window, window_feel newFeel) 1958 { 1959 if (window->Feel() == newFeel) 1960 return; 1961 1962 LockAllWindows(); 1963 1964 bool wasNormal = window->IsNormal(); 1965 1966 window->SetFeel(newFeel); 1967 1968 // move the window out of or into the subset window list as needed 1969 if (window->IsNormal() && !wasNormal) 1970 fSubsetWindows.RemoveWindow(window); 1971 else if (!window->IsNormal() && wasNormal) 1972 fSubsetWindows.AddWindow(window); 1973 1974 // A normal window that was once a floating or modal window will 1975 // adopt the window's current workspaces 1976 1977 if (!window->IsNormal()) 1978 _ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces()); 1979 1980 // make sure the window has the correct position in the window lists 1981 // (ie. all floating windows have to be on the top, ...) 1982 1983 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1984 if (!workspace_in_workspaces(i, window->Workspaces())) 1985 continue; 1986 1987 bool changed = false; 1988 BRegion visibleBefore; 1989 if (i == fCurrentWorkspace && window->IsVisible()) 1990 visibleBefore = window->VisibleRegion(); 1991 1992 WindowLayer* backmost = window->Backmost(_Windows(i).LastWindow(), i); 1993 if (backmost != NULL) { 1994 // check if the backmost window is really behind it 1995 WindowLayer* previous = window->PreviousWindow(i); 1996 while (previous != NULL) { 1997 if (previous == backmost) 1998 break; 1999 2000 previous = previous->PreviousWindow(i); 2001 } 2002 2003 if (previous == NULL) { 2004 // need to reinsert window before its backmost window 2005 _Windows(i).RemoveWindow(window); 2006 _Windows(i).AddWindow(window, backmost->NextWindow(i)); 2007 changed = true; 2008 } 2009 } 2010 2011 WindowLayer* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i); 2012 if (frontmost != NULL) { 2013 // check if the frontmost window is really in front of it 2014 WindowLayer* next = window->NextWindow(i); 2015 while (next != NULL) { 2016 if (next == frontmost) 2017 break; 2018 2019 next = next->NextWindow(i); 2020 } 2021 2022 if (next == NULL) { 2023 // need to reinsert window behind its frontmost window 2024 _Windows(i).RemoveWindow(window); 2025 _Windows(i).AddWindow(window, frontmost); 2026 changed = true; 2027 } 2028 } 2029 2030 if (i == fCurrentWorkspace && changed) { 2031 BRegion dummy; 2032 _RebuildClippingForAllWindows(dummy); 2033 2034 // mark everything dirty that is no longer visible, or 2035 // is now visible and wasn't before 2036 BRegion visibleAfter(window->VisibleRegion()); 2037 BRegion dirty(visibleAfter); 2038 dirty.Exclude(&visibleBefore); 2039 visibleBefore.Exclude(&visibleAfter); 2040 dirty.Include(&visibleBefore); 2041 2042 MarkDirty(dirty); 2043 } 2044 } 2045 2046 _UpdateFronts(); 2047 2048 if (window == FocusWindow() && !window->IsVisible()) 2049 SetFocusWindow(_CurrentWindows().LastWindow()); 2050 2051 UnlockAllWindows(); 2052 } 2053 2054 2055 void 2056 Desktop::SetWindowFlags(WindowLayer *window, uint32 newFlags) 2057 { 2058 if (window->Flags() == newFlags) 2059 return; 2060 2061 if (!LockAllWindows()) 2062 return; 2063 2064 BRegion dirty; 2065 window->SetFlags(newFlags, &dirty); 2066 // TODO: test what happens when the window 2067 // finds out it needs to resize itself... 2068 2069 BRegion stillAvailableOnScreen; 2070 _RebuildClippingForAllWindows(stillAvailableOnScreen); 2071 _SetBackground(stillAvailableOnScreen); 2072 _WindowChanged(window); 2073 2074 _TriggerWindowRedrawing(dirty); 2075 2076 UnlockAllWindows(); 2077 } 2078 2079 2080 void 2081 Desktop::SetWindowTitle(WindowLayer *window, const char* title) 2082 { 2083 if (!LockAllWindows()) 2084 return; 2085 2086 BRegion dirty; 2087 window->SetTitle(title, dirty); 2088 2089 if (window->IsVisible() && dirty.CountRects() > 0) { 2090 BRegion stillAvailableOnScreen; 2091 _RebuildClippingForAllWindows(stillAvailableOnScreen); 2092 _SetBackground(stillAvailableOnScreen); 2093 2094 _TriggerWindowRedrawing(dirty); 2095 } 2096 2097 UnlockAllWindows(); 2098 } 2099 2100 2101 /*! 2102 Returns the window under the mouse cursor. 2103 You need to have acquired the All Windows lock when calling this method. 2104 */ 2105 WindowLayer* 2106 Desktop::WindowAt(BPoint where) 2107 { 2108 for (WindowLayer* window = _CurrentWindows().LastWindow(); window; 2109 window = window->PreviousWindow(fCurrentWorkspace)) { 2110 if (window->IsVisible() && window->VisibleRegion().Contains(where)) 2111 return window; 2112 } 2113 2114 return NULL; 2115 } 2116 2117 2118 void 2119 Desktop::SetMouseEventWindow(WindowLayer* window) 2120 { 2121 fMouseEventWindow = window; 2122 } 2123 2124 2125 void 2126 Desktop::SetViewUnderMouse(const WindowLayer* window, int32 viewToken) 2127 { 2128 fWindowUnderMouse = window; 2129 fViewUnderMouse = viewToken; 2130 } 2131 2132 2133 int32 2134 Desktop::ViewUnderMouse(const WindowLayer* window) 2135 { 2136 if (window != NULL && fWindowUnderMouse == window) 2137 return fViewUnderMouse; 2138 2139 return B_NULL_TOKEN; 2140 } 2141 2142 2143 WindowLayer * 2144 Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID) 2145 { 2146 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2147 window = window->NextWindow(kAllWindowList)) { 2148 if (window->ServerWindow()->ClientToken() == token 2149 && window->ServerWindow()->ClientTeam() == teamID) { 2150 return window; 2151 } 2152 } 2153 2154 return NULL; 2155 } 2156 2157 2158 void 2159 Desktop::MinimizeApplication(team_id team) 2160 { 2161 AutoWriteLocker locker(fWindowLock); 2162 2163 // Just minimize all windows of that application 2164 2165 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2166 window = window->NextWindow(kAllWindowList)) { 2167 if (window->ServerWindow()->ClientTeam() != team) 2168 continue; 2169 2170 window->ServerWindow()->NotifyMinimize(true); 2171 } 2172 } 2173 2174 2175 void 2176 Desktop::BringApplicationToFront(team_id team) 2177 { 2178 AutoWriteLocker locker(fWindowLock); 2179 2180 // TODO: for now, just maximize all windows of that application 2181 2182 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2183 window = window->NextWindow(kAllWindowList)) { 2184 if (window->ServerWindow()->ClientTeam() != team) 2185 continue; 2186 2187 window->ServerWindow()->NotifyMinimize(false); 2188 } 2189 } 2190 2191 2192 void 2193 Desktop::WindowAction(int32 windowToken, int32 action) 2194 { 2195 if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT) 2196 return; 2197 2198 LockAllWindows(); 2199 2200 ::ServerWindow* serverWindow; 2201 WindowLayer* window; 2202 if (BPrivate::gDefaultTokens.GetToken(windowToken, 2203 B_SERVER_TOKEN, (void**)&serverWindow) != B_OK 2204 || (window = serverWindow->Window()) == NULL) { 2205 UnlockAllWindows(); 2206 return; 2207 } 2208 2209 if (action == B_BRING_TO_FRONT 2210 && !window->IsMinimized()) { 2211 // the window is visible, we just need to make it the front window 2212 ActivateWindow(window); 2213 } else 2214 serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW); 2215 2216 UnlockAllWindows(); 2217 } 2218 2219 2220 void 2221 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 2222 { 2223 AutoWriteLocker locker(fWindowLock); 2224 2225 // compute the number of windows 2226 2227 int32 count = 0; 2228 2229 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2230 window = window->NextWindow(kAllWindowList)) { 2231 if (team < B_OK || window->ServerWindow()->ClientTeam() == team) 2232 count++; 2233 } 2234 2235 // write list 2236 2237 sender.StartMessage(B_OK); 2238 sender.Attach<int32>(count); 2239 2240 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2241 window = window->NextWindow(kAllWindowList)) { 2242 if (team >= B_OK && window->ServerWindow()->ClientTeam() != team) 2243 continue; 2244 2245 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2246 } 2247 2248 sender.Flush(); 2249 } 2250 2251 2252 void 2253 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 2254 { 2255 AutoWriteLocker locker(fWindowLock); 2256 BAutolock tokenLocker(BPrivate::gDefaultTokens); 2257 2258 ::ServerWindow* window; 2259 if (BPrivate::gDefaultTokens.GetToken(serverToken, 2260 B_SERVER_TOKEN, (void**)&window) != B_OK) { 2261 sender.StartMessage(B_ENTRY_NOT_FOUND); 2262 sender.Flush(); 2263 return; 2264 } 2265 2266 window_info info; 2267 window->GetInfo(info); 2268 2269 int32 length = window->Title() ? strlen(window->Title()) : 0; 2270 2271 sender.StartMessage(B_OK); 2272 sender.Attach<int32>(sizeof(window_info) + length + 1); 2273 sender.Attach(&info, sizeof(window_info)); 2274 if (length > 0) 2275 sender.Attach(window->Title(), length + 1); 2276 else 2277 sender.Attach<char>('\0'); 2278 sender.Flush(); 2279 } 2280 2281 2282 void 2283 Desktop::MarkDirty(BRegion& region) 2284 { 2285 if (region.CountRects() == 0) 2286 return; 2287 2288 if (LockAllWindows()) { 2289 // send redraw messages to all windows intersecting the dirty region 2290 _TriggerWindowRedrawing(region); 2291 2292 UnlockAllWindows(); 2293 } 2294 } 2295 2296 2297 void 2298 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen) 2299 { 2300 // the available region on screen starts with the entire screen area 2301 // each window on the screen will take a portion from that area 2302 2303 // figure out what the entire screen area is 2304 stillAvailableOnScreen = fScreenRegion; 2305 2306 // set clipping of each window 2307 for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL; 2308 window = window->PreviousWindow(fCurrentWorkspace)) { 2309 if (!window->IsHidden()) { 2310 window->SetClipping(&stillAvailableOnScreen); 2311 // that windows region is not available on screen anymore 2312 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 2313 } 2314 } 2315 } 2316 2317 2318 void 2319 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion) 2320 { 2321 // send redraw messages to all windows intersecting the dirty region 2322 for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL; 2323 window = window->PreviousWindow(fCurrentWorkspace)) { 2324 if (!window->IsHidden() 2325 && newDirtyRegion.Intersects(window->VisibleRegion().Frame())) 2326 window->ProcessDirtyRegion(newDirtyRegion); 2327 } 2328 } 2329 2330 2331 void 2332 Desktop::_SetBackground(BRegion& background) 2333 { 2334 // NOTE: the drawing operation is caried out 2335 // in the clipping region rebuild, but it is 2336 // ok actually, because it also avoids trails on 2337 // moving windows 2338 2339 // remember the region not covered by any windows 2340 // and redraw the dirty background 2341 BRegion dirtyBackground(background); 2342 dirtyBackground.Exclude(&fBackgroundRegion); 2343 dirtyBackground.IntersectWith(&background); 2344 fBackgroundRegion = background; 2345 if (dirtyBackground.Frame().IsValid()) { 2346 if (GetDrawingEngine()->LockParallelAccess()) { 2347 GetDrawingEngine()->FillRegion(dirtyBackground, 2348 fWorkspaces[fCurrentWorkspace].Color()); 2349 2350 GetDrawingEngine()->UnlockParallelAccess(); 2351 } 2352 } 2353 } 2354