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