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