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 UnlockAllWindows(); 1494 return; 1495 } 1496 1497 if (WorkspacesLayer* layer = dynamic_cast<WorkspacesLayer*>(window->TopLayer())) 1498 fWorkspacesLayer = layer; 1499 1500 UnlockAllWindows(); 1501 1502 // If the mouse cursor is directly over the newly visible window, 1503 // we'll send a fake mouse moved message to the window, so that 1504 // it knows the mouse is over it. 1505 1506 _SendFakeMouseMoved(window); 1507 } 1508 1509 1510 void 1511 Desktop::HideWindow(WindowLayer* window) 1512 { 1513 if (window->IsHidden()) 1514 return; 1515 1516 if (!LockAllWindows()) 1517 return; 1518 1519 window->SetHidden(true); 1520 if (fMouseEventWindow == window) 1521 fMouseEventWindow = NULL; 1522 1523 if (window->InWorkspace(fCurrentWorkspace)) { 1524 _UpdateSubsetWorkspaces(window); 1525 _HideWindow(window); 1526 _UpdateFronts(); 1527 1528 if (FocusWindow() == window) 1529 SetFocusWindow(_CurrentWindows().LastWindow()); 1530 } 1531 1532 if (fWorkspacesLayer != NULL) 1533 fWorkspacesLayer->WindowRemoved(window); 1534 1535 if (dynamic_cast<WorkspacesLayer*>(window->TopLayer()) != NULL) 1536 fWorkspacesLayer = NULL; 1537 1538 UnlockAllWindows(); 1539 1540 if (window == fWindowUnderMouse) 1541 _SendFakeMouseMoved(); 1542 } 1543 1544 1545 /*! 1546 Shows the window on the screen - it does this independently of the 1547 WindowLayer::IsHidden() state. 1548 */ 1549 void 1550 Desktop::_ShowWindow(WindowLayer* window, bool affectsOtherWindows) 1551 { 1552 BRegion background; 1553 _RebuildClippingForAllWindows(background); 1554 _SetBackground(background); 1555 _WindowChanged(window); 1556 1557 BRegion dirty(window->VisibleRegion()); 1558 1559 if (!affectsOtherWindows) { 1560 // everything that is now visible in the 1561 // window needs a redraw, but other windows 1562 // are not affected, we can call ProcessDirtyRegion() 1563 // of the window, and don't have to use MarkDirty() 1564 window->ProcessDirtyRegion(dirty); 1565 } else 1566 MarkDirty(dirty); 1567 } 1568 1569 1570 /*! 1571 Hides the window from the screen - it does this independently of the 1572 WindowLayer::IsHidden() state. 1573 */ 1574 void 1575 Desktop::_HideWindow(WindowLayer* window) 1576 { 1577 // after rebuilding the clipping, 1578 // this window will not have a visible 1579 // region anymore, so we need to remember 1580 // it now 1581 // (actually that's not true, since 1582 // hidden windows are excluded from the 1583 // clipping calculation, but anyways) 1584 BRegion dirty(window->VisibleRegion()); 1585 1586 BRegion background; 1587 _RebuildClippingForAllWindows(background); 1588 _SetBackground(background); 1589 _WindowChanged(window); 1590 1591 MarkDirty(dirty); 1592 } 1593 1594 1595 void 1596 Desktop::MoveWindowBy(WindowLayer* window, float x, float y, int32 workspace) 1597 { 1598 if (!LockAllWindows()) 1599 return; 1600 1601 if (workspace == -1) 1602 workspace = fCurrentWorkspace; 1603 1604 if (!window->IsVisible() || workspace != fCurrentWorkspace) { 1605 if (workspace != fCurrentWorkspace) { 1606 // move the window on another workspace - this doesn't change it's 1607 // current position 1608 if (window->Anchor(workspace).position == kInvalidWindowPosition) 1609 window->Anchor(workspace).position = window->Frame().LeftTop(); 1610 1611 window->Anchor(workspace).position += BPoint(x, y); 1612 _WindowChanged(window); 1613 } else 1614 window->MoveBy(x, y); 1615 1616 UnlockAllWindows(); 1617 return; 1618 } 1619 1620 // the dirty region starts with the visible area of the window being moved 1621 BRegion newDirtyRegion(window->VisibleRegion()); 1622 1623 // no more drawing for DirectWindows 1624 window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP); 1625 1626 window->MoveBy(x, y); 1627 1628 BRegion background; 1629 _RebuildClippingForAllWindows(background); 1630 1631 // construct the region that is possible to be blitted 1632 // to move the contents of the window 1633 BRegion copyRegion(window->VisibleRegion()); 1634 copyRegion.OffsetBy(-x, -y); 1635 copyRegion.IntersectWith(&newDirtyRegion); 1636 // newDirtyRegion == the windows old visible region 1637 1638 // include the the new visible region of the window being 1639 // moved into the dirty region (for now) 1640 newDirtyRegion.Include(&window->VisibleRegion()); 1641 1642 GetDrawingEngine()->CopyRegion(©Region, x, y); 1643 1644 // allow DirectWindows to draw again after the visual 1645 // content is at the new location 1646 window->ServerWindow()->HandleDirectConnection(B_DIRECT_START | B_BUFFER_MOVED); 1647 1648 // in the dirty region, exclude the parts that we 1649 // could move by blitting 1650 copyRegion.OffsetBy(x, y); 1651 newDirtyRegion.Exclude(©Region); 1652 1653 MarkDirty(newDirtyRegion); 1654 _SetBackground(background); 1655 _WindowChanged(window); 1656 1657 UnlockAllWindows(); 1658 } 1659 1660 1661 void 1662 Desktop::ResizeWindowBy(WindowLayer* window, float x, float y) 1663 { 1664 if (!LockAllWindows()) 1665 return; 1666 1667 if (!window->IsVisible()) { 1668 window->ResizeBy(x, y, NULL); 1669 UnlockAllWindows(); 1670 return; 1671 } 1672 1673 // the dirty region for the inside of the window is 1674 // constructed by the window itself in ResizeBy() 1675 BRegion newDirtyRegion; 1676 // track the dirty region outside the window in case 1677 // it is shrunk in "previouslyOccupiedRegion" 1678 BRegion previouslyOccupiedRegion(window->VisibleRegion()); 1679 1680 window->ResizeBy(x, y, &newDirtyRegion); 1681 1682 BRegion background; 1683 _RebuildClippingForAllWindows(background); 1684 1685 // we just care for the region outside the window 1686 previouslyOccupiedRegion.Exclude(&window->VisibleRegion()); 1687 1688 // make sure the window cannot mark stuff dirty outside 1689 // its visible region... 1690 newDirtyRegion.IntersectWith(&window->VisibleRegion()); 1691 // ...because we do this outself 1692 newDirtyRegion.Include(&previouslyOccupiedRegion); 1693 1694 MarkDirty(newDirtyRegion); 1695 _SetBackground(background); 1696 _WindowChanged(window); 1697 1698 UnlockAllWindows(); 1699 } 1700 1701 1702 bool 1703 Desktop::SetWindowTabLocation(WindowLayer* window, float location) 1704 { 1705 if (!LockAllWindows()) 1706 return false; 1707 1708 BRegion dirty; 1709 bool changed = window->SetTabLocation(location, dirty); 1710 1711 if (changed && window->IsVisible() && dirty.CountRects() > 0) { 1712 BRegion stillAvailableOnScreen; 1713 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1714 _SetBackground(stillAvailableOnScreen); 1715 1716 _WindowChanged(window); 1717 _TriggerWindowRedrawing(dirty); 1718 } 1719 1720 UnlockAllWindows(); 1721 1722 return changed; 1723 } 1724 1725 1726 bool 1727 Desktop::SetWindowDecoratorSettings(WindowLayer* window, 1728 const BMessage& settings) 1729 { 1730 // TODO: almost exact code duplication to above function... 1731 1732 if (!LockAllWindows()) 1733 return false; 1734 1735 BRegion dirty; 1736 bool changed = window->SetDecoratorSettings(settings, dirty); 1737 1738 if (changed && window->IsVisible() && dirty.CountRects() > 0) { 1739 BRegion stillAvailableOnScreen; 1740 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1741 _SetBackground(stillAvailableOnScreen); 1742 1743 _TriggerWindowRedrawing(dirty); 1744 } 1745 1746 UnlockAllWindows(); 1747 1748 return changed; 1749 } 1750 1751 1752 /*! 1753 Updates the workspaces of all subset windows with regard to the 1754 specifed window. 1755 If newIndex is not -1, it will move all subset windows that belong to 1756 the specifed window to the new workspace; this form is only called by 1757 SetWorkspace(). 1758 */ 1759 void 1760 Desktop::_UpdateSubsetWorkspaces(WindowLayer* window, int32 previousIndex, 1761 int32 newIndex) 1762 { 1763 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, window->Title())); 1764 1765 // if the window is hidden, the subset windows are up-to-date already 1766 if (!window->IsNormal() || window->IsHidden()) 1767 return; 1768 1769 for (WindowLayer* subset = fSubsetWindows.FirstWindow(); subset != NULL; 1770 subset = subset->NextWindow(kSubsetList)) { 1771 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL 1772 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) { 1773 // These windows are always visible on all workspaces, 1774 // no need to update them. 1775 continue; 1776 } 1777 1778 if (subset->IsFloating()) { 1779 // Floating windows are inserted and removed to the current 1780 // workspace as the need arises - they are not handled here 1781 // but in _UpdateFront() 1782 continue; 1783 } 1784 1785 if (subset->HasInSubset(window)) { 1786 // adopt the workspace change 1787 if (newIndex != -1) { 1788 _Windows(newIndex).AddWindow(subset); 1789 _Windows(previousIndex).RemoveWindow(subset); 1790 } else 1791 SetWindowWorkspaces(subset, subset->SubsetWorkspaces()); 1792 } 1793 } 1794 } 1795 1796 1797 /*! 1798 \brief Adds or removes the window to or from the workspaces it's on. 1799 */ 1800 void 1801 Desktop::_ChangeWindowWorkspaces(WindowLayer* window, uint32 oldWorkspaces, 1802 uint32 newWorkspaces) 1803 { 1804 // apply changes to the workspaces' window lists 1805 1806 LockAllWindows(); 1807 1808 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1809 if (workspace_in_workspaces(i, oldWorkspaces)) { 1810 // window is on this workspace, is it anymore? 1811 if (!workspace_in_workspaces(i, newWorkspaces)) { 1812 _Windows(i).RemoveWindow(window); 1813 1814 if (i == CurrentWorkspace()) { 1815 // remove its appearance from the current workspace 1816 window->SetCurrentWorkspace(-1); 1817 1818 if (!window->IsHidden()) 1819 _HideWindow(window); 1820 } 1821 } 1822 } else { 1823 // window was not on this workspace, is it now? 1824 if (workspace_in_workspaces(i, newWorkspaces)) { 1825 _Windows(i).AddWindow(window, 1826 window->Frontmost(_Windows(i).FirstWindow(), i)); 1827 1828 if (i == CurrentWorkspace()) { 1829 // make the window visible in current workspace 1830 window->SetCurrentWorkspace(fCurrentWorkspace); 1831 1832 if (!window->IsHidden()) { 1833 // this only affects other windows if this windows has floating or 1834 // modal windows that need to be shown as well 1835 // TODO: take care of this 1836 _ShowWindow(window, FrontWindow() == window); 1837 } 1838 } 1839 } 1840 } 1841 } 1842 1843 // take care about modals and floating windows 1844 _UpdateSubsetWorkspaces(window); 1845 1846 UnlockAllWindows(); 1847 } 1848 1849 1850 void 1851 Desktop::SetWindowWorkspaces(WindowLayer* window, uint32 workspaces) 1852 { 1853 LockAllWindows(); 1854 1855 if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE) 1856 workspaces = workspace_to_workspaces(CurrentWorkspace()); 1857 1858 uint32 oldWorkspaces = window->Workspaces(); 1859 1860 window->WorkspacesChanged(oldWorkspaces, workspaces); 1861 _ChangeWindowWorkspaces(window, oldWorkspaces, workspaces); 1862 1863 UnlockAllWindows(); 1864 } 1865 1866 1867 /*! \brief Adds the window to the desktop. 1868 At this point, the window is still hidden and must be shown explicetly 1869 via ShowWindow(). 1870 */ 1871 void 1872 Desktop::AddWindow(WindowLayer *window) 1873 { 1874 LockAllWindows(); 1875 1876 fAllWindows.AddWindow(window); 1877 if (!window->IsNormal()) 1878 fSubsetWindows.AddWindow(window); 1879 1880 if (window->IsNormal()) { 1881 if (window->Workspaces() == B_CURRENT_WORKSPACE) 1882 window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace())); 1883 } else { 1884 // subset windows are visible on all workspaces their subset is on 1885 window->SetWorkspaces(window->SubsetWorkspaces()); 1886 } 1887 1888 _ChangeWindowWorkspaces(window, 0, window->Workspaces()); 1889 UnlockAllWindows(); 1890 } 1891 1892 1893 void 1894 Desktop::RemoveWindow(WindowLayer *window) 1895 { 1896 LockAllWindows(); 1897 1898 if (!window->IsHidden()) 1899 HideWindow(window); 1900 1901 fAllWindows.RemoveWindow(window); 1902 if (!window->IsNormal()) 1903 fSubsetWindows.RemoveWindow(window); 1904 1905 _ChangeWindowWorkspaces(window, window->Workspaces(), 0); 1906 UnlockAllWindows(); 1907 1908 // make sure this window won't get any events anymore 1909 1910 EventDispatcher().RemoveTarget(window->EventTarget()); 1911 } 1912 1913 1914 bool 1915 Desktop::AddWindowToSubset(WindowLayer* subset, WindowLayer* window) 1916 { 1917 if (!subset->AddToSubset(window)) 1918 return false; 1919 1920 _ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces()); 1921 return true; 1922 } 1923 1924 1925 void 1926 Desktop::RemoveWindowFromSubset(WindowLayer* subset, WindowLayer* window) 1927 { 1928 subset->RemoveFromSubset(window); 1929 _ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces()); 1930 } 1931 1932 1933 void 1934 Desktop::SetWindowLook(WindowLayer *window, window_look newLook) 1935 { 1936 if (window->Look() == newLook) 1937 return; 1938 1939 if (!LockAllWindows()) 1940 return; 1941 1942 BRegion dirty; 1943 window->SetLook(newLook, &dirty); 1944 // TODO: test what happens when the window 1945 // finds out it needs to resize itself... 1946 1947 BRegion stillAvailableOnScreen; 1948 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1949 _SetBackground(stillAvailableOnScreen); 1950 _WindowChanged(window); 1951 1952 _TriggerWindowRedrawing(dirty); 1953 1954 UnlockAllWindows(); 1955 } 1956 1957 1958 void 1959 Desktop::SetWindowFeel(WindowLayer *window, window_feel newFeel) 1960 { 1961 if (window->Feel() == newFeel) 1962 return; 1963 1964 LockAllWindows(); 1965 1966 bool wasNormal = window->IsNormal(); 1967 1968 window->SetFeel(newFeel); 1969 1970 // move the window out of or into the subset window list as needed 1971 if (window->IsNormal() && !wasNormal) 1972 fSubsetWindows.RemoveWindow(window); 1973 else if (!window->IsNormal() && wasNormal) 1974 fSubsetWindows.AddWindow(window); 1975 1976 // A normal window that was once a floating or modal window will 1977 // adopt the window's current workspaces 1978 1979 if (!window->IsNormal()) 1980 _ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces()); 1981 1982 // make sure the window has the correct position in the window lists 1983 // (ie. all floating windows have to be on the top, ...) 1984 1985 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1986 if (!workspace_in_workspaces(i, window->Workspaces())) 1987 continue; 1988 1989 bool changed = false; 1990 BRegion visibleBefore; 1991 if (i == fCurrentWorkspace && window->IsVisible()) 1992 visibleBefore = window->VisibleRegion(); 1993 1994 WindowLayer* backmost = window->Backmost(_Windows(i).LastWindow(), i); 1995 if (backmost != NULL) { 1996 // check if the backmost window is really behind it 1997 WindowLayer* previous = window->PreviousWindow(i); 1998 while (previous != NULL) { 1999 if (previous == backmost) 2000 break; 2001 2002 previous = previous->PreviousWindow(i); 2003 } 2004 2005 if (previous == NULL) { 2006 // need to reinsert window before its backmost window 2007 _Windows(i).RemoveWindow(window); 2008 _Windows(i).AddWindow(window, backmost->NextWindow(i)); 2009 changed = true; 2010 } 2011 } 2012 2013 WindowLayer* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i); 2014 if (frontmost != NULL) { 2015 // check if the frontmost window is really in front of it 2016 WindowLayer* next = window->NextWindow(i); 2017 while (next != NULL) { 2018 if (next == frontmost) 2019 break; 2020 2021 next = next->NextWindow(i); 2022 } 2023 2024 if (next == NULL) { 2025 // need to reinsert window behind its frontmost window 2026 _Windows(i).RemoveWindow(window); 2027 _Windows(i).AddWindow(window, frontmost); 2028 changed = true; 2029 } 2030 } 2031 2032 if (i == fCurrentWorkspace && changed) { 2033 BRegion dummy; 2034 _RebuildClippingForAllWindows(dummy); 2035 2036 // mark everything dirty that is no longer visible, or 2037 // is now visible and wasn't before 2038 BRegion visibleAfter(window->VisibleRegion()); 2039 BRegion dirty(visibleAfter); 2040 dirty.Exclude(&visibleBefore); 2041 visibleBefore.Exclude(&visibleAfter); 2042 dirty.Include(&visibleBefore); 2043 2044 MarkDirty(dirty); 2045 } 2046 } 2047 2048 _UpdateFronts(); 2049 2050 if (window == FocusWindow() && !window->IsVisible()) 2051 SetFocusWindow(_CurrentWindows().LastWindow()); 2052 2053 UnlockAllWindows(); 2054 } 2055 2056 2057 void 2058 Desktop::SetWindowFlags(WindowLayer *window, uint32 newFlags) 2059 { 2060 if (window->Flags() == newFlags) 2061 return; 2062 2063 if (!LockAllWindows()) 2064 return; 2065 2066 BRegion dirty; 2067 window->SetFlags(newFlags, &dirty); 2068 // TODO: test what happens when the window 2069 // finds out it needs to resize itself... 2070 2071 BRegion stillAvailableOnScreen; 2072 _RebuildClippingForAllWindows(stillAvailableOnScreen); 2073 _SetBackground(stillAvailableOnScreen); 2074 _WindowChanged(window); 2075 2076 _TriggerWindowRedrawing(dirty); 2077 2078 UnlockAllWindows(); 2079 } 2080 2081 2082 void 2083 Desktop::SetWindowTitle(WindowLayer *window, const char* title) 2084 { 2085 if (!LockAllWindows()) 2086 return; 2087 2088 BRegion dirty; 2089 window->SetTitle(title, dirty); 2090 2091 if (window->IsVisible() && dirty.CountRects() > 0) { 2092 BRegion stillAvailableOnScreen; 2093 _RebuildClippingForAllWindows(stillAvailableOnScreen); 2094 _SetBackground(stillAvailableOnScreen); 2095 2096 _TriggerWindowRedrawing(dirty); 2097 } 2098 2099 UnlockAllWindows(); 2100 } 2101 2102 2103 /*! 2104 Returns the window under the mouse cursor. 2105 You need to have acquired the All Windows lock when calling this method. 2106 */ 2107 WindowLayer* 2108 Desktop::WindowAt(BPoint where) 2109 { 2110 for (WindowLayer* window = _CurrentWindows().LastWindow(); window; 2111 window = window->PreviousWindow(fCurrentWorkspace)) { 2112 if (window->IsVisible() && window->VisibleRegion().Contains(where)) 2113 return window; 2114 } 2115 2116 return NULL; 2117 } 2118 2119 2120 void 2121 Desktop::SetMouseEventWindow(WindowLayer* window) 2122 { 2123 fMouseEventWindow = window; 2124 } 2125 2126 2127 void 2128 Desktop::SetViewUnderMouse(const WindowLayer* window, int32 viewToken) 2129 { 2130 fWindowUnderMouse = window; 2131 fViewUnderMouse = viewToken; 2132 } 2133 2134 2135 int32 2136 Desktop::ViewUnderMouse(const WindowLayer* window) 2137 { 2138 if (window != NULL && fWindowUnderMouse == window) 2139 return fViewUnderMouse; 2140 2141 return B_NULL_TOKEN; 2142 } 2143 2144 2145 WindowLayer * 2146 Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID) 2147 { 2148 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2149 window = window->NextWindow(kAllWindowList)) { 2150 if (window->ServerWindow()->ClientToken() == token 2151 && window->ServerWindow()->ClientTeam() == teamID) { 2152 return window; 2153 } 2154 } 2155 2156 return NULL; 2157 } 2158 2159 2160 void 2161 Desktop::MinimizeApplication(team_id team) 2162 { 2163 AutoWriteLocker locker(fWindowLock); 2164 2165 // Just minimize all windows of that application 2166 2167 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2168 window = window->NextWindow(kAllWindowList)) { 2169 if (window->ServerWindow()->ClientTeam() != team) 2170 continue; 2171 2172 window->ServerWindow()->NotifyMinimize(true); 2173 } 2174 } 2175 2176 2177 void 2178 Desktop::BringApplicationToFront(team_id team) 2179 { 2180 AutoWriteLocker locker(fWindowLock); 2181 2182 // TODO: for now, just maximize all windows of that application 2183 2184 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2185 window = window->NextWindow(kAllWindowList)) { 2186 if (window->ServerWindow()->ClientTeam() != team) 2187 continue; 2188 2189 window->ServerWindow()->NotifyMinimize(false); 2190 } 2191 } 2192 2193 2194 void 2195 Desktop::WindowAction(int32 windowToken, int32 action) 2196 { 2197 if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT) 2198 return; 2199 2200 LockAllWindows(); 2201 2202 ::ServerWindow* serverWindow; 2203 WindowLayer* window; 2204 if (BPrivate::gDefaultTokens.GetToken(windowToken, 2205 B_SERVER_TOKEN, (void**)&serverWindow) != B_OK 2206 || (window = serverWindow->Window()) == NULL) { 2207 UnlockAllWindows(); 2208 return; 2209 } 2210 2211 if (action == B_BRING_TO_FRONT 2212 && !window->IsMinimized()) { 2213 // the window is visible, we just need to make it the front window 2214 ActivateWindow(window); 2215 } else 2216 serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW); 2217 2218 UnlockAllWindows(); 2219 } 2220 2221 2222 void 2223 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 2224 { 2225 AutoWriteLocker locker(fWindowLock); 2226 2227 // compute the number of windows 2228 2229 int32 count = 0; 2230 2231 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2232 window = window->NextWindow(kAllWindowList)) { 2233 if (team < B_OK || window->ServerWindow()->ClientTeam() == team) 2234 count++; 2235 } 2236 2237 // write list 2238 2239 sender.StartMessage(B_OK); 2240 sender.Attach<int32>(count); 2241 2242 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 2243 window = window->NextWindow(kAllWindowList)) { 2244 if (team >= B_OK && window->ServerWindow()->ClientTeam() != team) 2245 continue; 2246 2247 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 2248 } 2249 2250 sender.Flush(); 2251 } 2252 2253 2254 void 2255 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 2256 { 2257 AutoWriteLocker locker(fWindowLock); 2258 BAutolock tokenLocker(BPrivate::gDefaultTokens); 2259 2260 ::ServerWindow* window; 2261 if (BPrivate::gDefaultTokens.GetToken(serverToken, 2262 B_SERVER_TOKEN, (void**)&window) != B_OK) { 2263 sender.StartMessage(B_ENTRY_NOT_FOUND); 2264 sender.Flush(); 2265 return; 2266 } 2267 2268 window_info info; 2269 window->GetInfo(info); 2270 2271 int32 length = window->Title() ? strlen(window->Title()) : 0; 2272 2273 sender.StartMessage(B_OK); 2274 sender.Attach<int32>(sizeof(window_info) + length + 1); 2275 sender.Attach(&info, sizeof(window_info)); 2276 if (length > 0) 2277 sender.Attach(window->Title(), length + 1); 2278 else 2279 sender.Attach<char>('\0'); 2280 sender.Flush(); 2281 } 2282 2283 2284 void 2285 Desktop::MarkDirty(BRegion& region) 2286 { 2287 if (region.CountRects() == 0) 2288 return; 2289 2290 if (LockAllWindows()) { 2291 // send redraw messages to all windows intersecting the dirty region 2292 _TriggerWindowRedrawing(region); 2293 2294 UnlockAllWindows(); 2295 } 2296 } 2297 2298 2299 void 2300 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen) 2301 { 2302 // the available region on screen starts with the entire screen area 2303 // each window on the screen will take a portion from that area 2304 2305 // figure out what the entire screen area is 2306 stillAvailableOnScreen = fScreenRegion; 2307 2308 // set clipping of each window 2309 for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL; 2310 window = window->PreviousWindow(fCurrentWorkspace)) { 2311 if (!window->IsHidden()) { 2312 window->SetClipping(&stillAvailableOnScreen); 2313 // that windows region is not available on screen anymore 2314 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 2315 } 2316 } 2317 } 2318 2319 2320 void 2321 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion) 2322 { 2323 // send redraw messages to all windows intersecting the dirty region 2324 for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL; 2325 window = window->PreviousWindow(fCurrentWorkspace)) { 2326 if (!window->IsHidden() 2327 && newDirtyRegion.Intersects(window->VisibleRegion().Frame())) 2328 window->ProcessDirtyRegion(newDirtyRegion); 2329 } 2330 } 2331 2332 2333 void 2334 Desktop::_SetBackground(BRegion& background) 2335 { 2336 // NOTE: the drawing operation is caried out 2337 // in the clipping region rebuild, but it is 2338 // ok actually, because it also avoids trails on 2339 // moving windows 2340 2341 // remember the region not covered by any windows 2342 // and redraw the dirty background 2343 BRegion dirtyBackground(background); 2344 dirtyBackground.Exclude(&fBackgroundRegion); 2345 dirtyBackground.IntersectWith(&background); 2346 fBackgroundRegion = background; 2347 if (dirtyBackground.Frame().IsValid()) { 2348 if (GetDrawingEngine()->LockParallelAccess()) { 2349 GetDrawingEngine()->FillRegion(dirtyBackground, 2350 fWorkspaces[fCurrentWorkspace].Color()); 2351 2352 GetDrawingEngine()->UnlockParallelAccess(); 2353 } 2354 } 2355 } 2356