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