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