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