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