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