1 /* 2 * Copyright 2001-2005, 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 "ServerApp.h" 22 #include "ServerConfig.h" 23 #include "ServerScreen.h" 24 #include "ServerWindow.h" 25 #include "WindowLayer.h" 26 #include "Workspace.h" 27 #include "WorkspacesLayer.h" 28 29 #include <WindowInfo.h> 30 #include <ServerProtocol.h> 31 32 #include <Entry.h> 33 #include <Message.h> 34 #include <MessageFilter.h> 35 #include <Region.h> 36 37 #include <stdio.h> 38 39 #if TEST_MODE 40 # include "EventStream.h" 41 #endif 42 43 //#define DEBUG_DESKTOP 44 #ifdef DEBUG_DESKTOP 45 # define STRACE(a) printf(a) 46 #else 47 # define STRACE(a) ; 48 #endif 49 50 51 class KeyboardFilter : public EventFilter { 52 public: 53 KeyboardFilter(Desktop* desktop); 54 55 virtual filter_result Filter(BMessage* message, EventTarget** _target, 56 int32* _viewToken); 57 58 private: 59 Desktop* fDesktop; 60 EventTarget* fLastFocus; 61 bigtime_t fTimestamp; 62 }; 63 64 class MouseFilter : public EventFilter { 65 public: 66 MouseFilter(Desktop* desktop); 67 68 virtual filter_result Filter(BMessage* message, EventTarget** _target, 69 int32* _viewToken); 70 71 private: 72 Desktop* fDesktop; 73 }; 74 75 76 // #pragma mark - 77 78 79 KeyboardFilter::KeyboardFilter(Desktop* desktop) 80 : 81 fDesktop(desktop), 82 fLastFocus(NULL), 83 fTimestamp(0) 84 { 85 } 86 87 88 filter_result 89 KeyboardFilter::Filter(BMessage* message, EventTarget** _target, 90 int32* /*_viewToken*/) 91 { 92 int32 key; 93 int32 modifiers; 94 95 if (message->what != B_KEY_DOWN 96 || message->FindInt32("key", &key) != B_OK 97 || message->FindInt32("modifiers", &modifiers) != B_OK) 98 return B_DISPATCH_MESSAGE; 99 100 // Check for safe video mode (F12 + l-cmd + l-ctrl + l-shift) 101 if (key == 0x0d 102 && (modifiers & (B_LEFT_COMMAND_KEY 103 | B_LEFT_CONTROL_KEY | B_LEFT_SHIFT_KEY)) != 0) 104 { 105 // TODO: Set to Safe Mode in KeyboardEventHandler:B_KEY_DOWN. 106 STRACE(("Safe Video Mode invoked - code unimplemented\n")); 107 return B_SKIP_MESSAGE; 108 } 109 110 if (key > 0x01 && key < 0x0e) { 111 // workspace change, F1-F12 112 113 #if !TEST_MODE 114 if (modifiers & B_COMMAND_KEY) 115 #else 116 if (modifiers & B_CONTROL_KEY) 117 #endif 118 { 119 STRACE(("Set Workspace %ld\n", key - 1)); 120 121 fDesktop->SetWorkspace(key - 2); 122 return B_SKIP_MESSAGE; 123 } 124 } 125 126 // TODO: this should be moved client side! 127 // (that's how it is done in BeOS, clients could need this key for 128 // different purposes - also, it's preferrable to let the client 129 // write the dump within his own environment) 130 if (key == 0xe) { 131 // screen dump, PrintScreen 132 char filename[128]; 133 BEntry entry; 134 135 int32 index = 1; 136 do { 137 sprintf(filename, "/boot/home/screen%ld.png", index++); 138 entry.SetTo(filename); 139 } while(entry.Exists()); 140 141 fDesktop->GetDrawingEngine()->DumpToFile(filename); 142 return B_SKIP_MESSAGE; 143 } 144 145 bigtime_t now = system_time(); 146 147 if (!fDesktop->LockSingleWindow()) 148 return B_DISPATCH_MESSAGE; 149 150 EventTarget* focus = NULL; 151 if (fDesktop->FocusWindow() != NULL) 152 focus = &fDesktop->FocusWindow()->EventTarget(); 153 154 // TODO: this is a try to not steal focus from the current window 155 // in case you enter some text and a window pops up you haven't 156 // triggered yourself (like a pop-up window in your browser while 157 // you're typing a password in another window) - maybe this should 158 // be done differently, though (using something like B_LOCK_WINDOW_FOCUS) 159 // (at least B_WINDOW_ACTIVATED must be postponed) 160 161 if (focus != fLastFocus && now - fTimestamp > 100000) { 162 // if the time span between the key presses is very short 163 // we keep our previous focus alive - this is save even 164 // if the target doesn't exist anymore, as we don't reset 165 // it, and the event focus passed in is always valid (or NULL) 166 *_target = focus; 167 fLastFocus = focus; 168 } 169 170 fDesktop->UnlockSingleWindow(); 171 172 // we always allow to switch focus after the enter key has pressed 173 if (key == B_ENTER) 174 fTimestamp = 0; 175 else 176 fTimestamp = now; 177 178 return B_DISPATCH_MESSAGE; 179 } 180 181 182 // #pragma mark - 183 184 185 MouseFilter::MouseFilter(Desktop* desktop) 186 : 187 fDesktop(desktop) 188 { 189 } 190 191 192 filter_result 193 MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken) 194 { 195 BPoint where; 196 if (message->FindPoint("where", &where) != B_OK) 197 return B_DISPATCH_MESSAGE; 198 199 if (!fDesktop->LockAllWindows()) 200 return B_DISPATCH_MESSAGE; 201 202 WindowLayer* window = fDesktop->MouseEventWindow(); 203 if (window == NULL) 204 window = fDesktop->WindowAt(where); 205 206 if (window != NULL) { 207 // dispatch event in the window layers 208 switch (message->what) { 209 case B_MOUSE_DOWN: 210 window->MouseDown(message, where, _viewToken); 211 break; 212 213 case B_MOUSE_UP: 214 window->MouseUp(message, where, _viewToken); 215 fDesktop->SetMouseEventWindow(NULL); 216 break; 217 218 case B_MOUSE_MOVED: 219 window->MouseMoved(message, where, _viewToken); 220 break; 221 } 222 223 if (*_viewToken != B_NULL_TOKEN) 224 *_target = &window->EventTarget(); 225 else 226 *_target = NULL; 227 } else 228 *_target = NULL; 229 230 fDesktop->UnlockAllWindows(); 231 232 return B_DISPATCH_MESSAGE; 233 } 234 235 236 // #pragma mark - 237 238 239 static inline uint32 240 workspace_to_workspaces(int32 index) 241 { 242 return 1UL << index; 243 } 244 245 246 static inline bool 247 workspace_in_workspaces(int32 index, uint32 workspaces) 248 { 249 return (workspaces & (1UL << index)) != 0; 250 } 251 252 253 // #pragma mark - 254 255 256 Desktop::Desktop(uid_t userID) 257 : MessageLooper("desktop"), 258 259 fUserID(userID), 260 fSettings(new DesktopSettings::Private()), 261 fApplicationsLock("application list"), 262 fShutdownSemaphore(-1), 263 fAllWindows(kAllWindowList), 264 fSubsetWindows(kSubsetList), 265 fWorkspacesLayer(NULL), 266 fActiveScreen(NULL), 267 fWindowLock("window lock") 268 { 269 char name[B_OS_NAME_LENGTH]; 270 Desktop::_GetLooperName(name, sizeof(name)); 271 272 for (int32 i = 0; i < kMaxWorkspaces; i++) { 273 _Windows(i).SetIndex(i); 274 } 275 276 fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name); 277 if (fMessagePort < B_OK) 278 return; 279 280 fLink.SetReceiverPort(fMessagePort); 281 gFontManager->AttachUser(fUserID); 282 } 283 284 285 Desktop::~Desktop() 286 { 287 delete fSettings; 288 289 delete_port(fMessagePort); 290 gFontManager->DetachUser(fUserID); 291 } 292 293 294 void 295 Desktop::Init() 296 { 297 fVirtualScreen.RestoreConfiguration(*this, fSettings->WorkspacesMessage(0)); 298 299 // TODO: temporary workaround, fActiveScreen will be removed 300 fActiveScreen = fVirtualScreen.ScreenAt(0); 301 302 #if TEST_MODE 303 gInputManager->AddStream(new InputServerStream); 304 #endif 305 fEventDispatcher.SetTo(gInputManager->GetStream()); 306 fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface()); 307 308 fEventDispatcher.SetMouseFilter(new MouseFilter(this)); 309 fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this)); 310 311 // take care of setting the default cursor 312 ServerCursor *cursor = fCursorManager.GetCursor(B_CURSOR_DEFAULT); 313 if (cursor) 314 fVirtualScreen.HWInterface()->SetCursor(cursor); 315 316 fVirtualScreen.HWInterface()->MoveCursorTo(fVirtualScreen.Frame().Width() / 2, 317 fVirtualScreen.Frame().Height() / 2); 318 fVirtualScreen.HWInterface()->SetCursorVisible(true); 319 320 // draw the background 321 322 fScreenRegion = fVirtualScreen.Frame(); 323 324 BRegion stillAvailableOnScreen; 325 _RebuildClippingForAllWindows(stillAvailableOnScreen); 326 _SetBackground(stillAvailableOnScreen); 327 } 328 329 330 void 331 Desktop::_GetLooperName(char* name, size_t length) 332 { 333 snprintf(name, length, "d:%d:%s", /*id*/0, /*name*/"baron"); 334 } 335 336 337 void 338 Desktop::_PrepareQuit() 339 { 340 // let's kill all remaining applications 341 342 fApplicationsLock.Lock(); 343 344 int32 count = fApplications.CountItems(); 345 for (int32 i = 0; i < count; i++) { 346 ServerApp *app = fApplications.ItemAt(i); 347 team_id clientTeam = app->ClientTeam(); 348 349 app->Quit(); 350 kill_team(clientTeam); 351 } 352 353 // wait for the last app to die 354 if (count > 0) 355 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 250000); 356 357 fApplicationsLock.Unlock(); 358 } 359 360 361 void 362 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) 363 { 364 switch (code) { 365 case AS_CREATE_APP: 366 { 367 // Create the ServerApp to node monitor a new BApplication 368 369 // Attached data: 370 // 1) port_id - receiver port of a regular app 371 // 2) port_id - client looper port - for sending messages to the client 372 // 2) team_id - app's team ID 373 // 3) int32 - handler token of the regular app 374 // 4) char * - signature of the regular app 375 376 // Find the necessary data 377 team_id clientTeamID = -1; 378 port_id clientLooperPort = -1; 379 port_id clientReplyPort = -1; 380 int32 htoken = B_NULL_TOKEN; 381 char *appSignature = NULL; 382 383 link.Read<port_id>(&clientReplyPort); 384 link.Read<port_id>(&clientLooperPort); 385 link.Read<team_id>(&clientTeamID); 386 link.Read<int32>(&htoken); 387 if (link.ReadString(&appSignature) != B_OK) 388 break; 389 390 ServerApp *app = new ServerApp(this, clientReplyPort, 391 clientLooperPort, clientTeamID, htoken, appSignature); 392 if (app->InitCheck() == B_OK 393 && app->Run()) { 394 // add the new ServerApp to the known list of ServerApps 395 fApplicationsLock.Lock(); 396 fApplications.AddItem(app); 397 fApplicationsLock.Unlock(); 398 } else { 399 delete app; 400 401 // if everything went well, ServerApp::Run() will notify 402 // the client - but since it didn't, we do it here 403 BPrivate::LinkSender reply(clientReplyPort); 404 reply.StartMessage(SERVER_FALSE); 405 reply.Flush(); 406 } 407 408 // This is necessary because BPortLink::ReadString allocates memory 409 free(appSignature); 410 break; 411 } 412 413 case AS_DELETE_APP: 414 { 415 // Delete a ServerApp. Received only from the respective ServerApp when a 416 // BApplication asks it to quit. 417 418 // Attached Data: 419 // 1) thread_id - thread ID of the ServerApp to be deleted 420 421 thread_id thread = -1; 422 if (link.Read<thread_id>(&thread) < B_OK) 423 break; 424 425 fApplicationsLock.Lock(); 426 427 // Run through the list of apps and nuke the proper one 428 429 int32 count = fApplications.CountItems(); 430 ServerApp *removeApp = NULL; 431 432 for (int32 i = 0; i < count; i++) { 433 ServerApp *app = fApplications.ItemAt(i); 434 435 if (app != NULL && app->Thread() == thread) { 436 fApplications.RemoveItemAt(i); 437 removeApp = app; 438 break; 439 } 440 } 441 442 fApplicationsLock.Unlock(); 443 444 if (removeApp != NULL) 445 removeApp->Quit(fShutdownSemaphore); 446 447 if (fQuitting && count <= 1) { 448 // wait for the last app to die 449 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 500000); 450 PostMessage(kMsgQuitLooper); 451 } 452 break; 453 } 454 455 case AS_ACTIVATE_APP: 456 { 457 // Someone is requesting to activation of a certain app. 458 459 // Attached data: 460 // 1) port_id reply port 461 // 2) team_id team 462 463 status_t status; 464 465 // get the parameters 466 port_id replyPort; 467 team_id team; 468 if (link.Read(&replyPort) == B_OK 469 && link.Read(&team) == B_OK) 470 status = _ActivateApp(team); 471 else 472 status = B_ERROR; 473 474 // send the reply 475 BPrivate::PortLink replyLink(replyPort); 476 replyLink.StartMessage(status); 477 replyLink.Flush(); 478 break; 479 } 480 481 case AS_SET_SYSCURSOR_DEFAULTS: 482 { 483 GetCursorManager().SetDefaults(); 484 break; 485 } 486 487 case B_QUIT_REQUESTED: 488 // We've been asked to quit, so (for now) broadcast to all 489 // test apps to quit. This situation will occur only when the server 490 // is compiled as a regular Be application. 491 492 fApplicationsLock.Lock(); 493 fShutdownSemaphore = create_sem(0, "desktop shutdown"); 494 fShutdownCount = fApplications.CountItems(); 495 fApplicationsLock.Unlock(); 496 497 fQuitting = true; 498 BroadcastToAllApps(AS_QUIT_APP); 499 500 // We now need to process the remaining AS_DELETE_APP messages and 501 // wait for the kMsgShutdownServer message. 502 // If an application does not quit as asked, the picasso thread 503 // will send us this message in 2-3 seconds. 504 505 // if there are no apps to quit, shutdown directly 506 if (fShutdownCount == 0) 507 PostMessage(kMsgQuitLooper); 508 break; 509 510 default: 511 printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", code); 512 513 if (link.NeedsReply()) { 514 // the client is now blocking and waiting for a reply! 515 fLink.StartMessage(B_ERROR); 516 fLink.Flush(); 517 } 518 break; 519 } 520 } 521 522 523 /*! 524 \brief activate one of the app's windows. 525 */ 526 status_t 527 Desktop::_ActivateApp(team_id team) 528 { 529 status_t status = B_BAD_TEAM_ID; 530 531 // search for an unhidden window to give focus to 532 533 for (WindowLayer* window = fAllWindows.FirstWindow(); window != NULL; 534 window = window->NextWindow(kAllWindowList)) { 535 // if window is a normal window of the team, and not hidden, 536 // we've found our target 537 if (!window->IsHidden() && window->IsNormal() 538 && window->ServerWindow()->ClientTeam() == team) { 539 ActivateWindow(window); 540 return B_OK; 541 } 542 } 543 544 return status; 545 } 546 547 548 /*! 549 \brief Send a quick (no attachments) message to all applications 550 551 Quite useful for notification for things like server shutdown, system 552 color changes, etc. 553 */ 554 void 555 Desktop::BroadcastToAllApps(int32 code) 556 { 557 BAutolock locker(fApplicationsLock); 558 559 for (int32 i = 0; i < fApplications.CountItems(); i++) { 560 fApplications.ItemAt(i)->PostMessage(code); 561 } 562 } 563 564 565 void 566 Desktop::UpdateWorkspaces() 567 { 568 // TODO: maybe this should be replaced by a SetWorkspacesCount() method 569 570 _WindowChanged(NULL); 571 } 572 573 574 void 575 Desktop::SetWorkspace(int32 index) 576 { 577 LockAllWindows(); 578 DesktopSettings settings(this); 579 580 if (index < 0 || index >= settings.WorkspacesCount() || index == fCurrentWorkspace) { 581 UnlockAllWindows(); 582 return; 583 } 584 585 int32 previousIndex = fCurrentWorkspace; 586 587 if (fMouseEventWindow != NULL) { 588 if (!fMouseEventWindow->InWorkspace(index)) { 589 // the window currently being dragged will follow us to this workspace 590 // if it's not already on it 591 if (fMouseEventWindow->IsNormal()) { 592 // but only normal windows are following 593 _Windows(index).AddWindow(fMouseEventWindow); 594 _Windows(previousIndex).RemoveWindow(fMouseEventWindow); 595 } 596 } else { 597 // make sure it's frontmost 598 _Windows(index).RemoveWindow(fMouseEventWindow); 599 _Windows(index).AddWindow(fMouseEventWindow, 600 fMouseEventWindow->Frontmost(_Windows(index).FirstWindow(), index)); 601 } 602 603 fMouseEventWindow->Anchor(index).position = fMouseEventWindow->Frame().LeftTop(); 604 } 605 606 // build region of windows that are no longer visible in the new workspace 607 608 BRegion dirty; 609 610 for (WindowLayer* window = _CurrentWindows().FirstWindow(); 611 window != NULL; window = window->NextWindow(previousIndex)) { 612 // store current position in Workspace anchor 613 window->Anchor(previousIndex).position = window->Frame().LeftTop(); 614 615 if (window->InWorkspace(index)) 616 continue; 617 618 if (!window->IsHidden()) { 619 // this window will no longer be visible 620 dirty.Include(&window->VisibleRegion()); 621 } 622 623 window->SetCurrentWorkspace(-1); 624 } 625 626 fCurrentWorkspace = index; 627 628 // show windows, and include them in the changed region - but only 629 // those that were not visible before (or whose position changed) 630 631 for (WindowLayer* window = _Windows(index).FirstWindow(); 632 window != NULL; window = window->NextWindow(index)) { 633 BPoint position = window->Anchor(index).position; 634 635 window->SetCurrentWorkspace(index); 636 637 if (window->IsHidden()) 638 continue; 639 640 if (position == kInvalidWindowPosition) { 641 // if you enter a workspace for the first time, the position 642 // of the window in the previous workspace is adopted 643 position = window->Frame().LeftTop(); 644 // TODO: make sure the window is still on-screen if it 645 // was before! 646 } 647 648 if (!window->InWorkspace(previousIndex)) { 649 // this window was not visible before 650 continue; 651 } 652 653 if (window->Frame().LeftTop() != position) { 654 // the window was visible before, but its on-screen location changed 655 BPoint offset = position - window->Frame().LeftTop(); 656 MoveWindowBy(window, offset.x, offset.y); 657 // TODO: be a bit smarter than this... 658 } 659 } 660 661 BRegion stillAvailableOnScreen; 662 _RebuildClippingForAllWindows(stillAvailableOnScreen); 663 _SetBackground(stillAvailableOnScreen); 664 665 for (WindowLayer* window = _Windows(index).FirstWindow(); window != NULL; 666 window = window->NextWindow(index)) { 667 if (window->InWorkspace(previousIndex) || window == fMouseEventWindow) { 668 // this window was visible before, and is already handled in the above loop 669 continue; 670 } 671 672 dirty.Include(&window->VisibleRegion()); 673 } 674 675 _UpdateFronts(false); 676 _UpdateFloating(previousIndex, index); 677 678 // Set new focus to the front window, but keep focus to a floating 679 // window if still visible 680 if (!_Windows(index).HasWindow(FocusWindow()) || !FocusWindow()->IsFloating()) 681 SetFocusWindow(FrontWindow()); 682 683 _WindowChanged(NULL); 684 MarkDirty(dirty); 685 686 UnlockAllWindows(); 687 } 688 689 690 void 691 Desktop::ScreenChanged(Screen* screen) 692 { 693 // TODO: confirm that everywhere this is used, 694 // the Window WriteLock is held 695 696 // the entire screen is dirty, because we're actually 697 // operating on an all new buffer in memory 698 BRegion dirty(screen->Frame()); 699 // update our cached screen region 700 fScreenRegion.Set(screen->Frame()); 701 702 BRegion background; 703 _RebuildClippingForAllWindows(background); 704 705 fBackgroundRegion.MakeEmpty(); 706 // makes sure that the complete background is redrawn 707 _SetBackground(background); 708 709 // figure out dirty region 710 dirty.Exclude(&background); 711 _TriggerWindowRedrawing(dirty); 712 713 // send B_SCREEN_CHANGED to windows on that screen 714 BMessage update(B_SCREEN_CHANGED); 715 update.AddInt64("when", real_time_clock_usecs()); 716 update.AddRect("frame", screen->Frame()); 717 update.AddInt32("mode", screen->ColorSpace()); 718 719 // TODO: currently ignores the screen argument! 720 for (WindowLayer* window = fAllWindows.FirstWindow(); window != NULL; 721 window = window->NextWindow(kAllWindowList)) { 722 window->ServerWindow()->SendMessageToClient(&update); 723 } 724 } 725 726 727 // #pragma mark - Methods for WindowLayer manipulation 728 729 730 WindowList& 731 Desktop::_CurrentWindows() 732 { 733 return fWorkspaces[fCurrentWorkspace].Windows(); 734 } 735 736 737 WindowList& 738 Desktop::_Windows(int32 index) 739 { 740 return fWorkspaces[index].Windows(); 741 } 742 743 744 void 745 Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace) 746 { 747 if (fFront == NULL) 748 return; 749 750 if (previousWorkspace == -1) 751 previousWorkspace = fCurrentWorkspace; 752 if (nextWorkspace == -1) 753 nextWorkspace = previousWorkspace; 754 755 for (WindowLayer* floating = fSubsetWindows.FirstWindow(); floating != NULL; 756 floating = floating->NextWindow(kSubsetList)) { 757 // we only care about app/subset floating windows 758 if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL 759 && floating->Feel() != B_FLOATING_APP_WINDOW_FEEL) 760 continue; 761 762 if (fFront->IsNormal() && floating->HasInSubset(fFront)) { 763 // is now visible 764 if (_Windows(previousWorkspace).HasWindow(floating) 765 && previousWorkspace != nextWorkspace) { 766 // but no longer on the previous workspace 767 _Windows(previousWorkspace).RemoveWindow(floating); 768 floating->SetCurrentWorkspace(-1); 769 } 770 if (!_Windows(nextWorkspace).HasWindow(floating)) { 771 // but wasn't before 772 _Windows(nextWorkspace).AddWindow(floating, 773 floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), nextWorkspace)); 774 floating->SetCurrentWorkspace(nextWorkspace); 775 _ShowWindow(floating); 776 777 // TODO: 778 // put the floating last in the floating window list to preserve 779 // the on screen window order 780 } 781 } else if (_Windows(previousWorkspace).HasWindow(floating)) { 782 // was visible, but is no longer 783 _Windows(previousWorkspace).RemoveWindow(floating); 784 floating->SetCurrentWorkspace(-1); 785 _HideWindow(floating); 786 } 787 } 788 } 789 790 791 /*! 792 Search the visible windows for a valid back window 793 (only normal windows can be back windows) 794 */ 795 void 796 Desktop::_UpdateBack() 797 { 798 fBack = NULL; 799 800 for (WindowLayer* window = _CurrentWindows().FirstWindow(); 801 window != NULL; window = window->NextWindow(fCurrentWorkspace)) { 802 if (window->IsHidden() || !window->SupportsFront()) 803 continue; 804 805 fBack = window; 806 break; 807 } 808 } 809 810 811 /*! 812 Search the visible windows for a valid front window 813 (only normal and modal windows can be front windows) 814 815 The only place where you don't want to update floating windows is 816 during a workspace change - because then you'll call _UpdateFloating() 817 yourself. 818 */ 819 void 820 Desktop::_UpdateFront(bool updateFloating) 821 { 822 fFront = NULL; 823 824 for (WindowLayer* window = _CurrentWindows().LastWindow(); 825 window != NULL; window = window->PreviousWindow(fCurrentWorkspace)) { 826 if (window->IsHidden() || window->IsFloating() || !window->SupportsFront()) 827 continue; 828 829 fFront = window; 830 break; 831 } 832 833 if (updateFloating) 834 _UpdateFloating(); 835 } 836 837 838 void 839 Desktop::_UpdateFronts(bool updateFloating) 840 { 841 _UpdateBack(); 842 _UpdateFront(updateFloating); 843 } 844 845 846 bool 847 Desktop::_WindowHasModal(WindowLayer* window) 848 { 849 if (window == NULL || window->IsFloating()) 850 return false; 851 852 for (WindowLayer* modal = fSubsetWindows.FirstWindow(); modal != NULL; 853 modal = modal->NextWindow(kSubsetList)) { 854 // only visible modal windows count 855 if (!modal->IsModal() || modal->IsHidden()) 856 continue; 857 858 if (modal->HasInSubset(window)) 859 return true; 860 } 861 862 return false; 863 } 864 865 866 void 867 Desktop::_WindowChanged(WindowLayer* window) 868 { 869 if (fWorkspacesLayer == NULL) 870 return; 871 872 fWorkspacesLayer->WindowChanged(window); 873 } 874 875 876 void 877 Desktop::SetFocusWindow(WindowLayer* focus) 878 { 879 if (!LockAllWindows()) 880 return; 881 882 bool hasModal = _WindowHasModal(focus); 883 884 // TODO: test for FFM and B_LOCK_WINDOW_FOCUS 885 886 if (focus == fFocus && focus != NULL && (focus->Flags() & B_AVOID_FOCUS) == 0 887 && !hasModal) { 888 // the window that is supposed to get focus already has focus 889 UnlockAllWindows(); 890 return; 891 } 892 893 if (focus == NULL || hasModal) { 894 focus = FrontWindow(); 895 if (focus == NULL) { 896 // there might be no front window in case of only a single 897 // window with B_FLOATING_ALL_WINDOW_FEEL 898 focus = _CurrentWindows().LastWindow(); 899 } 900 } 901 902 // make sure no window is chosen that doesn't want focus or cannot have it 903 while (focus != NULL 904 && ((focus->Flags() & B_AVOID_FOCUS) != 0 905 || _WindowHasModal(focus) 906 || focus->IsHidden())) { 907 focus = focus->PreviousWindow(fCurrentWorkspace); 908 } 909 910 if (fFocus != NULL) 911 fFocus->SetFocus(false); 912 913 fFocus = focus; 914 915 if (focus != NULL) 916 focus->SetFocus(true); 917 918 UnlockAllWindows(); 919 } 920 921 922 void 923 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, 924 bool wereVisible) 925 { 926 // we don't need to redraw what is currently 927 // visible of the window 928 BRegion clean; 929 930 for (WindowLayer* window = windows.FirstWindow(); window != NULL; 931 window = window->NextWindow(list)) { 932 if (wereVisible) 933 clean.Include(&window->VisibleRegion()); 934 935 _CurrentWindows().AddWindow(window, 936 window->Frontmost(_CurrentWindows().FirstWindow(), 937 fCurrentWorkspace)); 938 939 _WindowChanged(window); 940 } 941 942 BRegion dummy; 943 _RebuildClippingForAllWindows(dummy); 944 945 // redraw what became visible of the window(s) 946 947 BRegion dirty; 948 for (WindowLayer* window = windows.FirstWindow(); window != NULL; 949 window = window->NextWindow(list)) { 950 dirty.Include(&window->VisibleRegion()); 951 } 952 953 dirty.Exclude(&clean); 954 MarkDirty(dirty); 955 956 _UpdateFront(); 957 958 if (windows.FirstWindow() == fBack || fBack == NULL) 959 _UpdateBack(); 960 } 961 962 963 /*! 964 \brief Tries to move the specified window to the front of the screen, 965 and make it the focus window. 966 967 If there are any modal windows on this screen, it might not actually 968 become the frontmost window, though, as modal windows stay in front 969 of their subset. 970 */ 971 void 972 Desktop::ActivateWindow(WindowLayer* window) 973 { 974 // printf("ActivateWindow(%p, %s)\n", window, window ? window->Title() : "<none>"); 975 976 if (window == NULL) { 977 fBack = NULL; 978 fFront = NULL; 979 return; 980 } 981 982 // TODO: support B_NO_WORKSPACE_ACTIVATION 983 // TODO: support B_NOT_ANCHORED_ON_ACTIVATE 984 // TODO: take care about floating windows 985 986 if (!LockAllWindows()) 987 return; 988 989 if (window == FrontWindow()) { 990 SetFocusWindow(window); 991 992 UnlockAllWindows(); 993 return; 994 } 995 996 // we don't need to redraw what is currently 997 // visible of the window 998 BRegion clean(window->VisibleRegion()); 999 WindowList windows(kWorkingList); 1000 1001 WindowLayer* frontmost = window->Frontmost(); 1002 1003 _CurrentWindows().RemoveWindow(window); 1004 windows.AddWindow(window); 1005 1006 if (frontmost != NULL && frontmost->IsModal()) { 1007 // all modal windows follow their subsets to the front 1008 // (ie. they are staying in front of them, but they are 1009 // not supposed to change their order because of that) 1010 1011 WindowLayer* nextModal; 1012 for (WindowLayer* modal = frontmost; modal != NULL; modal = nextModal) { 1013 // get the next modal window 1014 nextModal = modal->NextWindow(fCurrentWorkspace); 1015 while (nextModal != NULL && !nextModal->IsModal()) { 1016 nextModal = nextModal->NextWindow(fCurrentWorkspace); 1017 } 1018 if (nextModal != NULL && !nextModal->HasInSubset(window)) 1019 nextModal = NULL; 1020 1021 _CurrentWindows().RemoveWindow(modal); 1022 windows.AddWindow(modal); 1023 } 1024 } 1025 1026 _BringWindowsToFront(windows, kWorkingList, true); 1027 SetFocusWindow(window); 1028 1029 UnlockAllWindows(); 1030 } 1031 1032 1033 void 1034 Desktop::SendWindowBehind(WindowLayer* window, WindowLayer* behindOf) 1035 { 1036 if (window == BackWindow() || !LockAllWindows()) 1037 return; 1038 1039 // Is this a valid behindOf window? 1040 if (behindOf != NULL && window->HasInSubset(behindOf)) 1041 behindOf = NULL; 1042 1043 // what is currently visible of the window 1044 // might be dirty after the window is send to back 1045 BRegion dirty(window->VisibleRegion()); 1046 1047 // detach window and re-attach at desired position 1048 WindowLayer* backmost = window->Backmost(behindOf); 1049 1050 _CurrentWindows().RemoveWindow(window); 1051 _CurrentWindows().AddWindow(window, backmost 1052 ? backmost->NextWindow(fCurrentWorkspace) : BackWindow()); 1053 1054 BRegion dummy; 1055 _RebuildClippingForAllWindows(dummy); 1056 1057 // mark everything dirty that is no longer visible 1058 BRegion clean(window->VisibleRegion()); 1059 dirty.Exclude(&clean); 1060 MarkDirty(dirty); 1061 1062 // TODO: if this window has any floating windows, remove them here 1063 1064 _UpdateFronts(); 1065 SetFocusWindow(FrontWindow()); 1066 //_WindowsChanged(); 1067 1068 UnlockAllWindows(); 1069 } 1070 1071 1072 void 1073 Desktop::ShowWindow(WindowLayer* window) 1074 { 1075 if (!window->IsHidden()) 1076 return; 1077 1078 LockAllWindows(); 1079 1080 window->SetHidden(false); 1081 1082 if (window->InWorkspace(fCurrentWorkspace)) { 1083 _ShowWindow(window, true); 1084 _UpdateSubsetWorkspaces(window); 1085 ActivateWindow(window); 1086 } else { 1087 // then we don't need to send the fake mouse event either 1088 UnlockAllWindows(); 1089 return; 1090 } 1091 1092 if (WorkspacesLayer* layer = dynamic_cast<WorkspacesLayer*>(window->TopLayer())) 1093 fWorkspacesLayer = layer; 1094 1095 UnlockAllWindows(); 1096 1097 // If the mouse cursor is directly over the newly visible window, 1098 // we'll send a fake mouse moved message to the window, so that 1099 // it knows the mouse is over it. 1100 1101 BPoint where; 1102 int32 buttons; 1103 EventDispatcher().GetMouse(where, buttons); 1104 1105 int32 viewToken = B_NULL_TOKEN; 1106 1107 LockAllWindows(); 1108 1109 if (WindowAt(where) == window) { 1110 ViewLayer* view = window->ViewAt(where); 1111 if (view != NULL) 1112 viewToken = view->Token(); 1113 } 1114 UnlockAllWindows(); 1115 1116 if (viewToken != B_NULL_TOKEN) 1117 EventDispatcher().SendFakeMouseMoved(window->EventTarget(), viewToken); 1118 } 1119 1120 1121 void 1122 Desktop::HideWindow(WindowLayer* window) 1123 { 1124 if (window->IsHidden()) 1125 return; 1126 1127 if (!LockAllWindows()) 1128 return; 1129 1130 window->SetHidden(true); 1131 1132 if (window->InWorkspace(fCurrentWorkspace)) { 1133 _UpdateSubsetWorkspaces(window); 1134 _HideWindow(window); 1135 _UpdateFronts(); 1136 1137 if (FocusWindow() == window) 1138 SetFocusWindow(FrontWindow()); 1139 } 1140 1141 if (dynamic_cast<WorkspacesLayer*>(window->TopLayer()) != NULL) 1142 fWorkspacesLayer = NULL; 1143 1144 UnlockAllWindows(); 1145 } 1146 1147 1148 /*! 1149 Shows the window on the screen - it does this independently of the 1150 WindowLayer::IsHidden() state. 1151 */ 1152 void 1153 Desktop::_ShowWindow(WindowLayer* window, bool affectsOtherWindows) 1154 { 1155 BRegion background; 1156 _RebuildClippingForAllWindows(background); 1157 _SetBackground(background); 1158 _WindowChanged(window); 1159 1160 BRegion dirty(window->VisibleRegion()); 1161 1162 if (!affectsOtherWindows) { 1163 // everything that is now visible in the 1164 // window needs a redraw, but other windows 1165 // are not affected, we can call ProcessDirtyRegion() 1166 // of the window, and don't have to use MarkDirty() 1167 window->ProcessDirtyRegion(dirty); 1168 } else 1169 MarkDirty(dirty); 1170 } 1171 1172 1173 /*! 1174 Hides the window from the screen - it does this independently of the 1175 WindowLayer::IsHidden() state. 1176 */ 1177 void 1178 Desktop::_HideWindow(WindowLayer* window) 1179 { 1180 // after rebuilding the clipping, 1181 // this window will not have a visible 1182 // region anymore, so we need to remember 1183 // it now 1184 // (actually that's not true, since 1185 // hidden windows are excluded from the 1186 // clipping calculation, but anyways) 1187 BRegion dirty(window->VisibleRegion()); 1188 1189 BRegion background; 1190 _RebuildClippingForAllWindows(background); 1191 _SetBackground(background); 1192 _WindowChanged(window); 1193 1194 MarkDirty(dirty); 1195 } 1196 1197 1198 void 1199 Desktop::MoveWindowBy(WindowLayer* window, float x, float y) 1200 { 1201 if (!LockAllWindows()) 1202 return; 1203 1204 // the dirty region starts with the visible area of the window being moved 1205 BRegion newDirtyRegion(window->VisibleRegion()); 1206 1207 window->MoveBy(x, y); 1208 1209 BRegion background; 1210 _RebuildClippingForAllWindows(background); 1211 1212 // construct the region that is possible to be blitted 1213 // to move the contents of the window 1214 BRegion copyRegion(window->VisibleRegion()); 1215 copyRegion.OffsetBy(-x, -y); 1216 copyRegion.IntersectWith(&newDirtyRegion); 1217 1218 // include the the new visible region of the window being 1219 // moved into the dirty region (for now) 1220 newDirtyRegion.Include(&window->VisibleRegion()); 1221 1222 if (GetDrawingEngine()->Lock()) { 1223 GetDrawingEngine()->CopyRegion(©Region, x, y); 1224 1225 // in the dirty region, exclude the parts that we 1226 // could move by blitting 1227 copyRegion.OffsetBy(x, y); 1228 newDirtyRegion.Exclude(©Region); 1229 1230 GetDrawingEngine()->Unlock(); 1231 } 1232 1233 MarkDirty(newDirtyRegion); 1234 _SetBackground(background); 1235 _WindowChanged(window); 1236 1237 UnlockAllWindows(); 1238 } 1239 1240 1241 void 1242 Desktop::ResizeWindowBy(WindowLayer* window, float x, float y) 1243 { 1244 if (!LockAllWindows()) 1245 return; 1246 1247 BRegion newDirtyRegion; 1248 BRegion previouslyOccupiedRegion(window->VisibleRegion()); 1249 1250 window->ResizeBy(x, y, &newDirtyRegion); 1251 1252 BRegion background; 1253 _RebuildClippingForAllWindows(background); 1254 1255 previouslyOccupiedRegion.Exclude(&window->VisibleRegion()); 1256 1257 newDirtyRegion.IntersectWith(&window->VisibleRegion()); 1258 newDirtyRegion.Include(&previouslyOccupiedRegion); 1259 1260 MarkDirty(newDirtyRegion); 1261 _SetBackground(background); 1262 _WindowChanged(window); 1263 1264 UnlockAllWindows(); 1265 } 1266 1267 1268 /*! 1269 Updates the workspaces of all subset windows with regard to the 1270 specifed window. 1271 */ 1272 void 1273 Desktop::_UpdateSubsetWorkspaces(WindowLayer* window) 1274 { 1275 // if the window is hidden, the subset windows are up-to-date already 1276 if (!window->IsNormal() || window->IsHidden()) 1277 return; 1278 1279 for (WindowLayer* subset = fSubsetWindows.FirstWindow(); subset != NULL; 1280 subset = subset->NextWindow(kSubsetList)) { 1281 if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL 1282 || subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) { 1283 // These windows are always visible on all workspaces, 1284 // no need to update them. 1285 continue; 1286 } 1287 1288 if (subset->IsFloating()) { 1289 // Floating windows are inserted and removed to the current 1290 // workspace as the need arises - they are not handled here 1291 // but in _UpdateFront() 1292 continue; 1293 } 1294 1295 if (subset->HasInSubset(window)) { 1296 // adopt the workspace change 1297 SetWindowWorkspaces(subset, subset->SubsetWorkspaces()); 1298 } 1299 } 1300 } 1301 1302 1303 /*! 1304 \brief Adds or removes the window to or from the workspaces it's on. 1305 */ 1306 void 1307 Desktop::_ChangeWindowWorkspaces(WindowLayer* window, uint32 oldWorkspaces, 1308 uint32 newWorkspaces) 1309 { 1310 // apply changes to the workspaces' window lists 1311 1312 LockAllWindows(); 1313 1314 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1315 if (workspace_in_workspaces(i, oldWorkspaces)) { 1316 // window is on this workspace, is it anymore? 1317 if (!workspace_in_workspaces(i, newWorkspaces)) { 1318 _Windows(i).RemoveWindow(window); 1319 1320 if (i == CurrentWorkspace()) { 1321 // remove its appearance from the current workspace 1322 window->SetCurrentWorkspace(-1); 1323 1324 if (!window->IsHidden()) 1325 _HideWindow(window); 1326 } 1327 } 1328 } else { 1329 // window was not on this workspace, is it now? 1330 if (workspace_in_workspaces(i, newWorkspaces)) { 1331 _Windows(i).AddWindow(window, 1332 window->Frontmost(_Windows(i).FirstWindow(), i)); 1333 1334 if (i == CurrentWorkspace()) { 1335 // make the window visible in current workspace 1336 window->SetCurrentWorkspace(fCurrentWorkspace); 1337 1338 if (!window->IsHidden()) { 1339 // this only affects other windows if this windows has floating or 1340 // modal windows that need to be shown as well 1341 // TODO: take care of this 1342 _ShowWindow(window, FrontWindow() == window); 1343 } 1344 } 1345 } 1346 } 1347 } 1348 1349 // take care about modals and floating windows 1350 _UpdateSubsetWorkspaces(window); 1351 1352 UnlockAllWindows(); 1353 } 1354 1355 1356 void 1357 Desktop::SetWindowWorkspaces(WindowLayer* window, uint32 workspaces) 1358 { 1359 LockAllWindows(); 1360 1361 if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE) 1362 workspaces = workspace_to_workspaces(CurrentWorkspace()); 1363 1364 _ChangeWindowWorkspaces(window, window->Workspaces(), workspaces); 1365 UnlockAllWindows(); 1366 } 1367 1368 1369 void 1370 Desktop::AddWindow(WindowLayer *window) 1371 { 1372 LockAllWindows(); 1373 1374 fAllWindows.AddWindow(window); 1375 if (!window->IsNormal()) 1376 fSubsetWindows.AddWindow(window); 1377 1378 if (window->IsNormal()) { 1379 if (window->Workspaces() == B_CURRENT_WORKSPACE) 1380 window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace())); 1381 } else { 1382 // subset windows are visible on all workspaces their subset is on 1383 window->SetWorkspaces(window->SubsetWorkspaces()); 1384 } 1385 1386 _ChangeWindowWorkspaces(window, 0, window->Workspaces()); 1387 UnlockAllWindows(); 1388 } 1389 1390 1391 void 1392 Desktop::RemoveWindow(WindowLayer *window) 1393 { 1394 LockAllWindows(); 1395 1396 if (!window->IsHidden()) 1397 HideWindow(window); 1398 1399 fAllWindows.RemoveWindow(window); 1400 if (!window->IsNormal()) 1401 fSubsetWindows.RemoveWindow(window); 1402 1403 _ChangeWindowWorkspaces(window, window->Workspaces(), 0); 1404 UnlockAllWindows(); 1405 1406 // make sure this window won't get any events anymore 1407 1408 EventDispatcher().RemoveTarget(window->EventTarget()); 1409 } 1410 1411 1412 bool 1413 Desktop::AddWindowToSubset(WindowLayer* subset, WindowLayer* window) 1414 { 1415 if (!subset->AddToSubset(window)) 1416 return false; 1417 1418 _ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces()); 1419 return true; 1420 } 1421 1422 1423 void 1424 Desktop::RemoveWindowFromSubset(WindowLayer* subset, WindowLayer* window) 1425 { 1426 subset->RemoveFromSubset(window); 1427 _ChangeWindowWorkspaces(subset, subset->Workspaces(), subset->SubsetWorkspaces()); 1428 } 1429 1430 1431 void 1432 Desktop::SetWindowLook(WindowLayer *window, window_look newLook) 1433 { 1434 if (window->Look() == newLook) 1435 return; 1436 1437 if (!LockAllWindows()) 1438 return; 1439 1440 BRegion dirty; 1441 window->SetLook(newLook, &dirty); 1442 // TODO: test what happens when the window 1443 // finds out it needs to resize itself... 1444 1445 BRegion stillAvailableOnScreen; 1446 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1447 _SetBackground(stillAvailableOnScreen); 1448 _WindowChanged(window); 1449 1450 _TriggerWindowRedrawing(dirty); 1451 1452 UnlockAllWindows(); 1453 } 1454 1455 1456 void 1457 Desktop::SetWindowFeel(WindowLayer *window, window_feel newFeel) 1458 { 1459 if (window->Feel() == newFeel) 1460 return; 1461 1462 LockAllWindows(); 1463 1464 bool wasNormal = window->IsNormal(); 1465 1466 window->SetFeel(newFeel); 1467 1468 // move the window out of or into the subset window list as needed 1469 if (window->IsNormal() && !wasNormal) 1470 fSubsetWindows.RemoveWindow(window); 1471 else if (!window->IsNormal() && wasNormal) 1472 fSubsetWindows.AddWindow(window); 1473 1474 // A normal window that was once a floating or modal window will 1475 // adopt the window's current workspaces 1476 1477 if (!window->IsNormal()) 1478 _ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces()); 1479 1480 // make sure the window has the correct position in the window lists 1481 // (ie. all floating windows have to be on the top, ...) 1482 1483 for (int32 i = 0; i < kMaxWorkspaces; i++) { 1484 if (!workspace_in_workspaces(i, window->Workspaces())) 1485 continue; 1486 1487 WindowLayer* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i); 1488 if (frontmost == NULL) 1489 continue; 1490 1491 // check if the frontmost window is really in front of it 1492 1493 WindowLayer* next = window->NextWindow(i); 1494 while (next != NULL) { 1495 if (next == frontmost) 1496 break; 1497 1498 next = next->NextWindow(i); 1499 } 1500 1501 if (next == NULL) { 1502 // need to reinsert window behind its frontmost window 1503 _Windows(i).RemoveWindow(window); 1504 _Windows(i).AddWindow(window, frontmost); 1505 } 1506 } 1507 1508 _UpdateFronts(); 1509 1510 if (window == FocusWindow() && !window->IsVisible()) 1511 SetFocusWindow(FrontWindow()); 1512 1513 UnlockAllWindows(); 1514 } 1515 1516 1517 void 1518 Desktop::SetWindowFlags(WindowLayer *window, uint32 newFlags) 1519 { 1520 if (window->Flags() == newFlags) 1521 return; 1522 1523 if (!LockAllWindows()) 1524 return; 1525 1526 BRegion dirty; 1527 window->SetFlags(newFlags, &dirty); 1528 // TODO: test what happens when the window 1529 // finds out it needs to resize itself... 1530 1531 BRegion stillAvailableOnScreen; 1532 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1533 _SetBackground(stillAvailableOnScreen); 1534 _WindowChanged(window); 1535 1536 _TriggerWindowRedrawing(dirty); 1537 1538 1539 UnlockAllWindows(); 1540 } 1541 1542 1543 void 1544 Desktop::SetWindowTitle(WindowLayer *window, const char* title) 1545 { 1546 if (!LockAllWindows()) 1547 return; 1548 1549 BRegion dirty; 1550 window->SetTitle(title, dirty); 1551 1552 if (window->IsVisible() && dirty.CountRects() > 0) { 1553 BRegion stillAvailableOnScreen; 1554 _RebuildClippingForAllWindows(stillAvailableOnScreen); 1555 _SetBackground(stillAvailableOnScreen); 1556 1557 _TriggerWindowRedrawing(dirty); 1558 } 1559 1560 UnlockAllWindows(); 1561 } 1562 1563 1564 /*! 1565 Returns the window under the mouse cursor. 1566 You need to have the window write lock acquired when calling this method. 1567 */ 1568 WindowLayer* 1569 Desktop::WindowAt(BPoint where) 1570 { 1571 for (WindowLayer* window = _CurrentWindows().LastWindow(); window; 1572 window = window->PreviousWindow(fCurrentWorkspace)) { 1573 if (window->VisibleRegion().Contains(where)) 1574 return window; 1575 } 1576 1577 return NULL; 1578 } 1579 1580 1581 void 1582 Desktop::SetMouseEventWindow(WindowLayer* window) 1583 { 1584 fMouseEventWindow = window; 1585 } 1586 1587 1588 WindowLayer * 1589 Desktop::FindWindowLayerByClientToken(int32 token, team_id teamID) 1590 { 1591 LockSingleWindow(); 1592 1593 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 1594 window = window->NextWindow(kAllWindowList)) { 1595 if (window->ServerWindow()->ClientToken() == token 1596 && window->ServerWindow()->ClientTeam() == teamID) { 1597 UnlockSingleWindow(); 1598 return window; 1599 } 1600 } 1601 1602 UnlockSingleWindow(); 1603 return NULL; 1604 } 1605 1606 1607 void 1608 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender) 1609 { 1610 BAutolock locker(fWindowLock); 1611 1612 // compute the number of windows 1613 1614 int32 count = 0; 1615 1616 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 1617 window = window->NextWindow(kAllWindowList)) { 1618 if (team < B_OK || window->ServerWindow()->ClientTeam() == team) 1619 count++; 1620 } 1621 1622 // write list 1623 1624 sender.StartMessage(B_OK); 1625 sender.Attach<int32>(count); 1626 1627 for (WindowLayer *window = fAllWindows.FirstWindow(); window != NULL; 1628 window = window->NextWindow(kAllWindowList)) { 1629 if (team >= B_OK && window->ServerWindow()->ClientTeam() != team) 1630 continue; 1631 1632 sender.Attach<int32>(window->ServerWindow()->ServerToken()); 1633 } 1634 1635 sender.Flush(); 1636 } 1637 1638 1639 void 1640 Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender) 1641 { 1642 BAutolock locker(fWindowLock); 1643 BAutolock tokenLocker(BPrivate::gDefaultTokens); 1644 1645 ::ServerWindow* window; 1646 if (BPrivate::gDefaultTokens.GetToken(serverToken, 1647 B_SERVER_TOKEN, (void**)&window) != B_OK) { 1648 sender.StartMessage(B_ENTRY_NOT_FOUND); 1649 sender.Flush(); 1650 return; 1651 } 1652 1653 window_info info; 1654 window->GetInfo(info); 1655 1656 int32 length = window->Title() ? strlen(window->Title()) : 0; 1657 1658 sender.StartMessage(B_OK); 1659 sender.Attach<int32>(sizeof(window_info) + length + 1); 1660 sender.Attach(&info, sizeof(window_info)); 1661 if (length > 0) 1662 sender.Attach(window->Title(), length + 1); 1663 else 1664 sender.Attach<char>('\0'); 1665 sender.Flush(); 1666 } 1667 1668 1669 void 1670 Desktop::MarkDirty(BRegion& region) 1671 { 1672 if (region.CountRects() == 0) 1673 return; 1674 1675 if (LockAllWindows()) { 1676 // send redraw messages to all windows intersecting the dirty region 1677 _TriggerWindowRedrawing(region); 1678 1679 UnlockAllWindows(); 1680 } 1681 } 1682 1683 1684 void 1685 Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen) 1686 { 1687 // the available region on screen starts with the entire screen area 1688 // each window on the screen will take a portion from that area 1689 1690 // figure out what the entire screen area is 1691 stillAvailableOnScreen = fScreenRegion; 1692 1693 // set clipping of each window 1694 for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL; 1695 window = window->PreviousWindow(fCurrentWorkspace)) { 1696 if (!window->IsHidden()) { 1697 window->SetClipping(&stillAvailableOnScreen); 1698 // that windows region is not available on screen anymore 1699 stillAvailableOnScreen.Exclude(&window->VisibleRegion()); 1700 } 1701 } 1702 } 1703 1704 1705 void 1706 Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion) 1707 { 1708 // send redraw messages to all windows intersecting the dirty region 1709 for (WindowLayer* window = _CurrentWindows().LastWindow(); window != NULL; 1710 window = window->PreviousWindow(fCurrentWorkspace)) { 1711 if (!window->IsHidden() 1712 && newDirtyRegion.Intersects(window->VisibleRegion().Frame())) 1713 window->ProcessDirtyRegion(newDirtyRegion); 1714 } 1715 } 1716 1717 1718 void 1719 Desktop::_SetBackground(BRegion& background) 1720 { 1721 // NOTE: the drawing operation is caried out 1722 // in the clipping region rebuild, but it is 1723 // ok actually, because it also avoids trails on 1724 // moving windows 1725 1726 // remember the region not covered by any windows 1727 // and redraw the dirty background 1728 BRegion dirtyBackground(background); 1729 dirtyBackground.Exclude(&fBackgroundRegion); 1730 dirtyBackground.IntersectWith(&background); 1731 fBackgroundRegion = background; 1732 if (dirtyBackground.Frame().IsValid()) { 1733 if (GetDrawingEngine()->Lock()) { 1734 GetDrawingEngine()->ConstrainClippingRegion(NULL); 1735 GetDrawingEngine()->FillRegion(dirtyBackground, 1736 fWorkspaces[fCurrentWorkspace].Color()); 1737 1738 GetDrawingEngine()->Unlock(); 1739 } 1740 } 1741 } 1742