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