#include "Desktop.h"
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <Debug.h>
#include <debugger.h>
#include <DirectWindow.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <Message.h>
#include <MessageFilter.h>
#include <Path.h>
#include <PthreadMutexLocker.h>
#include <Region.h>
#include <Roster.h>
#include <PrivateScreen.h>
#include <ServerProtocol.h>
#include <ServerReadOnlyMemory.h>
#include <ViewPrivate.h>
#include <WindowInfo.h>
#include "AppServer.h"
#include "ClickTarget.h"
#include "DecorManager.h"
#include "DesktopSettingsPrivate.h"
#include "DrawingEngine.h"
#include "GlobalFontManager.h"
#include "HWInterface.h"
#include "InputManager.h"
#include "Screen.h"
#include "ScreenManager.h"
#include "ServerApp.h"
#include "ServerConfig.h"
#include "ServerCursor.h"
#include "ServerWindow.h"
#include "SystemPalette.h"
#include "WindowPrivate.h"
#include "Window.h"
#include "Workspace.h"
#include "WorkspacesView.h"
#if TEST_MODE
# include "EventStream.h"
#endif
#ifdef DEBUG_DESKTOP
# define STRACE(a) printf a
#else
# define STRACE(a) ;
#endif
static inline float
square_vector_length(float x, float y)
{
return x * x + y * y;
}
static inline float
square_distance(const BPoint& a, const BPoint& b)
{
return square_vector_length(a.x - b.x, a.y - b.y);
}
class KeyboardFilter : public EventFilter {
public:
KeyboardFilter(Desktop* desktop);
virtual filter_result Filter(BMessage* message, EventTarget** _target,
int32* _viewToken, BMessage* latestMouseMoved);
virtual void RemoveTarget(EventTarget* target);
private:
void _UpdateFocus(int32 key, uint32 modifiers, EventTarget** _target);
Desktop* fDesktop;
EventTarget* fLastFocus;
bigtime_t fTimestamp;
};
class MouseFilter : public EventFilter {
public:
MouseFilter(Desktop* desktop);
virtual filter_result Filter(BMessage* message, EventTarget** _target,
int32* _viewToken, BMessage* latestMouseMoved);
private:
Desktop* fDesktop;
int32 fLastClickButtons;
int32 fLastClickModifiers;
int32 fResetClickCount;
BPoint fLastClickPoint;
ClickTarget fLastClickTarget;
};
KeyboardFilter::KeyboardFilter(Desktop* desktop)
:
fDesktop(desktop),
fLastFocus(NULL),
fTimestamp(0)
{
}
void
KeyboardFilter::_UpdateFocus(int32 key, uint32 modifiers, EventTarget** _target)
{
if (!fDesktop->LockSingleWindow())
return;
EventTarget* focus = fDesktop->KeyboardEventTarget();
#if 0
bigtime_t now = system_time();
if (fLastFocus == NULL
|| (focus != fLastFocus && now - fTimestamp > 100000)) {
*_target = focus;
fLastFocus = focus;
}
#endif
*_target = focus;
fLastFocus = focus;
fDesktop->UnlockSingleWindow();
#if 0
if (key == B_ENTER || modifiers == B_COMMAND_KEY
|| modifiers == B_CONTROL_KEY || modifiers == B_OPTION_KEY)
fTimestamp = 0;
else
fTimestamp = now;
#endif
}
filter_result
KeyboardFilter::Filter(BMessage* message, EventTarget** _target,
int32* , BMessage* )
{
int32 key = 0;
int32 modifiers = 0;
message->FindInt32("key", &key);
message->FindInt32("modifiers", &modifiers);
if ((message->what == B_KEY_DOWN || message->what == B_UNMAPPED_KEY_DOWN)) {
if (key == 0x01 && (modifiers & B_COMMAND_KEY) != 0
&& (modifiers & B_CONTROL_KEY) != 0
&& (modifiers & B_SHIFT_KEY) != 0) {
system("screenmode --fall-back &");
return B_SKIP_MESSAGE;
}
bool takeWindow = (modifiers & B_SHIFT_KEY) != 0
|| fDesktop->MouseEventWindow() != NULL;
if (key >= B_F1_KEY && key <= B_F12_KEY) {
#if !TEST_MODE
if ((modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY))
== B_COMMAND_KEY)
#else
if ((modifiers & B_CONTROL_KEY) != 0)
#endif
{
STRACE(("Set Workspace %" B_PRId32 "\n", key - 1));
fDesktop->SetWorkspaceAsync(key - B_F1_KEY, takeWindow);
return B_SKIP_MESSAGE;
}
} else if (key == 0x11
&& (modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY))
== B_COMMAND_KEY) {
fDesktop->SetWorkspaceAsync(-1, takeWindow);
return B_SKIP_MESSAGE;
}
}
if (message->what == B_KEY_DOWN
|| message->what == B_MODIFIERS_CHANGED
|| message->what == B_UNMAPPED_KEY_DOWN
|| message->what == B_INPUT_METHOD_EVENT)
_UpdateFocus(key, modifiers, _target);
return fDesktop->KeyEvent(message->what, key, modifiers);
}
void
KeyboardFilter::RemoveTarget(EventTarget* target)
{
if (target == fLastFocus)
fLastFocus = NULL;
}
MouseFilter::MouseFilter(Desktop* desktop)
:
fDesktop(desktop),
fLastClickButtons(0),
fLastClickModifiers(0),
fResetClickCount(0),
fLastClickPoint(),
fLastClickTarget()
{
}
filter_result
MouseFilter::Filter(BMessage* message, EventTarget** _target, int32* _viewToken,
BMessage* latestMouseMoved)
{
BPoint where;
if (message->FindPoint("where", &where) != B_OK)
return B_DISPATCH_MESSAGE;
int32 buttons;
if (message->FindInt32("buttons", &buttons) != B_OK)
buttons = 0;
if (!fDesktop->LockAllWindows())
return B_DISPATCH_MESSAGE;
int32 viewToken = B_NULL_TOKEN;
Window* window = fDesktop->MouseEventWindow();
if (window == NULL)
window = fDesktop->WindowAt(where);
if (window != NULL) {
switch (message->what) {
case B_MOUSE_DOWN:
{
int32 windowToken = window->ServerWindow()->ServerToken();
int32 modifiers = message->FindInt32("modifiers");
int32 originalClickCount = message->FindInt32("clicks");
if (originalClickCount <= 0)
originalClickCount = 1;
int32 clickCount = originalClickCount;
if (clickCount > 1) {
if (modifiers != fLastClickModifiers
|| buttons != fLastClickButtons
|| !fLastClickTarget.IsValid()
|| fLastClickTarget.WindowToken() != windowToken
|| square_distance(where, fLastClickPoint) >= 16
|| clickCount - fResetClickCount < 1) {
clickCount = 1;
} else
clickCount -= fResetClickCount;
}
ClickTarget clickTarget;
window->MouseDown(message, where, fLastClickTarget, clickCount,
clickTarget);
if (clickCount != 1 && clickTarget != fLastClickTarget)
clickCount = 1;
fResetClickCount = originalClickCount - clickCount;
fLastClickTarget = clickTarget;
fLastClickButtons = buttons;
fLastClickModifiers = modifiers;
fLastClickPoint = where;
if (clickTarget.GetType() == ClickTarget::TYPE_WINDOW_CONTENTS)
viewToken = clickTarget.WindowElement();
if (clickCount != originalClickCount) {
if (message->HasInt32("clicks"))
message->ReplaceInt32("clicks", clickCount);
else
message->AddInt32("clicks", clickCount);
}
fDesktop->NotifyMouseDown(window, message, where);
break;
}
case B_MOUSE_UP:
window->MouseUp(message, where, &viewToken);
if (buttons == 0)
fDesktop->SetMouseEventWindow(NULL);
fDesktop->NotifyMouseUp(window, message, where);
break;
case B_MOUSE_MOVED:
window->MouseMoved(message, where, &viewToken,
latestMouseMoved == NULL || latestMouseMoved == message,
false);
fDesktop->NotifyMouseMoved(window, message, where);
break;
}
if (viewToken != B_NULL_TOKEN) {
fDesktop->SetViewUnderMouse(window, viewToken);
*_viewToken = viewToken;
*_target = &window->EventTarget();
}
} else if (message->what == B_MOUSE_DOWN) {
fResetClickCount = 0;
fLastClickTarget = ClickTarget();
fLastClickButtons = message->FindInt32("buttons");
fLastClickModifiers = message->FindInt32("modifiers");
fLastClickPoint = where;
}
if (window == NULL || viewToken == B_NULL_TOKEN) {
fDesktop->SetViewUnderMouse(window, B_NULL_TOKEN);
fDesktop->SetCursor(NULL);
*_target = NULL;
}
fDesktop->SetLastMouseState(where, buttons, window);
fDesktop->NotifyMouseEvent(message);
fDesktop->UnlockAllWindows();
return B_DISPATCH_MESSAGE;
}
static inline uint32
workspace_to_workspaces(int32 index)
{
return 1UL << index;
}
static inline bool
workspace_in_workspaces(int32 index, uint32 workspaces)
{
return (workspaces & (1UL << index)) != 0;
}
Desktop::Desktop(uid_t userID, const char* targetScreen)
:
MessageLooper("desktop"),
fUserID(userID),
fTargetScreen(strdup(targetScreen)),
fSettings(NULL),
fSharedReadOnlyArea(-1),
fApplicationsLock("application list"),
fShutdownSemaphore(-1),
fShutdownCount(0),
fScreenLock("screen lock"),
fDirectScreenLock("direct screen lock"),
fDirectScreenTeam(-1),
fCurrentWorkspace(0),
fPreviousWorkspace(0),
fAllWindows(kAllWindowList),
fSubsetWindows(kSubsetList),
fFocusList(kFocusList),
fWorkspacesViews(false),
fWorkspacesLock("workspaces list"),
fWindowLock("window lock"),
fMouseEventWindow(NULL),
fWindowUnderMouse(NULL),
fLockedFocusWindow(NULL),
fViewUnderMouse(B_NULL_TOKEN),
fLastMousePosition(B_ORIGIN),
fLastMouseButtons(0),
fFocus(NULL),
fFront(NULL),
fBack(NULL)
{
memset(fLastWorkspaceFocus, 0, sizeof(fLastWorkspaceFocus));
char name[B_OS_NAME_LENGTH];
Desktop::_GetLooperName(name, sizeof(name));
fMessagePort = create_port(DEFAULT_MONITOR_PORT_SIZE, name);
if (fMessagePort < B_OK)
return;
fLink.SetReceiverPort(fMessagePort);
RegisterListener(&fStackAndTile);
const DesktopListenerList& newListeners
= gDecorManager.GetDesktopListeners();
for (int i = 0; i < newListeners.CountItems(); i++)
RegisterListener(newListeners.ItemAt(i));
}
Desktop::~Desktop()
{
delete_area(fSharedReadOnlyArea);
delete_port(fMessagePort);
free(fTargetScreen);
}
void
Desktop::RegisterListener(DesktopListener* listener)
{
DesktopObservable::RegisterListener(listener, this);
}
status_t
Desktop::Init()
{
if (fMessagePort < B_OK)
return fMessagePort;
InitializeColorMap();
const size_t areaSize = sizeof(server_read_only_memory);
char name[B_OS_NAME_LENGTH];
snprintf(name, sizeof(name), "d:%d:shared read only", fUserID);
fSharedReadOnlyArea = create_area(name, (void **)&fServerReadOnlyMemory,
B_ANY_ADDRESS, areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA | B_CLONEABLE_AREA);
if (fSharedReadOnlyArea < B_OK)
return fSharedReadOnlyArea;
fSettings.SetTo(new DesktopSettingsPrivate(fServerReadOnlyMemory));
for (int32 i = 0; i < kMaxWorkspaces; i++) {
_Windows(i).SetIndex(i);
fWorkspaces[i].RestoreConfiguration(*fSettings->WorkspacesMessage(i));
}
status_t status = fVirtualScreen.SetConfiguration(*this,
fWorkspaces[0].CurrentScreenConfiguration());
if (status != B_OK) {
debug_printf("app_server: Failed to initialize virtual screen configuration: %s\n",
strerror(status));
return status;
}
if (fVirtualScreen.HWInterface() == NULL) {
debug_printf("Could not initialize graphics output. Exiting.\n");
return B_ERROR;
}
if (fSettings->DefaultPlainFont() == *gFontManager->DefaultPlainFont()
&& !fSettings->DidLoadFontSettings()) {
float fontSize = fSettings->DefaultPlainFont().Size();
gScreenManager->Lock();
Screen* screen = gScreenManager->ScreenAt(0);
if (screen != NULL) {
display_mode mode;
screen->GetMode(mode);
if (mode.virtual_width > 3840 && mode.virtual_height > 2160)
fontSize *= 2.0f;
else if (mode.virtual_width > 1920 && mode.virtual_height > 1080)
fontSize *= 1.5f;
}
gScreenManager->Unlock();
const_cast<ServerFont&>(fSettings->DefaultPlainFont()).SetSize(fontSize);
const_cast<ServerFont&>(fSettings->DefaultBoldFont()).SetSize(fontSize);
const_cast<ServerFont&>(fSettings->DefaultFixedFont()).SetSize(fontSize);
const_cast<menu_info&>(fSettings->MenuInfo()).font_size = fontSize;
}
#if 1
if (fSettings->UIColor(B_CONTROL_BACKGROUND_COLOR) == make_color(245, 245, 245)
&& fSettings->ControlLook().IsEmpty()) {
rgb_color newControlBackground = make_color(222, 222, 222);
fSettings->SetUIColor(B_CONTROL_BACKGROUND_COLOR, newControlBackground);
fSettings->SetUIColor(B_SCROLL_BAR_THUMB_COLOR, newControlBackground);
}
#endif
fCursorManager.InitializeCursors(fSettings->DefaultBoldFont().Size() / 12.0f);
HWInterface()->SetDPMSMode(B_DPMS_ON);
float brightness = fWorkspaces[0].StoredScreenConfiguration().Brightness(0);
if (brightness > 0)
HWInterface()->SetBrightness(brightness);
fVirtualScreen.HWInterface()->MoveCursorTo(
fVirtualScreen.Frame().Width() / 2,
fVirtualScreen.Frame().Height() / 2);
#if TEST_MODE
gInputManager->AddStream(new InputServerStream);
#endif
EventStream* stream = fVirtualScreen.HWInterface()->CreateEventStream();
if (stream == NULL)
stream = gInputManager->GetStream();
fEventDispatcher.SetDesktop(this);
fEventDispatcher.SetTo(stream);
if (fEventDispatcher.InitCheck() != B_OK)
_LaunchInputServer();
fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface());
fEventDispatcher.SetMouseFilter(new MouseFilter(this));
fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this));
fScreenRegion = fVirtualScreen.Frame();
BRegion stillAvailableOnScreen;
_RebuildClippingForAllWindows(stillAvailableOnScreen);
_SetBackground(stillAvailableOnScreen);
SetCursor(NULL);
fVirtualScreen.HWInterface()->SetCursorVisible(true);
return B_OK;
}
void
Desktop::BroadcastToAllApps(int32 code)
{
BAutolock locker(fApplicationsLock);
for (int32 i = fApplications.CountItems(); i-- > 0;) {
fApplications.ItemAt(i)->PostMessage(code);
}
}
void
Desktop::BroadcastToAllWindows(int32 code)
{
AutoReadLocker _(fWindowLock);
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
window->ServerWindow()->PostMessage(code);
}
}
int32
Desktop::GetAllWindowTargets(DelayedMessage& message)
{
AutoReadLocker _(fWindowLock);
int32 count = 0;
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
message.AddTarget(window->ServerWindow()->MessagePort());
++count;
}
return count;
}
int32
Desktop::GetAllAppTargets(DelayedMessage& message)
{
BAutolock _(fApplicationsLock);
for (int32 index = 0; index < fApplications.CountItems(); ++index)
message.AddTarget(fApplications.ItemAt(index)->MessagePort());
return fApplications.CountItems();
}
filter_result
Desktop::KeyEvent(uint32 what, int32 key, int32 modifiers)
{
filter_result result = B_DISPATCH_MESSAGE;
if (LockAllWindows()) {
Window* window = MouseEventWindow();
if (window == NULL)
window = WindowAt(fLastMousePosition);
if (window != NULL) {
if (what == B_MODIFIERS_CHANGED)
window->ModifiersChanged(modifiers);
}
if (NotifyKeyPressed(what, key, modifiers))
result = B_SKIP_MESSAGE;
UnlockAllWindows();
}
return result;
}
void
Desktop::SetCursor(ServerCursor* newCursor)
{
AutoWriteLocker _(fWindowLock);
if (newCursor == NULL)
newCursor = fCursorManager.GetCursor(B_CURSOR_ID_SYSTEM_DEFAULT);
if (newCursor == fCursor)
return;
fCursor.SetTo(newCursor, false);
if (!fManagementCursor.IsSet())
HWInterface()->SetCursor(newCursor);
}
ServerCursorReference
Desktop::Cursor() const
{
return fCursor;
}
void
Desktop::SetManagementCursor(ServerCursor* newCursor)
{
AutoWriteLocker _(fWindowLock);
if (newCursor == fManagementCursor)
return;
fManagementCursor.SetTo(newCursor, false);
HWInterface()->SetCursor(newCursor != NULL ? newCursor : fCursor.Get());
}
void
Desktop::SetLastMouseState(const BPoint& position, int32 buttons,
Window* windowUnderMouse)
{
fLastMousePosition = position;
fLastMouseButtons = buttons;
if (fLastMouseButtons == 0 && fLockedFocusWindow) {
fLockedFocusWindow = NULL;
if (fSettings->FocusFollowsMouse())
SetFocusWindow(windowUnderMouse);
}
}
void
Desktop::GetLastMouseState(BPoint* position, int32* buttons) const
{
*position = fLastMousePosition;
*buttons = fLastMouseButtons;
}
status_t
Desktop::SetScreenMode(int32 workspace, int32 id, const display_mode& mode,
bool makeDefault)
{
AutoWriteLocker _(fWindowLock);
if (workspace == B_CURRENT_WORKSPACE_INDEX)
workspace = fCurrentWorkspace;
if (workspace < 0 || workspace >= kMaxWorkspaces)
return B_BAD_VALUE;
Screen* screen = fVirtualScreen.ScreenByID(id);
if (screen == NULL)
return B_NAME_NOT_FOUND;
if (workspace == fCurrentWorkspace) {
display_mode oldMode;
screen->GetMode(oldMode);
if (!memcmp(&oldMode, &mode, sizeof(display_mode)))
return B_OK;
_SuspendDirectFrameBufferAccess();
AutoWriteLocker locker(fScreenLock);
status_t status = screen->SetMode(mode);
if (status != B_OK) {
locker.Unlock();
_ResumeDirectFrameBufferAccess();
return status;
}
} else {
screen_configuration* configuration
= fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(
screen->ID());
if (configuration != NULL
&& !memcmp(&configuration->mode, &mode, sizeof(display_mode)))
return B_OK;
}
monitor_info info;
bool hasInfo = screen->GetMonitorInfo(info) == B_OK;
fWorkspaces[workspace].CurrentScreenConfiguration().Set(id,
hasInfo ? &info : NULL, screen->Frame(), mode);
if (makeDefault) {
fWorkspaces[workspace].StoredScreenConfiguration().Set(id,
hasInfo ? &info : NULL, screen->Frame(), mode);
StoreWorkspaceConfiguration(workspace);
}
_ScreenChanged(screen);
if (workspace == fCurrentWorkspace)
_ResumeDirectFrameBufferAccess();
return B_OK;
}
status_t
Desktop::GetScreenMode(int32 workspace, int32 id, display_mode& mode)
{
AutoReadLocker _(fScreenLock);
if (workspace == B_CURRENT_WORKSPACE_INDEX)
workspace = fCurrentWorkspace;
if (workspace < 0 || workspace >= kMaxWorkspaces)
return B_BAD_VALUE;
if (workspace == fCurrentWorkspace) {
Screen* screen = fVirtualScreen.ScreenByID(id);
if (screen == NULL)
return B_NAME_NOT_FOUND;
screen->GetMode(mode);
return B_OK;
}
screen_configuration* configuration
= fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(id);
if (configuration == NULL)
return B_NAME_NOT_FOUND;
mode = configuration->mode;
return B_OK;
}
status_t
Desktop::GetScreenFrame(int32 workspace, int32 id, BRect& frame)
{
AutoReadLocker _(fScreenLock);
if (workspace == B_CURRENT_WORKSPACE_INDEX)
workspace = fCurrentWorkspace;
if (workspace < 0 || workspace >= kMaxWorkspaces)
return B_BAD_VALUE;
if (workspace == fCurrentWorkspace) {
Screen* screen = fVirtualScreen.ScreenByID(id);
if (screen == NULL)
return B_NAME_NOT_FOUND;
frame = screen->Frame();
return B_OK;
}
screen_configuration* configuration
= fWorkspaces[workspace].CurrentScreenConfiguration().CurrentByID(id);
if (configuration == NULL)
return B_NAME_NOT_FOUND;
frame = configuration->frame;
return B_OK;
}
void
Desktop::RevertScreenModes(uint32 workspaces)
{
if (workspaces == 0)
return;
AutoWriteLocker _(fWindowLock);
for (int32 workspace = 0; workspace < kMaxWorkspaces; workspace++) {
if ((workspaces & (1U << workspace)) == 0)
continue;
for (int32 index = 0; index < fVirtualScreen.CountScreens(); index++) {
Screen* screen = fVirtualScreen.ScreenAt(index);
screen_configuration* stored = fWorkspaces[workspace]
.StoredScreenConfiguration().CurrentByID(screen->ID());
screen_configuration* current = fWorkspaces[workspace]
.CurrentScreenConfiguration().CurrentByID(screen->ID());
if ((stored != NULL && current != NULL
&& !memcmp(&stored->mode, ¤t->mode,
sizeof(display_mode)))
|| (stored == NULL && current == NULL))
continue;
if (stored == NULL) {
fWorkspaces[workspace].CurrentScreenConfiguration()
.Remove(current);
if (workspace == fCurrentWorkspace) {
_SuspendDirectFrameBufferAccess();
_SetCurrentWorkspaceConfiguration();
_ResumeDirectFrameBufferAccess();
}
} else
SetScreenMode(workspace, screen->ID(), stored->mode, false);
}
}
}
status_t
Desktop::SetBrightness(int32 id, float brightness)
{
status_t result = HWInterface()->SetBrightness(brightness);
if (result == B_OK) {
if (fWorkspaces[0].StoredScreenConfiguration().CurrentByID(id) == NULL) {
screen_configuration* current
= fWorkspaces[0].CurrentScreenConfiguration().CurrentByID(id);
fWorkspaces[0].StoredScreenConfiguration().Set(id,
current->has_info ? ¤t->info : NULL, current->frame, current->mode);
}
fWorkspaces[0].StoredScreenConfiguration().SetBrightness(id,
brightness);
StoreWorkspaceConfiguration(0);
}
return result;
}
status_t
Desktop::LockDirectScreen(team_id team)
{
status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
if (status == B_OK)
fDirectScreenTeam = team;
return status;
}
status_t
Desktop::UnlockDirectScreen(team_id team)
{
if (fDirectScreenTeam == team) {
fDirectScreenLock.Unlock();
fDirectScreenTeam = -1;
return B_OK;
}
return B_PERMISSION_DENIED;
}
void
Desktop::SetWorkspaceAsync(int32 index, bool moveFocusWindow)
{
BPrivate::LinkSender link(MessagePort());
link.StartMessage(AS_ACTIVATE_WORKSPACE);
link.Attach<int32>(index);
link.Attach<bool>(moveFocusWindow);
link.Flush();
}
void
Desktop::SetWorkspace(int32 index, bool moveFocusWindow)
{
LockAllWindows();
DesktopSettings settings(this);
if (index < 0 || index >= settings.WorkspacesCount()
|| index == fCurrentWorkspace) {
UnlockAllWindows();
return;
}
_SetWorkspace(index, moveFocusWindow);
UnlockAllWindows();
_SendFakeMouseMoved();
}
status_t
Desktop::SetWorkspacesLayout(int32 newColumns, int32 newRows)
{
int32 newCount = newColumns * newRows;
if (newCount < 1 || newCount > kMaxWorkspaces)
return B_BAD_VALUE;
if (!LockAllWindows())
return B_ERROR;
fSettings->SetWorkspacesLayout(newColumns, newRows);
bool workspaceChanged = CurrentWorkspace() >= newCount;
if (workspaceChanged)
_SetWorkspace(newCount - 1);
else
_WindowChanged(NULL);
UnlockAllWindows();
if (workspaceChanged)
_SendFakeMouseMoved();
return B_OK;
}
BRect
Desktop::WorkspaceFrame(int32 index) const
{
BRect frame;
if (index == fCurrentWorkspace)
frame = fVirtualScreen.Frame();
else if (index >= 0 && index < fSettings->WorkspacesCount()) {
BMessage screenData;
if (fSettings->WorkspacesMessage(index)->FindMessage("screen",
&screenData) != B_OK
|| screenData.FindRect("frame", &frame) != B_OK) {
frame = fVirtualScreen.Frame();
}
}
return frame;
}
void
Desktop::StoreWorkspaceConfiguration(int32 index)
{
BMessage settings;
fWorkspaces[index].StoreConfiguration(settings);
fSettings->SetWorkspacesMessage(index, settings);
fSettings->Save(kWorkspacesSettings);
}
void
Desktop::AddWorkspacesView(WorkspacesView* view)
{
if (view->Window() == NULL || view->Window()->IsHidden())
return;
BAutolock _(fWorkspacesLock);
if (!fWorkspacesViews.HasItem(view))
fWorkspacesViews.AddItem(view);
}
void
Desktop::RemoveWorkspacesView(WorkspacesView* view)
{
BAutolock _(fWorkspacesLock);
fWorkspacesViews.RemoveItem(view);
}
void
Desktop::SelectWindow(Window* window)
{
if (fSettings->ClickToFocusMouse()) {
if (window != fWindowUnderMouse
|| (window == fWindowUnderMouse && window != FocusWindow()))
ActivateWindow(window);
else
SetFocusWindow(window);
} else
ActivateWindow(window);
}
void
Desktop::ActivateWindow(Window* window)
{
STRACE(("ActivateWindow(%p, %s)\n", window, window
? window->Title() : "<none>"));
if (window == NULL) {
fBack = NULL;
fFront = NULL;
return;
}
if (window->Workspaces() == 0 && window->IsNormal())
return;
AutoWriteLocker allWindowLocker(fWindowLock);
NotifyWindowActivated(window);
bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace);
if (windowOnOtherWorkspace
&& (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) {
if ((window->Flags() & B_NO_WORKSPACE_ACTIVATION) == 0) {
uint32 workspaces = window->Workspaces();
for (int32 i = 0; i < fSettings->WorkspacesCount(); i++) {
uint32 workspace = workspace_to_workspaces(i);
if (workspaces & workspace) {
SetWorkspace(i);
windowOnOtherWorkspace = false;
break;
}
}
} else
return;
}
if (windowOnOtherWorkspace) {
if (!window->IsNormal()) {
Window* front = _LastFocusSubsetWindow(window);
if (front == NULL) {
return;
}
ActivateWindow(front);
if (!window->InWorkspace(fCurrentWorkspace)) {
return;
}
} else {
uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace);
SetWindowWorkspaces(window, workspaces);
}
}
if (window->IsMinimized()) {
window->SetMinimized(false);
ShowWindow(window);
}
if (window == FrontWindow()) {
Window* avoidsFront = window->NextWindow(fCurrentWorkspace);
while (avoidsFront && avoidsFront->IsNormal()
&& (avoidsFront->Flags() & B_AVOID_FRONT) == 0) {
avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace);
}
if (avoidsFront == NULL) {
if ((window->Flags() & B_AVOID_FOCUS) == 0)
SetFocusWindow(window);
return;
}
}
WindowList windows(kWorkingList);
Window* frontmost = window->Frontmost();
const Window* lastWindowUnderMouse = fWindowUnderMouse;
CurrentWindows().RemoveWindow(window);
windows.AddWindow(window);
window->MoveToTopStackLayer();
if (frontmost != NULL && frontmost->IsModal()) {
Window* nextModal;
for (Window* modal = frontmost; modal != NULL; modal = nextModal) {
nextModal = modal->NextWindow(fCurrentWorkspace);
while (nextModal != NULL && !nextModal->IsModal()) {
nextModal = nextModal->NextWindow(fCurrentWorkspace);
}
if (nextModal != NULL && !nextModal->HasInSubset(window))
nextModal = NULL;
CurrentWindows().RemoveWindow(modal);
windows.AddWindow(modal);
}
}
_BringWindowsToFront(windows, kWorkingList, true);
if ((window->Flags() & B_AVOID_FOCUS) == 0)
SetFocusWindow(window);
bool sendFakeMouseMoved = _CheckSendFakeMouseMoved(lastWindowUnderMouse);
allWindowLocker.Unlock();
if (sendFakeMouseMoved)
_SendFakeMouseMoved();
}
void
Desktop::SendWindowBehind(Window* window, Window* behindOf, bool sendStack)
{
if (!LockAllWindows())
return;
Window* orgWindow = window;
WindowStack* stack = window->GetWindowStack();
if (sendStack && stack != NULL)
window = stack->TopLayerWindow();
if (window == BackWindow()
|| !window->InWorkspace(fCurrentWorkspace)
|| (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace))) {
UnlockAllWindows();
return;
}
if (behindOf != NULL && window->HasInSubset(behindOf))
behindOf = NULL;
BRegion dirty(window->VisibleRegion());
Window* backmost = window->Backmost(behindOf);
const Window* lastWindowUnderMouse = fWindowUnderMouse;
CurrentWindows().RemoveWindow(window);
CurrentWindows().AddWindow(window, backmost
? backmost->NextWindow(fCurrentWorkspace) : BackWindow());
BRegion dummy;
_RebuildClippingForAllWindows(dummy);
if (sendStack) {
BRegion clean(window->VisibleRegion());
dirty.Exclude(&clean);
MarkDirty(dirty);
}
_UpdateFronts();
if (fSettings->FocusFollowsMouse())
SetFocusWindow(WindowAt(fLastMousePosition));
else if (fSettings->NormalMouse())
SetFocusWindow(NULL);
_WindowChanged(window);
if (sendStack && stack != NULL) {
for (int32 i = 0; i < stack->CountWindows(); i++) {
Window* stackWindow = stack->LayerOrder().ItemAt(i);
if (stackWindow == window)
continue;
SendWindowBehind(stackWindow, behindOf, false);
}
}
bool sendFakeMouseMoved = _CheckSendFakeMouseMoved(lastWindowUnderMouse);
NotifyWindowSentBehind(orgWindow, behindOf);
UnlockAllWindows();
if (sendFakeMouseMoved)
_SendFakeMouseMoved();
}
void
Desktop::ShowWindow(Window* window)
{
if (!window->IsHidden())
return;
AutoWriteLocker locker(fWindowLock);
window->SetHidden(false);
fFocusList.AddWindow(window);
if (window->InWorkspace(fCurrentWorkspace)
|| (window->IsFloating() && _LastFocusSubsetWindow(window) != NULL)) {
_ShowWindow(window, true);
_UpdateSubsetWorkspaces(window);
ActivateWindow(window);
} else {
_WindowChanged(window);
return;
}
if (window->HasWorkspacesViews()) {
BAutolock _(fWorkspacesLock);
window->FindWorkspacesViews(fWorkspacesViews);
}
_SendFakeMouseMoved(window);
}
void
Desktop::HideWindow(Window* window, bool fromMinimize)
{
if (window->IsHidden())
return;
if (!LockAllWindows())
return;
window->SetHidden(true);
fFocusList.RemoveWindow(window);
if (fMouseEventWindow == window) {
BMessage message;
int32 viewToken;
window->MouseUp(&message, fLastMousePosition, &viewToken);
fMouseEventWindow = NULL;
}
if (fLockedFocusWindow == window) {
fLockedFocusWindow = NULL;
}
if (window->InWorkspace(fCurrentWorkspace)) {
_UpdateSubsetWorkspaces(window);
_HideWindow(window);
_UpdateFronts();
} else
_WindowChanged(window);
if (FocusWindow() == window)
SetFocusWindow();
_WindowRemoved(window);
if (window->HasWorkspacesViews()) {
BObjectList<WorkspacesView> list(false);
window->FindWorkspacesViews(list);
BAutolock _(fWorkspacesLock);
while (WorkspacesView* view = list.RemoveItemAt(0)) {
fWorkspacesViews.RemoveItem(view);
}
}
NotifyWindowHidden(window, fromMinimize);
UnlockAllWindows();
if (window == fWindowUnderMouse)
_SendFakeMouseMoved();
}
void
Desktop::MinimizeWindow(Window* window, bool minimize)
{
if (!LockAllWindows())
return;
if (minimize && !window->IsHidden()) {
HideWindow(window, true);
window->SetMinimized(minimize);
NotifyWindowMinimized(window, minimize);
} else if (!minimize && window->IsHidden()) {
ActivateWindow(window);
NotifyWindowMinimized(window, minimize);
}
UnlockAllWindows();
}
void
Desktop::MoveWindowBy(Window* window, float x, float y, int32 workspace)
{
if (x == 0 && y == 0)
return;
AutoWriteLocker _(fWindowLock);
Window* topWindow = window->TopLayerStackWindow();
if (topWindow != NULL)
window = topWindow;
if (workspace == -1)
workspace = fCurrentWorkspace;
if (!window->IsVisible() || workspace != fCurrentWorkspace) {
if (workspace != fCurrentWorkspace) {
WindowStack* stack = window->GetWindowStack();
if (stack != NULL) {
for (int32 s = 0; s < stack->CountWindows(); s++) {
Window* stackWindow = stack->WindowAt(s);
if (stackWindow->Anchor(workspace).position
== kInvalidWindowPosition) {
stackWindow->Anchor(workspace).position
= stackWindow->Frame().LeftTop();
}
stackWindow->Anchor(workspace).position += BPoint(x, y);
stackWindow->SetPriorWorkspace(workspace);
_WindowChanged(stackWindow);
}
}
} else
window->MoveBy((int32)x, (int32)y);
NotifyWindowMoved(window);
return;
}
BRegion newDirtyRegion(window->VisibleRegion());
bool direct = false;
if (window->ServerWindow()->IsDirectlyAccessing()) {
window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
direct = true;
}
window->MoveBy((int32)x, (int32)y);
BRegion background;
_RebuildClippingForAllWindows(background);
BRegion copyRegion(window->VisibleRegion());
copyRegion.OffsetBy((int32)-x, (int32)-y);
copyRegion.IntersectWith(&newDirtyRegion);
newDirtyRegion.Include(&window->VisibleRegion());
if (GetDrawingEngine()->LockParallelAccess()) {
GetDrawingEngine()->CopyRegion(©Region, (int32)x, (int32)y);
GetDrawingEngine()->UnlockParallelAccess();
}
copyRegion.OffsetBy((int32)x, (int32)y);
newDirtyRegion.Exclude(©Region);
MarkDirty(newDirtyRegion);
_SetBackground(background);
_WindowChanged(window);
if (direct) {
window->ServerWindow()->HandleDirectConnection(
B_DIRECT_START | B_BUFFER_MOVED | B_CLIPPING_MODIFIED);
}
NotifyWindowMoved(window);
}
void
Desktop::ResizeWindowBy(Window* window, float x, float y)
{
if (x == 0 && y == 0)
return;
AutoWriteLocker _(fWindowLock);
Window* topWindow = window->TopLayerStackWindow();
if (topWindow)
window = topWindow;
if (!window->IsVisible()) {
window->ResizeBy((int32)x, (int32)y, NULL);
NotifyWindowResized(window);
return;
}
BRegion newDirtyRegion;
BRegion previouslyOccupiedRegion(window->VisibleRegion());
BRegion previousVisibleContentRegion(window->VisibleContentRegion());
bool direct = false;
if (window->ServerWindow()->IsDirectlyAccessing()) {
window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
direct = true;
}
window->ResizeBy((int32)x, (int32)y, &newDirtyRegion);
BRegion background;
_RebuildClippingForAllWindows(background);
previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
newDirtyRegion.IntersectWith(&window->VisibleRegion());
newDirtyRegion.Include(&previouslyOccupiedRegion);
BRegion exposeRegion(previouslyOccupiedRegion);
exposeRegion.Exclude(&window->VisibleRegion());
BRegion tmp(window->VisibleContentRegion());
tmp.Exclude(&previousVisibleContentRegion);
exposeRegion.Include(&tmp);
MarkDirty(newDirtyRegion, exposeRegion);
_SetBackground(background);
_WindowChanged(window);
if (direct) {
window->ServerWindow()->HandleDirectConnection(
B_DIRECT_START | B_BUFFER_RESIZED | B_CLIPPING_MODIFIED);
}
NotifyWindowResized(window);
}
void
Desktop::SetWindowOutlinesDelta(Window* window, BPoint delta)
{
AutoWriteLocker _(fWindowLock);
if (!window->IsVisible())
return;
BRegion newDirtyRegion;
window->SetOutlinesDelta(delta, &newDirtyRegion);
BRegion background;
_RebuildClippingForAllWindows(background);
MarkDirty(newDirtyRegion);
_SetBackground(background);
}
bool
Desktop::SetWindowTabLocation(Window* window, float location, bool isShifting)
{
AutoWriteLocker _(fWindowLock);
BRegion dirty;
bool changed = window->SetTabLocation(location, isShifting, dirty);
if (changed)
RebuildAndRedrawAfterWindowChange(window, dirty);
NotifyWindowTabLocationChanged(window, location, isShifting);
return changed;
}
bool
Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings)
{
AutoWriteLocker _(fWindowLock);
BRegion dirty;
bool changed = window->SetDecoratorSettings(settings, dirty);
bool listenerChanged = SetDecoratorSettings(window, settings);
if (changed || listenerChanged)
RebuildAndRedrawAfterWindowChange(window, dirty);
return changed;
}
void
Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces)
{
LockAllWindows();
if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
workspaces = workspace_to_workspaces(CurrentWorkspace());
WindowStack* stack = window->GetWindowStack();
if (stack != NULL) {
for (int32 s = 0; s < stack->CountWindows(); s++) {
window = stack->LayerOrder().ItemAt(s);
uint32 oldWorkspaces = window->Workspaces();
window->WorkspacesChanged(oldWorkspaces, workspaces);
_ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
}
}
UnlockAllWindows();
}
void
Desktop::AddWindow(Window *window)
{
LockAllWindows();
fAllWindows.AddWindow(window);
if (!window->IsNormal())
fSubsetWindows.AddWindow(window);
if (window->IsNormal()) {
if (window->Workspaces() == B_CURRENT_WORKSPACE)
window->SetWorkspaces(workspace_to_workspaces(CurrentWorkspace()));
} else {
window->SetWorkspaces(window->SubsetWorkspaces());
}
_ChangeWindowWorkspaces(window, 0, window->Workspaces());
NotifyWindowAdded(window);
UnlockAllWindows();
}
void
Desktop::RemoveWindow(Window *window)
{
LockAllWindows();
if (!window->IsHidden())
HideWindow(window);
fAllWindows.RemoveWindow(window);
if (!window->IsNormal())
fSubsetWindows.RemoveWindow(window);
_ChangeWindowWorkspaces(window, window->Workspaces(), 0);
NotifyWindowRemoved(window);
UnlockAllWindows();
EventDispatcher().RemoveTarget(window->EventTarget());
}
bool
Desktop::AddWindowToSubset(Window* subset, Window* window)
{
if (!subset->AddToSubset(window))
return false;
_ChangeWindowWorkspaces(subset, subset->Workspaces(),
subset->SubsetWorkspaces());
return true;
}
void
Desktop::RemoveWindowFromSubset(Window* subset, Window* window)
{
subset->RemoveFromSubset(window);
_ChangeWindowWorkspaces(subset, subset->Workspaces(),
subset->SubsetWorkspaces());
}
void
Desktop::FontsChanged(Window* window)
{
AutoWriteLocker _(fWindowLock);
BRegion dirty;
window->FontsChanged(&dirty);
RebuildAndRedrawAfterWindowChange(window, dirty);
fCursorManager.InitializeCursors(fSettings->DefaultBoldFont().Size() / 12.0f);
}
void
Desktop::ColorUpdated(Window* window, color_which which, rgb_color color)
{
AutoWriteLocker _(fWindowLock);
window->TopView()->ColorUpdated(which, color);
switch (which) {
case B_WINDOW_TAB_COLOR:
case B_WINDOW_TEXT_COLOR:
case B_WINDOW_INACTIVE_TAB_COLOR:
case B_WINDOW_INACTIVE_TEXT_COLOR:
case B_WINDOW_BORDER_COLOR:
case B_WINDOW_INACTIVE_BORDER_COLOR:
break;
default:
return;
}
BRegion dirty;
window->ColorsChanged(&dirty);
RebuildAndRedrawAfterWindowChange(window, dirty);
}
void
Desktop::SetWindowLook(Window* window, window_look newLook)
{
if (window->Look() == newLook)
return;
AutoWriteLocker _(fWindowLock);
BRegion dirty;
window->SetLook(newLook, &dirty);
RebuildAndRedrawAfterWindowChange(window, dirty);
NotifyWindowLookChanged(window, newLook);
}
void
Desktop::SetWindowFeel(Window* window, window_feel newFeel)
{
if (window->Feel() == newFeel)
return;
LockAllWindows();
bool wasNormal = window->IsNormal();
window->SetFeel(newFeel);
if (window->IsNormal() && !wasNormal)
fSubsetWindows.RemoveWindow(window);
else if (!window->IsNormal() && wasNormal)
fSubsetWindows.AddWindow(window);
if (!window->IsNormal()) {
_ChangeWindowWorkspaces(window, window->Workspaces(),
window->SubsetWorkspaces());
}
for (int32 i = 0; i < kMaxWorkspaces; i++) {
if (!workspace_in_workspaces(i, window->Workspaces()))
continue;
bool changed = false;
BRegion visibleBefore;
if (i == fCurrentWorkspace && window->IsVisible())
visibleBefore = window->VisibleRegion();
Window* backmost = window->Backmost(_Windows(i).LastWindow(), i);
if (backmost != NULL) {
Window* previous = window->PreviousWindow(i);
while (previous != NULL) {
if (previous == backmost)
break;
previous = previous->PreviousWindow(i);
}
if (previous == NULL) {
_Windows(i).RemoveWindow(window);
_Windows(i).AddWindow(window, backmost->NextWindow(i));
changed = true;
}
}
Window* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
if (frontmost != NULL) {
Window* next = window->NextWindow(i);
while (next != NULL) {
if (next == frontmost)
break;
next = next->NextWindow(i);
}
if (next == NULL) {
_Windows(i).RemoveWindow(window);
_Windows(i).AddWindow(window, frontmost);
changed = true;
}
}
if (i == fCurrentWorkspace && changed) {
BRegion dummy;
_RebuildClippingForAllWindows(dummy);
BRegion visibleAfter(window->VisibleRegion());
BRegion dirty(visibleAfter);
dirty.Exclude(&visibleBefore);
visibleBefore.Exclude(&visibleAfter);
dirty.Include(&visibleBefore);
MarkDirty(dirty);
}
}
_UpdateFronts();
if (window == FocusWindow() && !window->IsVisible())
SetFocusWindow();
NotifyWindowFeelChanged(window, newFeel);
UnlockAllWindows();
}
void
Desktop::SetWindowFlags(Window *window, uint32 newFlags)
{
if (window->Flags() == newFlags)
return;
AutoWriteLocker _(fWindowLock);
BRegion dirty;
window->SetFlags(newFlags, &dirty);
RebuildAndRedrawAfterWindowChange(window, dirty);
}
void
Desktop::SetWindowTitle(Window *window, const char* title)
{
AutoWriteLocker _(fWindowLock);
BRegion dirty;
window->SetTitle(title, dirty);
RebuildAndRedrawAfterWindowChange(window, dirty);
}
Window*
Desktop::WindowAt(BPoint where)
{
for (Window* window = CurrentWindows().LastWindow(); window;
window = window->PreviousWindow(fCurrentWorkspace)) {
if (window->IsVisible() && window->VisibleRegion().Contains(where))
return window->StackedWindowAt(where);
}
return NULL;
}
void
Desktop::SetMouseEventWindow(Window* window)
{
fMouseEventWindow = window;
}
void
Desktop::SetViewUnderMouse(const Window* window, int32 viewToken)
{
fWindowUnderMouse = window;
fViewUnderMouse = viewToken;
}
int32
Desktop::ViewUnderMouse(const Window* window)
{
if (window != NULL && fWindowUnderMouse == window)
return fViewUnderMouse;
return B_NULL_TOKEN;
}
EventTarget*
Desktop::KeyboardEventTarget()
{
Window* window = CurrentWindows().LastWindow();
while (window != NULL && window->IsHidden()) {
window = window->PreviousWindow(fCurrentWorkspace);
}
if (window != NULL && (window->Flags() & kAcceptKeyboardFocusFlag) != 0)
return &window->EventTarget();
if (FocusWindow() != NULL)
return &FocusWindow()->EventTarget();
return NULL;
}
void
Desktop::SetFocusWindow(Window* nextFocus)
{
if (!LockAllWindows())
return;
if (fLockedFocusWindow && nextFocus != fLockedFocusWindow) {
UnlockAllWindows();
return;
}
bool hasModal = _WindowHasModal(nextFocus);
bool hasWindowScreen = false;
if (!hasModal && nextFocus != NULL) {
Window* window = nextFocus;
while (true) {
window = window->NextWindow(fCurrentWorkspace);
if (window == NULL || window->Feel() == kWindowScreenFeel)
break;
}
if (window != NULL)
hasWindowScreen = true;
}
if (nextFocus == fFocus && nextFocus != NULL && !nextFocus->IsHidden()
&& (nextFocus->Flags() & B_AVOID_FOCUS) == 0
&& !hasModal && !hasWindowScreen) {
UnlockAllWindows();
return;
}
uint32 listIndex = fCurrentWorkspace;
WindowList* list = &_Windows(fCurrentWorkspace);
if (!fSettings->NormalMouse()) {
listIndex = kFocusList;
list = &fFocusList;
}
if (nextFocus == NULL || hasModal || hasWindowScreen) {
nextFocus = list->LastWindow();
if (fSettings->NormalMouse()) {
Window* lastFocus = fFocusList.LastWindow();
if (lastFocus != NULL && !lastFocus->SupportsFront()
&& _WindowCanHaveFocus(lastFocus)) {
nextFocus = lastFocus;
}
}
}
while (nextFocus != NULL && !_WindowCanHaveFocus(nextFocus)) {
nextFocus = nextFocus->PreviousWindow(listIndex);
}
if (fFocus == nextFocus) {
UnlockAllWindows();
return;
}
team_id oldActiveApp = -1;
team_id newActiveApp = -1;
if (fFocus != NULL) {
fFocus->SetFocus(false);
oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
}
fFocus = nextFocus;
if (fFocus != NULL) {
fFocus->SetFocus(true);
newActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
fFocusList.RemoveWindow(fFocus);
fFocusList.AddWindow(fFocus);
}
if (newActiveApp == -1) {
HWInterface()->SetCursorVisible(true);
}
UnlockAllWindows();
if (oldActiveApp == newActiveApp)
return;
BAutolock locker(fApplicationsLock);
for (int32 i = 0; i < fApplications.CountItems(); i++) {
ServerApp* app = fApplications.ItemAt(i);
if (oldActiveApp != -1 && app->ClientTeam() == oldActiveApp)
app->Activate(false);
else if (newActiveApp != -1 && app->ClientTeam() == newActiveApp)
app->Activate(true);
}
}
void
Desktop::SetFocusLocked(const Window* window)
{
AutoWriteLocker _(fWindowLock);
if (window != NULL) {
if (fLastMouseButtons == 0)
return;
}
fLockedFocusWindow = window;
}
Window*
Desktop::FindWindowByClientToken(int32 token, team_id teamID)
{
for (Window *window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (window->ServerWindow()->ClientToken() == token
&& window->ServerWindow()->ClientTeam() == teamID) {
return window;
}
}
return NULL;
}
::EventTarget*
Desktop::FindTarget(BMessenger& messenger)
{
for (Window *window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (window->EventTarget().Messenger() == messenger)
return &window->EventTarget();
}
return NULL;
}
void
Desktop::MarkDirty(BRegion& dirtyRegion, BRegion& exposeRegion)
{
if (dirtyRegion.CountRects() == 0)
return;
if (LockAllWindows()) {
_TriggerWindowRedrawing(dirtyRegion, exposeRegion);
UnlockAllWindows();
}
}
void
Desktop::Redraw()
{
BRegion dirty(fVirtualScreen.Frame());
MarkDirty(dirty);
}
void
Desktop::RedrawBackground()
{
LockAllWindows();
BRegion redraw;
Window* window = CurrentWindows().FirstWindow();
if (window != NULL && window->Feel() == kDesktopWindowFeel) {
redraw = window->VisibleContentRegion();
View* view = window->TopView();
if (view != NULL)
view = view->FirstChild();
while (view != NULL) {
if (view->IsDesktopBackground()) {
view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color());
break;
}
view = view->NextSibling();
}
window->ProcessDirtyRegion(redraw);
} else {
redraw = BackgroundRegion();
fBackgroundRegion.MakeEmpty();
_SetBackground(redraw);
}
_WindowChanged(NULL);
UnlockAllWindows();
}
bool
Desktop::ReloadDecor(DecorAddOn* oldDecor)
{
AutoWriteLocker _(fWindowLock);
bool returnValue = true;
if (oldDecor != NULL) {
const DesktopListenerList* oldListeners
= &oldDecor->GetDesktopListeners();
for (int i = 0; i < oldListeners->CountItems(); i++)
UnregisterListener(oldListeners->ItemAt(i));
}
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
BRegion oldBorder;
window->GetBorderRegion(&oldBorder);
if (!window->ReloadDecor()) {
returnValue = false;
}
BRegion border;
window->GetBorderRegion(&border);
border.Include(&oldBorder);
RebuildAndRedrawAfterWindowChange(window, border);
}
const DesktopListenerList& newListeners
= gDecorManager.GetDesktopListeners();
for (int i = 0; i < newListeners.CountItems(); i++)
RegisterListener(newListeners.ItemAt(i));
return returnValue;
}
void
Desktop::MinimizeApplication(team_id team)
{
AutoWriteLocker locker(fWindowLock);
for (Window *window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (window->ServerWindow()->ClientTeam() != team)
continue;
window->ServerWindow()->NotifyMinimize(true);
}
}
void
Desktop::BringApplicationToFront(team_id team)
{
AutoWriteLocker locker(fWindowLock);
for (Window *window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (window->ServerWindow()->ClientTeam() != team)
continue;
window->ServerWindow()->NotifyMinimize(false);
}
}
void
Desktop::WindowAction(int32 windowToken, int32 action)
{
if (action != B_MINIMIZE_WINDOW && action != B_BRING_TO_FRONT)
return;
LockAllWindows();
::ServerWindow* serverWindow;
Window* window;
if (BPrivate::gDefaultTokens.GetToken(windowToken,
B_SERVER_TOKEN, (void**)&serverWindow) != B_OK
|| (window = serverWindow->Window()) == NULL) {
UnlockAllWindows();
return;
}
if (action == B_BRING_TO_FRONT && !window->IsMinimized()) {
ActivateWindow(window);
} else {
serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
}
UnlockAllWindows();
}
void
Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
{
AutoWriteLocker locker(fWindowLock);
int32 count = 0;
for (Window *window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
count++;
}
sender.StartMessage(B_OK);
sender.Attach<int32>(count);
for (Window *window = CurrentWindows().LastWindow(); window != NULL;
window = window->PreviousWindow(fCurrentWorkspace)) {
if (team >= B_OK && window->ServerWindow()->ClientTeam() != team)
continue;
sender.Attach<int32>(window->ServerWindow()->ServerToken());
}
for (Window *window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if ((team >= B_OK && window->ServerWindow()->ClientTeam() != team)
|| window->InWorkspace(fCurrentWorkspace))
continue;
sender.Attach<int32>(window->ServerWindow()->ServerToken());
}
sender.Flush();
}
void
Desktop::WriteWindowInfo(int32 serverToken, BPrivate::LinkSender& sender)
{
AutoWriteLocker locker(fWindowLock);
PthreadMutexLocker tokenLocker(BPrivate::gDefaultTokens.GetLock());
::ServerWindow* window;
if (BPrivate::gDefaultTokens.GetToken(serverToken,
B_SERVER_TOKEN, (void**)&window) != B_OK) {
sender.StartMessage(B_ENTRY_NOT_FOUND);
sender.Flush();
return;
}
window_info info;
window->GetInfo(info);
float tabSize = 0.0;
float borderSize = 0.0;
::Window* tmp = window->Window();
if (tmp) {
BMessage message;
if (tmp->GetDecoratorSettings(&message)) {
BRect tabFrame;
message.FindRect("tab frame", &tabFrame);
tabSize = tabFrame.bottom - tabFrame.top;
message.FindFloat("border width", &borderSize);
}
}
int32 length = window->Title() ? strlen(window->Title()) : 0;
sender.StartMessage(B_OK);
sender.Attach<int32>(sizeof(client_window_info) + length + 1);
sender.Attach(&info, sizeof(window_info));
sender.Attach<float>(tabSize);
sender.Attach<float>(borderSize);
if (length > 0)
sender.Attach(window->Title(), length + 1);
else
sender.Attach<char>('\0');
sender.Flush();
}
void
Desktop::WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender)
{
LockSingleWindow();
if (workspace < 0)
workspace = fCurrentWorkspace;
else if (workspace >= kMaxWorkspaces) {
sender.StartMessage(B_BAD_VALUE);
sender.Flush();
UnlockSingleWindow();
return;
}
int32 count = _Windows(workspace).Count();
sender.StartMessage(B_OK);
sender.Attach<int32>(count);
for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
window = window->PreviousWindow(workspace)) {
sender.Attach<int32>(window->ServerWindow()->ServerToken());
}
sender.Flush();
UnlockSingleWindow();
}
void
Desktop::WriteApplicationOrder(int32 workspace, BPrivate::LinkSender& sender)
{
fApplicationsLock.Lock();
LockSingleWindow();
int32 maxCount = fApplications.CountItems();
fApplicationsLock.Unlock();
if (workspace < 0)
workspace = fCurrentWorkspace;
else if (workspace >= kMaxWorkspaces) {
sender.StartMessage(B_BAD_VALUE);
sender.Flush();
UnlockSingleWindow();
return;
}
team_id* teams = (team_id*)malloc(maxCount * sizeof(team_id));
if (teams == NULL) {
sender.StartMessage(B_NO_MEMORY);
sender.Flush();
UnlockSingleWindow();
return;
}
int32 count = 0;
for (Window *window = _Windows(workspace).LastWindow(); window != NULL;
window = window->PreviousWindow(workspace)) {
team_id team = window->ServerWindow()->ClientTeam();
if (count > 1) {
bool found = false;
for (int32 i = 0; i < count; i++) {
if (teams[i] == team) {
found = true;
break;
}
}
if (found)
continue;
}
ASSERT(count < maxCount);
teams[count++] = team;
}
UnlockSingleWindow();
sender.StartMessage(B_OK);
sender.Attach<int32>(count);
for (int32 i = 0; i < count; i++) {
sender.Attach<int32>(teams[i]);
}
sender.Flush();
free(teams);
}
void
Desktop::_LaunchInputServer()
{
BRoster roster;
status_t status = roster.Launch("application/x-vnd.Be-input_server");
if (status == B_OK || status == B_ALREADY_RUNNING)
return;
BEntry entry;
BPath inputServerPath;
if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, &inputServerPath) == B_OK
&& inputServerPath.Append("input_server") == B_OK) {
entry.SetTo(inputServerPath.Path());
} else
entry.SetTo("/system/servers/input_server");
entry_ref ref;
status_t entryStatus = entry.GetRef(&ref);
if (entryStatus == B_OK)
entryStatus = roster.Launch(&ref);
if (entryStatus == B_OK || entryStatus == B_ALREADY_RUNNING) {
syslog(LOG_ERR, "Failed to launch the input server by signature: %s!\n",
strerror(status));
return;
}
syslog(LOG_ERR, "Failed to launch the input server: %s!\n",
strerror(entryStatus));
}
void
Desktop::_GetLooperName(char* name, size_t length)
{
snprintf(name, length, "d:%d:%s", fUserID,
fTargetScreen == NULL ? "baron" : fTargetScreen);
}
void
Desktop::_PrepareQuit()
{
fApplicationsLock.Lock();
int32 count = fApplications.CountItems();
for (int32 i = 0; i < count; i++) {
ServerApp *app = fApplications.ItemAt(i);
team_id clientTeam = app->ClientTeam();
app->Quit();
kill_team(clientTeam);
}
if (count > 0) {
acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT,
250000);
}
fApplicationsLock.Unlock();
}
void
Desktop::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
{
switch (code) {
case AS_CREATE_APP:
{
team_id clientTeamID = -1;
port_id clientLooperPort = -1;
port_id clientReplyPort = -1;
int32 htoken = B_NULL_TOKEN;
char* appSignature = NULL;
link.Read<port_id>(&clientReplyPort);
link.Read<port_id>(&clientLooperPort);
link.Read<team_id>(&clientTeamID);
link.Read<int32>(&htoken);
if (link.ReadString(&appSignature) != B_OK)
break;
ObjectDeleter<ServerApp> app(new (std::nothrow) ServerApp(this, clientReplyPort,
clientLooperPort, clientTeamID, htoken, appSignature));
status_t status = B_OK;
if (!app.IsSet())
status = B_NO_MEMORY;
if (status == B_OK)
status = app->InitCheck();
if (status == B_OK)
status = app->Run();
if (status == B_OK) {
fApplicationsLock.Lock();
fApplications.AddItem(app.Detach());
fApplicationsLock.Unlock();
} else {
BPrivate::LinkSender reply(clientReplyPort);
reply.StartMessage(status);
reply.Flush();
}
free(appSignature);
break;
}
case AS_DELETE_APP:
{
thread_id thread = -1;
if (link.Read<thread_id>(&thread) < B_OK)
break;
fApplicationsLock.Lock();
int32 count = fApplications.CountItems();
ServerApp* removeApp = NULL;
for (int32 i = 0; i < count; i++) {
ServerApp* app = fApplications.ItemAt(i);
if (app->Thread() == thread) {
fApplications.RemoveItemAt(i);
removeApp = app;
break;
}
}
fApplicationsLock.Unlock();
if (removeApp != NULL)
removeApp->Quit(fShutdownSemaphore);
if (fQuitting && count <= 1) {
acquire_sem_etc(fShutdownSemaphore, fShutdownCount,
B_RELATIVE_TIMEOUT, 500000);
PostMessage(kMsgQuitLooper);
}
break;
}
case AS_ACTIVATE_APP:
{
status_t status;
port_id replyPort;
team_id team;
if (link.Read(&replyPort) == B_OK
&& link.Read(&team) == B_OK)
status = _ActivateApp(team);
else
status = B_ERROR;
BPrivate::PortLink replyLink(replyPort);
replyLink.StartMessage(status);
replyLink.Flush();
break;
}
case AS_APP_CRASHED:
case AS_DUMP_ALLOCATOR:
case AS_DUMP_BITMAPS:
{
BAutolock locker(fApplicationsLock);
team_id team;
if (link.Read(&team) != B_OK)
break;
for (int32 i = 0; i < fApplications.CountItems(); i++) {
ServerApp* app = fApplications.ItemAt(i);
if (app->ClientTeam() == team)
app->PostMessage(code);
}
break;
}
case AS_EVENT_STREAM_CLOSED:
_LaunchInputServer();
break;
case B_QUIT_REQUESTED:
fApplicationsLock.Lock();
fShutdownSemaphore = create_sem(0, "desktop shutdown");
fShutdownCount = fApplications.CountItems();
fApplicationsLock.Unlock();
fQuitting = true;
BroadcastToAllApps(AS_QUIT_APP);
if (fShutdownCount == 0)
PostMessage(kMsgQuitLooper);
break;
case AS_ACTIVATE_WORKSPACE:
{
int32 index;
link.Read<int32>(&index);
if (index == -1)
index = fPreviousWorkspace;
bool moveFocusWindow;
link.Read<bool>(&moveFocusWindow);
SetWorkspace(index, moveFocusWindow);
break;
}
case AS_TALK_TO_DESKTOP_LISTENER:
{
port_id clientReplyPort;
if (link.Read<port_id>(&clientReplyPort) != B_OK)
break;
BPrivate::LinkSender reply(clientReplyPort);
AutoWriteLocker locker(fWindowLock);
if (MessageForListener(NULL, link, reply) != true) {
if (link.NeedsReply()) {
reply.StartMessage(B_ERROR);
reply.Flush();
}
}
break;
}
case AS_SET_UI_COLOR:
{
color_which which;
rgb_color color;
if (link.Read<color_which>(&which) == B_OK
&& link.Read<rgb_color>(&color) == B_OK) {
const char* colorName = ui_color_name(which);
fPendingColors.SetColor(colorName, color);
DelayedMessage delayed(AS_SET_UI_COLORS, DM_60HZ_DELAY);
delayed.AddTarget(MessagePort());
delayed.SetMerge(DM_MERGE_CANCEL);
delayed.Attach<bool>(true);
delayed.Flush();
}
break;
}
case AS_SET_UI_COLORS:
{
bool flushPendingOnly = false;
if (link.Read<bool>(&flushPendingOnly) != B_OK
|| (flushPendingOnly &&
fPendingColors.CountNames(B_RGB_32_BIT_TYPE) == 0)) {
break;
}
if (!flushPendingOnly) {
color_which which = B_NO_COLOR;
rgb_color color;
do {
if (link.Read<color_which>(&which) != B_OK
|| link.Read<rgb_color>(&color) != B_OK)
break;
fPendingColors.SetColor(ui_color_name(which), color);
} while (which != B_NO_COLOR);
}
_FlushPendingColors();
break;
}
case 'KDLE':
{
BRegion dirty;
dirty.Include(fVirtualScreen.Frame());
MarkDirty(dirty);
break;
}
default:
printf("Desktop %d:%s received unexpected code %" B_PRId32 "\n", 0,
"baron", code);
if (link.NeedsReply()) {
fLink.StartMessage(B_ERROR);
fLink.Flush();
}
break;
}
}
WindowList&
Desktop::CurrentWindows()
{
return fWorkspaces[fCurrentWorkspace].Windows();
}
WindowList&
Desktop::AllWindows()
{
return fAllWindows;
}
Window*
Desktop::WindowForClientLooperPort(port_id port)
{
ASSERT_MULTI_LOCKED(fWindowLock);
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (window->ServerWindow()->ClientLooperPort() == port)
return window;
}
return NULL;
}
WindowList&
Desktop::_Windows(int32 index)
{
ASSERT(index >= 0 && index < kMaxWorkspaces);
return fWorkspaces[index].Windows();
}
void
Desktop::_FlushPendingColors()
{
int32 count = fPendingColors.CountNames(B_RGB_32_BIT_TYPE);
if (count == 0)
return;
bool changed[count];
LockedDesktopSettings settings(this);
settings.SetUIColors(fPendingColors, &changed[0]);
int32 index = 0;
char* name = NULL;
type_code type = B_RGB_32_BIT_TYPE;
rgb_color color;
color_which which = B_NO_COLOR;
BMessage clientMessage(B_COLORS_UPDATED);
while (fPendingColors.GetInfo(type, index, &name, &type) == B_OK) {
which = which_ui_color(name);
if (which == B_NO_COLOR || fPendingColors.FindColor(name,
&color) != B_OK || !changed[index]) {
++index;
continue;
}
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
ColorUpdated(window, which, color);
}
clientMessage.AddColor(name, color);
++index;
}
BAutolock appListLock(fApplicationsLock);
for (int32 index = 0; index < fApplications.CountItems(); ++index) {
fApplications.ItemAt(index)->SendMessageToClient(&clientMessage);
}
fPendingColors.MakeEmpty();
}
void
Desktop::_UpdateFloating(int32 previousWorkspace, int32 nextWorkspace,
Window* mouseEventWindow)
{
if (previousWorkspace == -1)
previousWorkspace = fCurrentWorkspace;
if (nextWorkspace == -1)
nextWorkspace = previousWorkspace;
for (Window* floating = fSubsetWindows.FirstWindow(); floating != NULL;
floating = floating->NextWindow(kSubsetList)) {
if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
&& floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
continue;
if (fFront != NULL && fFront->IsNormal()
&& floating->HasInSubset(fFront)) {
if (_Windows(previousWorkspace).HasWindow(floating)
&& previousWorkspace != nextWorkspace
&& !floating->InSubsetWorkspace(previousWorkspace)) {
_Windows(previousWorkspace).RemoveWindow(floating);
floating->SetCurrentWorkspace(-1);
}
if (!_Windows(nextWorkspace).HasWindow(floating)) {
_Windows(nextWorkspace).AddWindow(floating,
floating->Frontmost(_Windows(nextWorkspace).FirstWindow(),
nextWorkspace));
floating->SetCurrentWorkspace(nextWorkspace);
if (mouseEventWindow != fFront)
_ShowWindow(floating);
}
} else if (_Windows(previousWorkspace).HasWindow(floating)
&& !floating->InSubsetWorkspace(previousWorkspace)) {
_Windows(previousWorkspace).RemoveWindow(floating);
floating->SetCurrentWorkspace(-1);
_HideWindow(floating);
if (FocusWindow() == floating)
SetFocusWindow();
}
}
}
void
Desktop::_UpdateBack()
{
fBack = NULL;
for (Window* window = CurrentWindows().FirstWindow(); window != NULL;
window = window->NextWindow(fCurrentWorkspace)) {
if (window->IsHidden() || window->Feel() == kDesktopWindowFeel)
continue;
fBack = window;
break;
}
}
void
Desktop::_UpdateFront(bool updateFloating)
{
fFront = NULL;
for (Window* window = CurrentWindows().LastWindow(); window != NULL;
window = window->PreviousWindow(fCurrentWorkspace)) {
if (window->IsHidden() || window->IsFloating()
|| !window->SupportsFront())
continue;
fFront = window;
break;
}
if (updateFloating)
_UpdateFloating();
}
void
Desktop::_UpdateFronts(bool updateFloating)
{
_UpdateBack();
_UpdateFront(updateFloating);
}
bool
Desktop::_WindowHasModal(Window* window) const
{
if (window == NULL)
return false;
for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL;
modal = modal->NextWindow(kSubsetList)) {
if (!modal->IsModal() || modal->IsHidden())
continue;
if (modal->HasInSubset(window))
return true;
}
return false;
}
bool
Desktop::_WindowCanHaveFocus(Window* window) const
{
return window != NULL
&& window->InWorkspace(fCurrentWorkspace)
&& (window->Flags() & B_AVOID_FOCUS) == 0
&& !_WindowHasModal(window)
&& !window->IsHidden();
}
void
Desktop::_WindowChanged(Window* window)
{
ASSERT_MULTI_LOCKED(fWindowLock);
BAutolock _(fWorkspacesLock);
for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
WorkspacesView* view = fWorkspacesViews.ItemAt(i);
view->WindowChanged(window);
}
}
void
Desktop::_WindowRemoved(Window* window)
{
ASSERT_MULTI_LOCKED(fWindowLock);
BAutolock _(fWorkspacesLock);
for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
WorkspacesView* view = fWorkspacesViews.ItemAt(i);
view->WindowRemoved(window);
}
}
void
Desktop::_ShowWindow(Window* window, bool affectsOtherWindows)
{
BRegion background;
_RebuildClippingForAllWindows(background);
_SetBackground(background);
_WindowChanged(window);
BRegion dirty(window->VisibleRegion());
if (!affectsOtherWindows) {
window->ProcessDirtyRegion(dirty);
} else
MarkDirty(dirty);
if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
window->ServerWindow()->HandleDirectConnection(
B_DIRECT_START | B_BUFFER_RESET);
}
}
void
Desktop::_HideWindow(Window* window)
{
if (window->ServerWindow()->IsDirectlyAccessing())
window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
BRegion dirty(window->VisibleRegion());
BRegion background;
_RebuildClippingForAllWindows(background);
_SetBackground(background);
_WindowChanged(window);
MarkDirty(dirty);
}
void
Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex,
int32 newIndex)
{
STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window,
window->Title()));
if (!window->IsNormal() || window->IsHidden())
return;
for (Window* subset = fSubsetWindows.FirstWindow(); subset != NULL;
subset = subset->NextWindow(kSubsetList)) {
if (subset->Feel() == B_MODAL_ALL_WINDOW_FEEL
|| subset->Feel() == B_FLOATING_ALL_WINDOW_FEEL) {
continue;
}
if (subset->IsFloating()) {
continue;
}
if (subset->HasInSubset(window)) {
SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
}
}
}
void
Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces,
uint32 newWorkspaces)
{
if (oldWorkspaces == newWorkspaces)
return;
LockAllWindows();
for (int32 i = 0; i < kMaxWorkspaces; i++) {
if (workspace_in_workspaces(i, oldWorkspaces)) {
if (!workspace_in_workspaces(i, newWorkspaces)) {
_Windows(i).RemoveWindow(window);
if (fLastWorkspaceFocus[i] == window)
fLastWorkspaceFocus[i] = NULL;
if (i == CurrentWorkspace()) {
window->SetCurrentWorkspace(-1);
if (!window->IsHidden())
_HideWindow(window);
}
}
} else {
if (workspace_in_workspaces(i, newWorkspaces)) {
_Windows(i).AddWindow(window,
window->Frontmost(_Windows(i).FirstWindow(), i));
if (i == CurrentWorkspace()) {
window->SetCurrentWorkspace(fCurrentWorkspace);
if (!window->IsHidden()) {
_ShowWindow(window, FrontWindow() == window);
}
}
}
}
}
int32 firstWorkspace = -1;
for (int32 i = 0; i < kMaxWorkspaces; i++) {
if ((newWorkspaces & (1L << i)) != 0) {
if (firstWorkspace != -1) {
firstWorkspace = -1;
break;
}
firstWorkspace = i;
}
}
if (firstWorkspace >= 0)
window->Anchor(firstWorkspace).position = window->Frame().LeftTop();
_UpdateSubsetWorkspaces(window);
NotifyWindowWorkspacesChanged(window, newWorkspaces);
UnlockAllWindows();
}
void
Desktop::_BringWindowsToFront(WindowList& windows, int32 list, bool wereVisible)
{
BRegion clean;
for (Window* window = windows.FirstWindow(); window != NULL;
window = window->NextWindow(list)) {
if (wereVisible)
clean.Include(&window->VisibleRegion());
CurrentWindows().AddWindow(window,
window->Frontmost(CurrentWindows().FirstWindow(),
fCurrentWorkspace));
_WindowChanged(window);
}
BRegion dummy;
_RebuildClippingForAllWindows(dummy);
BRegion dirty;
for (Window* window = windows.FirstWindow(); window != NULL;
window = window->NextWindow(list)) {
dirty.Include(&window->VisibleRegion());
}
dirty.Exclude(&clean);
MarkDirty(dirty);
_UpdateFront();
if (windows.FirstWindow() == fBack || fBack == NULL)
_UpdateBack();
}
Window*
Desktop::_LastFocusSubsetWindow(Window* window)
{
if (window == NULL)
return NULL;
for (Window* front = fFocusList.LastWindow(); front != NULL;
front = front->PreviousWindow(kFocusList)) {
if (front != window && !front->IsHidden()
&& window->HasInSubset(front))
return front;
}
return NULL;
}
bool
Desktop::_CheckSendFakeMouseMoved(const Window* lastWindowUnderMouse)
{
Window* window = WindowAt(fLastMousePosition);
return window != lastWindowUnderMouse;
}
void
Desktop::_SendFakeMouseMoved(Window* window)
{
int32 viewToken = B_NULL_TOKEN;
EventTarget* target = NULL;
LockAllWindows();
if (window == NULL)
window = WindowAt(fLastMousePosition);
if (window != NULL) {
BMessage message;
window->MouseMoved(&message, fLastMousePosition, &viewToken, true,
true);
if (viewToken != B_NULL_TOKEN)
target = &window->EventTarget();
}
if (viewToken != B_NULL_TOKEN)
SetViewUnderMouse(window, viewToken);
else {
SetViewUnderMouse(NULL, B_NULL_TOKEN);
SetCursor(NULL);
}
UnlockAllWindows();
if (target != NULL)
EventDispatcher().SendFakeMouseMoved(*target, viewToken);
}
Screen*
Desktop::_DetermineScreenFor(BRect frame)
{
AutoReadLocker _(fScreenLock);
return fVirtualScreen.ScreenAt(0);
}
void
Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
{
stillAvailableOnScreen = fScreenRegion;
for (Window* window = CurrentWindows().LastWindow(); window != NULL;
window = window->PreviousWindow(fCurrentWorkspace)) {
if (!window->IsHidden()) {
window->SetClipping(&stillAvailableOnScreen);
window->SetScreen(_DetermineScreenFor(window->Frame()));
if (window->ServerWindow()->IsDirectlyAccessing()) {
window->ServerWindow()->HandleDirectConnection(
B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
}
stillAvailableOnScreen.Exclude(&window->VisibleRegion());
}
}
}
void
Desktop::_TriggerWindowRedrawing(BRegion& dirtyRegion, BRegion& exposeRegion)
{
for (Window* window = CurrentWindows().LastWindow(); window != NULL;
window = window->PreviousWindow(fCurrentWorkspace)) {
if (!window->IsHidden()
&& dirtyRegion.Intersects(window->VisibleRegion().Frame()))
window->ProcessDirtyRegion(dirtyRegion, exposeRegion);
}
}
void
Desktop::_SetBackground(BRegion& background)
{
BRegion dirtyBackground(background);
dirtyBackground.Exclude(&fBackgroundRegion);
dirtyBackground.IntersectWith(&background);
fBackgroundRegion = background;
if (dirtyBackground.Frame().IsValid()) {
if (GetDrawingEngine()->LockParallelAccess()) {
GetDrawingEngine()->FillRegion(dirtyBackground,
fWorkspaces[fCurrentWorkspace].Color());
GetDrawingEngine()->UnlockParallelAccess();
}
}
}
void
Desktop::RebuildAndRedrawAfterWindowChange(Window* changedWindow,
BRegion& dirty)
{
ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
if (!changedWindow->IsVisible() || dirty.CountRects() == 0)
return;
BRegion stillAvailableOnScreen(fScreenRegion);
for (Window* window = CurrentWindows().LastWindow(); window != NULL;
window = window->PreviousWindow(fCurrentWorkspace)) {
if (!window->IsHidden()) {
if (window == changedWindow)
dirty.IntersectWith(&stillAvailableOnScreen);
window->SetClipping(&stillAvailableOnScreen);
window->SetScreen(_DetermineScreenFor(window->Frame()));
if (window->ServerWindow()->IsDirectlyAccessing()) {
window->ServerWindow()->HandleDirectConnection(
B_DIRECT_MODIFY | B_CLIPPING_MODIFIED);
}
stillAvailableOnScreen.Exclude(&window->VisibleRegion());
}
}
_SetBackground(stillAvailableOnScreen);
_WindowChanged(changedWindow);
_TriggerWindowRedrawing(dirty, dirty);
}
void
Desktop::_SuspendDirectFrameBufferAccess()
{
ASSERT_MULTI_LOCKED(fWindowLock);
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (window->ServerWindow()->IsDirectlyAccessing())
window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
}
}
void
Desktop::_ResumeDirectFrameBufferAccess()
{
ASSERT_MULTI_LOCKED(fWindowLock);
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (window->IsHidden() || !window->InWorkspace(fCurrentWorkspace))
continue;
if (window->ServerWindow()->HasDirectFrameBufferAccess()) {
window->ServerWindow()->HandleDirectConnection(
B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
}
}
}
void
Desktop::ScreenChanged(Screen* screen)
{
AutoWriteLocker windowLocker(fWindowLock);
AutoWriteLocker screenLocker(fScreenLock);
screen->SetPreferredMode();
screenLocker.Unlock();
_ScreenChanged(screen);
}
void
Desktop::_ScreenChanged(Screen* screen)
{
ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
BRegion dirty(screen->Frame());
fScreenRegion.Set(screen->Frame());
gInputManager->UpdateScreenBounds(screen->Frame());
BRegion background;
_RebuildClippingForAllWindows(background);
fBackgroundRegion.MakeEmpty();
_SetBackground(background);
dirty.Exclude(&background);
_TriggerWindowRedrawing(dirty, dirty);
BMessage update(B_SCREEN_CHANGED);
update.AddInt64("when", real_time_clock_usecs());
update.AddRect("frame", screen->Frame());
update.AddInt32("mode", screen->ColorSpace());
fVirtualScreen.UpdateFrame();
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (window->Screen() == screen)
window->ServerWindow()->ScreenChanged(&update);
}
}
status_t
Desktop::_ActivateApp(team_id team)
{
AutoWriteLocker locker(fWindowLock);
for (Window* window = CurrentWindows().LastWindow(); window != NULL;
window = window->PreviousWindow(fCurrentWorkspace)) {
if (!window->IsHidden() && window->IsNormal()
&& window->ServerWindow()->ClientTeam() == team) {
ActivateWindow(window);
return B_OK;
}
}
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
window = window->NextWindow(kAllWindowList)) {
if (!window->IsHidden() && window->IsNormal()
&& window->ServerWindow()->ClientTeam() == team) {
ActivateWindow(window);
return B_OK;
}
}
return B_BAD_VALUE;
}
void
Desktop::_SetCurrentWorkspaceConfiguration()
{
ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
status_t status = fDirectScreenLock.LockWithTimeout(1000000L);
if (status != B_OK) {
syslog(LOG_ERR, "Team %" B_PRId32 " did not give up its direct screen "
"lock.\n", fDirectScreenTeam);
debug_thread(fDirectScreenTeam);
fDirectScreenTeam = -1;
} else
fDirectScreenLock.Unlock();
AutoWriteLocker _(fScreenLock);
uint32 changedScreens;
fVirtualScreen.SetConfiguration(*this,
fWorkspaces[fCurrentWorkspace].CurrentScreenConfiguration(),
&changedScreens);
for (int32 i = 0; changedScreens != 0; i++, changedScreens /= 2) {
if ((changedScreens & (1 << i)) != 0)
_ScreenChanged(fVirtualScreen.ScreenAt(i));
}
}
void
Desktop::_SetWorkspace(int32 index, bool moveFocusWindow)
{
ASSERT_MULTI_WRITE_LOCKED(fWindowLock);
int32 previousIndex = fCurrentWorkspace;
rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color();
bool movedMouseEventWindow = false;
Window* movedWindow = NULL;
if (moveFocusWindow) {
if (fMouseEventWindow != NULL)
movedWindow = fMouseEventWindow;
else
movedWindow = FocusWindow();
}
if (movedWindow != NULL) {
if (movedWindow->IsNormal()) {
if (!movedWindow->InWorkspace(index)) {
uint32 oldWorkspaces = movedWindow->Workspaces();
WindowStack* stack = movedWindow->GetWindowStack();
if (stack != NULL) {
for (int32 s = 0; s < stack->CountWindows(); s++) {
Window* stackWindow = stack->LayerOrder().ItemAt(s);
_Windows(previousIndex).RemoveWindow(stackWindow);
_Windows(index).AddWindow(stackWindow,
stackWindow->Frontmost(
_Windows(index).FirstWindow(), index));
stackWindow->WorkspacesChanged(oldWorkspaces,
stackWindow->Workspaces());
}
}
movedMouseEventWindow = true;
NotifyWindowWorkspacesChanged(movedWindow,
movedWindow->Workspaces());
} else {
_Windows(index).RemoveWindow(movedWindow);
_Windows(index).AddWindow(movedWindow,
movedWindow->Frontmost(_Windows(index).FirstWindow(),
index));
}
}
movedWindow->Anchor(index).position = movedWindow->Frame().LeftTop();
}
if (movedWindow == NULL || movedWindow->InWorkspace(previousIndex))
fLastWorkspaceFocus[previousIndex] = FocusWindow();
else
fLastWorkspaceFocus[previousIndex] = NULL;
BRegion dirty;
for (Window* window = CurrentWindows().FirstWindow();
window != NULL; window = window->NextWindow(previousIndex)) {
window->Anchor(previousIndex).position = window->Frame().LeftTop();
if (!window->IsHidden()
&& window->ServerWindow()->IsDirectlyAccessing())
window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
window->WorkspaceActivated(previousIndex, false);
if (window->InWorkspace(index))
continue;
if (!window->IsHidden()) {
dirty.Include(&window->VisibleRegion());
}
window->SetCurrentWorkspace(-1);
}
fPreviousWorkspace = fCurrentWorkspace;
fCurrentWorkspace = index;
_SetCurrentWorkspaceConfiguration();
WindowList windows(kWorkingList);
BList previousRegions;
for (Window* window = _Windows(index).FirstWindow();
window != NULL; window = window->NextWindow(index)) {
BPoint position = window->Anchor(index).position;
window->SetCurrentWorkspace(index);
if (window->IsHidden())
continue;
if (position == kInvalidWindowPosition) {
position = window->Frame().LeftTop();
}
if (!window->InWorkspace(previousIndex)) {
if (window->Frame().LeftTop() != position) {
BPoint offset = position - window->Frame().LeftTop();
window->MoveBy((int32)offset.x, (int32)offset.y);
}
continue;
}
if (window->Frame().LeftTop() != position) {
BPoint offset = position - window->Frame().LeftTop();
MoveWindowBy(window, offset.x, offset.y);
} else {
ObjectDeleter<BRegion> region(new (std::nothrow)
BRegion(window->VisibleRegion()));
if (region.IsSet()) {
if (previousRegions.AddItem(region.Detach()))
windows.AddWindow(window);
}
}
}
_UpdateFronts(false);
_UpdateFloating(previousIndex, index,
movedMouseEventWindow ? movedWindow : NULL);
BRegion stillAvailableOnScreen;
_RebuildClippingForAllWindows(stillAvailableOnScreen);
_SetBackground(stillAvailableOnScreen);
for (Window* window = _Windows(index).FirstWindow(); window != NULL;
window = window->NextWindow(index)) {
window->WorkspaceActivated(index, true);
if (!window->IsHidden()
&& window->ServerWindow()->HasDirectFrameBufferAccess()) {
window->ServerWindow()->HandleDirectConnection(
B_DIRECT_START | B_BUFFER_RESET, B_MODE_CHANGED);
}
if (window->InWorkspace(previousIndex) || window->IsHidden()
|| (window == movedWindow && movedWindow->IsNormal())
|| (!window->IsNormal()
&& window->HasInSubset(movedWindow))) {
continue;
}
dirty.Include(&window->VisibleRegion());
}
int32 i = 0;
for (Window* window = windows.FirstWindow(); window != NULL;
window = window->NextWindow(kWorkingList), i++) {
BRegion* region = (BRegion*)previousRegions.ItemAt(i);
region->ExclusiveInclude(&window->VisibleRegion());
dirty.Include(region);
delete region;
}
if (movedWindow != NULL)
SetFocusWindow(movedWindow);
else if (!_Windows(index).HasWindow(FocusWindow())
|| (FocusWindow() != NULL && !FocusWindow()->IsFloating()))
SetFocusWindow(fLastWorkspaceFocus[index]);
_WindowChanged(NULL);
MarkDirty(dirty);
#if 0
if (GetDrawingEngine()->LockParallelAccess()) {
GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0});
GetDrawingEngine()->UnlockParallelAccess();
snooze(100000);
}
#endif
if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
RedrawBackground();
}