2 * Copyright 2001-2016, 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 (shift + cmd + ctrl + escape)
190 if (key
== 0x01 && (modifiers
& B_COMMAND_KEY
) != 0
191 && (modifiers
& B_CONTROL_KEY
) != 0
192 && (modifiers
& B_SHIFT_KEY
) != 0) {
193 system("screenmode --fall-back &");
194 return B_SKIP_MESSAGE
;
197 bool takeWindow
= (modifiers
& B_SHIFT_KEY
) != 0
198 || fDesktop
->MouseEventWindow() != NULL
;
199 if (key
>= B_F1_KEY
&& key
<= B_F12_KEY
) {
203 if ((modifiers
& (B_COMMAND_KEY
| B_CONTROL_KEY
| B_OPTION_KEY
))
206 if ((modifiers
& B_CONTROL_KEY
) != 0)
209 STRACE(("Set Workspace %" B_PRId32
"\n", key
- 1));
211 fDesktop
->SetWorkspaceAsync(key
- B_F1_KEY
, takeWindow
);
212 return B_SKIP_MESSAGE
;
215 && (modifiers
& (B_COMMAND_KEY
| B_CONTROL_KEY
| B_OPTION_KEY
))
217 // switch to previous workspace (command + `)
218 fDesktop
->SetWorkspaceAsync(-1, takeWindow
);
219 return B_SKIP_MESSAGE
;
223 if (message
->what
== B_KEY_DOWN
224 || message
->what
== B_MODIFIERS_CHANGED
225 || message
->what
== B_UNMAPPED_KEY_DOWN
226 || message
->what
== B_INPUT_METHOD_EVENT
)
227 _UpdateFocus(key
, modifiers
, _target
);
229 return fDesktop
->KeyEvent(message
->what
, key
, modifiers
);
234 KeyboardFilter::RemoveTarget(EventTarget
* target
)
236 if (target
== fLastFocus
)
244 MouseFilter::MouseFilter(Desktop
* desktop
)
247 fLastClickButtons(0),
248 fLastClickModifiers(0),
257 MouseFilter::Filter(BMessage
* message
, EventTarget
** _target
, int32
* _viewToken
,
258 BMessage
* latestMouseMoved
)
261 if (message
->FindPoint("where", &where
) != B_OK
)
262 return B_DISPATCH_MESSAGE
;
265 if (message
->FindInt32("buttons", &buttons
) != B_OK
)
268 if (!fDesktop
->LockAllWindows())
269 return B_DISPATCH_MESSAGE
;
271 int32 viewToken
= B_NULL_TOKEN
;
273 Window
* window
= fDesktop
->MouseEventWindow();
275 window
= fDesktop
->WindowAt(where
);
277 if (window
!= NULL
) {
278 // dispatch event to the window
279 switch (message
->what
) {
282 int32 windowToken
= window
->ServerWindow()->ServerToken();
284 // First approximation of click count validation. We reset the
285 // click count when modifiers or pressed buttons have changed
286 // or when we've got a different click target, or when the
287 // previous click location is too far from the new one. We can
288 // only check the window of the click target here; we'll recheck
289 // after asking the window.
290 int32 modifiers
= message
->FindInt32("modifiers");
292 int32 originalClickCount
= message
->FindInt32("clicks");
293 if (originalClickCount
<= 0)
294 originalClickCount
= 1;
296 int32 clickCount
= originalClickCount
;
297 if (clickCount
> 1) {
298 if (modifiers
!= fLastClickModifiers
299 || buttons
!= fLastClickButtons
300 || !fLastClickTarget
.IsValid()
301 || fLastClickTarget
.WindowToken() != windowToken
302 || square_distance(where
, fLastClickPoint
) >= 16
303 || clickCount
- fResetClickCount
< 1) {
306 clickCount
-= fResetClickCount
;
310 ClickTarget clickTarget
;
311 window
->MouseDown(message
, where
, fLastClickTarget
, clickCount
,
314 // If the click target changed, always reset the click count.
315 if (clickCount
!= 1 && clickTarget
!= fLastClickTarget
)
318 // update our click count management attributes
319 fResetClickCount
= originalClickCount
- clickCount
;
320 fLastClickTarget
= clickTarget
;
321 fLastClickButtons
= buttons
;
322 fLastClickModifiers
= modifiers
;
323 fLastClickPoint
= where
;
325 // get the view token from the click target
326 if (clickTarget
.GetType() == ClickTarget::TYPE_WINDOW_CONTENTS
)
327 viewToken
= clickTarget
.WindowElement();
329 // update the message's "clicks" field, if necessary
330 if (clickCount
!= originalClickCount
) {
331 if (message
->HasInt32("clicks"))
332 message
->ReplaceInt32("clicks", clickCount
);
334 message
->AddInt32("clicks", clickCount
);
337 // notify desktop listeners
338 fDesktop
->NotifyMouseDown(window
, message
, where
);
343 window
->MouseUp(message
, where
, &viewToken
);
345 fDesktop
->SetMouseEventWindow(NULL
);
346 fDesktop
->NotifyMouseUp(window
, message
, where
);
350 window
->MouseMoved(message
, where
, &viewToken
,
351 latestMouseMoved
== NULL
|| latestMouseMoved
== message
,
353 fDesktop
->NotifyMouseMoved(window
, message
, where
);
357 if (viewToken
!= B_NULL_TOKEN
) {
358 fDesktop
->SetViewUnderMouse(window
, viewToken
);
360 *_viewToken
= viewToken
;
361 *_target
= &window
->EventTarget();
363 } else if (message
->what
== B_MOUSE_DOWN
) {
364 // the mouse-down didn't hit a window -- reset the click target
365 fResetClickCount
= 0;
366 fLastClickTarget
= ClickTarget();
367 fLastClickButtons
= message
->FindInt32("buttons");
368 fLastClickModifiers
= message
->FindInt32("modifiers");
369 fLastClickPoint
= where
;
372 if (window
== NULL
|| viewToken
== B_NULL_TOKEN
) {
373 // mouse is not over a window or over a decorator
374 fDesktop
->SetViewUnderMouse(window
, B_NULL_TOKEN
);
375 fDesktop
->SetCursor(NULL
);
380 fDesktop
->SetLastMouseState(where
, buttons
, window
);
382 fDesktop
->NotifyMouseEvent(message
);
384 fDesktop
->UnlockAllWindows();
386 return B_DISPATCH_MESSAGE
;
394 workspace_to_workspaces(int32 index
)
401 workspace_in_workspaces(int32 index
, uint32 workspaces
)
403 return (workspaces
& (1UL << index
)) != 0;
410 Desktop::Desktop(uid_t userID
, const char* targetScreen
)
412 MessageLooper("desktop"),
415 fTargetScreen(strdup(targetScreen
)),
417 fSharedReadOnlyArea(-1),
418 fApplicationsLock("application list"),
419 fShutdownSemaphore(-1),
421 fScreenLock("screen lock"),
422 fDirectScreenLock("direct screen lock"),
423 fDirectScreenTeam(-1),
424 fCurrentWorkspace(0),
425 fPreviousWorkspace(0),
426 fAllWindows(kAllWindowList
),
427 fSubsetWindows(kSubsetList
),
428 fFocusList(kFocusList
),
429 fWorkspacesViews(false),
431 fWorkspacesLock("workspaces list"),
432 fWindowLock("window lock"),
434 fMouseEventWindow(NULL
),
435 fWindowUnderMouse(NULL
),
436 fLockedFocusWindow(NULL
),
437 fViewUnderMouse(B_NULL_TOKEN
),
438 fLastMousePosition(B_ORIGIN
),
439 fLastMouseButtons(0),
445 memset(fLastWorkspaceFocus
, 0, sizeof(fLastWorkspaceFocus
));
447 char name
[B_OS_NAME_LENGTH
];
448 Desktop::_GetLooperName(name
, sizeof(name
));
450 fMessagePort
= create_port(DEFAULT_MONITOR_PORT_SIZE
, name
);
451 if (fMessagePort
< B_OK
)
454 fLink
.SetReceiverPort(fMessagePort
);
456 // register listeners
457 RegisterListener(&fStackAndTile
);
459 const DesktopListenerList
& newListeners
460 = gDecorManager
.GetDesktopListeners();
461 for (int i
= 0; i
< newListeners
.CountItems(); i
++)
462 RegisterListener(newListeners
.ItemAt(i
));
470 delete_area(fSharedReadOnlyArea
);
471 delete_port(fMessagePort
);
472 gFontManager
->DetachUser(fUserID
);
479 Desktop::RegisterListener(DesktopListener
* listener
)
481 DesktopObservable::RegisterListener(listener
, this);
485 /*! This method is allowed to throw exceptions.
490 if (fMessagePort
< B_OK
)
493 // the system palette needs to be initialized before the
494 // desktop settings, since it is used there already
495 InitializeColorMap();
497 const size_t areaSize
= B_PAGE_SIZE
;
498 char name
[B_OS_NAME_LENGTH
];
499 snprintf(name
, sizeof(name
), "d:%d:shared read only", fUserID
);
500 fSharedReadOnlyArea
= create_area(name
, (void **)&fServerReadOnlyMemory
,
501 B_ANY_ADDRESS
, areaSize
, B_NO_LOCK
, B_READ_AREA
| B_WRITE_AREA
);
502 if (fSharedReadOnlyArea
< B_OK
)
503 return fSharedReadOnlyArea
;
505 gFontManager
->AttachUser(fUserID
);
507 fSettings
= new DesktopSettingsPrivate(fServerReadOnlyMemory
);
509 for (int32 i
= 0; i
< kMaxWorkspaces
; i
++) {
510 _Windows(i
).SetIndex(i
);
511 fWorkspaces
[i
].RestoreConfiguration(*fSettings
->WorkspacesMessage(i
));
514 fVirtualScreen
.SetConfiguration(*this,
515 fWorkspaces
[0].CurrentScreenConfiguration());
517 if (fVirtualScreen
.HWInterface() == NULL
) {
518 debug_printf("Could not initialize graphics output. Exiting.\n");
522 fVirtualScreen
.HWInterface()->MoveCursorTo(
523 fVirtualScreen
.Frame().Width() / 2,
524 fVirtualScreen
.Frame().Height() / 2);
527 gInputManager
->AddStream(new InputServerStream
);
530 EventStream
* stream
= fVirtualScreen
.HWInterface()->CreateEventStream();
532 stream
= gInputManager
->GetStream();
534 fEventDispatcher
.SetDesktop(this);
535 fEventDispatcher
.SetTo(stream
);
536 if (fEventDispatcher
.InitCheck() != B_OK
)
537 _LaunchInputServer();
539 fEventDispatcher
.SetHWInterface(fVirtualScreen
.HWInterface());
541 fEventDispatcher
.SetMouseFilter(new MouseFilter(this));
542 fEventDispatcher
.SetKeyboardFilter(new KeyboardFilter(this));
544 // draw the background
546 fScreenRegion
= fVirtualScreen
.Frame();
548 BRegion stillAvailableOnScreen
;
549 _RebuildClippingForAllWindows(stillAvailableOnScreen
);
550 _SetBackground(stillAvailableOnScreen
);
553 // this will set the default cursor
555 fVirtualScreen
.HWInterface()->SetCursorVisible(true);
561 /*! \brief Send a quick (no attachments) message to all applications.
563 Quite useful for notification for things like server shutdown, system
567 Desktop::BroadcastToAllApps(int32 code
)
569 BAutolock
locker(fApplicationsLock
);
571 for (int32 i
= fApplications
.CountItems(); i
-- > 0;) {
572 fApplications
.ItemAt(i
)->PostMessage(code
);
577 /*! \brief Send a quick (no attachments) message to all windows.
580 Desktop::BroadcastToAllWindows(int32 code
)
582 AutoReadLocker
_(fWindowLock
);
584 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
585 window
= window
->NextWindow(kAllWindowList
)) {
586 window
->ServerWindow()->PostMessage(code
);
592 Desktop::GetAllWindowTargets(DelayedMessage
& message
)
594 AutoReadLocker
_(fWindowLock
);
597 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
598 window
= window
->NextWindow(kAllWindowList
)) {
599 message
.AddTarget(window
->ServerWindow()->MessagePort());
608 Desktop::GetAllAppTargets(DelayedMessage
& message
)
610 BAutolock
_(fApplicationsLock
);
612 for (int32 index
= 0; index
< fApplications
.CountItems(); ++index
)
613 message
.AddTarget(fApplications
.ItemAt(index
)->MessagePort());
615 return fApplications
.CountItems();
620 Desktop::KeyEvent(uint32 what
, int32 key
, int32 modifiers
)
622 filter_result result
= B_DISPATCH_MESSAGE
;
623 if (LockAllWindows()) {
624 Window
* window
= MouseEventWindow();
626 window
= WindowAt(fLastMousePosition
);
628 if (window
!= NULL
) {
629 if (what
== B_MODIFIERS_CHANGED
)
630 window
->ModifiersChanged(modifiers
);
633 if (NotifyKeyPressed(what
, key
, modifiers
))
634 result
= B_SKIP_MESSAGE
;
643 // #pragma mark - Mouse and cursor methods
647 Desktop::SetCursor(ServerCursor
* newCursor
)
649 if (newCursor
== NULL
)
650 newCursor
= fCursorManager
.GetCursor(B_CURSOR_ID_SYSTEM_DEFAULT
);
652 if (newCursor
== fCursor
)
657 if (fManagementCursor
.Get() == NULL
)
658 HWInterface()->SetCursor(newCursor
);
662 ServerCursorReference
663 Desktop::Cursor() const
670 Desktop::SetManagementCursor(ServerCursor
* newCursor
)
672 if (newCursor
== fManagementCursor
)
675 fManagementCursor
= newCursor
;
677 HWInterface()->SetCursor(newCursor
!= NULL
? newCursor
: fCursor
.Get());
682 Desktop::SetLastMouseState(const BPoint
& position
, int32 buttons
,
683 Window
* windowUnderMouse
)
685 // The all-window-lock is write-locked.
686 fLastMousePosition
= position
;
687 fLastMouseButtons
= buttons
;
689 if (fLastMouseButtons
== 0 && fLockedFocusWindow
) {
690 fLockedFocusWindow
= NULL
;
691 if (fSettings
->FocusFollowsMouse())
692 SetFocusWindow(windowUnderMouse
);
698 Desktop::GetLastMouseState(BPoint
* position
, int32
* buttons
) const
700 *position
= fLastMousePosition
;
701 *buttons
= fLastMouseButtons
;
705 // #pragma mark - Screen methods
709 Desktop::SetScreenMode(int32 workspace
, int32 id
, const display_mode
& mode
,
712 AutoWriteLocker
_(fWindowLock
);
714 if (workspace
== B_CURRENT_WORKSPACE_INDEX
)
715 workspace
= fCurrentWorkspace
;
717 if (workspace
< 0 || workspace
>= kMaxWorkspaces
)
720 Screen
* screen
= fVirtualScreen
.ScreenByID(id
);
722 return B_NAME_NOT_FOUND
;
724 // Check if the mode has actually changed
726 if (workspace
== fCurrentWorkspace
) {
727 // retrieve from current screen
728 display_mode oldMode
;
729 screen
->GetMode(oldMode
);
731 if (!memcmp(&oldMode
, &mode
, sizeof(display_mode
)))
736 _SuspendDirectFrameBufferAccess();
738 AutoWriteLocker
locker(fScreenLock
);
740 status_t status
= screen
->SetMode(mode
);
741 if (status
!= B_OK
) {
744 _ResumeDirectFrameBufferAccess();
748 // retrieve from settings
749 screen_configuration
* configuration
750 = fWorkspaces
[workspace
].CurrentScreenConfiguration().CurrentByID(
752 if (configuration
!= NULL
753 && !memcmp(&configuration
->mode
, &mode
, sizeof(display_mode
)))
757 // Update our configurations
760 bool hasInfo
= screen
->GetMonitorInfo(info
) == B_OK
;
762 fWorkspaces
[workspace
].CurrentScreenConfiguration().Set(id
,
763 hasInfo
? &info
: NULL
, screen
->Frame(), mode
);
765 fWorkspaces
[workspace
].StoredScreenConfiguration().Set(id
,
766 hasInfo
? &info
: NULL
, screen
->Frame(), mode
);
767 StoreWorkspaceConfiguration(workspace
);
770 _ScreenChanged(screen
);
771 if (workspace
== fCurrentWorkspace
)
772 _ResumeDirectFrameBufferAccess();
779 Desktop::GetScreenMode(int32 workspace
, int32 id
, display_mode
& mode
)
781 AutoReadLocker
_(fScreenLock
);
783 if (workspace
== B_CURRENT_WORKSPACE_INDEX
)
784 workspace
= fCurrentWorkspace
;
786 if (workspace
< 0 || workspace
>= kMaxWorkspaces
)
789 if (workspace
== fCurrentWorkspace
) {
790 // retrieve from current screen
791 Screen
* screen
= fVirtualScreen
.ScreenByID(id
);
793 return B_NAME_NOT_FOUND
;
795 screen
->GetMode(mode
);
799 // retrieve from settings
800 screen_configuration
* configuration
801 = fWorkspaces
[workspace
].CurrentScreenConfiguration().CurrentByID(id
);
802 if (configuration
== NULL
)
803 return B_NAME_NOT_FOUND
;
805 mode
= configuration
->mode
;
811 Desktop::GetScreenFrame(int32 workspace
, int32 id
, BRect
& frame
)
813 AutoReadLocker
_(fScreenLock
);
815 if (workspace
== B_CURRENT_WORKSPACE_INDEX
)
816 workspace
= fCurrentWorkspace
;
818 if (workspace
< 0 || workspace
>= kMaxWorkspaces
)
821 if (workspace
== fCurrentWorkspace
) {
822 // retrieve from current screen
823 Screen
* screen
= fVirtualScreen
.ScreenByID(id
);
825 return B_NAME_NOT_FOUND
;
827 frame
= screen
->Frame();
831 // retrieve from settings
832 screen_configuration
* configuration
833 = fWorkspaces
[workspace
].CurrentScreenConfiguration().CurrentByID(id
);
834 if (configuration
== NULL
)
835 return B_NAME_NOT_FOUND
;
837 frame
= configuration
->frame
;
843 Desktop::RevertScreenModes(uint32 workspaces
)
848 AutoWriteLocker
_(fWindowLock
);
850 for (int32 workspace
= 0; workspace
< kMaxWorkspaces
; workspace
++) {
851 if ((workspaces
& (1U << workspace
)) == 0)
854 // Revert all screens on this workspace
856 // TODO: ideally, we would know which screens to revert - this way, too
857 // many of them could be reverted
859 for (int32 index
= 0; index
< fVirtualScreen
.CountScreens(); index
++) {
860 Screen
* screen
= fVirtualScreen
.ScreenAt(index
);
862 // retrieve configurations
863 screen_configuration
* stored
= fWorkspaces
[workspace
]
864 .StoredScreenConfiguration().CurrentByID(screen
->ID());
865 screen_configuration
* current
= fWorkspaces
[workspace
]
866 .CurrentScreenConfiguration().CurrentByID(screen
->ID());
868 if ((stored
!= NULL
&& current
!= NULL
869 && !memcmp(&stored
->mode
, ¤t
->mode
,
870 sizeof(display_mode
)))
871 || (stored
== NULL
&& current
== NULL
))
874 if (stored
== NULL
) {
875 fWorkspaces
[workspace
].CurrentScreenConfiguration()
878 if (workspace
== fCurrentWorkspace
) {
879 _SuspendDirectFrameBufferAccess();
880 _SetCurrentWorkspaceConfiguration();
881 _ResumeDirectFrameBufferAccess();
884 SetScreenMode(workspace
, screen
->ID(), stored
->mode
, false);
891 Desktop::LockDirectScreen(team_id team
)
893 // TODO: BWindowScreens should use the same mechanism as BDirectWindow,
894 // which would make this method superfluous.
896 status_t status
= fDirectScreenLock
.LockWithTimeout(1000000L);
898 fDirectScreenTeam
= team
;
905 Desktop::UnlockDirectScreen(team_id team
)
907 if (fDirectScreenTeam
== team
) {
908 fDirectScreenLock
.Unlock();
909 fDirectScreenTeam
= -1;
913 return B_PERMISSION_DENIED
;
917 // #pragma mark - Workspaces methods
920 /*! Changes the current workspace to the one specified by \a index.
923 Desktop::SetWorkspaceAsync(int32 index
, bool moveFocusWindow
)
925 BPrivate::LinkSender
link(MessagePort());
926 link
.StartMessage(AS_ACTIVATE_WORKSPACE
);
927 link
.Attach
<int32
>(index
);
928 link
.Attach
<bool>(moveFocusWindow
);
933 /*! Changes the current workspace to the one specified by \a index.
934 You must not hold any window lock when calling this method.
937 Desktop::SetWorkspace(int32 index
, bool moveFocusWindow
)
940 DesktopSettings
settings(this);
942 if (index
< 0 || index
>= settings
.WorkspacesCount()
943 || index
== fCurrentWorkspace
) {
948 _SetWorkspace(index
, moveFocusWindow
);
951 _SendFakeMouseMoved();
956 Desktop::SetWorkspacesLayout(int32 newColumns
, int32 newRows
)
958 int32 newCount
= newColumns
* newRows
;
959 if (newCount
< 1 || newCount
> kMaxWorkspaces
)
962 if (!LockAllWindows())
965 fSettings
->SetWorkspacesLayout(newColumns
, newRows
);
967 // either update the workspaces window, or switch to
968 // the last available workspace - which will update
969 // the workspaces window automatically
970 bool workspaceChanged
= CurrentWorkspace() >= newCount
;
971 if (workspaceChanged
)
972 _SetWorkspace(newCount
- 1);
974 _WindowChanged(NULL
);
978 if (workspaceChanged
)
979 _SendFakeMouseMoved();
985 /*! Returns the virtual screen frame of the workspace specified by \a index.
988 Desktop::WorkspaceFrame(int32 index
) const
991 if (index
== fCurrentWorkspace
)
992 frame
= fVirtualScreen
.Frame();
993 else if (index
>= 0 && index
< fSettings
->WorkspacesCount()) {
995 if (fSettings
->WorkspacesMessage(index
)->FindMessage("screen",
997 || screenData
.FindRect("frame", &frame
) != B_OK
) {
998 frame
= fVirtualScreen
.Frame();
1006 /*! \brief Stores the workspace configuration.
1007 You must hold the window lock when calling this method.
1010 Desktop::StoreWorkspaceConfiguration(int32 index
)
1012 // Retrieve settings
1015 fWorkspaces
[index
].StoreConfiguration(settings
);
1019 fSettings
->SetWorkspacesMessage(index
, settings
);
1020 fSettings
->Save(kWorkspacesSettings
);
1025 Desktop::AddWorkspacesView(WorkspacesView
* view
)
1027 if (view
->Window() == NULL
|| view
->Window()->IsHidden())
1030 BAutolock
_(fWorkspacesLock
);
1032 if (!fWorkspacesViews
.HasItem(view
))
1033 fWorkspacesViews
.AddItem(view
);
1038 Desktop::RemoveWorkspacesView(WorkspacesView
* view
)
1040 BAutolock
_(fWorkspacesLock
);
1041 fWorkspacesViews
.RemoveItem(view
);
1045 // #pragma mark - Methods for Window manipulation
1048 /*! \brief Activates or focusses the window based on the pointer position.
1051 Desktop::SelectWindow(Window
* window
)
1053 if (fSettings
->ClickToFocusMouse()) {
1054 // Only bring the window to front when it is not the window under the
1055 // mouse pointer. This should result in sensible behaviour.
1056 if (window
!= fWindowUnderMouse
1057 || (window
== fWindowUnderMouse
&& window
!= FocusWindow()))
1058 ActivateWindow(window
);
1060 SetFocusWindow(window
);
1062 ActivateWindow(window
);
1066 /*! \brief Tries to move the specified window to the front of the screen,
1067 and make it the focus window.
1069 If there are any modal windows on this screen, it might not actually
1070 become the frontmost window, though, as modal windows stay in front
1074 Desktop::ActivateWindow(Window
* window
)
1076 STRACE(("ActivateWindow(%p, %s)\n", window
, window
1077 ? window
->Title() : "<none>"));
1079 if (window
== NULL
) {
1084 if (window
->Workspaces() == 0 && window
->IsNormal())
1087 AutoWriteLocker
allWindowLocker(fWindowLock
);
1089 NotifyWindowActivated(window
);
1091 bool windowOnOtherWorkspace
= !window
->InWorkspace(fCurrentWorkspace
);
1092 if (windowOnOtherWorkspace
1093 && (window
->Flags() & B_NOT_ANCHORED_ON_ACTIVATE
) == 0) {
1094 if ((window
->Flags() & B_NO_WORKSPACE_ACTIVATION
) == 0) {
1095 // Switch to the workspace on which this window is
1096 // (we'll take the first one that the window is on)
1097 uint32 workspaces
= window
->Workspaces();
1098 for (int32 i
= 0; i
< fSettings
->WorkspacesCount(); i
++) {
1099 uint32 workspace
= workspace_to_workspaces(i
);
1100 if (workspaces
& workspace
) {
1102 windowOnOtherWorkspace
= false;
1110 if (windowOnOtherWorkspace
) {
1111 if (!window
->IsNormal()) {
1112 // Bring a window to front that this floating window belongs to
1113 Window
* front
= _LastFocusSubsetWindow(window
);
1114 if (front
== NULL
) {
1115 // We can't do anything about those.
1119 ActivateWindow(front
);
1121 if (!window
->InWorkspace(fCurrentWorkspace
)) {
1122 // This window can't be made active
1126 // Bring the window to the current workspace
1127 // TODO: what if this window is on multiple workspaces?!?
1128 uint32 workspaces
= workspace_to_workspaces(fCurrentWorkspace
);
1129 SetWindowWorkspaces(window
, workspaces
);
1133 if (window
->IsMinimized()) {
1134 // Unlike WindowAction(), this is called from the application itself,
1135 // so we will just unminimize the window here.
1136 window
->SetMinimized(false);
1140 if (window
== FrontWindow()) {
1141 // see if there is a normal B_AVOID_FRONT window still in front of us
1142 Window
* avoidsFront
= window
->NextWindow(fCurrentWorkspace
);
1143 while (avoidsFront
&& avoidsFront
->IsNormal()
1144 && (avoidsFront
->Flags() & B_AVOID_FRONT
) == 0) {
1145 avoidsFront
= avoidsFront
->NextWindow(fCurrentWorkspace
);
1148 if (avoidsFront
== NULL
) {
1149 // we're already the frontmost window, we might just not have focus
1151 if ((window
->Flags() & B_AVOID_FOCUS
) == 0)
1152 SetFocusWindow(window
);
1157 WindowList
windows(kWorkingList
);
1158 Window
* frontmost
= window
->Frontmost();
1159 const Window
* lastWindowUnderMouse
= fWindowUnderMouse
;
1161 CurrentWindows().RemoveWindow(window
);
1162 windows
.AddWindow(window
);
1163 window
->MoveToTopStackLayer();
1165 if (frontmost
!= NULL
&& frontmost
->IsModal()) {
1166 // all modal windows follow their subsets to the front
1167 // (ie. they are staying in front of them, but they are
1168 // not supposed to change their order because of that)
1171 for (Window
* modal
= frontmost
; modal
!= NULL
; modal
= nextModal
) {
1172 // get the next modal window
1173 nextModal
= modal
->NextWindow(fCurrentWorkspace
);
1174 while (nextModal
!= NULL
&& !nextModal
->IsModal()) {
1175 nextModal
= nextModal
->NextWindow(fCurrentWorkspace
);
1177 if (nextModal
!= NULL
&& !nextModal
->HasInSubset(window
))
1180 CurrentWindows().RemoveWindow(modal
);
1181 windows
.AddWindow(modal
);
1185 _BringWindowsToFront(windows
, kWorkingList
, true);
1187 if ((window
->Flags() & B_AVOID_FOCUS
) == 0)
1188 SetFocusWindow(window
);
1190 bool sendFakeMouseMoved
= _CheckSendFakeMouseMoved(lastWindowUnderMouse
);
1192 allWindowLocker
.Unlock();
1194 if (sendFakeMouseMoved
)
1195 _SendFakeMouseMoved();
1200 Desktop::SendWindowBehind(Window
* window
, Window
* behindOf
, bool sendStack
)
1202 if (!LockAllWindows())
1205 Window
* orgWindow
= window
;
1206 WindowStack
* stack
= window
->GetWindowStack();
1207 if (sendStack
&& stack
!= NULL
)
1208 window
= stack
->TopLayerWindow();
1210 // TODO: should the "not in current workspace" be handled anyway?
1211 // (the code below would have to be changed then, though)
1212 if (window
== BackWindow()
1213 || !window
->InWorkspace(fCurrentWorkspace
)
1214 || (behindOf
!= NULL
&& !behindOf
->InWorkspace(fCurrentWorkspace
))) {
1219 // Is this a valid behindOf window?
1220 if (behindOf
!= NULL
&& window
->HasInSubset(behindOf
))
1223 // what is currently visible of the window
1224 // might be dirty after the window is send to back
1225 BRegion
dirty(window
->VisibleRegion());
1227 Window
* backmost
= window
->Backmost(behindOf
);
1228 const Window
* lastWindowUnderMouse
= fWindowUnderMouse
;
1230 CurrentWindows().RemoveWindow(window
);
1231 CurrentWindows().AddWindow(window
, backmost
1232 ? backmost
->NextWindow(fCurrentWorkspace
) : BackWindow());
1235 _RebuildClippingForAllWindows(dummy
);
1237 // only redraw the top layer window to avoid flicker
1239 // mark everything dirty that is no longer visible
1240 BRegion
clean(window
->VisibleRegion());
1241 dirty
.Exclude(&clean
);
1246 if (fSettings
->FocusFollowsMouse())
1247 SetFocusWindow(WindowAt(fLastMousePosition
));
1248 else if (fSettings
->NormalMouse())
1249 SetFocusWindow(NULL
);
1251 _WindowChanged(window
);
1253 if (sendStack
&& stack
!= NULL
) {
1254 for (int32 i
= 0; i
< stack
->CountWindows(); i
++) {
1255 Window
* stackWindow
= stack
->LayerOrder().ItemAt(i
);
1256 if (stackWindow
== window
)
1258 SendWindowBehind(stackWindow
, behindOf
, false);
1262 bool sendFakeMouseMoved
= _CheckSendFakeMouseMoved(lastWindowUnderMouse
);
1263 NotifyWindowSentBehind(orgWindow
, behindOf
);
1267 if (sendFakeMouseMoved
)
1268 _SendFakeMouseMoved();
1273 Desktop::ShowWindow(Window
* window
)
1275 if (!window
->IsHidden())
1278 AutoWriteLocker
locker(fWindowLock
);
1280 window
->SetHidden(false);
1281 fFocusList
.AddWindow(window
);
1283 // If the window is on the current workspace, we'll show it. Special
1284 // handling for floating windows, as they can only be shown if their
1286 if (window
->InWorkspace(fCurrentWorkspace
)
1287 || (window
->IsFloating() && _LastFocusSubsetWindow(window
) != NULL
)) {
1288 _ShowWindow(window
, true);
1289 _UpdateSubsetWorkspaces(window
);
1290 ActivateWindow(window
);
1292 // then we don't need to send the fake mouse event either
1293 _WindowChanged(window
);
1297 if (window
->HasWorkspacesViews()) {
1298 // find workspaces views in view hierarchy
1299 BAutolock
_(fWorkspacesLock
);
1300 window
->FindWorkspacesViews(fWorkspacesViews
);
1303 // If the mouse cursor is directly over the newly visible window,
1304 // we'll send a fake mouse moved message to the window, so that
1305 // it knows the mouse is over it.
1307 _SendFakeMouseMoved(window
);
1312 Desktop::HideWindow(Window
* window
, bool fromMinimize
)
1314 if (window
->IsHidden())
1317 if (!LockAllWindows())
1320 window
->SetHidden(true);
1321 fFocusList
.RemoveWindow(window
);
1323 if (fMouseEventWindow
== window
) {
1324 // Make its decorator lose the current mouse action
1327 window
->MouseUp(&message
, fLastMousePosition
, &viewToken
);
1329 fMouseEventWindow
= NULL
;
1332 if (fLockedFocusWindow
== window
) {
1333 // Remove the focus lock so the focus can be changed below
1334 fLockedFocusWindow
= NULL
;
1337 if (window
->InWorkspace(fCurrentWorkspace
)) {
1338 _UpdateSubsetWorkspaces(window
);
1339 _HideWindow(window
);
1342 _WindowChanged(window
);
1344 if (FocusWindow() == window
)
1347 _WindowRemoved(window
);
1349 if (window
->HasWorkspacesViews()) {
1350 // remove workspaces views from this window
1351 BObjectList
<WorkspacesView
> list(false);
1352 window
->FindWorkspacesViews(list
);
1354 BAutolock
_(fWorkspacesLock
);
1356 while (WorkspacesView
* view
= list
.RemoveItemAt(0)) {
1357 fWorkspacesViews
.RemoveItem(view
);
1361 NotifyWindowHidden(window
, fromMinimize
);
1365 if (window
== fWindowUnderMouse
)
1366 _SendFakeMouseMoved();
1371 Desktop::MinimizeWindow(Window
* window
, bool minimize
)
1373 if (!LockAllWindows())
1376 if (minimize
&& !window
->IsHidden()) {
1377 HideWindow(window
, true);
1378 window
->SetMinimized(minimize
);
1379 NotifyWindowMinimized(window
, minimize
);
1380 } else if (!minimize
&& window
->IsHidden()) {
1381 ActivateWindow(window
);
1382 // this will unminimize the window for us
1383 NotifyWindowMinimized(window
, minimize
);
1391 Desktop::MoveWindowBy(Window
* window
, float x
, float y
, int32 workspace
)
1393 if (x
== 0 && y
== 0)
1396 AutoWriteLocker
_(fWindowLock
);
1398 Window
* topWindow
= window
->TopLayerStackWindow();
1399 if (topWindow
!= NULL
)
1402 if (workspace
== -1)
1403 workspace
= fCurrentWorkspace
;
1404 if (!window
->IsVisible() || workspace
!= fCurrentWorkspace
) {
1405 if (workspace
!= fCurrentWorkspace
) {
1406 WindowStack
* stack
= window
->GetWindowStack();
1407 if (stack
!= NULL
) {
1408 for (int32 s
= 0; s
< stack
->CountWindows(); s
++) {
1409 Window
* stackWindow
= stack
->WindowAt(s
);
1410 // move the window on another workspace - this doesn't
1411 // change it's current position
1412 if (stackWindow
->Anchor(workspace
).position
1413 == kInvalidWindowPosition
) {
1414 stackWindow
->Anchor(workspace
).position
1415 = stackWindow
->Frame().LeftTop();
1418 stackWindow
->Anchor(workspace
).position
+= BPoint(x
, y
);
1419 stackWindow
->SetCurrentWorkspace(workspace
);
1420 _WindowChanged(stackWindow
);
1424 window
->MoveBy((int32
)x
, (int32
)y
);
1426 NotifyWindowMoved(window
);
1430 // the dirty region starts with the visible area of the window being moved
1431 BRegion
newDirtyRegion(window
->VisibleRegion());
1433 // stop direct frame buffer access
1434 bool direct
= false;
1435 if (window
->ServerWindow()->IsDirectlyAccessing()) {
1436 window
->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP
);
1440 window
->MoveBy((int32
)x
, (int32
)y
);
1443 _RebuildClippingForAllWindows(background
);
1445 // construct the region that is possible to be blitted
1446 // to move the contents of the window
1447 BRegion
copyRegion(window
->VisibleRegion());
1448 copyRegion
.OffsetBy((int32
)-x
, (int32
)-y
);
1449 copyRegion
.IntersectWith(&newDirtyRegion
);
1450 // newDirtyRegion == the windows old visible region
1452 // include the the new visible region of the window being
1453 // moved into the dirty region (for now)
1454 newDirtyRegion
.Include(&window
->VisibleRegion());
1456 // NOTE: Having all windows locked should prevent any
1457 // problems with locking the drawing engine here.
1458 if (GetDrawingEngine()->LockParallelAccess()) {
1459 GetDrawingEngine()->CopyRegion(©Region
, (int32
)x
, (int32
)y
);
1460 GetDrawingEngine()->UnlockParallelAccess();
1463 // in the dirty region, exclude the parts that we
1464 // could move by blitting
1465 copyRegion
.OffsetBy((int32
)x
, (int32
)y
);
1466 newDirtyRegion
.Exclude(©Region
);
1468 MarkDirty(newDirtyRegion
);
1469 _SetBackground(background
);
1470 _WindowChanged(window
);
1472 // resume direct frame buffer access
1474 // TODO: the clipping actually only changes when we move our window
1475 // off screen, or behind some other window
1476 window
->ServerWindow()->HandleDirectConnection(
1477 B_DIRECT_START
| B_BUFFER_MOVED
| B_CLIPPING_MODIFIED
);
1480 NotifyWindowMoved(window
);
1485 Desktop::ResizeWindowBy(Window
* window
, float x
, float y
)
1487 if (x
== 0 && y
== 0)
1490 AutoWriteLocker
_(fWindowLock
);
1492 Window
* topWindow
= window
->TopLayerStackWindow();
1496 if (!window
->IsVisible()) {
1497 window
->ResizeBy((int32
)x
, (int32
)y
, NULL
);
1498 NotifyWindowResized(window
);
1502 // the dirty region for the inside of the window is
1503 // constructed by the window itself in ResizeBy()
1504 BRegion newDirtyRegion
;
1505 // track the dirty region outside the window in case
1506 // it is shrunk in "previouslyOccupiedRegion"
1507 BRegion
previouslyOccupiedRegion(window
->VisibleRegion());
1509 // stop direct frame buffer access
1510 bool direct
= false;
1511 if (window
->ServerWindow()->IsDirectlyAccessing()) {
1512 window
->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP
);
1516 window
->ResizeBy((int32
)x
, (int32
)y
, &newDirtyRegion
);
1519 _RebuildClippingForAllWindows(background
);
1521 // we just care for the region outside the window
1522 previouslyOccupiedRegion
.Exclude(&window
->VisibleRegion());
1524 // make sure the window cannot mark stuff dirty outside
1525 // its visible region...
1526 newDirtyRegion
.IntersectWith(&window
->VisibleRegion());
1527 // ...because we do this outself
1528 newDirtyRegion
.Include(&previouslyOccupiedRegion
);
1530 MarkDirty(newDirtyRegion
);
1531 _SetBackground(background
);
1532 _WindowChanged(window
);
1534 // resume direct frame buffer access
1536 window
->ServerWindow()->HandleDirectConnection(
1537 B_DIRECT_START
| B_BUFFER_RESIZED
| B_CLIPPING_MODIFIED
);
1540 NotifyWindowResized(window
);
1545 Desktop::SetWindowTabLocation(Window
* window
, float location
, bool isShifting
)
1547 AutoWriteLocker
_(fWindowLock
);
1550 bool changed
= window
->SetTabLocation(location
, isShifting
, dirty
);
1552 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1554 NotifyWindowTabLocationChanged(window
, location
, isShifting
);
1561 Desktop::SetWindowDecoratorSettings(Window
* window
, const BMessage
& settings
)
1563 AutoWriteLocker
_(fWindowLock
);
1566 bool changed
= window
->SetDecoratorSettings(settings
, dirty
);
1567 bool listenerChanged
= SetDecoratorSettings(window
, settings
);
1568 if (changed
|| listenerChanged
)
1569 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1576 Desktop::SetWindowWorkspaces(Window
* window
, uint32 workspaces
)
1580 if (window
->IsNormal() && workspaces
== B_CURRENT_WORKSPACE
)
1581 workspaces
= workspace_to_workspaces(CurrentWorkspace());
1583 WindowStack
* stack
= window
->GetWindowStack();
1584 if (stack
!= NULL
) {
1585 for (int32 s
= 0; s
< stack
->CountWindows(); s
++) {
1586 window
= stack
->LayerOrder().ItemAt(s
);
1588 uint32 oldWorkspaces
= window
->Workspaces();
1589 window
->WorkspacesChanged(oldWorkspaces
, workspaces
);
1590 _ChangeWindowWorkspaces(window
, oldWorkspaces
, workspaces
);
1597 /*! \brief Adds the window to the desktop.
1598 At this point, the window is still hidden and must be shown explicitly
1602 Desktop::AddWindow(Window
*window
)
1606 fAllWindows
.AddWindow(window
);
1607 if (!window
->IsNormal())
1608 fSubsetWindows
.AddWindow(window
);
1610 if (window
->IsNormal()) {
1611 if (window
->Workspaces() == B_CURRENT_WORKSPACE
)
1612 window
->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
1614 // subset windows are visible on all workspaces their subset is on
1615 window
->SetWorkspaces(window
->SubsetWorkspaces());
1618 _ChangeWindowWorkspaces(window
, 0, window
->Workspaces());
1620 NotifyWindowAdded(window
);
1627 Desktop::RemoveWindow(Window
*window
)
1631 if (!window
->IsHidden())
1634 fAllWindows
.RemoveWindow(window
);
1635 if (!window
->IsNormal())
1636 fSubsetWindows
.RemoveWindow(window
);
1638 _ChangeWindowWorkspaces(window
, window
->Workspaces(), 0);
1640 NotifyWindowRemoved(window
);
1644 // make sure this window won't get any events anymore
1646 EventDispatcher().RemoveTarget(window
->EventTarget());
1651 Desktop::AddWindowToSubset(Window
* subset
, Window
* window
)
1653 if (!subset
->AddToSubset(window
))
1656 _ChangeWindowWorkspaces(subset
, subset
->Workspaces(),
1657 subset
->SubsetWorkspaces());
1663 Desktop::RemoveWindowFromSubset(Window
* subset
, Window
* window
)
1665 subset
->RemoveFromSubset(window
);
1666 _ChangeWindowWorkspaces(subset
, subset
->Workspaces(),
1667 subset
->SubsetWorkspaces());
1672 Desktop::FontsChanged(Window
* window
)
1674 AutoWriteLocker
_(fWindowLock
);
1677 window
->FontsChanged(&dirty
);
1679 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1684 Desktop::ColorUpdated(Window
* window
, color_which which
, rgb_color color
)
1686 AutoWriteLocker
_(fWindowLock
);
1688 window
->TopView()->ColorUpdated(which
, color
);
1691 case B_WINDOW_TAB_COLOR
:
1692 case B_WINDOW_TEXT_COLOR
:
1693 case B_WINDOW_INACTIVE_TAB_COLOR
:
1694 case B_WINDOW_INACTIVE_TEXT_COLOR
:
1695 case B_WINDOW_BORDER_COLOR
:
1696 case B_WINDOW_INACTIVE_BORDER_COLOR
:
1703 window
->ColorsChanged(&dirty
);
1704 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1709 Desktop::SetWindowLook(Window
* window
, window_look newLook
)
1711 if (window
->Look() == newLook
)
1714 AutoWriteLocker
_(fWindowLock
);
1717 window
->SetLook(newLook
, &dirty
);
1718 // TODO: test what happens when the window
1719 // finds out it needs to resize itself...
1721 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1723 NotifyWindowLookChanged(window
, newLook
);
1728 Desktop::SetWindowFeel(Window
* window
, window_feel newFeel
)
1730 if (window
->Feel() == newFeel
)
1735 bool wasNormal
= window
->IsNormal();
1737 window
->SetFeel(newFeel
);
1739 // move the window out of or into the subset window list as needed
1740 if (window
->IsNormal() && !wasNormal
)
1741 fSubsetWindows
.RemoveWindow(window
);
1742 else if (!window
->IsNormal() && wasNormal
)
1743 fSubsetWindows
.AddWindow(window
);
1745 // A normal window that was once a floating or modal window will
1746 // adopt the window's current workspaces
1748 if (!window
->IsNormal()) {
1749 _ChangeWindowWorkspaces(window
, window
->Workspaces(),
1750 window
->SubsetWorkspaces());
1753 // make sure the window has the correct position in the window lists
1754 // (ie. all floating windows have to be on the top, ...)
1756 for (int32 i
= 0; i
< kMaxWorkspaces
; i
++) {
1757 if (!workspace_in_workspaces(i
, window
->Workspaces()))
1760 bool changed
= false;
1761 BRegion visibleBefore
;
1762 if (i
== fCurrentWorkspace
&& window
->IsVisible())
1763 visibleBefore
= window
->VisibleRegion();
1765 Window
* backmost
= window
->Backmost(_Windows(i
).LastWindow(), i
);
1766 if (backmost
!= NULL
) {
1767 // check if the backmost window is really behind it
1768 Window
* previous
= window
->PreviousWindow(i
);
1769 while (previous
!= NULL
) {
1770 if (previous
== backmost
)
1773 previous
= previous
->PreviousWindow(i
);
1776 if (previous
== NULL
) {
1777 // need to reinsert window before its backmost window
1778 _Windows(i
).RemoveWindow(window
);
1779 _Windows(i
).AddWindow(window
, backmost
->NextWindow(i
));
1784 Window
* frontmost
= window
->Frontmost(_Windows(i
).FirstWindow(), i
);
1785 if (frontmost
!= NULL
) {
1786 // check if the frontmost window is really in front of it
1787 Window
* next
= window
->NextWindow(i
);
1788 while (next
!= NULL
) {
1789 if (next
== frontmost
)
1792 next
= next
->NextWindow(i
);
1796 // need to reinsert window behind its frontmost window
1797 _Windows(i
).RemoveWindow(window
);
1798 _Windows(i
).AddWindow(window
, frontmost
);
1803 if (i
== fCurrentWorkspace
&& changed
) {
1805 _RebuildClippingForAllWindows(dummy
);
1807 // mark everything dirty that is no longer visible, or
1808 // is now visible and wasn't before
1809 BRegion
visibleAfter(window
->VisibleRegion());
1810 BRegion
dirty(visibleAfter
);
1811 dirty
.Exclude(&visibleBefore
);
1812 visibleBefore
.Exclude(&visibleAfter
);
1813 dirty
.Include(&visibleBefore
);
1821 if (window
== FocusWindow() && !window
->IsVisible())
1824 NotifyWindowFeelChanged(window
, newFeel
);
1831 Desktop::SetWindowFlags(Window
*window
, uint32 newFlags
)
1833 if (window
->Flags() == newFlags
)
1836 AutoWriteLocker
_(fWindowLock
);
1839 window
->SetFlags(newFlags
, &dirty
);
1840 // TODO: test what happens when the window
1841 // finds out it needs to resize itself...
1843 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1848 Desktop::SetWindowTitle(Window
*window
, const char* title
)
1850 AutoWriteLocker
_(fWindowLock
);
1853 window
->SetTitle(title
, dirty
);
1855 RebuildAndRedrawAfterWindowChange(window
, dirty
);
1859 /*! Returns the window under the mouse cursor.
1860 You need to have acquired the All Windows lock when calling this method.
1863 Desktop::WindowAt(BPoint where
)
1865 for (Window
* window
= CurrentWindows().LastWindow(); window
;
1866 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
1867 if (window
->IsVisible() && window
->VisibleRegion().Contains(where
))
1868 return window
->StackedWindowAt(where
);
1876 Desktop::SetMouseEventWindow(Window
* window
)
1878 fMouseEventWindow
= window
;
1883 Desktop::SetViewUnderMouse(const Window
* window
, int32 viewToken
)
1885 fWindowUnderMouse
= window
;
1886 fViewUnderMouse
= viewToken
;
1891 Desktop::ViewUnderMouse(const Window
* window
)
1893 if (window
!= NULL
&& fWindowUnderMouse
== window
)
1894 return fViewUnderMouse
;
1896 return B_NULL_TOKEN
;
1900 /*! Returns the current keyboard event target candidate - which is either the
1901 top-most window (in case it has the kAcceptKeyboardFocusFlag flag set), or
1902 the one having focus.
1903 The window lock must be held when calling this function.
1906 Desktop::KeyboardEventTarget()
1908 // Get the top most non-hidden window
1909 Window
* window
= CurrentWindows().LastWindow();
1910 while (window
!= NULL
&& window
->IsHidden()) {
1911 window
= window
->PreviousWindow(fCurrentWorkspace
);
1914 if (window
!= NULL
&& (window
->Flags() & kAcceptKeyboardFocusFlag
) != 0)
1915 return &window
->EventTarget();
1917 if (FocusWindow() != NULL
)
1918 return &FocusWindow()->EventTarget();
1924 /*! Tries to set the focus to the specified \a focus window. It will make sure,
1925 however, that the window actually can have focus. You are allowed to pass
1926 in a NULL pointer for \a focus.
1928 Besides the B_AVOID_FOCUS flag, a modal window, or a BWindowScreen can both
1929 prevent it from getting focus.
1931 In any case, this method makes sure that there is a focus window, if there
1932 is any window at all, that is.
1935 Desktop::SetFocusWindow(Window
* nextFocus
)
1937 if (!LockAllWindows())
1940 // test for B_LOCK_WINDOW_FOCUS
1941 if (fLockedFocusWindow
&& nextFocus
!= fLockedFocusWindow
) {
1946 bool hasModal
= _WindowHasModal(nextFocus
);
1947 bool hasWindowScreen
= false;
1949 if (!hasModal
&& nextFocus
!= NULL
) {
1950 // Check whether or not a window screen is in front of the window
1951 // (if it has a modal, the right thing is done, anyway)
1952 Window
* window
= nextFocus
;
1954 window
= window
->NextWindow(fCurrentWorkspace
);
1955 if (window
== NULL
|| window
->Feel() == kWindowScreenFeel
)
1959 hasWindowScreen
= true;
1962 if (nextFocus
== fFocus
&& nextFocus
!= NULL
&& !nextFocus
->IsHidden()
1963 && (nextFocus
->Flags() & B_AVOID_FOCUS
) == 0
1964 && !hasModal
&& !hasWindowScreen
) {
1965 // the window that is supposed to get focus already has focus
1970 uint32 listIndex
= fCurrentWorkspace
;
1971 WindowList
* list
= &_Windows(fCurrentWorkspace
);
1972 if (!fSettings
->NormalMouse()) {
1973 listIndex
= kFocusList
;
1977 if (nextFocus
== NULL
|| hasModal
|| hasWindowScreen
) {
1978 nextFocus
= list
->LastWindow();
1980 if (fSettings
->NormalMouse()) {
1981 // If the last window having focus is a window that cannot make it
1982 // to the front, we use that as the next focus
1983 Window
* lastFocus
= fFocusList
.LastWindow();
1984 if (lastFocus
!= NULL
&& !lastFocus
->SupportsFront()
1985 && _WindowCanHaveFocus(lastFocus
)) {
1986 nextFocus
= lastFocus
;
1991 // make sure no window is chosen that doesn't want focus or cannot have it
1992 while (nextFocus
!= NULL
&& !_WindowCanHaveFocus(nextFocus
)) {
1993 nextFocus
= nextFocus
->PreviousWindow(listIndex
);
1996 if (fFocus
== nextFocus
) {
1997 // turns out the window that is supposed to get focus now already has it
2002 team_id oldActiveApp
= -1;
2003 team_id newActiveApp
= -1;
2005 if (fFocus
!= NULL
) {
2006 fFocus
->SetFocus(false);
2007 oldActiveApp
= fFocus
->ServerWindow()->App()->ClientTeam();
2012 if (fFocus
!= NULL
) {
2013 fFocus
->SetFocus(true);
2014 newActiveApp
= fFocus
->ServerWindow()->App()->ClientTeam();
2016 // move current focus to the end of the focus list
2017 fFocusList
.RemoveWindow(fFocus
);
2018 fFocusList
.AddWindow(fFocus
);
2021 if (newActiveApp
== -1) {
2022 // make sure the cursor is visible
2023 HWInterface()->SetCursorVisible(true);
2028 // change the "active" app if appropriate
2029 if (oldActiveApp
== newActiveApp
)
2032 BAutolock
locker(fApplicationsLock
);
2034 for (int32 i
= 0; i
< fApplications
.CountItems(); i
++) {
2035 ServerApp
* app
= fApplications
.ItemAt(i
);
2037 if (oldActiveApp
!= -1 && app
->ClientTeam() == oldActiveApp
)
2038 app
->Activate(false);
2039 else if (newActiveApp
!= -1 && app
->ClientTeam() == newActiveApp
)
2040 app
->Activate(true);
2046 Desktop::SetFocusLocked(const Window
* window
)
2048 AutoWriteLocker
_(fWindowLock
);
2050 if (window
!= NULL
) {
2051 // Don't allow this to be set when no mouse buttons
2052 // are pressed. (BView::SetMouseEventMask() should only be called
2053 // from mouse hooks.)
2054 if (fLastMouseButtons
== 0)
2058 fLockedFocusWindow
= window
;
2063 Desktop::FindWindowByClientToken(int32 token
, team_id teamID
)
2065 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2066 window
= window
->NextWindow(kAllWindowList
)) {
2067 if (window
->ServerWindow()->ClientToken() == token
2068 && window
->ServerWindow()->ClientTeam() == teamID
) {
2078 Desktop::FindTarget(BMessenger
& messenger
)
2080 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2081 window
= window
->NextWindow(kAllWindowList
)) {
2082 if (window
->EventTarget().Messenger() == messenger
)
2083 return &window
->EventTarget();
2091 Desktop::MarkDirty(BRegion
& region
)
2093 if (region
.CountRects() == 0)
2096 if (LockAllWindows()) {
2097 // send redraw messages to all windows intersecting the dirty region
2098 _TriggerWindowRedrawing(region
);
2108 BRegion
dirty(fVirtualScreen
.Frame());
2113 /*! \brief Redraws the background (ie. the desktop window, if any).
2116 Desktop::RedrawBackground()
2122 Window
* window
= CurrentWindows().FirstWindow();
2123 if (window
!= NULL
&& window
->Feel() == kDesktopWindowFeel
) {
2124 redraw
= window
->VisibleContentRegion();
2126 // look for desktop background view, and update its background color
2127 // TODO: is there a better way to do this?
2128 View
* view
= window
->TopView();
2130 view
= view
->FirstChild();
2132 while (view
!= NULL
) {
2133 if (view
->IsDesktopBackground()) {
2134 view
->SetViewColor(fWorkspaces
[fCurrentWorkspace
].Color());
2137 view
= view
->NextSibling();
2140 window
->ProcessDirtyRegion(redraw
);
2142 redraw
= BackgroundRegion();
2143 fBackgroundRegion
.MakeEmpty();
2144 _SetBackground(redraw
);
2147 _WindowChanged(NULL
);
2148 // update workspaces view as well
2155 Desktop::ReloadDecor(DecorAddOn
* oldDecor
)
2157 AutoWriteLocker
_(fWindowLock
);
2159 bool returnValue
= true;
2161 if (oldDecor
!= NULL
) {
2162 const DesktopListenerList
* oldListeners
2163 = &oldDecor
->GetDesktopListeners();
2164 for (int i
= 0; i
< oldListeners
->CountItems(); i
++)
2165 UnregisterListener(oldListeners
->ItemAt(i
));
2168 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2169 window
= window
->NextWindow(kAllWindowList
)) {
2171 window
->GetBorderRegion(&oldBorder
);
2173 if (!window
->ReloadDecor()) {
2174 // prevent unloading previous add-on
2175 returnValue
= false;
2179 window
->GetBorderRegion(&border
);
2181 border
.Include(&oldBorder
);
2182 RebuildAndRedrawAfterWindowChange(window
, border
);
2185 // register new listeners
2186 const DesktopListenerList
& newListeners
2187 = gDecorManager
.GetDesktopListeners();
2188 for (int i
= 0; i
< newListeners
.CountItems(); i
++)
2189 RegisterListener(newListeners
.ItemAt(i
));
2196 Desktop::MinimizeApplication(team_id team
)
2198 AutoWriteLocker
locker(fWindowLock
);
2200 // Just minimize all windows of that application
2202 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2203 window
= window
->NextWindow(kAllWindowList
)) {
2204 if (window
->ServerWindow()->ClientTeam() != team
)
2207 window
->ServerWindow()->NotifyMinimize(true);
2213 Desktop::BringApplicationToFront(team_id team
)
2215 AutoWriteLocker
locker(fWindowLock
);
2217 // TODO: for now, just maximize all windows of that application
2218 // TODO: have the ability to lock the current workspace
2220 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2221 window
= window
->NextWindow(kAllWindowList
)) {
2222 if (window
->ServerWindow()->ClientTeam() != team
)
2225 window
->ServerWindow()->NotifyMinimize(false);
2231 Desktop::WindowAction(int32 windowToken
, int32 action
)
2233 if (action
!= B_MINIMIZE_WINDOW
&& action
!= B_BRING_TO_FRONT
)
2238 ::ServerWindow
* serverWindow
;
2240 if (BPrivate::gDefaultTokens
.GetToken(windowToken
,
2241 B_SERVER_TOKEN
, (void**)&serverWindow
) != B_OK
2242 || (window
= serverWindow
->Window()) == NULL
) {
2247 if (action
== B_BRING_TO_FRONT
&& !window
->IsMinimized()) {
2248 // the window is visible, we just need to make it the front window
2249 ActivateWindow(window
);
2251 // if not, ask the window if it wants to be unminimized
2252 serverWindow
->NotifyMinimize(action
== B_MINIMIZE_WINDOW
);
2260 Desktop::WriteWindowList(team_id team
, BPrivate::LinkSender
& sender
)
2262 AutoWriteLocker
locker(fWindowLock
);
2264 // compute the number of windows
2268 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2269 window
= window
->NextWindow(kAllWindowList
)) {
2270 if (team
< B_OK
|| window
->ServerWindow()->ClientTeam() == team
)
2276 sender
.StartMessage(B_OK
);
2277 sender
.Attach
<int32
>(count
);
2279 // first write the windows of the current workspace correctly ordered
2280 for (Window
*window
= CurrentWindows().LastWindow(); window
!= NULL
;
2281 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
2282 if (team
>= B_OK
&& window
->ServerWindow()->ClientTeam() != team
)
2285 sender
.Attach
<int32
>(window
->ServerWindow()->ServerToken());
2288 // then write all the other windows
2289 for (Window
*window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2290 window
= window
->NextWindow(kAllWindowList
)) {
2291 if ((team
>= B_OK
&& window
->ServerWindow()->ClientTeam() != team
)
2292 || window
->InWorkspace(fCurrentWorkspace
))
2295 sender
.Attach
<int32
>(window
->ServerWindow()->ServerToken());
2303 Desktop::WriteWindowInfo(int32 serverToken
, BPrivate::LinkSender
& sender
)
2305 AutoWriteLocker
locker(fWindowLock
);
2306 BAutolock
tokenLocker(BPrivate::gDefaultTokens
);
2308 ::ServerWindow
* window
;
2309 if (BPrivate::gDefaultTokens
.GetToken(serverToken
,
2310 B_SERVER_TOKEN
, (void**)&window
) != B_OK
) {
2311 sender
.StartMessage(B_ENTRY_NOT_FOUND
);
2317 window
->GetInfo(info
);
2319 float tabSize
= 0.0;
2320 float borderSize
= 0.0;
2321 ::Window
* tmp
= window
->Window();
2324 if (tmp
->GetDecoratorSettings(&message
)) {
2326 message
.FindRect("tab frame", &tabFrame
);
2327 tabSize
= tabFrame
.bottom
- tabFrame
.top
;
2328 message
.FindFloat("border width", &borderSize
);
2332 int32 length
= window
->Title() ? strlen(window
->Title()) : 0;
2334 sender
.StartMessage(B_OK
);
2335 sender
.Attach
<int32
>(sizeof(client_window_info
) + length
);
2336 sender
.Attach(&info
, sizeof(window_info
));
2337 sender
.Attach
<float>(tabSize
);
2338 sender
.Attach
<float>(borderSize
);
2341 sender
.Attach(window
->Title(), length
+ 1);
2343 sender
.Attach
<char>('\0');
2350 Desktop::WriteWindowOrder(int32 workspace
, BPrivate::LinkSender
& sender
)
2355 workspace
= fCurrentWorkspace
;
2356 else if (workspace
>= kMaxWorkspaces
) {
2357 sender
.StartMessage(B_BAD_VALUE
);
2359 UnlockSingleWindow();
2363 int32 count
= _Windows(workspace
).Count();
2367 sender
.StartMessage(B_OK
);
2368 sender
.Attach
<int32
>(count
);
2370 for (Window
*window
= _Windows(workspace
).LastWindow(); window
!= NULL
;
2371 window
= window
->PreviousWindow(workspace
)) {
2372 sender
.Attach
<int32
>(window
->ServerWindow()->ServerToken());
2377 UnlockSingleWindow();
2382 Desktop::WriteApplicationOrder(int32 workspace
, BPrivate::LinkSender
& sender
)
2384 fApplicationsLock
.Lock();
2387 int32 maxCount
= fApplications
.CountItems();
2389 fApplicationsLock
.Unlock();
2390 // as long as we hold the window lock, no new window can appear
2393 workspace
= fCurrentWorkspace
;
2394 else if (workspace
>= kMaxWorkspaces
) {
2395 sender
.StartMessage(B_BAD_VALUE
);
2397 UnlockSingleWindow();
2401 // compute the list of applications on this workspace
2403 team_id
* teams
= (team_id
*)malloc(maxCount
* sizeof(team_id
));
2404 if (teams
== NULL
) {
2405 sender
.StartMessage(B_NO_MEMORY
);
2407 UnlockSingleWindow();
2413 for (Window
*window
= _Windows(workspace
).LastWindow(); window
!= NULL
;
2414 window
= window
->PreviousWindow(workspace
)) {
2415 team_id team
= window
->ServerWindow()->ClientTeam();
2417 // see if we already have this team
2419 for (int32 i
= 0; i
< count
; i
++) {
2420 if (teams
[i
] == team
) {
2429 ASSERT(count
< maxCount
);
2430 teams
[count
++] = team
;
2433 UnlockSingleWindow();
2437 sender
.StartMessage(B_OK
);
2438 sender
.Attach
<int32
>(count
);
2440 for (int32 i
= 0; i
< count
; i
++) {
2441 sender
.Attach
<int32
>(teams
[i
]);
2450 Desktop::_LaunchInputServer()
2453 status_t status
= roster
.Launch("application/x-vnd.Be-input_server");
2454 if (status
== B_OK
|| status
== B_ALREADY_RUNNING
)
2457 // Could not load input_server by signature, try well-known location
2460 BPath inputServerPath
;
2461 if (find_directory(B_SYSTEM_SERVERS_DIRECTORY
, &inputServerPath
) == B_OK
2462 && inputServerPath
.Append("input_server") == B_OK
) {
2463 entry
.SetTo(inputServerPath
.Path());
2465 entry
.SetTo("/system/servers/input_server");
2467 status_t entryStatus
= entry
.GetRef(&ref
);
2468 if (entryStatus
== B_OK
)
2469 entryStatus
= roster
.Launch(&ref
);
2470 if (entryStatus
== B_OK
|| entryStatus
== B_ALREADY_RUNNING
) {
2471 syslog(LOG_ERR
, "Failed to launch the input server by signature: %s!\n",
2476 syslog(LOG_ERR
, "Failed to launch the input server: %s!\n",
2477 strerror(entryStatus
));
2482 Desktop::_GetLooperName(char* name
, size_t length
)
2484 snprintf(name
, length
, "d:%d:%s", fUserID
,
2485 fTargetScreen
== NULL
? "baron" : fTargetScreen
);
2490 Desktop::_PrepareQuit()
2492 // let's kill all remaining applications
2494 fApplicationsLock
.Lock();
2496 int32 count
= fApplications
.CountItems();
2497 for (int32 i
= 0; i
< count
; i
++) {
2498 ServerApp
*app
= fApplications
.ItemAt(i
);
2499 team_id clientTeam
= app
->ClientTeam();
2502 kill_team(clientTeam
);
2505 // wait for the last app to die
2507 acquire_sem_etc(fShutdownSemaphore
, fShutdownCount
, B_RELATIVE_TIMEOUT
,
2511 fApplicationsLock
.Unlock();
2516 Desktop::_DispatchMessage(int32 code
, BPrivate::LinkReceiver
& link
)
2521 // Create the ServerApp to node monitor a new BApplication
2524 // 1) port_id - receiver port of a regular app
2525 // 2) port_id - client looper port - for sending messages to the
2527 // 2) team_id - app's team ID
2528 // 3) int32 - handler token of the regular app
2529 // 4) char * - signature of the regular app
2531 // Find the necessary data
2532 team_id clientTeamID
= -1;
2533 port_id clientLooperPort
= -1;
2534 port_id clientReplyPort
= -1;
2535 int32 htoken
= B_NULL_TOKEN
;
2536 char* appSignature
= NULL
;
2538 link
.Read
<port_id
>(&clientReplyPort
);
2539 link
.Read
<port_id
>(&clientLooperPort
);
2540 link
.Read
<team_id
>(&clientTeamID
);
2541 link
.Read
<int32
>(&htoken
);
2542 if (link
.ReadString(&appSignature
) != B_OK
)
2545 ServerApp
* app
= new (std::nothrow
) ServerApp(this, clientReplyPort
,
2546 clientLooperPort
, clientTeamID
, htoken
, appSignature
);
2547 status_t status
= B_OK
;
2549 status
= B_NO_MEMORY
;
2551 status
= app
->InitCheck();
2553 status
= app
->Run();
2554 if (status
== B_OK
) {
2555 // add the new ServerApp to the known list of ServerApps
2556 fApplicationsLock
.Lock();
2557 fApplications
.AddItem(app
);
2558 fApplicationsLock
.Unlock();
2562 // if everything went well, ServerApp::Run() will notify
2563 // the client - but since it didn't, we do it here
2564 BPrivate::LinkSender
reply(clientReplyPort
);
2565 reply
.StartMessage(status
);
2569 // This is necessary because BPortLink::ReadString allocates memory
2576 // Delete a ServerApp. Received only from the respective ServerApp
2577 // when a BApplication asks it to quit.
2580 // 1) thread_id - thread ID of the ServerApp to be deleted
2582 thread_id thread
= -1;
2583 if (link
.Read
<thread_id
>(&thread
) < B_OK
)
2586 fApplicationsLock
.Lock();
2588 // Run through the list of apps and nuke the proper one
2590 int32 count
= fApplications
.CountItems();
2591 ServerApp
* removeApp
= NULL
;
2593 for (int32 i
= 0; i
< count
; i
++) {
2594 ServerApp
* app
= fApplications
.ItemAt(i
);
2596 if (app
->Thread() == thread
) {
2597 fApplications
.RemoveItemAt(i
);
2603 fApplicationsLock
.Unlock();
2605 if (removeApp
!= NULL
)
2606 removeApp
->Quit(fShutdownSemaphore
);
2608 if (fQuitting
&& count
<= 1) {
2609 // wait for the last app to die
2610 acquire_sem_etc(fShutdownSemaphore
, fShutdownCount
,
2611 B_RELATIVE_TIMEOUT
, 500000);
2612 PostMessage(kMsgQuitLooper
);
2617 case AS_ACTIVATE_APP
:
2619 // Someone is requesting to activation of a certain app.
2622 // 1) port_id reply port
2627 // get the parameters
2630 if (link
.Read(&replyPort
) == B_OK
2631 && link
.Read(&team
) == B_OK
)
2632 status
= _ActivateApp(team
);
2637 BPrivate::PortLink
replyLink(replyPort
);
2638 replyLink
.StartMessage(status
);
2643 case AS_APP_CRASHED
:
2644 case AS_DUMP_ALLOCATOR
:
2645 case AS_DUMP_BITMAPS
:
2647 BAutolock
locker(fApplicationsLock
);
2650 if (link
.Read(&team
) != B_OK
)
2653 for (int32 i
= 0; i
< fApplications
.CountItems(); i
++) {
2654 ServerApp
* app
= fApplications
.ItemAt(i
);
2656 if (app
->ClientTeam() == team
)
2657 app
->PostMessage(code
);
2662 case AS_EVENT_STREAM_CLOSED
:
2663 _LaunchInputServer();
2666 case B_QUIT_REQUESTED
:
2667 // We've been asked to quit, so (for now) broadcast to all
2668 // test apps to quit. This situation will occur only when the
2669 // server is compiled as a regular Be application.
2671 fApplicationsLock
.Lock();
2672 fShutdownSemaphore
= create_sem(0, "desktop shutdown");
2673 fShutdownCount
= fApplications
.CountItems();
2674 fApplicationsLock
.Unlock();
2677 BroadcastToAllApps(AS_QUIT_APP
);
2679 // We now need to process the remaining AS_DELETE_APP messages and
2680 // wait for the kMsgShutdownServer message.
2681 // If an application does not quit as asked, the picasso thread
2682 // will send us this message in 2-3 seconds.
2684 // if there are no apps to quit, shutdown directly
2685 if (fShutdownCount
== 0)
2686 PostMessage(kMsgQuitLooper
);
2689 case AS_ACTIVATE_WORKSPACE
:
2692 link
.Read
<int32
>(&index
);
2694 index
= fPreviousWorkspace
;
2696 bool moveFocusWindow
;
2697 link
.Read
<bool>(&moveFocusWindow
);
2699 SetWorkspace(index
, moveFocusWindow
);
2703 case AS_TALK_TO_DESKTOP_LISTENER
:
2705 port_id clientReplyPort
;
2706 if (link
.Read
<port_id
>(&clientReplyPort
) != B_OK
)
2709 BPrivate::LinkSender
reply(clientReplyPort
);
2710 AutoWriteLocker
locker(fWindowLock
);
2711 if (MessageForListener(NULL
, link
, reply
) != true) {
2712 // unhandled message, at least send an error if needed
2713 if (link
.NeedsReply()) {
2714 reply
.StartMessage(B_ERROR
);
2721 case AS_SET_UI_COLOR
:
2726 if (link
.Read
<color_which
>(&which
) == B_OK
2727 && link
.Read
<rgb_color
>(&color
) == B_OK
) {
2729 const char* colorName
= ui_color_name(which
);
2730 fPendingColors
.SetColor(colorName
, color
);
2732 DelayedMessage
delayed(AS_SET_UI_COLORS
, DM_60HZ_DELAY
);
2733 delayed
.AddTarget(MessagePort());
2734 delayed
.SetMerge(DM_MERGE_CANCEL
);
2736 delayed
.Attach
<bool>(true);
2743 case AS_SET_UI_COLORS
:
2745 bool flushPendingOnly
= false;
2747 if (link
.Read
<bool>(&flushPendingOnly
) != B_OK
2748 || (flushPendingOnly
&&
2749 fPendingColors
.CountNames(B_RGB_32_BIT_TYPE
) == 0)) {
2753 if (!flushPendingOnly
) {
2754 // Client wants to set a color map
2755 color_which which
= B_NO_COLOR
;
2759 if (link
.Read
<color_which
>(&which
) != B_OK
2760 || link
.Read
<rgb_color
>(&color
) != B_OK
)
2763 fPendingColors
.SetColor(ui_color_name(which
), color
);
2764 } while (which
!= B_NO_COLOR
);
2767 _FlushPendingColors();
2771 // ToDo: Remove this again. It is a message sent by the
2772 // invalidate_on_exit kernel debugger add-on to trigger a redraw
2773 // after exiting a kernel debugger session.
2777 dirty
.Include(fVirtualScreen
.Frame());
2783 printf("Desktop %d:%s received unexpected code %" B_PRId32
"\n", 0,
2786 if (link
.NeedsReply()) {
2787 // the client is now blocking and waiting for a reply!
2788 fLink
.StartMessage(B_ERROR
);
2797 Desktop::CurrentWindows()
2799 return fWorkspaces
[fCurrentWorkspace
].Windows();
2804 Desktop::AllWindows()
2811 Desktop::WindowForClientLooperPort(port_id port
)
2813 ASSERT_MULTI_LOCKED(fWindowLock
);
2815 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2816 window
= window
->NextWindow(kAllWindowList
)) {
2817 if (window
->ServerWindow()->ClientLooperPort() == port
)
2825 Desktop::_Windows(int32 index
)
2827 ASSERT(index
>= 0 && index
< kMaxWorkspaces
);
2828 return fWorkspaces
[index
].Windows();
2833 Desktop::_FlushPendingColors()
2835 // Update all windows while we are holding the write lock.
2837 int32 count
= fPendingColors
.CountNames(B_RGB_32_BIT_TYPE
);
2841 bool changed
[count
];
2842 LockedDesktopSettings
settings(this);
2843 settings
.SetUIColors(fPendingColors
, &changed
[0]);
2847 type_code type
= B_RGB_32_BIT_TYPE
;
2849 color_which which
= B_NO_COLOR
;
2850 BMessage
clientMessage(B_COLORS_UPDATED
);
2852 while (fPendingColors
.GetInfo(type
, index
, &name
, &type
) == B_OK
) {
2853 which
= which_ui_color(name
);
2854 if (which
== B_NO_COLOR
|| fPendingColors
.FindColor(name
,
2855 &color
) != B_OK
|| !changed
[index
]) {
2860 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
2861 window
= window
->NextWindow(kAllWindowList
)) {
2862 ColorUpdated(window
, which
, color
);
2865 // Ensure client only gets list of changed colors
2866 clientMessage
.AddColor(name
, color
);
2870 // Notify client applications
2871 BAutolock
appListLock(fApplicationsLock
);
2872 for (int32 index
= 0; index
< fApplications
.CountItems(); ++index
) {
2873 fApplications
.ItemAt(index
)->SendMessageToClient(&clientMessage
);
2876 fPendingColors
.MakeEmpty();
2881 Desktop::_UpdateFloating(int32 previousWorkspace
, int32 nextWorkspace
,
2882 Window
* mouseEventWindow
)
2884 if (previousWorkspace
== -1)
2885 previousWorkspace
= fCurrentWorkspace
;
2886 if (nextWorkspace
== -1)
2887 nextWorkspace
= previousWorkspace
;
2889 for (Window
* floating
= fSubsetWindows
.FirstWindow(); floating
!= NULL
;
2890 floating
= floating
->NextWindow(kSubsetList
)) {
2891 // we only care about app/subset floating windows
2892 if (floating
->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
2893 && floating
->Feel() != B_FLOATING_APP_WINDOW_FEEL
)
2896 if (fFront
!= NULL
&& fFront
->IsNormal()
2897 && floating
->HasInSubset(fFront
)) {
2899 if (_Windows(previousWorkspace
).HasWindow(floating
)
2900 && previousWorkspace
!= nextWorkspace
2901 && !floating
->InSubsetWorkspace(previousWorkspace
)) {
2902 // but no longer on the previous workspace
2903 _Windows(previousWorkspace
).RemoveWindow(floating
);
2904 floating
->SetCurrentWorkspace(-1);
2907 if (!_Windows(nextWorkspace
).HasWindow(floating
)) {
2908 // but wasn't before
2909 _Windows(nextWorkspace
).AddWindow(floating
,
2910 floating
->Frontmost(_Windows(nextWorkspace
).FirstWindow(),
2912 floating
->SetCurrentWorkspace(nextWorkspace
);
2913 if (mouseEventWindow
!= fFront
)
2914 _ShowWindow(floating
);
2916 // TODO: put the floating last in the floating window list to
2917 // preserve the on screen window order
2919 } else if (_Windows(previousWorkspace
).HasWindow(floating
)
2920 && !floating
->InSubsetWorkspace(previousWorkspace
)) {
2921 // was visible, but is no longer
2923 _Windows(previousWorkspace
).RemoveWindow(floating
);
2924 floating
->SetCurrentWorkspace(-1);
2925 _HideWindow(floating
);
2927 if (FocusWindow() == floating
)
2934 /*! Search the visible windows for a valid back window
2935 (only desktop windows can't be back windows)
2938 Desktop::_UpdateBack()
2942 for (Window
* window
= CurrentWindows().FirstWindow(); window
!= NULL
;
2943 window
= window
->NextWindow(fCurrentWorkspace
)) {
2944 if (window
->IsHidden() || window
->Feel() == kDesktopWindowFeel
)
2953 /*! Search the visible windows for a valid front window
2954 (only normal and modal windows can be front windows)
2956 The only place where you don't want to update floating windows is
2957 during a workspace change - because then you'll call _UpdateFloating()
2961 Desktop::_UpdateFront(bool updateFloating
)
2965 for (Window
* window
= CurrentWindows().LastWindow(); window
!= NULL
;
2966 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
2967 if (window
->IsHidden() || window
->IsFloating()
2968 || !window
->SupportsFront())
2981 Desktop::_UpdateFronts(bool updateFloating
)
2984 _UpdateFront(updateFloating
);
2989 Desktop::_WindowHasModal(Window
* window
) const
2994 for (Window
* modal
= fSubsetWindows
.FirstWindow(); modal
!= NULL
;
2995 modal
= modal
->NextWindow(kSubsetList
)) {
2996 // only visible modal windows count
2997 if (!modal
->IsModal() || modal
->IsHidden())
3000 if (modal
->HasInSubset(window
))
3008 /*! Determines whether or not the specified \a window can have focus at all.
3011 Desktop::_WindowCanHaveFocus(Window
* window
) const
3013 return window
!= NULL
3014 && window
->InWorkspace(fCurrentWorkspace
)
3015 && (window
->Flags() & B_AVOID_FOCUS
) == 0
3016 && !_WindowHasModal(window
)
3017 && !window
->IsHidden();
3021 /*! You must at least hold a single window lock when calling this method.
3024 Desktop::_WindowChanged(Window
* window
)
3026 ASSERT_MULTI_LOCKED(fWindowLock
);
3028 BAutolock
_(fWorkspacesLock
);
3030 for (uint32 i
= fWorkspacesViews
.CountItems(); i
-- > 0;) {
3031 WorkspacesView
* view
= fWorkspacesViews
.ItemAt(i
);
3032 view
->WindowChanged(window
);
3037 /*! You must at least hold a single window lock when calling this method.
3040 Desktop::_WindowRemoved(Window
* window
)
3042 ASSERT_MULTI_LOCKED(fWindowLock
);
3044 BAutolock
_(fWorkspacesLock
);
3046 for (uint32 i
= fWorkspacesViews
.CountItems(); i
-- > 0;) {
3047 WorkspacesView
* view
= fWorkspacesViews
.ItemAt(i
);
3048 view
->WindowRemoved(window
);
3053 /*! Shows the window on the screen - it does this independently of the
3054 Window::IsHidden() state.
3057 Desktop::_ShowWindow(Window
* window
, bool affectsOtherWindows
)
3060 _RebuildClippingForAllWindows(background
);
3061 _SetBackground(background
);
3062 _WindowChanged(window
);
3064 BRegion
dirty(window
->VisibleRegion());
3066 if (!affectsOtherWindows
) {
3067 // everything that is now visible in the
3068 // window needs a redraw, but other windows
3069 // are not affected, we can call ProcessDirtyRegion()
3070 // of the window, and don't have to use MarkDirty()
3071 window
->ProcessDirtyRegion(dirty
);
3075 if (window
->ServerWindow()->HasDirectFrameBufferAccess()) {
3076 window
->ServerWindow()->HandleDirectConnection(
3077 B_DIRECT_START
| B_BUFFER_RESET
);
3082 /*! Hides the window from the screen - it does this independently of the
3083 Window::IsHidden() state.
3086 Desktop::_HideWindow(Window
* window
)
3088 if (window
->ServerWindow()->IsDirectlyAccessing())
3089 window
->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP
);
3091 // after rebuilding the clipping,
3092 // this window will not have a visible
3093 // region anymore, so we need to remember
3095 // (actually that's not true, since
3096 // hidden windows are excluded from the
3097 // clipping calculation, but anyways)
3098 BRegion
dirty(window
->VisibleRegion());
3101 _RebuildClippingForAllWindows(background
);
3102 _SetBackground(background
);
3103 _WindowChanged(window
);
3109 /*! Updates the workspaces of all subset windows with regard to the
3111 If newIndex is not -1, it will move all subset windows that belong to
3112 the specifed window to the new workspace; this form is only called by
3116 Desktop::_UpdateSubsetWorkspaces(Window
* window
, int32 previousIndex
,
3119 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window
,
3122 // if the window is hidden, the subset windows are up-to-date already
3123 if (!window
->IsNormal() || window
->IsHidden())
3126 for (Window
* subset
= fSubsetWindows
.FirstWindow(); subset
!= NULL
;
3127 subset
= subset
->NextWindow(kSubsetList
)) {
3128 if (subset
->Feel() == B_MODAL_ALL_WINDOW_FEEL
3129 || subset
->Feel() == B_FLOATING_ALL_WINDOW_FEEL
) {
3130 // These windows are always visible on all workspaces,
3131 // no need to update them.
3135 if (subset
->IsFloating()) {
3136 // Floating windows are inserted and removed to the current
3137 // workspace as the need arises - they are not handled here
3138 // but in _UpdateFront()
3142 if (subset
->HasInSubset(window
)) {
3143 // adopt the workspace change
3144 SetWindowWorkspaces(subset
, subset
->SubsetWorkspaces());
3150 /*! \brief Adds or removes the window to or from the workspaces it's on.
3153 Desktop::_ChangeWindowWorkspaces(Window
* window
, uint32 oldWorkspaces
,
3154 uint32 newWorkspaces
)
3156 if (oldWorkspaces
== newWorkspaces
)
3159 // apply changes to the workspaces' window lists
3163 // NOTE: we bypass the anchor-mechanism by intention when switching
3164 // the workspace programmatically.
3166 for (int32 i
= 0; i
< kMaxWorkspaces
; i
++) {
3167 if (workspace_in_workspaces(i
, oldWorkspaces
)) {
3168 // window is on this workspace, is it anymore?
3169 if (!workspace_in_workspaces(i
, newWorkspaces
)) {
3170 _Windows(i
).RemoveWindow(window
);
3171 if (fLastWorkspaceFocus
[i
] == window
)
3172 fLastWorkspaceFocus
[i
] = NULL
;
3174 if (i
== CurrentWorkspace()) {
3175 // remove its appearance from the current workspace
3176 window
->SetCurrentWorkspace(-1);
3178 if (!window
->IsHidden())
3179 _HideWindow(window
);
3183 // window was not on this workspace, is it now?
3184 if (workspace_in_workspaces(i
, newWorkspaces
)) {
3185 _Windows(i
).AddWindow(window
,
3186 window
->Frontmost(_Windows(i
).FirstWindow(), i
));
3188 if (i
== CurrentWorkspace()) {
3189 // make the window visible in current workspace
3190 window
->SetCurrentWorkspace(fCurrentWorkspace
);
3192 if (!window
->IsHidden()) {
3193 // This only affects other windows if this window has
3194 // floating or modal windows that need to be shown as
3196 // TODO: take care of this
3197 _ShowWindow(window
, FrontWindow() == window
);
3204 // If the window is visible only on one workspace, we set it's current
3205 // position in that workspace (so that WorkspacesView will find us).
3206 int32 firstWorkspace
= -1;
3207 for (int32 i
= 0; i
< kMaxWorkspaces
; i
++) {
3208 if ((newWorkspaces
& (1L << i
)) != 0) {
3209 if (firstWorkspace
!= -1) {
3210 firstWorkspace
= -1;
3216 if (firstWorkspace
>= 0)
3217 window
->Anchor(firstWorkspace
).position
= window
->Frame().LeftTop();
3219 // take care about modals and floating windows
3220 _UpdateSubsetWorkspaces(window
);
3222 NotifyWindowWorkspacesChanged(window
, newWorkspaces
);
3229 Desktop::_BringWindowsToFront(WindowList
& windows
, int32 list
, bool wereVisible
)
3231 // we don't need to redraw what is currently
3232 // visible of the window
3235 for (Window
* window
= windows
.FirstWindow(); window
!= NULL
;
3236 window
= window
->NextWindow(list
)) {
3238 clean
.Include(&window
->VisibleRegion());
3240 CurrentWindows().AddWindow(window
,
3241 window
->Frontmost(CurrentWindows().FirstWindow(),
3242 fCurrentWorkspace
));
3244 _WindowChanged(window
);
3248 _RebuildClippingForAllWindows(dummy
);
3250 // redraw what became visible of the window(s)
3253 for (Window
* window
= windows
.FirstWindow(); window
!= NULL
;
3254 window
= window
->NextWindow(list
)) {
3255 dirty
.Include(&window
->VisibleRegion());
3258 dirty
.Exclude(&clean
);
3263 if (windows
.FirstWindow() == fBack
|| fBack
== NULL
)
3268 /*! Returns the last focussed non-hidden subset window belonging to the
3269 specified \a window.
3272 Desktop::_LastFocusSubsetWindow(Window
* window
)
3277 for (Window
* front
= fFocusList
.LastWindow(); front
!= NULL
;
3278 front
= front
->PreviousWindow(kFocusList
)) {
3279 if (front
!= window
&& !front
->IsHidden()
3280 && window
->HasInSubset(front
))
3288 /*! \brief Checks whether or not a fake mouse moved message needs to be sent
3289 to the previous mouse window.
3291 You need to have the all window lock held when calling this method.
3294 Desktop::_CheckSendFakeMouseMoved(const Window
* lastWindowUnderMouse
)
3296 Window
* window
= WindowAt(fLastMousePosition
);
3297 return window
!= lastWindowUnderMouse
;
3301 /*! \brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
3302 and also updates the current view under the mouse.
3304 This has only to be done in case the view changed without mouse movement,
3305 ie. because of a workspace change, a closing window, or programmatic window
3308 You must not have locked any windows when calling this method.
3311 Desktop::_SendFakeMouseMoved(Window
* window
)
3313 int32 viewToken
= B_NULL_TOKEN
;
3314 EventTarget
* target
= NULL
;
3319 window
= WindowAt(fLastMousePosition
);
3321 if (window
!= NULL
) {
3323 window
->MouseMoved(&message
, fLastMousePosition
, &viewToken
, true,
3326 if (viewToken
!= B_NULL_TOKEN
)
3327 target
= &window
->EventTarget();
3330 if (viewToken
!= B_NULL_TOKEN
)
3331 SetViewUnderMouse(window
, viewToken
);
3333 SetViewUnderMouse(NULL
, B_NULL_TOKEN
);
3340 EventDispatcher().SendFakeMouseMoved(*target
, viewToken
);
3345 Desktop::_DetermineScreenFor(BRect frame
)
3347 AutoReadLocker
_(fScreenLock
);
3349 // TODO: choose the screen depending on where most of the area is
3350 return fVirtualScreen
.ScreenAt(0);
3355 Desktop::_RebuildClippingForAllWindows(BRegion
& stillAvailableOnScreen
)
3357 // the available region on screen starts with the entire screen area
3358 // each window on the screen will take a portion from that area
3360 // figure out what the entire screen area is
3361 stillAvailableOnScreen
= fScreenRegion
;
3363 // set clipping of each window
3364 for (Window
* window
= CurrentWindows().LastWindow(); window
!= NULL
;
3365 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
3366 if (!window
->IsHidden()) {
3367 window
->SetClipping(&stillAvailableOnScreen
);
3368 window
->SetScreen(_DetermineScreenFor(window
->Frame()));
3370 if (window
->ServerWindow()->IsDirectlyAccessing()) {
3371 window
->ServerWindow()->HandleDirectConnection(
3372 B_DIRECT_MODIFY
| B_CLIPPING_MODIFIED
);
3375 // that windows region is not available on screen anymore
3376 stillAvailableOnScreen
.Exclude(&window
->VisibleRegion());
3383 Desktop::_TriggerWindowRedrawing(BRegion
& newDirtyRegion
)
3385 // send redraw messages to all windows intersecting the dirty region
3386 for (Window
* window
= CurrentWindows().LastWindow(); window
!= NULL
;
3387 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
3388 if (!window
->IsHidden()
3389 && newDirtyRegion
.Intersects(window
->VisibleRegion().Frame()))
3390 window
->ProcessDirtyRegion(newDirtyRegion
);
3396 Desktop::_SetBackground(BRegion
& background
)
3398 // NOTE: the drawing operation is caried out
3399 // in the clipping region rebuild, but it is
3400 // ok actually, because it also avoids trails on
3403 // remember the region not covered by any windows
3404 // and redraw the dirty background
3405 BRegion
dirtyBackground(background
);
3406 dirtyBackground
.Exclude(&fBackgroundRegion
);
3407 dirtyBackground
.IntersectWith(&background
);
3408 fBackgroundRegion
= background
;
3409 if (dirtyBackground
.Frame().IsValid()) {
3410 if (GetDrawingEngine()->LockParallelAccess()) {
3411 GetDrawingEngine()->FillRegion(dirtyBackground
,
3412 fWorkspaces
[fCurrentWorkspace
].Color());
3414 GetDrawingEngine()->UnlockParallelAccess();
3420 //! The all window lock must be held when calling this function.
3422 Desktop::RebuildAndRedrawAfterWindowChange(Window
* changedWindow
,
3425 ASSERT_MULTI_WRITE_LOCKED(fWindowLock
);
3426 if (!changedWindow
->IsVisible() || dirty
.CountRects() == 0)
3429 // The following loop is pretty much a copy of
3430 // _RebuildClippingForAllWindows(), but will also
3431 // take care about restricting our dirty region.
3433 // figure out what the entire screen area is
3434 BRegion
stillAvailableOnScreen(fScreenRegion
);
3436 // set clipping of each window
3437 for (Window
* window
= CurrentWindows().LastWindow(); window
!= NULL
;
3438 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
3439 if (!window
->IsHidden()) {
3440 if (window
== changedWindow
)
3441 dirty
.IntersectWith(&stillAvailableOnScreen
);
3443 window
->SetClipping(&stillAvailableOnScreen
);
3444 window
->SetScreen(_DetermineScreenFor(window
->Frame()));
3446 if (window
->ServerWindow()->IsDirectlyAccessing()) {
3447 window
->ServerWindow()->HandleDirectConnection(
3448 B_DIRECT_MODIFY
| B_CLIPPING_MODIFIED
);
3451 // that windows region is not available on screen anymore
3452 stillAvailableOnScreen
.Exclude(&window
->VisibleRegion());
3456 _SetBackground(stillAvailableOnScreen
);
3457 _WindowChanged(changedWindow
);
3459 _TriggerWindowRedrawing(dirty
);
3463 //! Suspend all windows with direct access to the frame buffer
3465 Desktop::_SuspendDirectFrameBufferAccess()
3467 ASSERT_MULTI_LOCKED(fWindowLock
);
3469 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
3470 window
= window
->NextWindow(kAllWindowList
)) {
3471 if (window
->ServerWindow()->IsDirectlyAccessing())
3472 window
->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP
);
3477 //! Resume all windows with direct access to the frame buffer
3479 Desktop::_ResumeDirectFrameBufferAccess()
3481 ASSERT_MULTI_LOCKED(fWindowLock
);
3483 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
3484 window
= window
->NextWindow(kAllWindowList
)) {
3485 if (window
->IsHidden() || !window
->InWorkspace(fCurrentWorkspace
))
3488 if (window
->ServerWindow()->HasDirectFrameBufferAccess()) {
3489 window
->ServerWindow()->HandleDirectConnection(
3490 B_DIRECT_START
| B_BUFFER_RESET
, B_MODE_CHANGED
);
3497 Desktop::ScreenChanged(Screen
* screen
)
3499 AutoWriteLocker
windowLocker(fWindowLock
);
3501 AutoWriteLocker
screenLocker(fScreenLock
);
3502 screen
->SetPreferredMode();
3503 screenLocker
.Unlock();
3505 _ScreenChanged(screen
);
3510 Desktop::_ScreenChanged(Screen
* screen
)
3512 ASSERT_MULTI_WRITE_LOCKED(fWindowLock
);
3514 // the entire screen is dirty, because we're actually
3515 // operating on an all new buffer in memory
3516 BRegion
dirty(screen
->Frame());
3518 // update our cached screen region
3519 fScreenRegion
.Set(screen
->Frame());
3520 gInputManager
->UpdateScreenBounds(screen
->Frame());
3523 _RebuildClippingForAllWindows(background
);
3525 fBackgroundRegion
.MakeEmpty();
3526 // makes sure that the complete background is redrawn
3527 _SetBackground(background
);
3529 // figure out dirty region
3530 dirty
.Exclude(&background
);
3531 _TriggerWindowRedrawing(dirty
);
3533 // send B_SCREEN_CHANGED to windows on that screen
3534 BMessage
update(B_SCREEN_CHANGED
);
3535 update
.AddInt64("when", real_time_clock_usecs());
3536 update
.AddRect("frame", screen
->Frame());
3537 update
.AddInt32("mode", screen
->ColorSpace());
3539 fVirtualScreen
.UpdateFrame();
3541 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
3542 window
= window
->NextWindow(kAllWindowList
)) {
3543 if (window
->Screen() == screen
)
3544 window
->ServerWindow()->ScreenChanged(&update
);
3549 /*! \brief activate one of the app's windows.
3552 Desktop::_ActivateApp(team_id team
)
3554 // search for an unhidden window in the current workspace
3556 AutoWriteLocker
locker(fWindowLock
);
3558 for (Window
* window
= CurrentWindows().LastWindow(); window
!= NULL
;
3559 window
= window
->PreviousWindow(fCurrentWorkspace
)) {
3560 if (!window
->IsHidden() && window
->IsNormal()
3561 && window
->ServerWindow()->ClientTeam() == team
) {
3562 ActivateWindow(window
);
3567 // search for an unhidden window to give focus to
3569 for (Window
* window
= fAllWindows
.FirstWindow(); window
!= NULL
;
3570 window
= window
->NextWindow(kAllWindowList
)) {
3571 // if window is a normal window of the team, and not hidden,
3572 // we've found our target
3573 if (!window
->IsHidden() && window
->IsNormal()
3574 && window
->ServerWindow()->ClientTeam() == team
) {
3575 ActivateWindow(window
);
3580 // TODO: we cannot maximize minimized windows here (with the window lock
3581 // write locked). To work-around this, we could forward the request to
3582 // the ServerApp of this team - it maintains its own window list, and can
3583 // therefore call ActivateWindow() without holding the window lock.
3589 Desktop::_SetCurrentWorkspaceConfiguration()
3591 ASSERT_MULTI_WRITE_LOCKED(fWindowLock
);
3593 status_t status
= fDirectScreenLock
.LockWithTimeout(1000000L);
3594 if (status
!= B_OK
) {
3595 // The application having the direct screen lock didn't give it up in
3596 // time, make it crash
3597 syslog(LOG_ERR
, "Team %" B_PRId32
" did not give up its direct screen "
3598 "lock.\n", fDirectScreenTeam
);
3600 debug_thread(fDirectScreenTeam
);
3601 fDirectScreenTeam
= -1;
3603 fDirectScreenLock
.Unlock();
3605 AutoWriteLocker
_(fScreenLock
);
3607 uint32 changedScreens
;
3608 fVirtualScreen
.SetConfiguration(*this,
3609 fWorkspaces
[fCurrentWorkspace
].CurrentScreenConfiguration(),
3612 for (int32 i
= 0; changedScreens
!= 0; i
++, changedScreens
/= 2) {
3613 if ((changedScreens
& (1 << i
)) != 0)
3614 _ScreenChanged(fVirtualScreen
.ScreenAt(i
));
3619 /*! Changes the current workspace to the one specified by \a index.
3620 You must hold the all window lock when calling this method.
3623 Desktop::_SetWorkspace(int32 index
, bool moveFocusWindow
)
3625 ASSERT_MULTI_WRITE_LOCKED(fWindowLock
);
3627 int32 previousIndex
= fCurrentWorkspace
;
3628 rgb_color previousColor
= fWorkspaces
[fCurrentWorkspace
].Color();
3629 bool movedMouseEventWindow
= false;
3630 Window
* movedWindow
= NULL
;
3631 if (moveFocusWindow
) {
3632 if (fMouseEventWindow
!= NULL
)
3633 movedWindow
= fMouseEventWindow
;
3635 movedWindow
= FocusWindow();
3638 if (movedWindow
!= NULL
) {
3639 if (movedWindow
->IsNormal()) {
3640 if (!movedWindow
->InWorkspace(index
)) {
3641 // The window currently being dragged will follow us to this
3642 // workspace if it's not already on it.
3643 // But only normal windows are following
3644 uint32 oldWorkspaces
= movedWindow
->Workspaces();
3646 WindowStack
* stack
= movedWindow
->GetWindowStack();
3647 if (stack
!= NULL
) {
3648 for (int32 s
= 0; s
< stack
->CountWindows(); s
++) {
3649 Window
* stackWindow
= stack
->LayerOrder().ItemAt(s
);
3651 _Windows(previousIndex
).RemoveWindow(stackWindow
);
3652 _Windows(index
).AddWindow(stackWindow
,
3653 stackWindow
->Frontmost(
3654 _Windows(index
).FirstWindow(), index
));
3656 // send B_WORKSPACES_CHANGED message
3657 stackWindow
->WorkspacesChanged(oldWorkspaces
,
3658 stackWindow
->Workspaces());
3661 // TODO: subset windows will always flicker this way
3663 movedMouseEventWindow
= true;
3665 NotifyWindowWorkspacesChanged(movedWindow
,
3666 movedWindow
->Workspaces());
3668 // make sure it's frontmost
3669 _Windows(index
).RemoveWindow(movedWindow
);
3670 _Windows(index
).AddWindow(movedWindow
,
3671 movedWindow
->Frontmost(_Windows(index
).FirstWindow(),
3676 movedWindow
->Anchor(index
).position
= movedWindow
->Frame().LeftTop();
3679 if (movedWindow
== NULL
|| movedWindow
->InWorkspace(previousIndex
))
3680 fLastWorkspaceFocus
[previousIndex
] = FocusWindow();
3682 fLastWorkspaceFocus
[previousIndex
] = NULL
;
3684 // build region of windows that are no longer visible in the new workspace
3688 for (Window
* window
= CurrentWindows().FirstWindow();
3689 window
!= NULL
; window
= window
->NextWindow(previousIndex
)) {
3690 // store current position in Workspace anchor
3691 window
->Anchor(previousIndex
).position
= window
->Frame().LeftTop();
3693 if (!window
->IsHidden()
3694 && window
->ServerWindow()->IsDirectlyAccessing())
3695 window
->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP
);
3697 window
->WorkspaceActivated(previousIndex
, false);
3699 if (window
->InWorkspace(index
))
3702 if (!window
->IsHidden()) {
3703 // this window will no longer be visible
3704 dirty
.Include(&window
->VisibleRegion());
3707 window
->SetCurrentWorkspace(-1);
3710 fPreviousWorkspace
= fCurrentWorkspace
;
3711 fCurrentWorkspace
= index
;
3713 // Change the display modes, if needed
3714 _SetCurrentWorkspaceConfiguration();
3716 // Show windows, and include them in the changed region - but only
3717 // those that were not visible before (or whose position changed)
3719 WindowList
windows(kWorkingList
);
3720 BList previousRegions
;
3722 for (Window
* window
= _Windows(index
).FirstWindow();
3723 window
!= NULL
; window
= window
->NextWindow(index
)) {
3724 BPoint position
= window
->Anchor(index
).position
;
3726 window
->SetCurrentWorkspace(index
);
3728 if (window
->IsHidden())
3731 if (position
== kInvalidWindowPosition
) {
3732 // if you enter a workspace for the first time, the position
3733 // of the window in the previous workspace is adopted
3734 position
= window
->Frame().LeftTop();
3735 // TODO: make sure the window is still on-screen if it
3739 if (!window
->InWorkspace(previousIndex
)) {
3740 // This window was not visible before, make sure its frame
3742 if (window
->Frame().LeftTop() != position
) {
3743 BPoint offset
= position
- window
->Frame().LeftTop();
3744 window
->MoveBy((int32
)offset
.x
, (int32
)offset
.y
);
3749 if (window
->Frame().LeftTop() != position
) {
3750 // the window was visible before, but its on-screen location changed
3751 BPoint offset
= position
- window
->Frame().LeftTop();
3752 MoveWindowBy(window
, offset
.x
, offset
.y
);
3753 // TODO: be a bit smarter than this...
3755 // We need to remember the previous visible region of the
3756 // window if they changed their order
3757 BRegion
* region
= new (std::nothrow
)
3758 BRegion(window
->VisibleRegion());
3759 if (region
!= NULL
) {
3760 if (previousRegions
.AddItem(region
))
3761 windows
.AddWindow(window
);
3768 _UpdateFronts(false);
3769 _UpdateFloating(previousIndex
, index
,
3770 movedMouseEventWindow
? movedWindow
: NULL
);
3772 BRegion stillAvailableOnScreen
;
3773 _RebuildClippingForAllWindows(stillAvailableOnScreen
);
3774 _SetBackground(stillAvailableOnScreen
);
3776 for (Window
* window
= _Windows(index
).FirstWindow(); window
!= NULL
;
3777 window
= window
->NextWindow(index
)) {
3778 // send B_WORKSPACE_ACTIVATED message
3779 window
->WorkspaceActivated(index
, true);
3781 if (!window
->IsHidden()
3782 && window
->ServerWindow()->HasDirectFrameBufferAccess()) {
3783 window
->ServerWindow()->HandleDirectConnection(
3784 B_DIRECT_START
| B_BUFFER_RESET
, B_MODE_CHANGED
);
3787 if (window
->InWorkspace(previousIndex
) || window
->IsHidden()
3788 || (window
== movedWindow
&& movedWindow
->IsNormal())
3789 || (!window
->IsNormal()
3790 && window
->HasInSubset(movedWindow
))) {
3791 // This window was visible before, and is already handled in the
3796 dirty
.Include(&window
->VisibleRegion());
3799 // Catch order changes in the new workspaces window list
3801 for (Window
* window
= windows
.FirstWindow(); window
!= NULL
;
3802 window
= window
->NextWindow(kWorkingList
), i
++) {
3803 BRegion
* region
= (BRegion
*)previousRegions
.ItemAt(i
);
3804 region
->ExclusiveInclude(&window
->VisibleRegion());
3805 dirty
.Include(region
);
3809 // Set new focus, but keep focus to a floating window if still visible
3810 if (movedWindow
!= NULL
)
3811 SetFocusWindow(movedWindow
);
3812 else if (!_Windows(index
).HasWindow(FocusWindow())
3813 || (FocusWindow() != NULL
&& !FocusWindow()->IsFloating()))
3814 SetFocusWindow(fLastWorkspaceFocus
[index
]);
3816 _WindowChanged(NULL
);
3820 // Show the dirty regions of this workspace switch
3821 if (GetDrawingEngine()->LockParallelAccess()) {
3822 GetDrawingEngine()->FillRegion(dirty
, (rgb_color
){255, 0, 0});
3823 GetDrawingEngine()->UnlockParallelAccess();
3828 if (previousColor
!= fWorkspaces
[fCurrentWorkspace
].Color())