Make UEFI boot-platform build again
[haiku.git] / src / servers / app / Desktop.cpp
blob962a6e1647d1f5a3a0c06f5f5195283f46998574
1 /*
2 * Copyright 2001-2016, Haiku.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Adrian Oanca <adioanca@cotty.iren.ro>
7 * Stephan Aßmus <superstippi@gmx.de>
8 * Axel Dörfler <axeld@pinc-software.de>
9 * Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>
10 * 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 */
20 #include "Desktop.h"
22 #include <stdio.h>
23 #include <string.h>
24 #include <syslog.h>
26 #include <Debug.h>
27 #include <debugger.h>
28 #include <DirectWindow.h>
29 #include <Entry.h>
30 #include <FindDirectory.h>
31 #include <Message.h>
32 #include <MessageFilter.h>
33 #include <Path.h>
34 #include <Region.h>
35 #include <Roster.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"
50 #include "Screen.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"
57 #include "Window.h"
58 #include "Workspace.h"
59 #include "WorkspacesView.h"
61 #if TEST_MODE
62 # include "EventStream.h"
63 #endif
66 //#define DEBUG_DESKTOP
67 #ifdef DEBUG_DESKTOP
68 # define STRACE(a) printf a
69 #else
70 # define STRACE(a) ;
71 #endif
74 static inline float
75 square_vector_length(float x, float y)
77 return x * x + y * y;
81 static inline float
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 {
89 public:
90 KeyboardFilter(Desktop* desktop);
92 virtual filter_result Filter(BMessage* message, EventTarget** _target,
93 int32* _viewToken, BMessage* latestMouseMoved);
94 virtual void RemoveTarget(EventTarget* target);
96 private:
97 void _UpdateFocus(int32 key, uint32 modifiers, EventTarget** _target);
99 Desktop* fDesktop;
100 EventTarget* fLastFocus;
101 bigtime_t fTimestamp;
105 class MouseFilter : public EventFilter {
106 public:
107 MouseFilter(Desktop* desktop);
109 virtual filter_result Filter(BMessage* message, EventTarget** _target,
110 int32* _viewToken, BMessage* latestMouseMoved);
112 private:
113 Desktop* fDesktop;
114 int32 fLastClickButtons;
115 int32 fLastClickModifiers;
116 int32 fResetClickCount;
117 BPoint fLastClickPoint;
118 ClickTarget fLastClickTarget;
122 // #pragma mark -
125 KeyboardFilter::KeyboardFilter(Desktop* desktop)
127 fDesktop(desktop),
128 fLastFocus(NULL),
129 fTimestamp(0)
134 void
135 KeyboardFilter::_UpdateFocus(int32 key, uint32 modifiers, EventTarget** _target)
137 if (!fDesktop->LockSingleWindow())
138 return;
140 EventTarget* focus = fDesktop->KeyboardEventTarget();
142 #if 0
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)
158 *_target = focus;
159 fLastFocus = focus;
161 #endif
162 *_target = focus;
163 fLastFocus = focus;
165 fDesktop->UnlockSingleWindow();
167 #if 0
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)
171 fTimestamp = 0;
172 else
173 fTimestamp = now;
174 #endif
178 filter_result
179 KeyboardFilter::Filter(BMessage* message, EventTarget** _target,
180 int32* /*_viewToken*/, BMessage* /*latestMouseMoved*/)
182 int32 key = 0;
183 int32 modifiers = 0;
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) {
200 // workspace change
202 #if !TEST_MODE
203 if ((modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY))
204 == B_COMMAND_KEY)
205 #else
206 if ((modifiers & B_CONTROL_KEY) != 0)
207 #endif
209 STRACE(("Set Workspace %" B_PRId32 "\n", key - 1));
211 fDesktop->SetWorkspaceAsync(key - B_F1_KEY, takeWindow);
212 return B_SKIP_MESSAGE;
214 } if (key == 0x11
215 && (modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY))
216 == B_COMMAND_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);
233 void
234 KeyboardFilter::RemoveTarget(EventTarget* target)
236 if (target == fLastFocus)
237 fLastFocus = NULL;
241 // #pragma mark -
244 MouseFilter::MouseFilter(Desktop* desktop)
246 fDesktop(desktop),
247 fLastClickButtons(0),
248 fLastClickModifiers(0),
249 fResetClickCount(0),
250 fLastClickPoint(),
251 fLastClickTarget()
256 filter_result
257 MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken,
258 BMessage* latestMouseMoved)
260 BPoint where;
261 if (message->FindPoint("where", &where) != B_OK)
262 return B_DISPATCH_MESSAGE;
264 int32 buttons;
265 if (message->FindInt32("buttons", &buttons) != B_OK)
266 buttons = 0;
268 if (!fDesktop->LockAllWindows())
269 return B_DISPATCH_MESSAGE;
271 int32 viewToken = B_NULL_TOKEN;
273 Window* window = fDesktop->MouseEventWindow();
274 if (window == NULL)
275 window = fDesktop->WindowAt(where);
277 if (window != NULL) {
278 // dispatch event to the window
279 switch (message->what) {
280 case B_MOUSE_DOWN:
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) {
304 clickCount = 1;
305 } else
306 clickCount -= fResetClickCount;
309 // notify the window
310 ClickTarget clickTarget;
311 window->MouseDown(message, where, fLastClickTarget, clickCount,
312 clickTarget);
314 // If the click target changed, always reset the click count.
315 if (clickCount != 1 && clickTarget != fLastClickTarget)
316 clickCount = 1;
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);
333 else
334 message->AddInt32("clicks", clickCount);
337 // notify desktop listeners
338 fDesktop->NotifyMouseDown(window, message, where);
339 break;
342 case B_MOUSE_UP:
343 window->MouseUp(message, where, &viewToken);
344 if (buttons == 0)
345 fDesktop->SetMouseEventWindow(NULL);
346 fDesktop->NotifyMouseUp(window, message, where);
347 break;
349 case B_MOUSE_MOVED:
350 window->MouseMoved(message, where, &viewToken,
351 latestMouseMoved == NULL || latestMouseMoved == message,
352 false);
353 fDesktop->NotifyMouseMoved(window, message, where);
354 break;
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);
377 *_target = NULL;
380 fDesktop->SetLastMouseState(where, buttons, window);
382 fDesktop->NotifyMouseEvent(message);
384 fDesktop->UnlockAllWindows();
386 return B_DISPATCH_MESSAGE;
390 // #pragma mark -
393 static inline uint32
394 workspace_to_workspaces(int32 index)
396 return 1UL << index;
400 static inline bool
401 workspace_in_workspaces(int32 index, uint32 workspaces)
403 return (workspaces & (1UL << index)) != 0;
407 // #pragma mark -
410 Desktop::Desktop(uid_t userID, const char* targetScreen)
412 MessageLooper("desktop"),
414 fUserID(userID),
415 fTargetScreen(strdup(targetScreen)),
416 fSettings(NULL),
417 fSharedReadOnlyArea(-1),
418 fApplicationsLock("application list"),
419 fShutdownSemaphore(-1),
420 fShutdownCount(0),
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),
441 fFocus(NULL),
442 fFront(NULL),
443 fBack(NULL)
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)
452 return;
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));
466 Desktop::~Desktop()
468 delete fSettings;
470 delete_area(fSharedReadOnlyArea);
471 delete_port(fMessagePort);
472 gFontManager->DetachUser(fUserID);
474 free(fTargetScreen);
478 void
479 Desktop::RegisterListener(DesktopListener* listener)
481 DesktopObservable::RegisterListener(listener, this);
485 /*! This method is allowed to throw exceptions.
487 status_t
488 Desktop::Init()
490 if (fMessagePort < B_OK)
491 return fMessagePort;
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");
519 return B_ERROR;
522 fVirtualScreen.HWInterface()->MoveCursorTo(
523 fVirtualScreen.Frame().Width() / 2,
524 fVirtualScreen.Frame().Height() / 2);
526 #if TEST_MODE
527 gInputManager->AddStream(new InputServerStream);
528 #endif
530 EventStream* stream = fVirtualScreen.HWInterface()->CreateEventStream();
531 if (stream == NULL)
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);
552 SetCursor(NULL);
553 // this will set the default cursor
555 fVirtualScreen.HWInterface()->SetCursorVisible(true);
557 return B_OK;
561 /*! \brief Send a quick (no attachments) message to all applications.
563 Quite useful for notification for things like server shutdown, system
564 color changes, etc.
566 void
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.
579 void
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);
591 int32
592 Desktop::GetAllWindowTargets(DelayedMessage& message)
594 AutoReadLocker _(fWindowLock);
595 int32 count = 0;
597 for (Window* window = fAllWindows.FirstWindow(); window != NULL;
598 window = window->NextWindow(kAllWindowList)) {
599 message.AddTarget(window->ServerWindow()->MessagePort());
600 ++count;
603 return count;
607 int32
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();
619 filter_result
620 Desktop::KeyEvent(uint32 what, int32 key, int32 modifiers)
622 filter_result result = B_DISPATCH_MESSAGE;
623 if (LockAllWindows()) {
624 Window* window = MouseEventWindow();
625 if (window == NULL)
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;
636 UnlockAllWindows();
639 return result;
643 // #pragma mark - Mouse and cursor methods
646 void
647 Desktop::SetCursor(ServerCursor* newCursor)
649 if (newCursor == NULL)
650 newCursor = fCursorManager.GetCursor(B_CURSOR_ID_SYSTEM_DEFAULT);
652 if (newCursor == fCursor)
653 return;
655 fCursor = newCursor;
657 if (fManagementCursor.Get() == NULL)
658 HWInterface()->SetCursor(newCursor);
662 ServerCursorReference
663 Desktop::Cursor() const
665 return fCursor;
669 void
670 Desktop::SetManagementCursor(ServerCursor* newCursor)
672 if (newCursor == fManagementCursor)
673 return;
675 fManagementCursor = newCursor;
677 HWInterface()->SetCursor(newCursor != NULL ? newCursor : fCursor.Get());
681 void
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);
697 void
698 Desktop::GetLastMouseState(BPoint* position, int32* buttons) const
700 *position = fLastMousePosition;
701 *buttons = fLastMouseButtons;
705 // #pragma mark - Screen methods
708 status_t
709 Desktop::SetScreenMode(int32 workspace, int32 id, const display_mode& mode,
710 bool makeDefault)
712 AutoWriteLocker _(fWindowLock);
714 if (workspace == B_CURRENT_WORKSPACE_INDEX)
715 workspace = fCurrentWorkspace;
717 if (workspace < 0 || workspace >= kMaxWorkspaces)
718 return B_BAD_VALUE;
720 Screen* screen = fVirtualScreen.ScreenByID(id);
721 if (screen == NULL)
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)))
732 return B_OK;
734 // Set the new one
736 _SuspendDirectFrameBufferAccess();
738 AutoWriteLocker locker(fScreenLock);
740 status_t status = screen->SetMode(mode);
741 if (status != B_OK) {
742 locker.Unlock();
744 _ResumeDirectFrameBufferAccess();
745 return status;
747 } else {
748 // retrieve from settings
749 screen_configuration* configuration
750 = fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(
751 screen->ID());
752 if (configuration != NULL
753 && !memcmp(&configuration->mode, &mode, sizeof(display_mode)))
754 return B_OK;
757 // Update our configurations
759 monitor_info info;
760 bool hasInfo = screen->GetMonitorInfo(info) == B_OK;
762 fWorkspaces[workspace].CurrentScreenConfiguration().Set(id,
763 hasInfo ? &info : NULL, screen->Frame(), mode);
764 if (makeDefault) {
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();
774 return B_OK;
778 status_t
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)
787 return B_BAD_VALUE;
789 if (workspace == fCurrentWorkspace) {
790 // retrieve from current screen
791 Screen* screen = fVirtualScreen.ScreenByID(id);
792 if (screen == NULL)
793 return B_NAME_NOT_FOUND;
795 screen->GetMode(mode);
796 return B_OK;
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;
806 return B_OK;
810 status_t
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)
819 return B_BAD_VALUE;
821 if (workspace == fCurrentWorkspace) {
822 // retrieve from current screen
823 Screen* screen = fVirtualScreen.ScreenByID(id);
824 if (screen == NULL)
825 return B_NAME_NOT_FOUND;
827 frame = screen->Frame();
828 return B_OK;
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;
838 return B_OK;
842 void
843 Desktop::RevertScreenModes(uint32 workspaces)
845 if (workspaces == 0)
846 return;
848 AutoWriteLocker _(fWindowLock);
850 for (int32 workspace = 0; workspace < kMaxWorkspaces; workspace++) {
851 if ((workspaces & (1U << workspace)) == 0)
852 continue;
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, &current->mode,
870 sizeof(display_mode)))
871 || (stored == NULL && current == NULL))
872 continue;
874 if (stored == NULL) {
875 fWorkspaces[workspace].CurrentScreenConfiguration()
876 .Remove(current);
878 if (workspace == fCurrentWorkspace) {
879 _SuspendDirectFrameBufferAccess();
880 _SetCurrentWorkspaceConfiguration();
881 _ResumeDirectFrameBufferAccess();
883 } else
884 SetScreenMode(workspace, screen->ID(), stored->mode, false);
890 status_t
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);
897 if (status == B_OK)
898 fDirectScreenTeam = team;
900 return status;
904 status_t
905 Desktop::UnlockDirectScreen(team_id team)
907 if (fDirectScreenTeam == team) {
908 fDirectScreenLock.Unlock();
909 fDirectScreenTeam = -1;
910 return B_OK;
913 return B_PERMISSION_DENIED;
917 // #pragma mark - Workspaces methods
920 /*! Changes the current workspace to the one specified by \a index.
922 void
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);
929 link.Flush();
933 /*! Changes the current workspace to the one specified by \a index.
934 You must not hold any window lock when calling this method.
936 void
937 Desktop::SetWorkspace(int32 index, bool moveFocusWindow)
939 LockAllWindows();
940 DesktopSettings settings(this);
942 if (index < 0 || index >= settings.WorkspacesCount()
943 || index == fCurrentWorkspace) {
944 UnlockAllWindows();
945 return;
948 _SetWorkspace(index, moveFocusWindow);
949 UnlockAllWindows();
951 _SendFakeMouseMoved();
955 status_t
956 Desktop::SetWorkspacesLayout(int32 newColumns, int32 newRows)
958 int32 newCount = newColumns * newRows;
959 if (newCount < 1 || newCount > kMaxWorkspaces)
960 return B_BAD_VALUE;
962 if (!LockAllWindows())
963 return B_ERROR;
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);
973 else
974 _WindowChanged(NULL);
976 UnlockAllWindows();
978 if (workspaceChanged)
979 _SendFakeMouseMoved();
981 return B_OK;
985 /*! Returns the virtual screen frame of the workspace specified by \a index.
987 BRect
988 Desktop::WorkspaceFrame(int32 index) const
990 BRect frame;
991 if (index == fCurrentWorkspace)
992 frame = fVirtualScreen.Frame();
993 else if (index >= 0 && index < fSettings->WorkspacesCount()) {
994 BMessage screenData;
995 if (fSettings->WorkspacesMessage(index)->FindMessage("screen",
996 &screenData) != B_OK
997 || screenData.FindRect("frame", &frame) != B_OK) {
998 frame = fVirtualScreen.Frame();
1002 return frame;
1006 /*! \brief Stores the workspace configuration.
1007 You must hold the window lock when calling this method.
1009 void
1010 Desktop::StoreWorkspaceConfiguration(int32 index)
1012 // Retrieve settings
1014 BMessage settings;
1015 fWorkspaces[index].StoreConfiguration(settings);
1017 // and store them
1019 fSettings->SetWorkspacesMessage(index, settings);
1020 fSettings->Save(kWorkspacesSettings);
1024 void
1025 Desktop::AddWorkspacesView(WorkspacesView* view)
1027 if (view->Window() == NULL || view->Window()->IsHidden())
1028 return;
1030 BAutolock _(fWorkspacesLock);
1032 if (!fWorkspacesViews.HasItem(view))
1033 fWorkspacesViews.AddItem(view);
1037 void
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.
1050 void
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);
1059 else
1060 SetFocusWindow(window);
1061 } else
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
1071 of their subset.
1073 void
1074 Desktop::ActivateWindow(Window* window)
1076 STRACE(("ActivateWindow(%p, %s)\n", window, window
1077 ? window->Title() : "<none>"));
1079 if (window == NULL) {
1080 fBack = NULL;
1081 fFront = NULL;
1082 return;
1084 if (window->Workspaces() == 0 && window->IsNormal())
1085 return;
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) {
1101 SetWorkspace(i);
1102 windowOnOtherWorkspace = false;
1103 break;
1106 } else
1107 return;
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.
1116 return;
1119 ActivateWindow(front);
1121 if (!window->InWorkspace(fCurrentWorkspace)) {
1122 // This window can't be made active
1123 return;
1125 } else {
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);
1137 ShowWindow(window);
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
1150 // yet
1151 if ((window->Flags() & B_AVOID_FOCUS) == 0)
1152 SetFocusWindow(window);
1153 return;
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)
1170 Window* nextModal;
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))
1178 nextModal = NULL;
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();
1199 void
1200 Desktop::SendWindowBehind(Window* window, Window* behindOf, bool sendStack)
1202 if (!LockAllWindows())
1203 return;
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))) {
1215 UnlockAllWindows();
1216 return;
1219 // Is this a valid behindOf window?
1220 if (behindOf != NULL && window->HasInSubset(behindOf))
1221 behindOf = NULL;
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());
1234 BRegion dummy;
1235 _RebuildClippingForAllWindows(dummy);
1237 // only redraw the top layer window to avoid flicker
1238 if (sendStack) {
1239 // mark everything dirty that is no longer visible
1240 BRegion clean(window->VisibleRegion());
1241 dirty.Exclude(&clean);
1242 MarkDirty(dirty);
1245 _UpdateFronts();
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)
1257 continue;
1258 SendWindowBehind(stackWindow, behindOf, false);
1262 bool sendFakeMouseMoved = _CheckSendFakeMouseMoved(lastWindowUnderMouse);
1263 NotifyWindowSentBehind(orgWindow, behindOf);
1265 UnlockAllWindows();
1267 if (sendFakeMouseMoved)
1268 _SendFakeMouseMoved();
1272 void
1273 Desktop::ShowWindow(Window* window)
1275 if (!window->IsHidden())
1276 return;
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
1285 // subset is.
1286 if (window->InWorkspace(fCurrentWorkspace)
1287 || (window->IsFloating() && _LastFocusSubsetWindow(window) != NULL)) {
1288 _ShowWindow(window, true);
1289 _UpdateSubsetWorkspaces(window);
1290 ActivateWindow(window);
1291 } else {
1292 // then we don't need to send the fake mouse event either
1293 _WindowChanged(window);
1294 return;
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);
1311 void
1312 Desktop::HideWindow(Window* window, bool fromMinimize)
1314 if (window->IsHidden())
1315 return;
1317 if (!LockAllWindows())
1318 return;
1320 window->SetHidden(true);
1321 fFocusList.RemoveWindow(window);
1323 if (fMouseEventWindow == window) {
1324 // Make its decorator lose the current mouse action
1325 BMessage message;
1326 int32 viewToken;
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);
1340 _UpdateFronts();
1341 } else
1342 _WindowChanged(window);
1344 if (FocusWindow() == window)
1345 SetFocusWindow();
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);
1363 UnlockAllWindows();
1365 if (window == fWindowUnderMouse)
1366 _SendFakeMouseMoved();
1370 void
1371 Desktop::MinimizeWindow(Window* window, bool minimize)
1373 if (!LockAllWindows())
1374 return;
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);
1386 UnlockAllWindows();
1390 void
1391 Desktop::MoveWindowBy(Window* window, float x, float y, int32 workspace)
1393 if (x == 0 && y == 0)
1394 return;
1396 AutoWriteLocker _(fWindowLock);
1398 Window* topWindow = window->TopLayerStackWindow();
1399 if (topWindow != NULL)
1400 window = topWindow;
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);
1423 } else
1424 window->MoveBy((int32)x, (int32)y);
1426 NotifyWindowMoved(window);
1427 return;
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);
1437 direct = true;
1440 window->MoveBy((int32)x, (int32)y);
1442 BRegion background;
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(&copyRegion, (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(&copyRegion);
1468 MarkDirty(newDirtyRegion);
1469 _SetBackground(background);
1470 _WindowChanged(window);
1472 // resume direct frame buffer access
1473 if (direct) {
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);
1484 void
1485 Desktop::ResizeWindowBy(Window* window, float x, float y)
1487 if (x == 0 && y == 0)
1488 return;
1490 AutoWriteLocker _(fWindowLock);
1492 Window* topWindow = window->TopLayerStackWindow();
1493 if (topWindow)
1494 window = topWindow;
1496 if (!window->IsVisible()) {
1497 window->ResizeBy((int32)x, (int32)y, NULL);
1498 NotifyWindowResized(window);
1499 return;
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);
1513 direct = true;
1516 window->ResizeBy((int32)x, (int32)y, &newDirtyRegion);
1518 BRegion background;
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
1535 if (direct) {
1536 window->ServerWindow()->HandleDirectConnection(
1537 B_DIRECT_START | B_BUFFER_RESIZED | B_CLIPPING_MODIFIED);
1540 NotifyWindowResized(window);
1544 bool
1545 Desktop::SetWindowTabLocation(Window* window, float location, bool isShifting)
1547 AutoWriteLocker _(fWindowLock);
1549 BRegion dirty;
1550 bool changed = window->SetTabLocation(location, isShifting, dirty);
1551 if (changed)
1552 RebuildAndRedrawAfterWindowChange(window, dirty);
1554 NotifyWindowTabLocationChanged(window, location, isShifting);
1556 return changed;
1560 bool
1561 Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings)
1563 AutoWriteLocker _(fWindowLock);
1565 BRegion dirty;
1566 bool changed = window->SetDecoratorSettings(settings, dirty);
1567 bool listenerChanged = SetDecoratorSettings(window, settings);
1568 if (changed || listenerChanged)
1569 RebuildAndRedrawAfterWindowChange(window, dirty);
1571 return changed;
1575 void
1576 Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces)
1578 LockAllWindows();
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);
1593 UnlockAllWindows();
1597 /*! \brief Adds the window to the desktop.
1598 At this point, the window is still hidden and must be shown explicitly
1599 via ShowWindow().
1601 void
1602 Desktop::AddWindow(Window *window)
1604 LockAllWindows();
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()));
1613 } else {
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);
1622 UnlockAllWindows();
1626 void
1627 Desktop::RemoveWindow(Window *window)
1629 LockAllWindows();
1631 if (!window->IsHidden())
1632 HideWindow(window);
1634 fAllWindows.RemoveWindow(window);
1635 if (!window->IsNormal())
1636 fSubsetWindows.RemoveWindow(window);
1638 _ChangeWindowWorkspaces(window, window->Workspaces(), 0);
1640 NotifyWindowRemoved(window);
1642 UnlockAllWindows();
1644 // make sure this window won't get any events anymore
1646 EventDispatcher().RemoveTarget(window->EventTarget());
1650 bool
1651 Desktop::AddWindowToSubset(Window* subset, Window* window)
1653 if (!subset->AddToSubset(window))
1654 return false;
1656 _ChangeWindowWorkspaces(subset, subset->Workspaces(),
1657 subset->SubsetWorkspaces());
1658 return true;
1662 void
1663 Desktop::RemoveWindowFromSubset(Window* subset, Window* window)
1665 subset->RemoveFromSubset(window);
1666 _ChangeWindowWorkspaces(subset, subset->Workspaces(),
1667 subset->SubsetWorkspaces());
1671 void
1672 Desktop::FontsChanged(Window* window)
1674 AutoWriteLocker _(fWindowLock);
1676 BRegion dirty;
1677 window->FontsChanged(&dirty);
1679 RebuildAndRedrawAfterWindowChange(window, dirty);
1683 void
1684 Desktop::ColorUpdated(Window* window, color_which which, rgb_color color)
1686 AutoWriteLocker _(fWindowLock);
1688 window->TopView()->ColorUpdated(which, color);
1690 switch (which) {
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:
1697 break;
1698 default:
1699 return;
1702 BRegion dirty;
1703 window->ColorsChanged(&dirty);
1704 RebuildAndRedrawAfterWindowChange(window, dirty);
1708 void
1709 Desktop::SetWindowLook(Window* window, window_look newLook)
1711 if (window->Look() == newLook)
1712 return;
1714 AutoWriteLocker _(fWindowLock);
1716 BRegion dirty;
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);
1727 void
1728 Desktop::SetWindowFeel(Window* window, window_feel newFeel)
1730 if (window->Feel() == newFeel)
1731 return;
1733 LockAllWindows();
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()))
1758 continue;
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)
1771 break;
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));
1780 changed = true;
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)
1790 break;
1792 next = next->NextWindow(i);
1795 if (next == NULL) {
1796 // need to reinsert window behind its frontmost window
1797 _Windows(i).RemoveWindow(window);
1798 _Windows(i).AddWindow(window, frontmost);
1799 changed = true;
1803 if (i == fCurrentWorkspace && changed) {
1804 BRegion dummy;
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);
1815 MarkDirty(dirty);
1819 _UpdateFronts();
1821 if (window == FocusWindow() && !window->IsVisible())
1822 SetFocusWindow();
1824 NotifyWindowFeelChanged(window, newFeel);
1826 UnlockAllWindows();
1830 void
1831 Desktop::SetWindowFlags(Window *window, uint32 newFlags)
1833 if (window->Flags() == newFlags)
1834 return;
1836 AutoWriteLocker _(fWindowLock);
1838 BRegion dirty;
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);
1847 void
1848 Desktop::SetWindowTitle(Window *window, const char* title)
1850 AutoWriteLocker _(fWindowLock);
1852 BRegion dirty;
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.
1862 Window*
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);
1871 return NULL;
1875 void
1876 Desktop::SetMouseEventWindow(Window* window)
1878 fMouseEventWindow = window;
1882 void
1883 Desktop::SetViewUnderMouse(const Window* window, int32 viewToken)
1885 fWindowUnderMouse = window;
1886 fViewUnderMouse = viewToken;
1890 int32
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.
1905 EventTarget*
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();
1920 return NULL;
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.
1934 void
1935 Desktop::SetFocusWindow(Window* nextFocus)
1937 if (!LockAllWindows())
1938 return;
1940 // test for B_LOCK_WINDOW_FOCUS
1941 if (fLockedFocusWindow && nextFocus != fLockedFocusWindow) {
1942 UnlockAllWindows();
1943 return;
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;
1953 while (true) {
1954 window = window->NextWindow(fCurrentWorkspace);
1955 if (window == NULL || window->Feel() == kWindowScreenFeel)
1956 break;
1958 if (window != NULL)
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
1966 UnlockAllWindows();
1967 return;
1970 uint32 listIndex = fCurrentWorkspace;
1971 WindowList* list = &_Windows(fCurrentWorkspace);
1972 if (!fSettings->NormalMouse()) {
1973 listIndex = kFocusList;
1974 list = &fFocusList;
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
1998 UnlockAllWindows();
1999 return;
2002 team_id oldActiveApp = -1;
2003 team_id newActiveApp = -1;
2005 if (fFocus != NULL) {
2006 fFocus->SetFocus(false);
2007 oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
2010 fFocus = nextFocus;
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);
2026 UnlockAllWindows();
2028 // change the "active" app if appropriate
2029 if (oldActiveApp == newActiveApp)
2030 return;
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);
2045 void
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)
2055 return;
2058 fLockedFocusWindow = window;
2062 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) {
2069 return window;
2073 return NULL;
2077 ::EventTarget*
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();
2086 return NULL;
2090 void
2091 Desktop::MarkDirty(BRegion& region)
2093 if (region.CountRects() == 0)
2094 return;
2096 if (LockAllWindows()) {
2097 // send redraw messages to all windows intersecting the dirty region
2098 _TriggerWindowRedrawing(region);
2100 UnlockAllWindows();
2105 void
2106 Desktop::Redraw()
2108 BRegion dirty(fVirtualScreen.Frame());
2109 MarkDirty(dirty);
2113 /*! \brief Redraws the background (ie. the desktop window, if any).
2115 void
2116 Desktop::RedrawBackground()
2118 LockAllWindows();
2120 BRegion redraw;
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();
2129 if (view != NULL)
2130 view = view->FirstChild();
2132 while (view != NULL) {
2133 if (view->IsDesktopBackground()) {
2134 view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color());
2135 break;
2137 view = view->NextSibling();
2140 window->ProcessDirtyRegion(redraw);
2141 } else {
2142 redraw = BackgroundRegion();
2143 fBackgroundRegion.MakeEmpty();
2144 _SetBackground(redraw);
2147 _WindowChanged(NULL);
2148 // update workspaces view as well
2150 UnlockAllWindows();
2154 bool
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)) {
2170 BRegion oldBorder;
2171 window->GetBorderRegion(&oldBorder);
2173 if (!window->ReloadDecor()) {
2174 // prevent unloading previous add-on
2175 returnValue = false;
2178 BRegion border;
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));
2191 return returnValue;
2195 void
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)
2205 continue;
2207 window->ServerWindow()->NotifyMinimize(true);
2212 void
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)
2223 continue;
2225 window->ServerWindow()->NotifyMinimize(false);
2230 void
2231 Desktop::WindowAction(int32 windowToken, int32 action)
2233 if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
2234 return;
2236 LockAllWindows();
2238 ::ServerWindow* serverWindow;
2239 Window* window;
2240 if (BPrivate::gDefaultTokens.GetToken(windowToken,
2241 B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
2242 || (window = serverWindow->Window()) == NULL) {
2243 UnlockAllWindows();
2244 return;
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);
2250 } else {
2251 // if not, ask the window if it wants to be unminimized
2252 serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
2255 UnlockAllWindows();
2259 void
2260 Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
2262 AutoWriteLocker locker(fWindowLock);
2264 // compute the number of windows
2266 int32 count = 0;
2268 for (Window *window = fAllWindows.FirstWindow(); window != NULL;
2269 window = window->NextWindow(kAllWindowList)) {
2270 if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
2271 count++;
2274 // write list
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)
2283 continue;
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))
2293 continue;
2295 sender.Attach<int32>(window->ServerWindow()->ServerToken());
2298 sender.Flush();
2302 void
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);
2312 sender.Flush();
2313 return;
2316 window_info info;
2317 window->GetInfo(info);
2319 float tabSize = 0.0;
2320 float borderSize = 0.0;
2321 ::Window* tmp = window->Window();
2322 if (tmp) {
2323 BMessage message;
2324 if (tmp->GetDecoratorSettings(&message)) {
2325 BRect tabFrame;
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);
2340 if (length > 0)
2341 sender.Attach(window->Title(), length + 1);
2342 else
2343 sender.Attach<char>('\0');
2345 sender.Flush();
2349 void
2350 Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender)
2352 LockSingleWindow();
2354 if (workspace < 0)
2355 workspace = fCurrentWorkspace;
2356 else if (workspace >= kMaxWorkspaces) {
2357 sender.StartMessage(B_BAD_VALUE);
2358 sender.Flush();
2359 UnlockSingleWindow();
2360 return;
2363 int32 count = _Windows(workspace).Count();
2365 // write list
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());
2375 sender.Flush();
2377 UnlockSingleWindow();
2381 void
2382 Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender)
2384 fApplicationsLock.Lock();
2385 LockSingleWindow();
2387 int32 maxCount = fApplications.CountItems();
2389 fApplicationsLock.Unlock();
2390 // as long as we hold the window lock, no new window can appear
2392 if (workspace < 0)
2393 workspace = fCurrentWorkspace;
2394 else if (workspace >= kMaxWorkspaces) {
2395 sender.StartMessage(B_BAD_VALUE);
2396 sender.Flush();
2397 UnlockSingleWindow();
2398 return;
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);
2406 sender.Flush();
2407 UnlockSingleWindow();
2408 return;
2411 int32 count = 0;
2413 for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
2414 window = window->PreviousWindow(workspace)) {
2415 team_id team = window->ServerWindow()->ClientTeam();
2416 if (count > 1) {
2417 // see if we already have this team
2418 bool found = false;
2419 for (int32 i = 0; i < count; i++) {
2420 if (teams[i] == team) {
2421 found = true;
2422 break;
2425 if (found)
2426 continue;
2429 ASSERT(count < maxCount);
2430 teams[count++] = team;
2433 UnlockSingleWindow();
2435 // write list
2437 sender.StartMessage(B_OK);
2438 sender.Attach<int32>(count);
2440 for (int32 i = 0; i < count; i++) {
2441 sender.Attach<int32>(teams[i]);
2444 sender.Flush();
2445 free(teams);
2449 void
2450 Desktop::_LaunchInputServer()
2452 BRoster roster;
2453 status_t status = roster.Launch("application/x-vnd.Be-input_server");
2454 if (status == B_OK || status == B_ALREADY_RUNNING)
2455 return;
2457 // Could not load input_server by signature, try well-known location
2459 BEntry entry;
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());
2464 } else
2465 entry.SetTo("/system/servers/input_server");
2466 entry_ref ref;
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",
2472 strerror(status));
2473 return;
2476 syslog(LOG_ERR, "Failed to launch the input server: %s!\n",
2477 strerror(entryStatus));
2481 void
2482 Desktop::_GetLooperName(char* name, size_t length)
2484 snprintf(name, length, "d:%d:%s", fUserID,
2485 fTargetScreen == NULL ? "baron" : fTargetScreen);
2489 void
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();
2501 app->Quit();
2502 kill_team(clientTeam);
2505 // wait for the last app to die
2506 if (count > 0) {
2507 acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT,
2508 250000);
2511 fApplicationsLock.Unlock();
2515 void
2516 Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
2518 switch (code) {
2519 case AS_CREATE_APP:
2521 // Create the ServerApp to node monitor a new BApplication
2523 // Attached data:
2524 // 1) port_id - receiver port of a regular app
2525 // 2) port_id - client looper port - for sending messages to the
2526 // client
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)
2543 break;
2545 ServerApp* app = new (std::nothrow) ServerApp(this, clientReplyPort,
2546 clientLooperPort, clientTeamID, htoken, appSignature);
2547 status_t status = B_OK;
2548 if (app == NULL)
2549 status = B_NO_MEMORY;
2550 if (status == B_OK)
2551 status = app->InitCheck();
2552 if (status == B_OK)
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();
2559 } else {
2560 delete app;
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);
2566 reply.Flush();
2569 // This is necessary because BPortLink::ReadString allocates memory
2570 free(appSignature);
2571 break;
2574 case AS_DELETE_APP:
2576 // Delete a ServerApp. Received only from the respective ServerApp
2577 // when a BApplication asks it to quit.
2579 // Attached Data:
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)
2584 break;
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);
2598 removeApp = app;
2599 break;
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);
2614 break;
2617 case AS_ACTIVATE_APP:
2619 // Someone is requesting to activation of a certain app.
2621 // Attached data:
2622 // 1) port_id reply port
2623 // 2) team_id team
2625 status_t status;
2627 // get the parameters
2628 port_id replyPort;
2629 team_id team;
2630 if (link.Read(&replyPort) == B_OK
2631 && link.Read(&team) == B_OK)
2632 status = _ActivateApp(team);
2633 else
2634 status = B_ERROR;
2636 // send the reply
2637 BPrivate::PortLink replyLink(replyPort);
2638 replyLink.StartMessage(status);
2639 replyLink.Flush();
2640 break;
2643 case AS_APP_CRASHED:
2644 case AS_DUMP_ALLOCATOR:
2645 case AS_DUMP_BITMAPS:
2647 BAutolock locker(fApplicationsLock);
2649 team_id team;
2650 if (link.Read(&team) != B_OK)
2651 break;
2653 for (int32 i = 0; i < fApplications.CountItems(); i++) {
2654 ServerApp* app = fApplications.ItemAt(i);
2656 if (app->ClientTeam() == team)
2657 app->PostMessage(code);
2659 break;
2662 case AS_EVENT_STREAM_CLOSED:
2663 _LaunchInputServer();
2664 break;
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();
2676 fQuitting = true;
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);
2687 break;
2689 case AS_ACTIVATE_WORKSPACE:
2691 int32 index;
2692 link.Read<int32>(&index);
2693 if (index == -1)
2694 index = fPreviousWorkspace;
2696 bool moveFocusWindow;
2697 link.Read<bool>(&moveFocusWindow);
2699 SetWorkspace(index, moveFocusWindow);
2700 break;
2703 case AS_TALK_TO_DESKTOP_LISTENER:
2705 port_id clientReplyPort;
2706 if (link.Read<port_id>(&clientReplyPort) != B_OK)
2707 break;
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);
2715 reply.Flush();
2718 break;
2721 case AS_SET_UI_COLOR:
2723 color_which which;
2724 rgb_color 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);
2737 delayed.Flush();
2740 break;
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)) {
2750 break;
2753 if (!flushPendingOnly) {
2754 // Client wants to set a color map
2755 color_which which = B_NO_COLOR;
2756 rgb_color color;
2758 do {
2759 if (link.Read<color_which>(&which) != B_OK
2760 || link.Read<rgb_color>(&color) != B_OK)
2761 break;
2763 fPendingColors.SetColor(ui_color_name(which), color);
2764 } while (which != B_NO_COLOR);
2767 _FlushPendingColors();
2768 break;
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.
2774 case 'KDLE':
2776 BRegion dirty;
2777 dirty.Include(fVirtualScreen.Frame());
2778 MarkDirty(dirty);
2779 break;
2782 default:
2783 printf("Desktop %d:%s received unexpected code %" B_PRId32 "\n", 0,
2784 "baron", code);
2786 if (link.NeedsReply()) {
2787 // the client is now blocking and waiting for a reply!
2788 fLink.StartMessage(B_ERROR);
2789 fLink.Flush();
2791 break;
2796 WindowList&
2797 Desktop::CurrentWindows()
2799 return fWorkspaces[fCurrentWorkspace].Windows();
2803 WindowList&
2804 Desktop::AllWindows()
2806 return fAllWindows;
2810 Window*
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)
2818 return window;
2820 return NULL;
2824 WindowList&
2825 Desktop::_Windows(int32 index)
2827 ASSERT(index >= 0 && index < kMaxWorkspaces);
2828 return fWorkspaces[index].Windows();
2832 void
2833 Desktop::_FlushPendingColors()
2835 // Update all windows while we are holding the write lock.
2837 int32 count = fPendingColors.CountNames(B_RGB_32_BIT_TYPE);
2838 if (count == 0)
2839 return;
2841 bool changed[count];
2842 LockedDesktopSettings settings(this);
2843 settings.SetUIColors(fPendingColors, &changed[0]);
2845 int32 index = 0;
2846 char* name = NULL;
2847 type_code type = B_RGB_32_BIT_TYPE;
2848 rgb_color color;
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]) {
2856 ++index;
2857 continue;
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);
2867 ++index;
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();
2880 void
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)
2894 continue;
2896 if (fFront != NULL && fFront->IsNormal()
2897 && floating->HasInSubset(fFront)) {
2898 // is now visible
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(),
2911 nextWorkspace));
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)
2928 SetFocusWindow();
2934 /*! Search the visible windows for a valid back window
2935 (only desktop windows can't be back windows)
2937 void
2938 Desktop::_UpdateBack()
2940 fBack = NULL;
2942 for (Window* window = CurrentWindows().FirstWindow(); window != NULL;
2943 window = window->NextWindow(fCurrentWorkspace)) {
2944 if (window->IsHidden() || window->Feel() == kDesktopWindowFeel)
2945 continue;
2947 fBack = window;
2948 break;
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()
2958 yourself.
2960 void
2961 Desktop::_UpdateFront(bool updateFloating)
2963 fFront = NULL;
2965 for (Window* window = CurrentWindows().LastWindow(); window != NULL;
2966 window = window->PreviousWindow(fCurrentWorkspace)) {
2967 if (window->IsHidden() || window->IsFloating()
2968 || !window->SupportsFront())
2969 continue;
2971 fFront = window;
2972 break;
2975 if (updateFloating)
2976 _UpdateFloating();
2980 void
2981 Desktop::_UpdateFronts(bool updateFloating)
2983 _UpdateBack();
2984 _UpdateFront(updateFloating);
2988 bool
2989 Desktop::_WindowHasModal(Window* window) const
2991 if (window == NULL)
2992 return false;
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())
2998 continue;
3000 if (modal->HasInSubset(window))
3001 return true;
3004 return false;
3008 /*! Determines whether or not the specified \a window can have focus at all.
3010 bool
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.
3023 void
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.
3039 void
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.
3056 void
3057 Desktop::_ShowWindow(Window* window, bool affectsOtherWindows)
3059 BRegion background;
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);
3072 } else
3073 MarkDirty(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.
3085 void
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
3094 // it now
3095 // (actually that's not true, since
3096 // hidden windows are excluded from the
3097 // clipping calculation, but anyways)
3098 BRegion dirty(window->VisibleRegion());
3100 BRegion background;
3101 _RebuildClippingForAllWindows(background);
3102 _SetBackground(background);
3103 _WindowChanged(window);
3105 MarkDirty(dirty);
3109 /*! Updates the workspaces of all subset windows with regard to the
3110 specifed window.
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
3113 SetWorkspace().
3115 void
3116 Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex,
3117 int32 newIndex)
3119 STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window,
3120 window->Title()));
3122 // if the window is hidden, the subset windows are up-to-date already
3123 if (!window->IsNormal() || window->IsHidden())
3124 return;
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.
3132 continue;
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()
3139 continue;
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.
3152 void
3153 Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces,
3154 uint32 newWorkspaces)
3156 if (oldWorkspaces == newWorkspaces)
3157 return;
3159 // apply changes to the workspaces' window lists
3161 LockAllWindows();
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);
3182 } else {
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
3195 // well
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;
3211 break;
3213 firstWorkspace = i;
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);
3224 UnlockAllWindows();
3228 void
3229 Desktop::_BringWindowsToFront(WindowList& windows, int32 list, bool wereVisible)
3231 // we don't need to redraw what is currently
3232 // visible of the window
3233 BRegion clean;
3235 for (Window* window = windows.FirstWindow(); window != NULL;
3236 window = window->NextWindow(list)) {
3237 if (wereVisible)
3238 clean.Include(&window->VisibleRegion());
3240 CurrentWindows().AddWindow(window,
3241 window->Frontmost(CurrentWindows().FirstWindow(),
3242 fCurrentWorkspace));
3244 _WindowChanged(window);
3247 BRegion dummy;
3248 _RebuildClippingForAllWindows(dummy);
3250 // redraw what became visible of the window(s)
3252 BRegion dirty;
3253 for (Window* window = windows.FirstWindow(); window != NULL;
3254 window = window->NextWindow(list)) {
3255 dirty.Include(&window->VisibleRegion());
3258 dirty.Exclude(&clean);
3259 MarkDirty(dirty);
3261 _UpdateFront();
3263 if (windows.FirstWindow() == fBack || fBack == NULL)
3264 _UpdateBack();
3268 /*! Returns the last focussed non-hidden subset window belonging to the
3269 specified \a window.
3271 Window*
3272 Desktop::_LastFocusSubsetWindow(Window* window)
3274 if (window == NULL)
3275 return NULL;
3277 for (Window* front = fFocusList.LastWindow(); front != NULL;
3278 front = front->PreviousWindow(kFocusList)) {
3279 if (front != window && !front->IsHidden()
3280 && window->HasInSubset(front))
3281 return front;
3284 return NULL;
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.
3293 bool
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
3306 movement.
3308 You must not have locked any windows when calling this method.
3310 void
3311 Desktop::_SendFakeMouseMoved(Window* window)
3313 int32 viewToken = B_NULL_TOKEN;
3314 EventTarget* target = NULL;
3316 LockAllWindows();
3318 if (window == NULL)
3319 window = WindowAt(fLastMousePosition);
3321 if (window != NULL) {
3322 BMessage message;
3323 window->MouseMoved(&message, fLastMousePosition, &viewToken, true,
3324 true);
3326 if (viewToken != B_NULL_TOKEN)
3327 target = &window->EventTarget();
3330 if (viewToken != B_NULL_TOKEN)
3331 SetViewUnderMouse(window, viewToken);
3332 else {
3333 SetViewUnderMouse(NULL, B_NULL_TOKEN);
3334 SetCursor(NULL);
3337 UnlockAllWindows();
3339 if (target != NULL)
3340 EventDispatcher().SendFakeMouseMoved(*target, viewToken);
3344 Screen*
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);
3354 void
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());
3382 void
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);
3395 void
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
3401 // moving windows
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.
3421 void
3422 Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow,
3423 BRegion& dirty)
3425 ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
3426 if (!changedWindow->IsVisible() || dirty.CountRects() == 0)
3427 return;
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
3464 void
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
3478 void
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))
3486 continue;
3488 if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
3489 window->ServerWindow()->HandleDirectConnection(
3490 B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
3496 void
3497 Desktop::ScreenChanged(Screen* screen)
3499 AutoWriteLocker windowLocker(fWindowLock);
3501 AutoWriteLocker screenLocker(fScreenLock);
3502 screen->SetPreferredMode();
3503 screenLocker.Unlock();
3505 _ScreenChanged(screen);
3509 void
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());
3522 BRegion background;
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.
3551 status_t
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);
3563 return B_OK;
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);
3576 return B_OK;
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.
3584 return B_BAD_VALUE;
3588 void
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;
3602 } else
3603 fDirectScreenLock.Unlock();
3605 AutoWriteLocker _(fScreenLock);
3607 uint32 changedScreens;
3608 fVirtualScreen.SetConfiguration(*this,
3609 fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(),
3610 &changedScreens);
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.
3622 void
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;
3634 else
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());
3667 } else {
3668 // make sure it's frontmost
3669 _Windows(index).RemoveWindow(movedWindow);
3670 _Windows(index).AddWindow(movedWindow,
3671 movedWindow->Frontmost(_Windows(index).FirstWindow(),
3672 index));
3676 movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop();
3679 if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex))
3680 fLastWorkspaceFocus[previousIndex] = FocusWindow();
3681 else
3682 fLastWorkspaceFocus[previousIndex] = NULL;
3684 // build region of windows that are no longer visible in the new workspace
3686 BRegion dirty;
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))
3700 continue;
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())
3729 continue;
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
3736 // was before!
3739 if (!window->InWorkspace(previousIndex)) {
3740 // This window was not visible before, make sure its frame
3741 // is up-to-date
3742 if (window->Frame().LeftTop() != position) {
3743 BPoint offset = position - window->Frame().LeftTop();
3744 window->MoveBy((int32)offset.x, (int32)offset.y);
3746 continue;
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...
3754 } else {
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);
3762 else
3763 delete region;
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
3792 // above loop
3793 continue;
3796 dirty.Include(&window->VisibleRegion());
3799 // Catch order changes in the new workspaces window list
3800 int32 i = 0;
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);
3806 delete 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);
3817 MarkDirty(dirty);
3819 #if 0
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();
3824 snooze(100000);
3826 #endif
3828 if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
3829 RedrawBackground();