2 * Copyright 2001-2015, Haiku.
3 * Distributed under the terms of the MIT License.
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 * Brecht Machiels <brecht@mos6581.org>
11 * Clemens Zeidler <haiku@clemens-zeidler.de>
12 * Ingo Weinhold <ingo_weinhold@gmx.de>
13 * Joseph Groover <looncraz@looncraz.net>
17 /*! Class used to encapsulate desktop management */
28 #include <DirectWindow.h>
30 #include <FindDirectory.h>
32 #include <MessageFilter.h>
37 #include <PrivateScreen.h>
38 #include <ServerProtocol.h>
39 #include <ViewPrivate.h>
40 #include <WindowInfo.h>
42 #include "AppServer.h"
43 #include "ClickTarget.h"
44 #include "DecorManager.h"
45 #include "DesktopSettingsPrivate.h"
46 #include "DrawingEngine.h"
47 #include "FontManager.h"
48 #include "HWInterface.h"
49 #include "InputManager.h"
51 #include "ServerApp.h"
52 #include "ServerConfig.h"
53 #include "ServerCursor.h"
54 #include "ServerWindow.h"
55 #include "SystemPalette.h"
56 #include "WindowPrivate.h"
58 #include "Workspace.h"
59 #include "WorkspacesView.h"
62 # include "EventStream.h"
66 //#define DEBUG_DESKTOP
68 # define STRACE(a) printf a
75 square_vector_length(float x
, float y
)
82 square_distance(const BPoint
& a
, const BPoint
& b
)
84 return square_vector_length(a
.x
- b
.x
, a
.y
- b
.y
);
88 class KeyboardFilter
: public EventFilter
{
90 KeyboardFilter(Desktop
* desktop
);
92 virtual filter_result
Filter(BMessage
* message
, EventTarget
** _target
,
93 int32
* _viewToken
, BMessage
* latestMouseMoved
);
94 virtual void RemoveTarget(EventTarget
* target
);
97 void _UpdateFocus(int32 key
, uint32 modifiers
, EventTarget
** _target
);
100 EventTarget
* fLastFocus
;
101 bigtime_t fTimestamp
;
105 class MouseFilter
: public EventFilter
{
107 MouseFilter(Desktop
* desktop
);
109 virtual filter_result
Filter(BMessage
* message
, EventTarget
** _target
,
110 int32
* _viewToken
, BMessage
* latestMouseMoved
);
114 int32 fLastClickButtons
;
115 int32 fLastClickModifiers
;
116 int32 fResetClickCount
;
117 BPoint fLastClickPoint
;
118 ClickTarget fLastClickTarget
;
125 KeyboardFilter::KeyboardFilter(Desktop
* desktop
)
135 KeyboardFilter::_UpdateFocus(int32 key
, uint32 modifiers
, EventTarget
** _target
)
137 if (!fDesktop
->LockSingleWindow())
140 EventTarget
* focus
= fDesktop
->KeyboardEventTarget();
143 bigtime_t now
= system_time();
145 // TODO: this is a try to not steal focus from the current window
146 // in case you enter some text and a window pops up you haven't
147 // triggered yourself (like a pop-up window in your browser while
148 // you're typing a password in another window) - maybe this should
149 // be done differently, though (using something like B_LOCK_WINDOW_FOCUS)
150 // (at least B_WINDOW_ACTIVATED must be postponed)
152 if (fLastFocus
== NULL
153 || (focus
!= fLastFocus
&& now
- fTimestamp
> 100000)) {
154 // if the time span between the key presses is very short
155 // we keep our previous focus alive - this is safe even
156 // if the target doesn't exist anymore, as we don't reset
157 // it, and the event focus passed in is always valid (or NULL)
165 fDesktop
->UnlockSingleWindow();
168 // we always allow to switch focus after the enter key has pressed
169 if (key
== B_ENTER
|| modifiers
== B_COMMAND_KEY
170 || modifiers
== B_CONTROL_KEY
|| modifiers
== B_OPTION_KEY
)
179 KeyboardFilter::Filter(BMessage
* message
, EventTarget
** _target
,
180 int32
* /*_viewToken*/, BMessage
* /*latestMouseMoved*/)
185 message
->FindInt32("key", &key
);
186 message
->FindInt32("modifiers", &modifiers
);
188 if ((message
->what
== B_KEY_DOWN
|| message
->what
== B_UNMAPPED_KEY_DOWN
)) {
189 // Check for safe video mode (cmd + ctrl + escape)
190 if (key
== 0x01 && (modifiers
& B_COMMAND_KEY
) != 0
191 && (modifiers
& B_CONTROL_KEY
) != 0) {
192 system("screenmode --fall-back &");
193 return B_SKIP_MESSAGE
;
196 bool takeWindow
= (modifiers
& B_SHIFT_KEY
) != 0
197 || fDesktop
->MouseEventWindow() != NULL
;
198 if (key
>= B_F1_KEY
&& key
<= B_F12_KEY
) {
202 if ((modifiers
& (B_COMMAND_KEY
| B_CONTROL_KEY
| B_OPTION_KEY
))
205 if ((modifiers
& B_CONTROL_KEY
) != 0)
208 STRACE(("Set Workspace %" B_PRId32
"\n", key
- 1));
210 fDesktop
->SetWorkspaceAsync(key
- B_F1_KEY
, takeWindow
);
211 return B_SKIP_MESSAGE
;
214 && (modifiers
& (B_COMMAND_KEY
| B_CONTROL_KEY
| B_OPTION_KEY
))
216 // switch to previous workspace (command + `)
217 fDesktop
->SetWorkspaceAsync(-1, takeWindow
);
218 return B_SKIP_MESSAGE
;
222 if (message
->what
== B_KEY_DOWN
223 || message
->what
== B_MODIFIERS_CHANGED
224 || message
->what
== B_UNMAPPED_KEY_DOWN
225 || message
->what
== B_INPUT_METHOD_EVENT
)
226 _UpdateFocus(key
, modifiers
, _target
);
228 return fDesktop
->KeyEvent(message
->what
, key
, modifiers
);
233 KeyboardFilter::RemoveTarget(EventTarget
* target
)
235 if (target
== fLastFocus
)
243 MouseFilter::MouseFilter(Desktop
* desktop
)
246 fLastClickButtons(0),
247 fLastClickModifiers(0),
256 MouseFilter::Filter(BMessage
* message
, EventTarget
** _target
, int32
* _viewToken
,
257 BMessage
* latestMouseMoved
)
260 if (message
->FindPoint("where", &where
) != B_OK
)
261 return B_DISPATCH_MESSAGE
;
264 if (message
->FindInt32("buttons", &buttons
) != B_OK
)
267 if (!fDesktop
->LockAllWindows())
268 return B_DISPATCH_MESSAGE
;
270 int32 viewToken
= B_NULL_TOKEN
;
272 Window
* window
= fDesktop
->MouseEventWindow();
274 window
= fDesktop
->WindowAt(where
);
276 if (window
!= NULL
) {
277 // dispatch event to the window
278 switch (message
->what
) {
281 int32 windowToken
= window
->ServerWindow()->ServerToken();
283 // First approximation of click count validation. We reset the
284 // click count when modifiers or pressed buttons have changed
285 // or when we've got a different click target, or when the
286 // previous click location is too far from the new one. We can
287 // only check the window of the click target here; we'll recheck
288 // after asking the window.
289 int32 modifiers
= message
->FindInt32("modifiers");
291 int32 originalClickCount
= message
->FindInt32("clicks");
292 if (originalClickCount
<= 0)
293 originalClickCount
= 1;
295 int32 clickCount
= originalClickCount
;
296 if (clickCount
> 1) {
297 if (modifiers
!= fLastClickModifiers
298 || buttons
!= fLastClickButtons
299 || !fLastClickTarget
.IsValid()
300 || fLastClickTarget
.WindowToken() != windowToken
301 || square_distance(where
, fLastClickPoint
) >= 16
302 || clickCount
- fResetClickCount
< 1) {
305 clickCount
-= fResetClickCount
;
309 ClickTarget clickTarget
;
310 window
->MouseDown(message
, where
, fLastClickTarget
, clickCount
,
313 // If the click target changed, always reset the click count.
314 if (clickCount
!= 1 && clickTarget
!= fLastClickTarget
)
317 // update our click count management attributes
318 fResetClickCount
= originalClickCount
- clickCount
;
319 fLastClickTarget
= clickTarget
;
320 fLastClickButtons
= buttons
;
321 fLastClickModifiers
= modifiers
;
322 fLastClickPoint
= where
;
324 // get the view token from the click target
325 if (clickTarget
.GetType() == ClickTarget::TYPE_WINDOW_CONTENTS
)
326 viewToken
= clickTarget
.WindowElement();
328 // update the message's "clicks" field, if necessary
329 if (clickCount
!= originalClickCount
) {
330 if (message
->HasInt32("clicks"))
331 message
->ReplaceInt32("clicks", clickCount
);
333 message
->AddInt32("clicks", clickCount
);
336 // notify desktop listeners
337 fDesktop
->NotifyMouseDown(window
, message
, where
);
342 window
->MouseUp(message
, where
, &viewToken
);
344 fDesktop
->SetMouseEventWindow(NULL
);
345 fDesktop
->NotifyMouseUp(window
, message
, where
);
349 window
->MouseMoved(message
, where
, &viewToken
,
350 latestMouseMoved
== NULL
|| latestMouseMoved
== message
,
352 fDesktop
->NotifyMouseMoved(window
, message
, where
);
356 if (viewToken
!= B_NULL_TOKEN
) {
357 fDesktop
->SetViewUnderMouse(window
, viewToken
);
359 *_viewToken
= viewToken
;
360 *_target
= &window
->EventTarget();
362 } else if (message
->what
== B_MOUSE_DOWN
) {
363 // the mouse-down didn't hit a window -- reset the click target
364 fResetClickCount
= 0;
365 fLastClickTarget
= ClickTarget();
366 fLastClickButtons
= message
->FindInt32("buttons");
367 fLastClickModifiers
= message
->FindInt32("modifiers");
368 fLastClickPoint
= where
;
371 if (window
== NULL
|| viewToken
== B_NULL_TOKEN
) {
372 // mouse is not over a window or over a decorator
373 fDesktop
->SetViewUnderMouse(window
, B_NULL_TOKEN
);
374 fDesktop
->SetCursor(NULL
);
379 fDesktop
->SetLastMouseState(where
, buttons
, window
);
381 fDesktop
->NotifyMouseEvent(message
);
383 fDesktop
->UnlockAllWindows();
385 return B_DISPATCH_MESSAGE
;
393 workspace_to_workspaces(int32 index
)
400 workspace_in_workspaces(int32 index
, uint32 workspaces
)
402 return (workspaces
& (1UL << index
)) != 0;
409 Desktop::Desktop(uid_t userID
, const char* targetScreen
)
411 MessageLooper("desktop"),
414 fTargetScreen(strdup(targetScreen
)),
416 fSharedReadOnlyArea(-1),
417 fApplicationsLock("application list"),
418 fShutdownSemaphore(-1),
420 fScreenLock("screen lock"),
421 fDirectScreenLock("direct screen lock"),
422 fDirectScreenTeam(-1),
423 fCurrentWorkspace(0),
424 fPreviousWorkspace(0),
425 fAllWindows(kAllWindowList
),
426 fSubsetWindows(kSubsetList
),
427 fFocusList(kFocusList
),
428 fWorkspacesViews(false),
430 fWorkspacesLock("workspaces list"),
431 fWindowLock("window lock"),
433 fMouseEventWindow(NULL
),
434 fWindowUnderMouse(NULL
),
435 fLockedFocusWindow(NULL
),
436 fViewUnderMouse(B_NULL_TOKEN
),
437 fLastMousePosition(B_ORIGIN
),
438 fLastMouseButtons(0),
444 memset(fLastWorkspaceFocus
, 0, sizeof(fLastWorkspaceFocus
));
446 char name
[B_OS_NAME_LENGTH
];
447 Desktop::_GetLooperName(name
, sizeof(name
));
449 fMessagePort
= create_port(DEFAULT_MONITOR_PORT_SIZE
, name
);
450 if (fMessagePort
< B_OK
)
453 fLink
.SetReceiverPort(fMessagePort
);
455 // register listeners
456 RegisterListener(&fStackAndTile
);
458 const DesktopListenerList
& newListeners
459 = gDecorManager
.GetDesktopListeners();
460 for (int i
= 0; i
< newListeners
.CountItems(); i
++)
461 RegisterListener(newListeners
.ItemAt(i
));
469 delete_area(fSharedReadOnlyArea
);
470 delete_port(fMessagePort
);
471 gFontManager
->DetachUser(fUserID
);
478 Desktop::RegisterListener(DesktopListener
* listener
)
480 DesktopObservable::RegisterListener(listener
, this);
487 if (fMessagePort
< B_OK
)
490 // the system palette needs to be initialized before the
491 // desktop settings, since it is used there already
492 InitializeColorMap();
494 const size_t areaSize
= B_PAGE_SIZE
;
495 char name
[B_OS_NAME_LENGTH
];
496 snprintf(name
, sizeof(name
), "d:%d:shared read only", fUserID
);
497 fSharedReadOnlyArea
= create_area(name
, (void **)&fServerReadOnlyMemory
,
498 B_ANY_ADDRESS
, areaSize
, B_NO_LOCK
, B_READ_AREA
| B_WRITE_AREA
);
499 if (fSharedReadOnlyArea
< B_OK
)
500 return fSharedReadOnlyArea
;
502 gFontManager
->AttachUser(fUserID
);
504 fSettings
= new DesktopSettingsPrivate(fServerReadOnlyMemory
);
506 for (int32 i
= 0; i
< kMaxWorkspaces
; i
++) {
507 _Windows(i
).SetIndex(i
);
508 fWorkspaces
[i
].RestoreConfiguration(*fSettings
->WorkspacesMessage(i
));
511 fVirtualScreen
.SetConfiguration(*this,
512 fWorkspaces
[0].CurrentScreenConfiguration());
514 if (fVirtualScreen
.HWInterface() == NULL
) {
515 debug_printf("Could not initialize graphics output. Exiting.\n");
519 fVirtualScreen
.HWInterface()->MoveCursorTo(
520 fVirtualScreen
.Frame().Width() / 2,
521 fVirtualScreen
.Frame().Height() / 2);
524 gInputManager
->AddStream(new InputServerStream
);
527 EventStream
* stream
= fVirtualScreen
.HWInterface()->CreateEventStream();
529 stream
= gInputManager
->GetStream();
531 fEventDispatcher
.SetDesktop(this);
532 fEventDispatcher
.SetTo(stream
);
533 if (fEventDispatcher
.InitCheck() != B_OK
)
534 _LaunchInputServer();
536 fEventDispatcher
.SetHWInterface(fVirtualScreen
.HWInterface());
538 fEventDispatcher
.SetMouseFilter(new MouseFilter(this));
539 fEventDispatcher
.SetKeyboardFilter(new KeyboardFilter(this));
541 // draw the background
543 fScreenRegion
= fVirtualScreen
.Frame();
545 BRegion stillAvailableOnScreen
;
546 _RebuildClippingForAllWindows(stillAvailableOnScreen
);
547 _SetBackground(stillAvailableOnScreen
);
550 // this will set the default cursor
552 fVirtualScreen
.HWInterface()->SetCursorVisible(true);
558 /*! \brief Send a quick (no attachments) message to all applications.
560 Quite useful for notification for things like server shutdown, system
564 Desktop::BroadcastToAllApps(int32 code
)
566 BAutolock
locker(fApplicationsLock
);
568 for (int32 i
= fApplications
.CountItems(); i
-- > 0;) {
569 fApplications
.ItemAt(i
)->PostMessage(code
);
574 /*! \brief Send a quick (no attachments) message to all windows.
577 Desktop::BroadcastToAllWindows(int32 code
)
579 AutoReadLocker
_(fWindowLock
);
581 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
582 window
= window
->NextWindow(kAllWindowList
)) {
583 window
->ServerWindow()->PostMessage(code
);
589 Desktop::GetAllWindowTargets(DelayedMessage
& message
)
591 AutoReadLocker
_(fWindowLock
);
594 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
595 window
= window
->NextWindow(kAllWindowList
)) {
596 message
.AddTarget(window
->ServerWindow()->MessagePort());
605 Desktop::GetAllAppTargets(DelayedMessage
& message
)
607 BAutolock
_(fApplicationsLock
);
609 for (int32 index
= 0; index
< fApplications
.CountItems(); ++index
)
610 message
.AddTarget(fApplications
.ItemAt(index
)->MessagePort());
612 return fApplications
.CountItems();
617 Desktop::KeyEvent(uint32 what
, int32 key
, int32 modifiers
)
619 filter_result result
= B_DISPATCH_MESSAGE
;
620 if (LockAllWindows()) {
621 Window
* window
= MouseEventWindow();
623 window
= WindowAt(fLastMousePosition
);
625 if (window
!= NULL
) {
626 if (what
== B_MODIFIERS_CHANGED
)
627 window
->ModifiersChanged(modifiers
);
630 if (NotifyKeyPressed(what
, key
, modifiers
))
631 result
= B_SKIP_MESSAGE
;
640 // #pragma mark - Mouse and cursor methods
644 Desktop::SetCursor(ServerCursor
* newCursor
)
646 if (newCursor
== NULL
)
647 newCursor
= fCursorManager
.GetCursor(B_CURSOR_ID_SYSTEM_DEFAULT
);
649 if (newCursor
== fCursor
)
654 if (fManagementCursor
.Get() == NULL
)
655 HWInterface()->SetCursor(newCursor
);
659 ServerCursorReference
660 Desktop::Cursor() const
667 Desktop::SetManagementCursor(ServerCursor
* newCursor
)
669 if (newCursor
== fManagementCursor
)
672 fManagementCursor
= newCursor
;
674 HWInterface()->SetCursor(newCursor
!= NULL
? newCursor
: fCursor
.Get());
679 Desktop::SetLastMouseState(const BPoint
& position
, int32 buttons
,
680 Window
* windowUnderMouse
)
682 // The all-window-lock is write-locked.
683 fLastMousePosition
= position
;
684 fLastMouseButtons
= buttons
;
686 if (fLastMouseButtons
== 0 && fLockedFocusWindow
) {
687 fLockedFocusWindow
= NULL
;
688 if (fSettings
->FocusFollowsMouse())
689 SetFocusWindow(windowUnderMouse
);
695 Desktop::GetLastMouseState(BPoint
* position
, int32
* buttons
) const
697 *position
= fLastMousePosition
;
698 *buttons
= fLastMouseButtons
;
702 // #pragma mark - Screen methods
706 Desktop::SetScreenMode(int32 workspace
, int32 id
, const display_mode
& mode
,
709 AutoWriteLocker
_(fWindowLock
);
711 if (workspace
== B_CURRENT_WORKSPACE_INDEX
)
712 workspace
= fCurrentWorkspace
;
714 if (workspace
< 0 || workspace
>= kMaxWorkspaces
)
717 Screen
* screen
= fVirtualScreen
.ScreenByID(id
);
719 return B_NAME_NOT_FOUND
;
721 // Check if the mode has actually changed
723 if (workspace
== fCurrentWorkspace
) {
724 // retrieve from current screen
725 display_mode oldMode
;
726 screen
->GetMode(oldMode
);
728 if (!memcmp(&oldMode
, &mode
, sizeof(display_mode
)))
733 _SuspendDirectFrameBufferAccess();
735 AutoWriteLocker
locker(fScreenLock
);
737 status_t status
= screen
->SetMode(mode
);
738 if (status
!= B_OK
) {
741 _ResumeDirectFrameBufferAccess();
745 // retrieve from settings
746 screen_configuration
* configuration
747 = fWorkspaces
[workspace
].CurrentScreenConfiguration().CurrentByID(
749 if (configuration
!= NULL
750 && !memcmp(&configuration
->mode
, &mode
, sizeof(display_mode
)))
754 // Update our configurations
757 bool hasInfo
= screen
->GetMonitorInfo(info
) == B_OK
;
759 fWorkspaces
[workspace
].CurrentScreenConfiguration().Set(id
,
760 hasInfo
? &info
: NULL
, screen
->Frame(), mode
);
762 fWorkspaces
[workspace
].StoredScreenConfiguration().Set(id
,
763 hasInfo
? &info
: NULL
, screen
->Frame(), mode
);
764 StoreWorkspaceConfiguration(workspace
);
767 _ScreenChanged(screen
);
768 if (workspace
== fCurrentWorkspace
)
769 _ResumeDirectFrameBufferAccess();
776 Desktop::GetScreenMode(int32 workspace
, int32 id
, display_mode
& mode
)
778 AutoReadLocker
_(fScreenLock
);
780 if (workspace
== B_CURRENT_WORKSPACE_INDEX
)
781 workspace
= fCurrentWorkspace
;
783 if (workspace
< 0 || workspace
>= kMaxWorkspaces
)
786 if (workspace
== fCurrentWorkspace
) {
787 // retrieve from current screen
788 Screen
* screen
= fVirtualScreen
.ScreenByID(id
);
790 return B_NAME_NOT_FOUND
;
792 screen
->GetMode(mode
);
796 // retrieve from settings
797 screen_configuration
* configuration
798 = fWorkspaces
[workspace
].CurrentScreenConfiguration().CurrentByID(id
);
799 if (configuration
== NULL
)
800 return B_NAME_NOT_FOUND
;
802 mode
= configuration
->mode
;
808 Desktop::GetScreenFrame(int32 workspace
, int32 id
, BRect
& frame
)
810 AutoReadLocker
_(fScreenLock
);
812 if (workspace
== B_CURRENT_WORKSPACE_INDEX
)
813 workspace
= fCurrentWorkspace
;
815 if (workspace
< 0 || workspace
>= kMaxWorkspaces
)
818 if (workspace
== fCurrentWorkspace
) {
819 // retrieve from current screen
820 Screen
* screen
= fVirtualScreen
.ScreenByID(id
);
822 return B_NAME_NOT_FOUND
;
824 frame
= screen
->Frame();
828 // retrieve from settings
829 screen_configuration
* configuration
830 = fWorkspaces
[workspace
].CurrentScreenConfiguration().CurrentByID(id
);
831 if (configuration
== NULL
)
832 return B_NAME_NOT_FOUND
;
834 frame
= configuration
->frame
;
840 Desktop::RevertScreenModes(uint32 workspaces
)
845 AutoWriteLocker
_(fWindowLock
);
847 for (int32 workspace
= 0; workspace
< kMaxWorkspaces
; workspace
++) {
848 if ((workspaces
& (1U << workspace
)) == 0)
851 // Revert all screens on this workspace
853 // TODO: ideally, we would know which screens to revert - this way, too
854 // many of them could be reverted
856 for (int32 index
= 0; index
< fVirtualScreen
.CountScreens(); index
++) {
857 Screen
* screen
= fVirtualScreen
.ScreenAt(index
);
859 // retrieve configurations
860 screen_configuration
* stored
= fWorkspaces
[workspace
]
861 .StoredScreenConfiguration().CurrentByID(screen
->ID());
862 screen_configuration
* current
= fWorkspaces
[workspace
]
863 .CurrentScreenConfiguration().CurrentByID(screen
->ID());
865 if ((stored
!= NULL
&& current
!= NULL
866 && !memcmp(&stored
->mode
, ¤t
->mode
,
867 sizeof(display_mode
)))
868 || (stored
== NULL
&& current
== NULL
))
871 if (stored
== NULL
) {
872 fWorkspaces
[workspace
].CurrentScreenConfiguration()
875 if (workspace
== fCurrentWorkspace
) {
876 _SuspendDirectFrameBufferAccess();
877 _SetCurrentWorkspaceConfiguration();
878 _ResumeDirectFrameBufferAccess();
881 SetScreenMode(workspace
, screen
->ID(), stored
->mode
, false);
888 Desktop::LockDirectScreen(team_id team
)
890 // TODO: BWindowScreens should use the same mechanism as BDirectWindow,
891 // which would make this method superfluous.
893 status_t status
= fDirectScreenLock
.LockWithTimeout(1000000L);
895 fDirectScreenTeam
= team
;
902 Desktop::UnlockDirectScreen(team_id team
)
904 if (fDirectScreenTeam
== team
) {
905 fDirectScreenLock
.Unlock();
906 fDirectScreenTeam
= -1;
910 return B_PERMISSION_DENIED
;
914 // #pragma mark - Workspaces methods
917 /*! Changes the current workspace to the one specified by \a index.
920 Desktop::SetWorkspaceAsync(int32 index
, bool moveFocusWindow
)
922 BPrivate::LinkSender
link(MessagePort());
923 link
.StartMessage(AS_ACTIVATE_WORKSPACE
);
924 link
.Attach
<int32
>(index
);
925 link
.Attach
<bool>(moveFocusWindow
);
930 /*! Changes the current workspace to the one specified by \a index.
931 You must not hold any window lock when calling this method.
934 Desktop::SetWorkspace(int32 index
, bool moveFocusWindow
)
937 DesktopSettings
settings(this);
939 if (index
< 0 || index
>= settings
.WorkspacesCount()
940 || index
== fCurrentWorkspace
) {
945 _SetWorkspace(index
, moveFocusWindow
);
948 _SendFakeMouseMoved();
953 Desktop::SetWorkspacesLayout(int32 newColumns
, int32 newRows
)
955 int32 newCount
= newColumns
* newRows
;
956 if (newCount
< 1 || newCount
> kMaxWorkspaces
)
959 if (!LockAllWindows())
962 fSettings
->SetWorkspacesLayout(newColumns
, newRows
);
964 // either update the workspaces window, or switch to
965 // the last available workspace - which will update
966 // the workspaces window automatically
967 bool workspaceChanged
= CurrentWorkspace() >= newCount
;
968 if (workspaceChanged
)
969 _SetWorkspace(newCount
- 1);
971 _WindowChanged(NULL
);
975 if (workspaceChanged
)
976 _SendFakeMouseMoved();
982 /*! Returns the virtual screen frame of the workspace specified by \a index.
985 Desktop::WorkspaceFrame(int32 index
) const
988 if (index
== fCurrentWorkspace
)
989 frame
= fVirtualScreen
.Frame();
990 else if (index
>= 0 && index
< fSettings
->WorkspacesCount()) {
992 if (fSettings
->WorkspacesMessage(index
)->FindMessage("screen",
994 || screenData
.FindRect("frame", &frame
) != B_OK
) {
995 frame
= fVirtualScreen
.Frame();
1003 /*! \brief Stores the workspace configuration.
1004 You must hold the window lock when calling this method.
1007 Desktop::StoreWorkspaceConfiguration(int32 index
)
1009 // Retrieve settings
1012 fWorkspaces
[index
].StoreConfiguration(settings
);
1016 fSettings
->SetWorkspacesMessage(index
, settings
);
1017 fSettings
->Save(kWorkspacesSettings
);
1022 Desktop::AddWorkspacesView(WorkspacesView
* view
)
1024 if (view
->Window() == NULL
|| view
->Window()->IsHidden())
1027 BAutolock
_(fWorkspacesLock
);
1029 if (!fWorkspacesViews
.HasItem(view
))
1030 fWorkspacesViews
.AddItem(view
);
1035 Desktop::RemoveWorkspacesView(WorkspacesView
* view
)
1037 BAutolock
_(fWorkspacesLock
);
1038 fWorkspacesViews
.RemoveItem(view
);
1042 // #pragma mark - Methods for Window manipulation
1045 /*! \brief Activates or focusses the window based on the pointer position.
1048 Desktop::SelectWindow(Window
* window
)
1050 if (fSettings
->ClickToFocusMouse()) {
1051 // Only bring the window to front when it is not the window under the
1052 // mouse pointer. This should result in sensible behaviour.
1053 if (window
!= fWindowUnderMouse
1054 || (window
== fWindowUnderMouse
&& window
!= FocusWindow()))
1055 ActivateWindow(window
);
1057 SetFocusWindow(window
);
1059 ActivateWindow(window
);
1063 /*! \brief Tries to move the specified window to the front of the screen,
1064 and make it the focus window.
1066 If there are any modal windows on this screen, it might not actually
1067 become the frontmost window, though, as modal windows stay in front
1071 Desktop::ActivateWindow(Window
* window
)
1073 STRACE(("ActivateWindow(%p, %s)\n", window
, window
1074 ? window
->Title() : "<none>"));
1076 if (window
== NULL
) {
1081 if (window
->Workspaces() == 0 && window
->IsNormal())
1084 AutoWriteLocker
allWindowLocker(fWindowLock
);
1086 NotifyWindowActivated(window
);
1088 bool windowOnOtherWorkspace
= !window
->InWorkspace(fCurrentWorkspace
);
1089 if (windowOnOtherWorkspace
1090 && (window
->Flags() & B_NOT_ANCHORED_ON_ACTIVATE
) == 0) {
1091 if ((window
->Flags() & B_NO_WORKSPACE_ACTIVATION
) == 0) {
1092 // Switch to the workspace on which this window is
1093 // (we'll take the first one that the window is on)
1094 uint32 workspaces
= window
->Workspaces();
1095 for (int32 i
= 0; i
< fSettings
->WorkspacesCount(); i
++) {
1096 uint32 workspace
= workspace_to_workspaces(i
);
1097 if (workspaces
& workspace
) {
1099 windowOnOtherWorkspace
= false;
1107 if (windowOnOtherWorkspace
) {
1108 if (!window
->IsNormal()) {
1109 // Bring a window to front that this floating window belongs to
1110 Window
* front
= _LastFocusSubsetWindow(window
);
1111 if (front
== NULL
) {
1112 // We can't do anything about those.
1116 ActivateWindow(front
);
1118 if (!window
->InWorkspace(fCurrentWorkspace
)) {
1119 // This window can't be made active
1123 // Bring the window to the current workspace
1124 // TODO: what if this window is on multiple workspaces?!?
1125 uint32 workspaces
= workspace_to_workspaces(fCurrentWorkspace
);
1126 SetWindowWorkspaces(window
, workspaces
);
1130 if (window
->IsMinimized()) {
1131 // Unlike WindowAction(), this is called from the application itself,
1132 // so we will just unminimize the window here.
1133 window
->SetMinimized(false);
1137 if (window
== FrontWindow()) {
1138 // see if there is a normal B_AVOID_FRONT window still in front of us
1139 Window
* avoidsFront
= window
->NextWindow(fCurrentWorkspace
);
1140 while (avoidsFront
&& avoidsFront
->IsNormal()
1141 && (avoidsFront
->Flags() & B_AVOID_FRONT
) == 0) {
1142 avoidsFront
= avoidsFront
->NextWindow(fCurrentWorkspace
);
1145 if (avoidsFront
== NULL
) {
1146 // we're already the frontmost window, we might just not have focus
1148 if ((window
->Flags() & B_AVOID_FOCUS
) == 0)
1149 SetFocusWindow(window
);
1154 WindowList
windows(kWorkingList
);
1155 Window
* frontmost
= window
->Frontmost();
1156 const Window
* lastWindowUnderMouse
= fWindowUnderMouse
;
1158 CurrentWindows().RemoveWindow(window
);
1159 windows
.AddWindow(window
);
1160 window
->MoveToTopStackLayer();
1162 if (frontmost
!= NULL
&& frontmost
->IsModal()) {
1163 // all modal windows follow their subsets to the front
1164 // (ie. they are staying in front of them, but they are
1165 // not supposed to change their order because of that)
1168 for (Window
* modal
= frontmost
; modal
!= NULL
; modal
= nextModal
) {
1169 // get the next modal window
1170 nextModal
= modal
->NextWindow(fCurrentWorkspace
);
1171 while (nextModal
!= NULL
&& !nextModal
->IsModal()) {
1172 nextModal
= nextModal
->NextWindow(fCurrentWorkspace
);
1174 if (nextModal
!= NULL
&& !nextModal
->HasInSubset(window
))
1177 CurrentWindows().RemoveWindow(modal
);
1178 windows
.AddWindow(modal
);
1182 _BringWindowsToFront(windows
, kWorkingList
, true);
1184 if ((window
->Flags() & B_AVOID_FOCUS
) == 0)
1185 SetFocusWindow(window
);
1187 bool sendFakeMouseMoved
= _CheckSendFakeMouseMoved(lastWindowUnderMouse
);
1189 allWindowLocker
.Unlock();
1191 if (sendFakeMouseMoved
)
1192 _SendFakeMouseMoved();
1197 Desktop::SendWindowBehind(Window
* window
, Window
* behindOf
, bool sendStack
)
1199 if (!LockAllWindows())
1202 Window
* orgWindow
= window
;
1203 WindowStack
* stack
= window
->GetWindowStack();
1204 if (sendStack
&& stack
!= NULL
)
1205 window
= stack
->TopLayerWindow();
1207 // TODO: should the "not in current workspace" be handled anyway?
1208 // (the code below would have to be changed then, though)
1209 if (window
== BackWindow()
1210 || !window
->InWorkspace(fCurrentWorkspace
)
1211 || (behindOf
!= NULL
&& !behindOf
->InWorkspace(fCurrentWorkspace
))) {
1216 // Is this a valid behindOf window?
1217 if (behindOf
!= NULL
&& window
->HasInSubset(behindOf
))
1220 // what is currently visible of the window
1221 // might be dirty after the window is send to back
1222 BRegion
dirty(window
->VisibleRegion());
1224 Window
* backmost
= window
->Backmost(behindOf
);
1225 const Window
* lastWindowUnderMouse
= fWindowUnderMouse
;
1227 CurrentWindows().RemoveWindow(window
);
1228 CurrentWindows().AddWindow(window
, backmost
1229 ? backmost
->NextWindow(fCurrentWorkspace
) : BackWindow());
1232 _RebuildClippingForAllWindows(dummy
);
1234 // only redraw the top layer window to avoid flicker
1236 // mark everything dirty that is no longer visible
1237 BRegion
clean(window
->VisibleRegion());
1238 dirty
.Exclude(&clean
);
1243 if (fSettings
->FocusFollowsMouse())
1244 SetFocusWindow(WindowAt(fLastMousePosition
));
1245 else if (fSettings
->NormalMouse())
1246 SetFocusWindow(NULL
);
1248 _WindowChanged(window
);
1250 if (sendStack
&& stack
!= NULL
) {
1251 for (int32 i
= 0; i
< stack
->CountWindows(); i
++) {
1252 Window
* stackWindow
= stack
->LayerOrder().ItemAt(i
);
1253 if (stackWindow
== window
)
1255 SendWindowBehind(stackWindow
, behindOf
, false);
1259 bool sendFakeMouseMoved
= _CheckSendFakeMouseMoved(lastWindowUnderMouse
);
1260 NotifyWindowSentBehind(orgWindow
, behindOf
);
1264 if (sendFakeMouseMoved
)
1265 _SendFakeMouseMoved();
1270 Desktop::ShowWindow(Window
* window
)
1272 if (!window
->IsHidden())
1275 AutoWriteLocker
locker(fWindowLock
);
1277 window
->SetHidden(false);
1278 fFocusList
.AddWindow(window
);
1280 // If the window is on the current workspace, we'll show it. Special
1281 // handling for floating windows, as they can only be shown if their
1283 if (window
->InWorkspace(fCurrentWorkspace
)
1284 || (window
->IsFloating() && _LastFocusSubsetWindow(window
) != NULL
)) {
1285 _ShowWindow(window
, true);
1286 _UpdateSubsetWorkspaces(window
);
1287 ActivateWindow(window
);
1289 // then we don't need to send the fake mouse event either
1290 _WindowChanged(window
);
1294 if (window
->HasWorkspacesViews()) {
1295 // find workspaces views in view hierarchy
1296 BAutolock
_(fWorkspacesLock
);
1297 window
->FindWorkspacesViews(fWorkspacesViews
);
1300 // If the mouse cursor is directly over the newly visible window,
1301 // we'll send a fake mouse moved message to the window, so that
1302 // it knows the mouse is over it.
1304 _SendFakeMouseMoved(window
);
1309 Desktop::HideWindow(Window
* window
, bool fromMinimize
)
1311 if (window
->IsHidden())
1314 if (!LockAllWindows())
1317 window
->SetHidden(true);
1318 fFocusList
.RemoveWindow(window
);
1320 if (fMouseEventWindow
== window
) {
1321 // Make its decorator lose the current mouse action
1324 window
->MouseUp(&message
, fLastMousePosition
, &viewToken
);
1326 fMouseEventWindow
= NULL
;
1329 if (fLockedFocusWindow
== window
) {
1330 // Remove the focus lock so the focus can be changed below
1331 fLockedFocusWindow
= NULL
;
1334 if (window
->InWorkspace(fCurrentWorkspace
)) {
1335 _UpdateSubsetWorkspaces(window
);
1336 _HideWindow(window
);
1339 _WindowChanged(window
);
1341 if (FocusWindow() == window
)
1344 _WindowRemoved(window
);
1346 if (window
->HasWorkspacesViews()) {
1347 // remove workspaces views from this window
1348 BObjectList
<WorkspacesView
> list(false);
1349 window
->FindWorkspacesViews(list
);
1351 BAutolock
_(fWorkspacesLock
);
1353 while (WorkspacesView
* view
= list
.RemoveItemAt(0)) {
1354 fWorkspacesViews
.RemoveItem(view
);
1358 NotifyWindowHidden(window
, fromMinimize
);
1362 if (window
== fWindowUnderMouse
)
1363 _SendFakeMouseMoved();
1368 Desktop::MinimizeWindow(Window
* window
, bool minimize
)
1370 if (!LockAllWindows())
1373 if (minimize
&& !window
->IsHidden()) {
1374 HideWindow(window
, true);
1375 window
->SetMinimized(minimize
);
1376 NotifyWindowMinimized(window
, minimize
);
1377 } else if (!minimize
&& window
->IsHidden()) {
1378 ActivateWindow(window
);
1379 // this will unminimize the window for us
1380 NotifyWindowMinimized(window
, minimize
);
1388 Desktop::MoveWindowBy(Window
* window
, float x
, float y
, int32 workspace
)
1390 if (x
== 0 && y
== 0)
1393 AutoWriteLocker
_(fWindowLock
);
1395 Window
* topWindow
= window
->TopLayerStackWindow();
1396 if (topWindow
!= NULL
)
1399 if (workspace
== -1)
1400 workspace
= fCurrentWorkspace
;
1401 if (!window
->IsVisible() || workspace
!= fCurrentWorkspace
) {
1402 if (workspace
!= fCurrentWorkspace
) {
1403 WindowStack
* stack
= window
->GetWindowStack();
1404 if (stack
!= NULL
) {
1405 for (int32 s
= 0; s
< stack
->CountWindows(); s
++) {
1406 Window
* stackWindow
= stack
->WindowAt(s
);
1407 // move the window on another workspace - this doesn't
1408 // change it's current position
1409 if (stackWindow
->Anchor(workspace
).position
1410 == kInvalidWindowPosition
) {
1411 stackWindow
->Anchor(workspace
).position
1412 = stackWindow
->Frame().LeftTop();
1415 stackWindow
->Anchor(workspace
).position
+= BPoint(x
, y
);
1416 stackWindow
->SetCurrentWorkspace(workspace
);
1417 _WindowChanged(stackWindow
);
1421 window
->MoveBy((int32
)x
, (int32
)y
);
1423 NotifyWindowMoved(window
);
1427 // the dirty region starts with the visible area of the window being moved
1428 BRegion
newDirtyRegion(window
->VisibleRegion());
1430 // stop direct frame buffer access
1431 bool direct
= false;
1432 if (window
->ServerWindow()->IsDirectlyAccessing()) {
1433 window
->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP
);
1437 window
->MoveBy((int32
)x
, (int32
)y
);
1440 _RebuildClippingForAllWindows(background
);
1442 // construct the region that is possible to be blitted
1443 // to move the contents of the window
1444 BRegion
copyRegion(window
->VisibleRegion());
1445 copyRegion
.OffsetBy((int32
)-x
, (int32
)-y
);
1446 copyRegion
.IntersectWith(&newDirtyRegion
);
1447 // newDirtyRegion == the windows old visible region
1449 // include the the new visible region of the window being
1450 // moved into the dirty region (for now)
1451 newDirtyRegion
.Include(&window
->VisibleRegion());
1453 // NOTE: Having all windows locked should prevent any
1454 // problems with locking the drawing engine here.
1455 if (GetDrawingEngine()->LockParallelAccess()) {
1456 GetDrawingEngine()->CopyRegion(©Region
, (int32
)x
, (int32
)y
);
1457 GetDrawingEngine()->UnlockParallelAccess();
1460 // in the dirty region, exclude the parts that we
1461 // could move by blitting
1462 copyRegion
.OffsetBy((int32
)x
, (int32
)y
);
1463 newDirtyRegion
.Exclude(©Region
);
1465 MarkDirty(newDirtyRegion
);
1466 _SetBackground(background
);
1467 _WindowChanged(window
);
1469 // resume direct frame buffer access
1471 // TODO: the clipping actually only changes when we move our window
1472 // off screen, or behind some other window
1473 window
->ServerWindow()->HandleDirectConnection(
1474 B_DIRECT_START
| B_BUFFER_MOVED
| B_CLIPPING_MODIFIED
);
1477 NotifyWindowMoved(window
);
1482 Desktop::ResizeWindowBy(Window
* window
, float x
, float y
)
1484 if (x
== 0 && y
== 0)
1487 AutoWriteLocker
_(fWindowLock
);
1489 Window
* topWindow
= window
->TopLayerStackWindow();
1493 if (!window
->IsVisible()) {
1494 window
->ResizeBy((int32
)x
, (int32
)y
, NULL
);
1495 NotifyWindowResized(window
);
1499 // the dirty region for the inside of the window is
1500 // constructed by the window itself in ResizeBy()
1501 BRegion newDirtyRegion
;
1502 // track the dirty region outside the window in case
1503 // it is shrunk in "previouslyOccupiedRegion"
1504 BRegion
previouslyOccupiedRegion(window
->VisibleRegion());
1506 // stop direct frame buffer access
1507 bool direct
= false;
1508 if (window
->ServerWindow()->IsDirectlyAccessing()) {
1509 window
->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP
);
1513 window
->ResizeBy((int32
)x
, (int32
)y
, &newDirtyRegion
);
1516 _RebuildClippingForAllWindows(background
);
1518 // we just care for the region outside the window
1519 previouslyOccupiedRegion
.Exclude(&window
->VisibleRegion());
1521 // make sure the window cannot mark stuff dirty outside
1522 // its visible region...
1523 newDirtyRegion
.IntersectWith(&window
->VisibleRegion());
1524 // ...because we do this outself
1525 newDirtyRegion
.Include(&previouslyOccupiedRegion
);
1527 MarkDirty(newDirtyRegion
);
1528 _SetBackground(background
);
1529 _WindowChanged(window
);
1531 // resume direct frame buffer access
1533 window
->ServerWindow()->HandleDirectConnection(
1534 B_DIRECT_START
| B_BUFFER_RESIZED
| B_CLIPPING_MODIFIED
);
1537 NotifyWindowResized(window
);
1542 Desktop::SetWindowTabLocation(Window
* window
, float location
, bool isShifting
)
1544 AutoWriteLocker
_(fWindowLock
);
1547 bool changed
= window
->SetTabLocation(location
, isShifting
, dirty
);
1549 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1551 NotifyWindowTabLocationChanged(window
, location
, isShifting
);
1558 Desktop::SetWindowDecoratorSettings(Window
* window
, const BMessage
& settings
)
1560 AutoWriteLocker
_(fWindowLock
);
1563 bool changed
= window
->SetDecoratorSettings(settings
, dirty
);
1564 bool listenerChanged
= SetDecoratorSettings(window
, settings
);
1565 if (changed
|| listenerChanged
)
1566 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1573 Desktop::SetWindowWorkspaces(Window
* window
, uint32 workspaces
)
1577 if (window
->IsNormal() && workspaces
== B_CURRENT_WORKSPACE
)
1578 workspaces
= workspace_to_workspaces(CurrentWorkspace());
1580 WindowStack
* stack
= window
->GetWindowStack();
1581 if (stack
!= NULL
) {
1582 for (int32 s
= 0; s
< stack
->CountWindows(); s
++) {
1583 window
= stack
->LayerOrder().ItemAt(s
);
1585 uint32 oldWorkspaces
= window
->Workspaces();
1586 window
->WorkspacesChanged(oldWorkspaces
, workspaces
);
1587 _ChangeWindowWorkspaces(window
, oldWorkspaces
, workspaces
);
1594 /*! \brief Adds the window to the desktop.
1595 At this point, the window is still hidden and must be shown explicetly
1599 Desktop::AddWindow(Window
*window
)
1603 fAllWindows
.AddWindow(window
);
1604 if (!window
->IsNormal())
1605 fSubsetWindows
.AddWindow(window
);
1607 if (window
->IsNormal()) {
1608 if (window
->Workspaces() == B_CURRENT_WORKSPACE
)
1609 window
->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1611 // subset windows are visible on all workspaces their subset is on
1612 window
->SetWorkspaces(window
->SubsetWorkspaces());
1615 _ChangeWindowWorkspaces(window
, 0, window
->Workspaces());
1617 NotifyWindowAdded(window
);
1624 Desktop::RemoveWindow(Window
*window
)
1628 if (!window
->IsHidden())
1631 fAllWindows
.RemoveWindow(window
);
1632 if (!window
->IsNormal())
1633 fSubsetWindows
.RemoveWindow(window
);
1635 _ChangeWindowWorkspaces(window
, window
->Workspaces(), 0);
1637 NotifyWindowRemoved(window
);
1641 // make sure this window won't get any events anymore
1643 EventDispatcher().RemoveTarget(window
->EventTarget());
1648 Desktop::AddWindowToSubset(Window
* subset
, Window
* window
)
1650 if (!subset
->AddToSubset(window
))
1653 _ChangeWindowWorkspaces(subset
, subset
->Workspaces(),
1654 subset
->SubsetWorkspaces());
1660 Desktop::RemoveWindowFromSubset(Window
* subset
, Window
* window
)
1662 subset
->RemoveFromSubset(window
);
1663 _ChangeWindowWorkspaces(subset
, subset
->Workspaces(),
1664 subset
->SubsetWorkspaces());
1669 Desktop::FontsChanged(Window
* window
)
1671 AutoWriteLocker
_(fWindowLock
);
1674 window
->FontsChanged(&dirty
);
1676 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1681 Desktop::ColorUpdated(Window
* window
, color_which which
, rgb_color color
)
1683 AutoWriteLocker
_(fWindowLock
);
1685 window
->TopView()->ColorUpdated(which
, color
);
1688 case B_WINDOW_TAB_COLOR
:
1689 case B_WINDOW_TEXT_COLOR
:
1690 case B_WINDOW_INACTIVE_TAB_COLOR
:
1691 case B_WINDOW_INACTIVE_TEXT_COLOR
:
1692 case B_WINDOW_BORDER_COLOR
:
1693 case B_WINDOW_INACTIVE_BORDER_COLOR
:
1700 window
->ColorsChanged(&dirty
);
1701 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1706 Desktop::SetWindowLook(Window
* window
, window_look newLook
)
1708 if (window
->Look() == newLook
)
1711 AutoWriteLocker
_(fWindowLock
);
1714 window
->SetLook(newLook
, &dirty
);
1715 // TODO: test what happens when the window
1716 // finds out it needs to resize itself...
1718 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1720 NotifyWindowLookChanged(window
, newLook
);
1725 Desktop::SetWindowFeel(Window
* window
, window_feel newFeel
)
1727 if (window
->Feel() == newFeel
)
1732 bool wasNormal
= window
->IsNormal();
1734 window
->SetFeel(newFeel
);
1736 // move the window out of or into the subset window list as needed
1737 if (window
->IsNormal() && !wasNormal
)
1738 fSubsetWindows
.RemoveWindow(window
);
1739 else if (!window
->IsNormal() && wasNormal
)
1740 fSubsetWindows
.AddWindow(window
);
1742 // A normal window that was once a floating or modal window will
1743 // adopt the window's current workspaces
1745 if (!window
->IsNormal()) {
1746 _ChangeWindowWorkspaces(window
, window
->Workspaces(),
1747 window
->SubsetWorkspaces());
1750 // make sure the window has the correct position in the window lists
1751 // (ie. all floating windows have to be on the top, ...)
1753 for (int32 i
= 0; i
< kMaxWorkspaces
; i
++) {
1754 if (!workspace_in_workspaces(i
, window
->Workspaces()))
1757 bool changed
= false;
1758 BRegion visibleBefore
;
1759 if (i
== fCurrentWorkspace
&& window
->IsVisible())
1760 visibleBefore
= window
->VisibleRegion();
1762 Window
* backmost
= window
->Backmost(_Windows(i
).LastWindow(), i
);
1763 if (backmost
!= NULL
) {
1764 // check if the backmost window is really behind it
1765 Window
* previous
= window
->PreviousWindow(i
);
1766 while (previous
!= NULL
) {
1767 if (previous
== backmost
)
1770 previous
= previous
->PreviousWindow(i
);
1773 if (previous
== NULL
) {
1774 // need to reinsert window before its backmost window
1775 _Windows(i
).RemoveWindow(window
);
1776 _Windows(i
).AddWindow(window
, backmost
->NextWindow(i
));
1781 Window
* frontmost
= window
->Frontmost(_Windows(i
).FirstWindow(), i
);
1782 if (frontmost
!= NULL
) {
1783 // check if the frontmost window is really in front of it
1784 Window
* next
= window
->NextWindow(i
);
1785 while (next
!= NULL
) {
1786 if (next
== frontmost
)
1789 next
= next
->NextWindow(i
);
1793 // need to reinsert window behind its frontmost window
1794 _Windows(i
).RemoveWindow(window
);
1795 _Windows(i
).AddWindow(window
, frontmost
);
1800 if (i
== fCurrentWorkspace
&& changed
) {
1802 _RebuildClippingForAllWindows(dummy
);
1804 // mark everything dirty that is no longer visible, or
1805 // is now visible and wasn't before
1806 BRegion
visibleAfter(window
->VisibleRegion());
1807 BRegion
dirty(visibleAfter
);
1808 dirty
.Exclude(&visibleBefore
);
1809 visibleBefore
.Exclude(&visibleAfter
);
1810 dirty
.Include(&visibleBefore
);
1818 if (window
== FocusWindow() && !window
->IsVisible())
1821 NotifyWindowFeelChanged(window
, newFeel
);
1828 Desktop::SetWindowFlags(Window
*window
, uint32 newFlags
)
1830 if (window
->Flags() == newFlags
)
1833 AutoWriteLocker
_(fWindowLock
);
1836 window
->SetFlags(newFlags
, &dirty
);
1837 // TODO: test what happens when the window
1838 // finds out it needs to resize itself...
1840 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1845 Desktop::SetWindowTitle(Window
*window
, const char* title
)
1847 AutoWriteLocker
_(fWindowLock
);
1850 window
->SetTitle(title
, dirty
);
1852 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1856 /*! Returns the window under the mouse cursor.
1857 You need to have acquired the All Windows lock when calling this method.
1860 Desktop::WindowAt(BPoint where
)
1862 for (Window
* window
= CurrentWindows().LastWindow(); window
;
1863 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
1864 if (window
->IsVisible() && window
->VisibleRegion().Contains(where
))
1865 return window
->StackedWindowAt(where
);
1873 Desktop::SetMouseEventWindow(Window
* window
)
1875 fMouseEventWindow
= window
;
1880 Desktop::SetViewUnderMouse(const Window
* window
, int32 viewToken
)
1882 fWindowUnderMouse
= window
;
1883 fViewUnderMouse
= viewToken
;
1888 Desktop::ViewUnderMouse(const Window
* window
)
1890 if (window
!= NULL
&& fWindowUnderMouse
== window
)
1891 return fViewUnderMouse
;
1893 return B_NULL_TOKEN
;
1897 /*! Returns the current keyboard event target candidate - which is either the
1898 top-most window (in case it has the kAcceptKeyboardFocusFlag flag set), or
1899 the one having focus.
1900 The window lock must be held when calling this function.
1903 Desktop::KeyboardEventTarget()
1905 // Get the top most non-hidden window
1906 Window
* window
= CurrentWindows().LastWindow();
1907 while (window
!= NULL
&& window
->IsHidden()) {
1908 window
= window
->PreviousWindow(fCurrentWorkspace
);
1911 if (window
!= NULL
&& (window
->Flags() & kAcceptKeyboardFocusFlag
) != 0)
1912 return &window
->EventTarget();
1914 if (FocusWindow() != NULL
)
1915 return &FocusWindow()->EventTarget();
1921 /*! Tries to set the focus to the specified \a focus window. It will make sure,
1922 however, that the window actually can have focus. You are allowed to pass
1923 in a NULL pointer for \a focus.
1925 Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both
1926 prevent it from getting focus.
1928 In any case, this method makes sure that there is a focus window, if there
1929 is any window at all, that is.
1932 Desktop::SetFocusWindow(Window
* nextFocus
)
1934 if (!LockAllWindows())
1937 // test for B_LOCK_WINDOW_FOCUS
1938 if (fLockedFocusWindow
&& nextFocus
!= fLockedFocusWindow
) {
1943 bool hasModal
= _WindowHasModal(nextFocus
);
1944 bool hasWindowScreen
= false;
1946 if (!hasModal
&& nextFocus
!= NULL
) {
1947 // Check whether or not a window screen is in front of the window
1948 // (if it has a modal, the right thing is done, anyway)
1949 Window
* window
= nextFocus
;
1951 window
= window
->NextWindow(fCurrentWorkspace
);
1952 if (window
== NULL
|| window
->Feel() == kWindowScreenFeel
)
1956 hasWindowScreen
= true;
1959 if (nextFocus
== fFocus
&& nextFocus
!= NULL
&& !nextFocus
->IsHidden()
1960 && (nextFocus
->Flags() & B_AVOID_FOCUS
) == 0
1961 && !hasModal
&& !hasWindowScreen
) {
1962 // the window that is supposed to get focus already has focus
1967 uint32 listIndex
= fCurrentWorkspace
;
1968 WindowList
* list
= &_Windows(fCurrentWorkspace
);
1969 if (!fSettings
->NormalMouse()) {
1970 listIndex
= kFocusList
;
1974 if (nextFocus
== NULL
|| hasModal
|| hasWindowScreen
) {
1975 nextFocus
= list
->LastWindow();
1977 if (fSettings
->NormalMouse()) {
1978 // If the last window having focus is a window that cannot make it
1979 // to the front, we use that as the next focus
1980 Window
* lastFocus
= fFocusList
.LastWindow();
1981 if (lastFocus
!= NULL
&& !lastFocus
->SupportsFront()
1982 && _WindowCanHaveFocus(lastFocus
)) {
1983 nextFocus
= lastFocus
;
1988 // make sure no window is chosen that doesn't want focus or cannot have it
1989 while (nextFocus
!= NULL
&& !_WindowCanHaveFocus(nextFocus
)) {
1990 nextFocus
= nextFocus
->PreviousWindow(listIndex
);
1993 if (fFocus
== nextFocus
) {
1994 // turns out the window that is supposed to get focus now already has it
1999 team_id oldActiveApp
= -1;
2000 team_id newActiveApp
= -1;
2002 if (fFocus
!= NULL
) {
2003 fFocus
->SetFocus(false);
2004 oldActiveApp
= fFocus
->ServerWindow()->App()->ClientTeam();
2009 if (fFocus
!= NULL
) {
2010 fFocus
->SetFocus(true);
2011 newActiveApp
= fFocus
->ServerWindow()->App()->ClientTeam();
2013 // move current focus to the end of the focus list
2014 fFocusList
.RemoveWindow(fFocus
);
2015 fFocusList
.AddWindow(fFocus
);
2018 if (newActiveApp
== -1) {
2019 // make sure the cursor is visible
2020 HWInterface()->SetCursorVisible(true);
2025 // change the "active" app if appropriate
2026 if (oldActiveApp
== newActiveApp
)
2029 BAutolock
locker(fApplicationsLock
);
2031 for (int32 i
= 0; i
< fApplications
.CountItems(); i
++) {
2032 ServerApp
* app
= fApplications
.ItemAt(i
);
2034 if (oldActiveApp
!= -1 && app
->ClientTeam() == oldActiveApp
)
2035 app
->Activate(false);
2036 else if (newActiveApp
!= -1 && app
->ClientTeam() == newActiveApp
)
2037 app
->Activate(true);
2043 Desktop::SetFocusLocked(const Window
* window
)
2045 AutoWriteLocker
_(fWindowLock
);
2047 if (window
!= NULL
) {
2048 // Don't allow this to be set when no mouse buttons
2049 // are pressed. (BView::SetMouseEventMask() should only be called
2050 // from mouse hooks.)
2051 if (fLastMouseButtons
== 0)
2055 fLockedFocusWindow
= window
;
2060 Desktop::FindWindowByClientToken(int32 token
, team_id teamID
)
2062 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2063 window
= window
->NextWindow(kAllWindowList
)) {
2064 if (window
->ServerWindow()->ClientToken() == token
2065 && window
->ServerWindow()->ClientTeam() == teamID
) {
2075 Desktop::FindTarget(BMessenger
& messenger
)
2077 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2078 window
= window
->NextWindow(kAllWindowList
)) {
2079 if (window
->EventTarget().Messenger() == messenger
)
2080 return &window
->EventTarget();
2088 Desktop::MarkDirty(BRegion
& region
)
2090 if (region
.CountRects() == 0)
2093 if (LockAllWindows()) {
2094 // send redraw messages to all windows intersecting the dirty region
2095 _TriggerWindowRedrawing(region
);
2105 BRegion
dirty(fVirtualScreen
.Frame());
2110 /*! \brief Redraws the background (ie. the desktop window, if any).
2113 Desktop::RedrawBackground()
2119 Window
* window
= CurrentWindows().FirstWindow();
2120 if (window
->Feel() == kDesktopWindowFeel
) {
2121 redraw
= window
->VisibleContentRegion();
2123 // look for desktop background view, and update its background color
2124 // TODO: is there a better way to do this?
2125 View
* view
= window
->TopView();
2127 view
= view
->FirstChild();
2130 if (view
->IsDesktopBackground()) {
2131 view
->SetViewColor(fWorkspaces
[fCurrentWorkspace
].Color());
2134 view
= view
->NextSibling();
2137 window
->ProcessDirtyRegion(redraw
);
2139 redraw
= BackgroundRegion();
2140 fBackgroundRegion
.MakeEmpty();
2141 _SetBackground(redraw
);
2144 _WindowChanged(NULL
);
2145 // update workspaces view as well
2152 Desktop::ReloadDecor(DecorAddOn
* oldDecor
)
2154 AutoWriteLocker
_(fWindowLock
);
2156 bool returnValue
= true;
2158 if (oldDecor
!= NULL
) {
2159 const DesktopListenerList
* oldListeners
2160 = &oldDecor
->GetDesktopListeners();
2161 for (int i
= 0; i
< oldListeners
->CountItems(); i
++)
2162 UnregisterListener(oldListeners
->ItemAt(i
));
2165 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2166 window
= window
->NextWindow(kAllWindowList
)) {
2168 window
->GetBorderRegion(&oldBorder
);
2170 if (!window
->ReloadDecor()) {
2171 // prevent unloading previous add-on
2172 returnValue
= false;
2176 window
->GetBorderRegion(&border
);
2178 border
.Include(&oldBorder
);
2179 RebuildAndRedrawAfterWindowChange(window
, border
);
2182 // register new listeners
2183 const DesktopListenerList
& newListeners
2184 = gDecorManager
.GetDesktopListeners();
2185 for (int i
= 0; i
< newListeners
.CountItems(); i
++)
2186 RegisterListener(newListeners
.ItemAt(i
));
2193 Desktop::MinimizeApplication(team_id team
)
2195 AutoWriteLocker
locker(fWindowLock
);
2197 // Just minimize all windows of that application
2199 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2200 window
= window
->NextWindow(kAllWindowList
)) {
2201 if (window
->ServerWindow()->ClientTeam() != team
)
2204 window
->ServerWindow()->NotifyMinimize(true);
2210 Desktop::BringApplicationToFront(team_id team
)
2212 AutoWriteLocker
locker(fWindowLock
);
2214 // TODO: for now, just maximize all windows of that application
2215 // TODO: have the ability to lock the current workspace
2217 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2218 window
= window
->NextWindow(kAllWindowList
)) {
2219 if (window
->ServerWindow()->ClientTeam() != team
)
2222 window
->ServerWindow()->NotifyMinimize(false);
2228 Desktop::WindowAction(int32 windowToken
, int32 action
)
2230 if (action
!= B_MINIMIZE_WINDOW
&& action
!= B_BRING_TO_FRONT
)
2235 ::ServerWindow
* serverWindow
;
2237 if (BPrivate::gDefaultTokens
.GetToken(windowToken
,
2238 B_SERVER_TOKEN
, (void**)&serverWindow
) != B_OK
2239 || (window
= serverWindow
->Window()) == NULL
) {
2244 if (action
== B_BRING_TO_FRONT
&& !window
->IsMinimized()) {
2245 // the window is visible, we just need to make it the front window
2246 ActivateWindow(window
);
2248 // if not, ask the window if it wants to be unminimized
2249 serverWindow
->NotifyMinimize(action
== B_MINIMIZE_WINDOW
);
2257 Desktop::WriteWindowList(team_id team
, BPrivate::LinkSender
& sender
)
2259 AutoWriteLocker
locker(fWindowLock
);
2261 // compute the number of windows
2265 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2266 window
= window
->NextWindow(kAllWindowList
)) {
2267 if (team
< B_OK
|| window
->ServerWindow()->ClientTeam() == team
)
2273 sender
.StartMessage(B_OK
);
2274 sender
.Attach
<int32
>(count
);
2276 // first write the windows of the current workspace correctly ordered
2277 for (Window
*window
= CurrentWindows().LastWindow(); window
!= NULL
;
2278 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
2279 if (team
>= B_OK
&& window
->ServerWindow()->ClientTeam() != team
)
2282 sender
.Attach
<int32
>(window
->ServerWindow()->ServerToken());
2285 // then write all the other windows
2286 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2287 window
= window
->NextWindow(kAllWindowList
)) {
2288 if ((team
>= B_OK
&& window
->ServerWindow()->ClientTeam() != team
)
2289 || window
->InWorkspace(fCurrentWorkspace
))
2292 sender
.Attach
<int32
>(window
->ServerWindow()->ServerToken());
2300 Desktop::WriteWindowInfo(int32 serverToken
, BPrivate::LinkSender
& sender
)
2302 AutoWriteLocker
locker(fWindowLock
);
2303 BAutolock
tokenLocker(BPrivate::gDefaultTokens
);
2305 ::ServerWindow
* window
;
2306 if (BPrivate::gDefaultTokens
.GetToken(serverToken
,
2307 B_SERVER_TOKEN
, (void**)&window
) != B_OK
) {
2308 sender
.StartMessage(B_ENTRY_NOT_FOUND
);
2314 window
->GetInfo(info
);
2316 float tabSize
= 0.0;
2317 float borderSize
= 0.0;
2318 ::Window
* tmp
= window
->Window();
2321 if (tmp
->GetDecoratorSettings(&message
)) {
2323 message
.FindRect("tab frame", &tabFrame
);
2324 tabSize
= tabFrame
.bottom
- tabFrame
.top
;
2325 message
.FindFloat("border width", &borderSize
);
2329 int32 length
= window
->Title() ? strlen(window
->Title()) : 0;
2331 sender
.StartMessage(B_OK
);
2332 sender
.Attach
<int32
>(sizeof(client_window_info
) + length
);
2333 sender
.Attach(&info
, sizeof(window_info
));
2334 sender
.Attach
<float>(tabSize
);
2335 sender
.Attach
<float>(borderSize
);
2338 sender
.Attach(window
->Title(), length
+ 1);
2340 sender
.Attach
<char>('\0');
2347 Desktop::WriteWindowOrder(int32 workspace
, BPrivate::LinkSender
& sender
)
2352 workspace
= fCurrentWorkspace
;
2353 else if (workspace
>= kMaxWorkspaces
) {
2354 sender
.StartMessage(B_BAD_VALUE
);
2356 UnlockSingleWindow();
2360 int32 count
= _Windows(workspace
).Count();
2364 sender
.StartMessage(B_OK
);
2365 sender
.Attach
<int32
>(count
);
2367 for (Window
*window
= _Windows(workspace
).LastWindow(); window
!= NULL
;
2368 window
= window
->PreviousWindow(workspace
)) {
2369 sender
.Attach
<int32
>(window
->ServerWindow()->ServerToken());
2374 UnlockSingleWindow();
2379 Desktop::WriteApplicationOrder(int32 workspace
, BPrivate::LinkSender
& sender
)
2381 fApplicationsLock
.Lock();
2384 int32 maxCount
= fApplications
.CountItems();
2386 fApplicationsLock
.Unlock();
2387 // as long as we hold the window lock, no new window can appear
2390 workspace
= fCurrentWorkspace
;
2391 else if (workspace
>= kMaxWorkspaces
) {
2392 sender
.StartMessage(B_BAD_VALUE
);
2394 UnlockSingleWindow();
2398 // compute the list of applications on this workspace
2400 team_id
* teams
= (team_id
*)malloc(maxCount
* sizeof(team_id
));
2401 if (teams
== NULL
) {
2402 sender
.StartMessage(B_NO_MEMORY
);
2404 UnlockSingleWindow();
2410 for (Window
*window
= _Windows(workspace
).LastWindow(); window
!= NULL
;
2411 window
= window
->PreviousWindow(workspace
)) {
2412 team_id team
= window
->ServerWindow()->ClientTeam();
2414 // see if we already have this team
2416 for (int32 i
= 0; i
< count
; i
++) {
2417 if (teams
[i
] == team
) {
2426 ASSERT(count
< maxCount
);
2427 teams
[count
++] = team
;
2430 UnlockSingleWindow();
2434 sender
.StartMessage(B_OK
);
2435 sender
.Attach
<int32
>(count
);
2437 for (int32 i
= 0; i
< count
; i
++) {
2438 sender
.Attach
<int32
>(teams
[i
]);
2447 Desktop::_LaunchInputServer()
2450 status_t status
= roster
.Launch("application/x-vnd.Be-input_server");
2451 if (status
== B_OK
|| status
== B_ALREADY_RUNNING
)
2454 // Could not load input_server by signature, try well-known location
2457 BPath inputServerPath
;
2458 if (find_directory(B_SYSTEM_SERVERS_DIRECTORY
, &inputServerPath
) == B_OK
2459 && inputServerPath
.Append("input_server") == B_OK
) {
2460 entry
.SetTo(inputServerPath
.Path());
2462 entry
.SetTo("/system/servers/input_server");
2464 status_t entryStatus
= entry
.GetRef(&ref
);
2465 if (entryStatus
== B_OK
)
2466 entryStatus
= roster
.Launch(&ref
);
2467 if (entryStatus
== B_OK
|| entryStatus
== B_ALREADY_RUNNING
) {
2468 syslog(LOG_ERR
, "Failed to launch the input server by signature: %s!\n",
2473 syslog(LOG_ERR
, "Failed to launch the input server: %s!\n",
2474 strerror(entryStatus
));
2479 Desktop::_GetLooperName(char* name
, size_t length
)
2481 snprintf(name
, length
, "d:%d:%s", fUserID
,
2482 fTargetScreen
== NULL
? "baron" : fTargetScreen
);
2487 Desktop::_PrepareQuit()
2489 // let's kill all remaining applications
2491 fApplicationsLock
.Lock();
2493 int32 count
= fApplications
.CountItems();
2494 for (int32 i
= 0; i
< count
; i
++) {
2495 ServerApp
*app
= fApplications
.ItemAt(i
);
2496 team_id clientTeam
= app
->ClientTeam();
2499 kill_team(clientTeam
);
2502 // wait for the last app to die
2504 acquire_sem_etc(fShutdownSemaphore
, fShutdownCount
, B_RELATIVE_TIMEOUT
,
2508 fApplicationsLock
.Unlock();
2513 Desktop::_DispatchMessage(int32 code
, BPrivate::LinkReceiver
& link
)
2518 // Create the ServerApp to node monitor a new BApplication
2521 // 1) port_id - receiver port of a regular app
2522 // 2) port_id - client looper port - for sending messages to the
2524 // 2) team_id - app's team ID
2525 // 3) int32 - handler token of the regular app
2526 // 4) char * - signature of the regular app
2528 // Find the necessary data
2529 team_id clientTeamID
= -1;
2530 port_id clientLooperPort
= -1;
2531 port_id clientReplyPort
= -1;
2532 int32 htoken
= B_NULL_TOKEN
;
2533 char* appSignature
= NULL
;
2535 link
.Read
<port_id
>(&clientReplyPort
);
2536 link
.Read
<port_id
>(&clientLooperPort
);
2537 link
.Read
<team_id
>(&clientTeamID
);
2538 link
.Read
<int32
>(&htoken
);
2539 if (link
.ReadString(&appSignature
) != B_OK
)
2542 ServerApp
* app
= new ServerApp(this, clientReplyPort
,
2543 clientLooperPort
, clientTeamID
, htoken
, appSignature
);
2544 if (app
->InitCheck() == B_OK
2546 // add the new ServerApp to the known list of ServerApps
2547 fApplicationsLock
.Lock();
2548 fApplications
.AddItem(app
);
2549 fApplicationsLock
.Unlock();
2553 // if everything went well, ServerApp::Run() will notify
2554 // the client - but since it didn't, we do it here
2555 BPrivate::LinkSender
reply(clientReplyPort
);
2556 reply
.StartMessage(B_ERROR
);
2560 // This is necessary because BPortLink::ReadString allocates memory
2567 // Delete a ServerApp. Received only from the respective ServerApp
2568 // when a BApplication asks it to quit.
2571 // 1) thread_id - thread ID of the ServerApp to be deleted
2573 thread_id thread
= -1;
2574 if (link
.Read
<thread_id
>(&thread
) < B_OK
)
2577 fApplicationsLock
.Lock();
2579 // Run through the list of apps and nuke the proper one
2581 int32 count
= fApplications
.CountItems();
2582 ServerApp
* removeApp
= NULL
;
2584 for (int32 i
= 0; i
< count
; i
++) {
2585 ServerApp
* app
= fApplications
.ItemAt(i
);
2587 if (app
->Thread() == thread
) {
2588 fApplications
.RemoveItemAt(i
);
2594 fApplicationsLock
.Unlock();
2596 if (removeApp
!= NULL
)
2597 removeApp
->Quit(fShutdownSemaphore
);
2599 if (fQuitting
&& count
<= 1) {
2600 // wait for the last app to die
2601 acquire_sem_etc(fShutdownSemaphore
, fShutdownCount
,
2602 B_RELATIVE_TIMEOUT
, 500000);
2603 PostMessage(kMsgQuitLooper
);
2608 case AS_ACTIVATE_APP
:
2610 // Someone is requesting to activation of a certain app.
2613 // 1) port_id reply port
2618 // get the parameters
2621 if (link
.Read(&replyPort
) == B_OK
2622 && link
.Read(&team
) == B_OK
)
2623 status
= _ActivateApp(team
);
2628 BPrivate::PortLink
replyLink(replyPort
);
2629 replyLink
.StartMessage(status
);
2634 case AS_APP_CRASHED
:
2635 case AS_DUMP_ALLOCATOR
:
2636 case AS_DUMP_BITMAPS
:
2638 BAutolock
locker(fApplicationsLock
);
2641 if (link
.Read(&team
) != B_OK
)
2644 for (int32 i
= 0; i
< fApplications
.CountItems(); i
++) {
2645 ServerApp
* app
= fApplications
.ItemAt(i
);
2647 if (app
->ClientTeam() == team
)
2648 app
->PostMessage(code
);
2653 case AS_EVENT_STREAM_CLOSED
:
2654 _LaunchInputServer();
2657 case B_QUIT_REQUESTED
:
2658 // We've been asked to quit, so (for now) broadcast to all
2659 // test apps to quit. This situation will occur only when the
2660 // server is compiled as a regular Be application.
2662 fApplicationsLock
.Lock();
2663 fShutdownSemaphore
= create_sem(0, "desktop shutdown");
2664 fShutdownCount
= fApplications
.CountItems();
2665 fApplicationsLock
.Unlock();
2668 BroadcastToAllApps(AS_QUIT_APP
);
2670 // We now need to process the remaining AS_DELETE_APP messages and
2671 // wait for the kMsgShutdownServer message.
2672 // If an application does not quit as asked, the picasso thread
2673 // will send us this message in 2-3 seconds.
2675 // if there are no apps to quit, shutdown directly
2676 if (fShutdownCount
== 0)
2677 PostMessage(kMsgQuitLooper
);
2680 case AS_ACTIVATE_WORKSPACE
:
2683 link
.Read
<int32
>(&index
);
2685 index
= fPreviousWorkspace
;
2687 bool moveFocusWindow
;
2688 link
.Read
<bool>(&moveFocusWindow
);
2690 SetWorkspace(index
, moveFocusWindow
);
2694 case AS_TALK_TO_DESKTOP_LISTENER
:
2696 port_id clientReplyPort
;
2697 if (link
.Read
<port_id
>(&clientReplyPort
) != B_OK
)
2700 BPrivate::LinkSender
reply(clientReplyPort
);
2701 AutoWriteLocker
locker(fWindowLock
);
2702 if (MessageForListener(NULL
, link
, reply
) != true) {
2703 // unhandled message, at least send an error if needed
2704 if (link
.NeedsReply()) {
2705 reply
.StartMessage(B_ERROR
);
2712 case AS_SET_UI_COLOR
:
2717 if (link
.Read
<color_which
>(&which
) == B_OK
2718 && link
.Read
<rgb_color
>(&color
) == B_OK
) {
2720 const char* colorName
= ui_color_name(which
);
2721 fPendingColors
.SetColor(colorName
, color
);
2723 DelayedMessage
delayed(AS_SET_UI_COLORS
, DM_60HZ_DELAY
);
2724 delayed
.AddTarget(MessagePort());
2725 delayed
.SetMerge(DM_MERGE_CANCEL
);
2727 delayed
.Attach
<bool>(true);
2734 case AS_SET_UI_COLORS
:
2736 bool flushPendingOnly
= false;
2738 if (link
.Read
<bool>(&flushPendingOnly
) != B_OK
2739 || (flushPendingOnly
&&
2740 fPendingColors
.CountNames(B_RGB_32_BIT_TYPE
) == 0)) {
2744 if (!flushPendingOnly
) {
2745 // Client wants to set a color map
2746 color_which which
= B_NO_COLOR
;
2750 if (link
.Read
<color_which
>(&which
) != B_OK
2751 || link
.Read
<rgb_color
>(&color
) != B_OK
)
2754 fPendingColors
.SetColor(ui_color_name(which
), color
);
2755 } while (which
!= B_NO_COLOR
);
2758 _FlushPendingColors();
2762 // ToDo: Remove this again. It is a message sent by the
2763 // invalidate_on_exit kernel debugger add-on to trigger a redraw
2764 // after exiting a kernel debugger session.
2768 dirty
.Include(fVirtualScreen
.Frame());
2774 printf("Desktop %d:%s received unexpected code %" B_PRId32
"\n", 0,
2777 if (link
.NeedsReply()) {
2778 // the client is now blocking and waiting for a reply!
2779 fLink
.StartMessage(B_ERROR
);
2788 Desktop::CurrentWindows()
2790 return fWorkspaces
[fCurrentWorkspace
].Windows();
2795 Desktop::AllWindows()
2802 Desktop::WindowForClientLooperPort(port_id port
)
2804 ASSERT_MULTI_LOCKED(fWindowLock
);
2806 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2807 window
= window
->NextWindow(kAllWindowList
)) {
2808 if (window
->ServerWindow()->ClientLooperPort() == port
)
2816 Desktop::_Windows(int32 index
)
2818 ASSERT(index
>= 0 && index
< kMaxWorkspaces
);
2819 return fWorkspaces
[index
].Windows();
2824 Desktop::_FlushPendingColors()
2826 // Update all windows while we are holding the write lock.
2828 int32 count
= fPendingColors
.CountNames(B_RGB_32_BIT_TYPE
);
2832 bool changed
[count
];
2833 LockedDesktopSettings
settings(this);
2834 settings
.SetUIColors(fPendingColors
, &changed
[0]);
2838 type_code type
= B_RGB_32_BIT_TYPE
;
2840 color_which which
= B_NO_COLOR
;
2841 BMessage
clientMessage(B_COLORS_UPDATED
);
2843 while (fPendingColors
.GetInfo(type
, index
, &name
, &type
) == B_OK
) {
2844 which
= which_ui_color(name
);
2845 if (which
== B_NO_COLOR
|| fPendingColors
.FindColor(name
,
2846 &color
) != B_OK
|| !changed
[index
]) {
2851 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2852 window
= window
->NextWindow(kAllWindowList
)) {
2853 ColorUpdated(window
, which
, color
);
2856 // Ensure client only gets list of changed colors
2857 clientMessage
.AddColor(name
, color
);
2861 // Notify client applications
2862 BAutolock
appListLock(fApplicationsLock
);
2863 for (int32 index
= 0; index
< fApplications
.CountItems(); ++index
) {
2864 fApplications
.ItemAt(index
)->SendMessageToClient(&clientMessage
);
2867 fPendingColors
.MakeEmpty();
2872 Desktop::_UpdateFloating(int32 previousWorkspace
, int32 nextWorkspace
,
2873 Window
* mouseEventWindow
)
2875 if (previousWorkspace
== -1)
2876 previousWorkspace
= fCurrentWorkspace
;
2877 if (nextWorkspace
== -1)
2878 nextWorkspace
= previousWorkspace
;
2880 for (Window
* floating
= fSubsetWindows
.FirstWindow(); floating
!= NULL
;
2881 floating
= floating
->NextWindow(kSubsetList
)) {
2882 // we only care about app/subset floating windows
2883 if (floating
->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
2884 && floating
->Feel() != B_FLOATING_APP_WINDOW_FEEL
)
2887 if (fFront
!= NULL
&& fFront
->IsNormal()
2888 && floating
->HasInSubset(fFront
)) {
2890 if (_Windows(previousWorkspace
).HasWindow(floating
)
2891 && previousWorkspace
!= nextWorkspace
2892 && !floating
->InSubsetWorkspace(previousWorkspace
)) {
2893 // but no longer on the previous workspace
2894 _Windows(previousWorkspace
).RemoveWindow(floating
);
2895 floating
->SetCurrentWorkspace(-1);
2898 if (!_Windows(nextWorkspace
).HasWindow(floating
)) {
2899 // but wasn't before
2900 _Windows(nextWorkspace
).AddWindow(floating
,
2901 floating
->Frontmost(_Windows(nextWorkspace
).FirstWindow(),
2903 floating
->SetCurrentWorkspace(nextWorkspace
);
2904 if (mouseEventWindow
!= fFront
)
2905 _ShowWindow(floating
);
2907 // TODO: put the floating last in the floating window list to
2908 // preserve the on screen window order
2910 } else if (_Windows(previousWorkspace
).HasWindow(floating
)
2911 && !floating
->InSubsetWorkspace(previousWorkspace
)) {
2912 // was visible, but is no longer
2914 _Windows(previousWorkspace
).RemoveWindow(floating
);
2915 floating
->SetCurrentWorkspace(-1);
2916 _HideWindow(floating
);
2918 if (FocusWindow() == floating
)
2925 /*! Search the visible windows for a valid back window
2926 (only desktop windows can't be back windows)
2929 Desktop::_UpdateBack()
2933 for (Window
* window
= CurrentWindows().FirstWindow(); window
!= NULL
;
2934 window
= window
->NextWindow(fCurrentWorkspace
)) {
2935 if (window
->IsHidden() || window
->Feel() == kDesktopWindowFeel
)
2944 /*! Search the visible windows for a valid front window
2945 (only normal and modal windows can be front windows)
2947 The only place where you don't want to update floating windows is
2948 during a workspace change - because then you'll call _UpdateFloating()
2952 Desktop::_UpdateFront(bool updateFloating
)
2956 for (Window
* window
= CurrentWindows().LastWindow(); window
!= NULL
;
2957 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
2958 if (window
->IsHidden() || window
->IsFloating()
2959 || !window
->SupportsFront())
2972 Desktop::_UpdateFronts(bool updateFloating
)
2975 _UpdateFront(updateFloating
);
2980 Desktop::_WindowHasModal(Window
* window
) const
2985 for (Window
* modal
= fSubsetWindows
.FirstWindow(); modal
!= NULL
;
2986 modal
= modal
->NextWindow(kSubsetList
)) {
2987 // only visible modal windows count
2988 if (!modal
->IsModal() || modal
->IsHidden())
2991 if (modal
->HasInSubset(window
))
2999 /*! Determines whether or not the specified \a window can have focus at all.
3002 Desktop::_WindowCanHaveFocus(Window
* window
) const
3004 return window
!= NULL
3005 && window
->InWorkspace(fCurrentWorkspace
)
3006 && (window
->Flags() & B_AVOID_FOCUS
) == 0
3007 && !_WindowHasModal(window
)
3008 && !window
->IsHidden();
3012 /*! You must at least hold a single window lock when calling this method.
3015 Desktop::_WindowChanged(Window
* window
)
3017 ASSERT_MULTI_LOCKED(fWindowLock
);
3019 BAutolock
_(fWorkspacesLock
);
3021 for (uint32 i
= fWorkspacesViews
.CountItems(); i
-- > 0;) {
3022 WorkspacesView
* view
= fWorkspacesViews
.ItemAt(i
);
3023 view
->WindowChanged(window
);
3028 /*! You must at least hold a single window lock when calling this method.
3031 Desktop::_WindowRemoved(Window
* window
)
3033 ASSERT_MULTI_LOCKED(fWindowLock
);
3035 BAutolock
_(fWorkspacesLock
);
3037 for (uint32 i
= fWorkspacesViews
.CountItems(); i
-- > 0;) {
3038 WorkspacesView
* view
= fWorkspacesViews
.ItemAt(i
);
3039 view
->WindowRemoved(window
);
3044 /*! Shows the window on the screen - it does this independently of the
3045 Window::IsHidden() state.
3048 Desktop::_ShowWindow(Window
* window
, bool affectsOtherWindows
)
3051 _RebuildClippingForAllWindows(background
);
3052 _SetBackground(background
);
3053 _WindowChanged(window
);
3055 BRegion
dirty(window
->VisibleRegion());
3057 if (!affectsOtherWindows
) {
3058 // everything that is now visible in the
3059 // window needs a redraw, but other windows
3060 // are not affected, we can call ProcessDirtyRegion()
3061 // of the window, and don't have to use MarkDirty()
3062 window
->ProcessDirtyRegion(dirty
);
3066 if (window
->ServerWindow()->HasDirectFrameBufferAccess()) {
3067 window
->ServerWindow()->HandleDirectConnection(
3068 B_DIRECT_START
| B_BUFFER_RESET
);
3073 /*! Hides the window from the screen - it does this independently of the
3074 Window::IsHidden() state.
3077 Desktop::_HideWindow(Window
* window
)
3079 if (window
->ServerWindow()->IsDirectlyAccessing())
3080 window
->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP
);
3082 // after rebuilding the clipping,
3083 // this window will not have a visible
3084 // region anymore, so we need to remember
3086 // (actually that's not true, since
3087 // hidden windows are excluded from the
3088 // clipping calculation, but anyways)
3089 BRegion
dirty(window
->VisibleRegion());
3092 _RebuildClippingForAllWindows(background
);
3093 _SetBackground(background
);
3094 _WindowChanged(window
);
3100 /*! Updates the workspaces of all subset windows with regard to the
3102 If newIndex is not -1, it will move all subset windows that belong to
3103 the specifed window to the new workspace; this form is only called by
3107 Desktop::_UpdateSubsetWorkspaces(Window
* window
, int32 previousIndex
,
3110 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window
,
3113 // if the window is hidden, the subset windows are up-to-date already
3114 if (!window
->IsNormal() || window
->IsHidden())
3117 for (Window
* subset
= fSubsetWindows
.FirstWindow(); subset
!= NULL
;
3118 subset
= subset
->NextWindow(kSubsetList
)) {
3119 if (subset
->Feel() == B_MODAL_ALL_WINDOW_FEEL
3120 || subset
->Feel() == B_FLOATING_ALL_WINDOW_FEEL
) {
3121 // These windows are always visible on all workspaces,
3122 // no need to update them.
3126 if (subset
->IsFloating()) {
3127 // Floating windows are inserted and removed to the current
3128 // workspace as the need arises - they are not handled here
3129 // but in _UpdateFront()
3133 if (subset
->HasInSubset(window
)) {
3134 // adopt the workspace change
3135 SetWindowWorkspaces(subset
, subset
->SubsetWorkspaces());
3141 /*! \brief Adds or removes the window to or from the workspaces it's on.
3144 Desktop::_ChangeWindowWorkspaces(Window
* window
, uint32 oldWorkspaces
,
3145 uint32 newWorkspaces
)
3147 if (oldWorkspaces
== newWorkspaces
)
3150 // apply changes to the workspaces' window lists
3154 // NOTE: we bypass the anchor-mechanism by intention when switching
3155 // the workspace programmatically.
3157 for (int32 i
= 0; i
< kMaxWorkspaces
; i
++) {
3158 if (workspace_in_workspaces(i
, oldWorkspaces
)) {
3159 // window is on this workspace, is it anymore?
3160 if (!workspace_in_workspaces(i
, newWorkspaces
)) {
3161 _Windows(i
).RemoveWindow(window
);
3162 if (fLastWorkspaceFocus
[i
] == window
)
3163 fLastWorkspaceFocus
[i
] = NULL
;
3165 if (i
== CurrentWorkspace()) {
3166 // remove its appearance from the current workspace
3167 window
->SetCurrentWorkspace(-1);
3169 if (!window
->IsHidden())
3170 _HideWindow(window
);
3174 // window was not on this workspace, is it now?
3175 if (workspace_in_workspaces(i
, newWorkspaces
)) {
3176 _Windows(i
).AddWindow(window
,
3177 window
->Frontmost(_Windows(i
).FirstWindow(), i
));
3179 if (i
== CurrentWorkspace()) {
3180 // make the window visible in current workspace
3181 window
->SetCurrentWorkspace(fCurrentWorkspace
);
3183 if (!window
->IsHidden()) {
3184 // This only affects other windows if this window has
3185 // floating or modal windows that need to be shown as
3187 // TODO: take care of this
3188 _ShowWindow(window
, FrontWindow() == window
);
3195 // If the window is visible only on one workspace, we set it's current
3196 // position in that workspace (so that WorkspacesView will find us).
3197 int32 firstWorkspace
= -1;
3198 for (int32 i
= 0; i
< kMaxWorkspaces
; i
++) {
3199 if ((newWorkspaces
& (1L << i
)) != 0) {
3200 if (firstWorkspace
!= -1) {
3201 firstWorkspace
= -1;
3207 if (firstWorkspace
>= 0)
3208 window
->Anchor(firstWorkspace
).position
= window
->Frame().LeftTop();
3210 // take care about modals and floating windows
3211 _UpdateSubsetWorkspaces(window
);
3213 NotifyWindowWorkspacesChanged(window
, newWorkspaces
);
3220 Desktop::_BringWindowsToFront(WindowList
& windows
, int32 list
, bool wereVisible
)
3222 // we don't need to redraw what is currently
3223 // visible of the window
3226 for (Window
* window
= windows
.FirstWindow(); window
!= NULL
;
3227 window
= window
->NextWindow(list
)) {
3229 clean
.Include(&window
->VisibleRegion());
3231 CurrentWindows().AddWindow(window
,
3232 window
->Frontmost(CurrentWindows().FirstWindow(),
3233 fCurrentWorkspace
));
3235 _WindowChanged(window
);
3239 _RebuildClippingForAllWindows(dummy
);
3241 // redraw what became visible of the window(s)
3244 for (Window
* window
= windows
.FirstWindow(); window
!= NULL
;
3245 window
= window
->NextWindow(list
)) {
3246 dirty
.Include(&window
->VisibleRegion());
3249 dirty
.Exclude(&clean
);
3254 if (windows
.FirstWindow() == fBack
|| fBack
== NULL
)
3259 /*! Returns the last focussed non-hidden subset window belonging to the
3260 specified \a window.
3263 Desktop::_LastFocusSubsetWindow(Window
* window
)
3268 for (Window
* front
= fFocusList
.LastWindow(); front
!= NULL
;
3269 front
= front
->PreviousWindow(kFocusList
)) {
3270 if (front
!= window
&& !front
->IsHidden()
3271 && window
->HasInSubset(front
))
3279 /*! \brief Checks whether or not a fake mouse moved message needs to be sent
3280 to the previous mouse window.
3282 You need to have the all window lock held when calling this method.
3285 Desktop::_CheckSendFakeMouseMoved(const Window
* lastWindowUnderMouse
)
3287 Window
* window
= WindowAt(fLastMousePosition
);
3288 return window
!= lastWindowUnderMouse
;
3292 /*! \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
3293 and also updates the current view under the mouse.
3295 This has only to be done in case the view changed without mouse movement,
3296 ie. because of a workspace change, a closing window, or programmatic window
3299 You must not have locked any windows when calling this method.
3302 Desktop::_SendFakeMouseMoved(Window
* window
)
3304 int32 viewToken
= B_NULL_TOKEN
;
3305 EventTarget
* target
= NULL
;
3310 window
= WindowAt(fLastMousePosition
);
3312 if (window
!= NULL
) {
3314 window
->MouseMoved(&message
, fLastMousePosition
, &viewToken
, true,
3317 if (viewToken
!= B_NULL_TOKEN
)
3318 target
= &window
->EventTarget();
3321 if (viewToken
!= B_NULL_TOKEN
)
3322 SetViewUnderMouse(window
, viewToken
);
3324 SetViewUnderMouse(NULL
, B_NULL_TOKEN
);
3331 EventDispatcher().SendFakeMouseMoved(*target
, viewToken
);
3336 Desktop::_DetermineScreenFor(BRect frame
)
3338 AutoReadLocker
_(fScreenLock
);
3340 // TODO: choose the screen depending on where most of the area is
3341 return fVirtualScreen
.ScreenAt(0);
3346 Desktop::_RebuildClippingForAllWindows(BRegion
& stillAvailableOnScreen
)
3348 // the available region on screen starts with the entire screen area
3349 // each window on the screen will take a portion from that area
3351 // figure out what the entire screen area is
3352 stillAvailableOnScreen
= fScreenRegion
;
3354 // set clipping of each window
3355 for (Window
* window
= CurrentWindows().LastWindow(); window
!= NULL
;
3356 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
3357 if (!window
->IsHidden()) {
3358 window
->SetClipping(&stillAvailableOnScreen
);
3359 window
->SetScreen(_DetermineScreenFor(window
->Frame()));
3361 if (window
->ServerWindow()->IsDirectlyAccessing()) {
3362 window
->ServerWindow()->HandleDirectConnection(
3363 B_DIRECT_MODIFY
| B_CLIPPING_MODIFIED
);
3366 // that windows region is not available on screen anymore
3367 stillAvailableOnScreen
.Exclude(&window
->VisibleRegion());
3374 Desktop::_TriggerWindowRedrawing(BRegion
& newDirtyRegion
)
3376 // send redraw messages to all windows intersecting the dirty region
3377 for (Window
* window
= CurrentWindows().LastWindow(); window
!= NULL
;
3378 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
3379 if (!window
->IsHidden()
3380 && newDirtyRegion
.Intersects(window
->VisibleRegion().Frame()))
3381 window
->ProcessDirtyRegion(newDirtyRegion
);
3387 Desktop::_SetBackground(BRegion
& background
)
3389 // NOTE: the drawing operation is caried out
3390 // in the clipping region rebuild, but it is
3391 // ok actually, because it also avoids trails on
3394 // remember the region not covered by any windows
3395 // and redraw the dirty background
3396 BRegion
dirtyBackground(background
);
3397 dirtyBackground
.Exclude(&fBackgroundRegion
);
3398 dirtyBackground
.IntersectWith(&background
);
3399 fBackgroundRegion
= background
;
3400 if (dirtyBackground
.Frame().IsValid()) {
3401 if (GetDrawingEngine()->LockParallelAccess()) {
3402 GetDrawingEngine()->FillRegion(dirtyBackground
,
3403 fWorkspaces
[fCurrentWorkspace
].Color());
3405 GetDrawingEngine()->UnlockParallelAccess();
3411 //! The all window lock must be held when calling this function.
3413 Desktop::RebuildAndRedrawAfterWindowChange(Window
* changedWindow
,
3416 ASSERT_MULTI_WRITE_LOCKED(fWindowLock
);
3417 if (!changedWindow
->IsVisible() || dirty
.CountRects() == 0)
3420 // The following loop is pretty much a copy of
3421 // _RebuildClippingForAllWindows(), but will also
3422 // take care about restricting our dirty region.
3424 // figure out what the entire screen area is
3425 BRegion
stillAvailableOnScreen(fScreenRegion
);
3427 // set clipping of each window
3428 for (Window
* window
= CurrentWindows().LastWindow(); window
!= NULL
;
3429 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
3430 if (!window
->IsHidden()) {
3431 if (window
== changedWindow
)
3432 dirty
.IntersectWith(&stillAvailableOnScreen
);
3434 window
->SetClipping(&stillAvailableOnScreen
);
3435 window
->SetScreen(_DetermineScreenFor(window
->Frame()));
3437 if (window
->ServerWindow()->IsDirectlyAccessing()) {
3438 window
->ServerWindow()->HandleDirectConnection(
3439 B_DIRECT_MODIFY
| B_CLIPPING_MODIFIED
);
3442 // that windows region is not available on screen anymore
3443 stillAvailableOnScreen
.Exclude(&window
->VisibleRegion());
3447 _SetBackground(stillAvailableOnScreen
);
3448 _WindowChanged(changedWindow
);
3450 _TriggerWindowRedrawing(dirty
);
3454 //! Suspend all windows with direct access to the frame buffer
3456 Desktop::_SuspendDirectFrameBufferAccess()
3458 ASSERT_MULTI_LOCKED(fWindowLock
);
3460 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
3461 window
= window
->NextWindow(kAllWindowList
)) {
3462 if (window
->ServerWindow()->IsDirectlyAccessing())
3463 window
->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP
);
3468 //! Resume all windows with direct access to the frame buffer
3470 Desktop::_ResumeDirectFrameBufferAccess()
3472 ASSERT_MULTI_LOCKED(fWindowLock
);
3474 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
3475 window
= window
->NextWindow(kAllWindowList
)) {
3476 if (window
->IsHidden() || !window
->InWorkspace(fCurrentWorkspace
))
3479 if (window
->ServerWindow()->HasDirectFrameBufferAccess()) {
3480 window
->ServerWindow()->HandleDirectConnection(
3481 B_DIRECT_START
| B_BUFFER_RESET
, B_MODE_CHANGED
);
3488 Desktop::_ScreenChanged(Screen
* screen
)
3490 ASSERT_MULTI_WRITE_LOCKED(fWindowLock
);
3492 // the entire screen is dirty, because we're actually
3493 // operating on an all new buffer in memory
3494 BRegion
dirty(screen
->Frame());
3496 // update our cached screen region
3497 fScreenRegion
.Set(screen
->Frame());
3498 gInputManager
->UpdateScreenBounds(screen
->Frame());
3501 _RebuildClippingForAllWindows(background
);
3503 fBackgroundRegion
.MakeEmpty();
3504 // makes sure that the complete background is redrawn
3505 _SetBackground(background
);
3507 // figure out dirty region
3508 dirty
.Exclude(&background
);
3509 _TriggerWindowRedrawing(dirty
);
3511 // send B_SCREEN_CHANGED to windows on that screen
3512 BMessage
update(B_SCREEN_CHANGED
);
3513 update
.AddInt64("when", real_time_clock_usecs());
3514 update
.AddRect("frame", screen
->Frame());
3515 update
.AddInt32("mode", screen
->ColorSpace());
3517 fVirtualScreen
.UpdateFrame();
3519 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
3520 window
= window
->NextWindow(kAllWindowList
)) {
3521 if (window
->Screen() == screen
)
3522 window
->ServerWindow()->ScreenChanged(&update
);
3527 /*! \brief activate one of the app's windows.
3530 Desktop::_ActivateApp(team_id team
)
3532 // search for an unhidden window in the current workspace
3534 AutoWriteLocker
locker(fWindowLock
);
3536 for (Window
* window
= CurrentWindows().LastWindow(); window
!= NULL
;
3537 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
3538 if (!window
->IsHidden() && window
->IsNormal()
3539 && window
->ServerWindow()->ClientTeam() == team
) {
3540 ActivateWindow(window
);
3545 // search for an unhidden window to give focus to
3547 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
3548 window
= window
->NextWindow(kAllWindowList
)) {
3549 // if window is a normal window of the team, and not hidden,
3550 // we've found our target
3551 if (!window
->IsHidden() && window
->IsNormal()
3552 && window
->ServerWindow()->ClientTeam() == team
) {
3553 ActivateWindow(window
);
3558 // TODO: we cannot maximize minimized windows here (with the window lock
3559 // write locked). To work-around this, we could forward the request to
3560 // the ServerApp of this team - it maintains its own window list, and can
3561 // therefore call ActivateWindow() without holding the window lock.
3567 Desktop::_SetCurrentWorkspaceConfiguration()
3569 ASSERT_MULTI_WRITE_LOCKED(fWindowLock
);
3571 status_t status
= fDirectScreenLock
.LockWithTimeout(1000000L);
3572 if (status
!= B_OK
) {
3573 // The application having the direct screen lock didn't give it up in
3574 // time, make it crash
3575 syslog(LOG_ERR
, "Team %" B_PRId32
" did not give up its direct screen "
3576 "lock.\n", fDirectScreenTeam
);
3578 debug_thread(fDirectScreenTeam
);
3579 fDirectScreenTeam
= -1;
3581 fDirectScreenLock
.Unlock();
3583 AutoWriteLocker
_(fScreenLock
);
3585 uint32 changedScreens
;
3586 fVirtualScreen
.SetConfiguration(*this,
3587 fWorkspaces
[fCurrentWorkspace
].CurrentScreenConfiguration(),
3590 for (int32 i
= 0; changedScreens
!= 0; i
++, changedScreens
/= 2) {
3591 if ((changedScreens
& (1 << i
)) != 0)
3592 _ScreenChanged(fVirtualScreen
.ScreenAt(i
));
3597 /*! Changes the current workspace to the one specified by \a index.
3598 You must hold the all window lock when calling this method.
3601 Desktop::_SetWorkspace(int32 index
, bool moveFocusWindow
)
3603 ASSERT_MULTI_WRITE_LOCKED(fWindowLock
);
3605 int32 previousIndex
= fCurrentWorkspace
;
3606 rgb_color previousColor
= fWorkspaces
[fCurrentWorkspace
].Color();
3607 bool movedMouseEventWindow
= false;
3608 Window
* movedWindow
= NULL
;
3609 if (moveFocusWindow
) {
3610 if (fMouseEventWindow
!= NULL
)
3611 movedWindow
= fMouseEventWindow
;
3613 movedWindow
= FocusWindow();
3616 if (movedWindow
!= NULL
) {
3617 if (movedWindow
->IsNormal()) {
3618 if (!movedWindow
->InWorkspace(index
)) {
3619 // The window currently being dragged will follow us to this
3620 // workspace if it's not already on it.
3621 // But only normal windows are following
3622 uint32 oldWorkspaces
= movedWindow
->Workspaces();
3624 WindowStack
* stack
= movedWindow
->GetWindowStack();
3625 if (stack
!= NULL
) {
3626 for (int32 s
= 0; s
< stack
->CountWindows(); s
++) {
3627 Window
* stackWindow
= stack
->LayerOrder().ItemAt(s
);
3629 _Windows(previousIndex
).RemoveWindow(stackWindow
);
3630 _Windows(index
).AddWindow(stackWindow
,
3631 stackWindow
->Frontmost(
3632 _Windows(index
).FirstWindow(), index
));
3634 // send B_WORKSPACES_CHANGED message
3635 stackWindow
->WorkspacesChanged(oldWorkspaces
,
3636 stackWindow
->Workspaces());
3639 // TODO: subset windows will always flicker this way
3641 movedMouseEventWindow
= true;
3643 NotifyWindowWorkspacesChanged(movedWindow
,
3644 movedWindow
->Workspaces());
3646 // make sure it's frontmost
3647 _Windows(index
).RemoveWindow(movedWindow
);
3648 _Windows(index
).AddWindow(movedWindow
,
3649 movedWindow
->Frontmost(_Windows(index
).FirstWindow(),
3654 movedWindow
->Anchor(index
).position
= movedWindow
->Frame().LeftTop();
3657 if (movedWindow
== NULL
|| movedWindow
->InWorkspace(previousIndex
))
3658 fLastWorkspaceFocus
[previousIndex
] = FocusWindow();
3660 fLastWorkspaceFocus
[previousIndex
] = NULL
;
3662 // build region of windows that are no longer visible in the new workspace
3666 for (Window
* window
= CurrentWindows().FirstWindow();
3667 window
!= NULL
; window
= window
->NextWindow(previousIndex
)) {
3668 // store current position in Workspace anchor
3669 window
->Anchor(previousIndex
).position
= window
->Frame().LeftTop();
3671 if (!window
->IsHidden()
3672 && window
->ServerWindow()->IsDirectlyAccessing())
3673 window
->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP
);
3675 window
->WorkspaceActivated(previousIndex
, false);
3677 if (window
->InWorkspace(index
))
3680 if (!window
->IsHidden()) {
3681 // this window will no longer be visible
3682 dirty
.Include(&window
->VisibleRegion());
3685 window
->SetCurrentWorkspace(-1);
3688 fPreviousWorkspace
= fCurrentWorkspace
;
3689 fCurrentWorkspace
= index
;
3691 // Change the display modes, if needed
3692 _SetCurrentWorkspaceConfiguration();
3694 // Show windows, and include them in the changed region - but only
3695 // those that were not visible before (or whose position changed)
3697 WindowList
windows(kWorkingList
);
3698 BList previousRegions
;
3700 for (Window
* window
= _Windows(index
).FirstWindow();
3701 window
!= NULL
; window
= window
->NextWindow(index
)) {
3702 BPoint position
= window
->Anchor(index
).position
;
3704 window
->SetCurrentWorkspace(index
);
3706 if (window
->IsHidden())
3709 if (position
== kInvalidWindowPosition
) {
3710 // if you enter a workspace for the first time, the position
3711 // of the window in the previous workspace is adopted
3712 position
= window
->Frame().LeftTop();
3713 // TODO: make sure the window is still on-screen if it
3717 if (!window
->InWorkspace(previousIndex
)) {
3718 // This window was not visible before, make sure its frame
3720 if (window
->Frame().LeftTop() != position
) {
3721 BPoint offset
= position
- window
->Frame().LeftTop();
3722 window
->MoveBy((int32
)offset
.x
, (int32
)offset
.y
);
3727 if (window
->Frame().LeftTop() != position
) {
3728 // the window was visible before, but its on-screen location changed
3729 BPoint offset
= position
- window
->Frame().LeftTop();
3730 MoveWindowBy(window
, offset
.x
, offset
.y
);
3731 // TODO: be a bit smarter than this...
3733 // We need to remember the previous visible region of the
3734 // window if they changed their order
3735 BRegion
* region
= new (std::nothrow
)
3736 BRegion(window
->VisibleRegion());
3737 if (region
!= NULL
) {
3738 if (previousRegions
.AddItem(region
))
3739 windows
.AddWindow(window
);
3746 _UpdateFronts(false);
3747 _UpdateFloating(previousIndex
, index
,
3748 movedMouseEventWindow
? movedWindow
: NULL
);
3750 BRegion stillAvailableOnScreen
;
3751 _RebuildClippingForAllWindows(stillAvailableOnScreen
);
3752 _SetBackground(stillAvailableOnScreen
);
3754 for (Window
* window
= _Windows(index
).FirstWindow(); window
!= NULL
;
3755 window
= window
->NextWindow(index
)) {
3756 // send B_WORKSPACE_ACTIVATED message
3757 window
->WorkspaceActivated(index
, true);
3759 if (!window
->IsHidden()
3760 && window
->ServerWindow()->HasDirectFrameBufferAccess()) {
3761 window
->ServerWindow()->HandleDirectConnection(
3762 B_DIRECT_START
| B_BUFFER_RESET
, B_MODE_CHANGED
);
3765 if (window
->InWorkspace(previousIndex
) || window
->IsHidden()
3766 || (window
== movedWindow
&& movedWindow
->IsNormal())
3767 || (!window
->IsNormal()
3768 && window
->HasInSubset(movedWindow
))) {
3769 // This window was visible before, and is already handled in the
3774 dirty
.Include(&window
->VisibleRegion());
3777 // Catch order changes in the new workspaces window list
3779 for (Window
* window
= windows
.FirstWindow(); window
!= NULL
;
3780 window
= window
->NextWindow(kWorkingList
), i
++) {
3781 BRegion
* region
= (BRegion
*)previousRegions
.ItemAt(i
);
3782 region
->ExclusiveInclude(&window
->VisibleRegion());
3783 dirty
.Include(region
);
3787 // Set new focus, but keep focus to a floating window if still visible
3788 if (movedWindow
!= NULL
)
3789 SetFocusWindow(movedWindow
);
3790 else if (!_Windows(index
).HasWindow(FocusWindow())
3791 || (FocusWindow() != NULL
&& !FocusWindow()->IsFloating()))
3792 SetFocusWindow(fLastWorkspaceFocus
[index
]);
3794 _WindowChanged(NULL
);
3798 // Show the dirty regions of this workspace switch
3799 if (GetDrawingEngine()->LockParallelAccess()) {
3800 GetDrawingEngine()->FillRegion(dirty
, (rgb_color
){255, 0, 0});
3801 GetDrawingEngine()->UnlockParallelAccess();
3806 if (previousColor
!= fWorkspaces
[fCurrentWorkspace
].Color())