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