926b6fdb08
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31161 a95241bf-73f2-0310-859d-f6bbb57e9c96
2843 lines
68 KiB
C++
2843 lines
68 KiB
C++
/*
|
|
* Copyright 2001-2009, Haiku.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Adrian Oanca <adioanca@cotty.iren.ro>
|
|
* Stephan Aßmus <superstippi@gmx.de>
|
|
* Axel Dörfler, axeld@pinc-software.de
|
|
* Andrej Spielmann, <andrej.spielmann@seh.ox.ac.uk>
|
|
*/
|
|
|
|
/*! Class used to encapsulate desktop management */
|
|
|
|
|
|
#include "Desktop.h"
|
|
|
|
#include "AppServer.h"
|
|
#include "DesktopSettingsPrivate.h"
|
|
#include "DrawingEngine.h"
|
|
#include "HWInterface.h"
|
|
#include "InputManager.h"
|
|
#include "Screen.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"
|
|
|
|
#include <ViewPrivate.h>
|
|
#include <WindowInfo.h>
|
|
#include <ServerProtocol.h>
|
|
|
|
#include <Debug.h>
|
|
#include <DirectWindow.h>
|
|
#include <Entry.h>
|
|
#include <Message.h>
|
|
#include <MessageFilter.h>
|
|
#include <Region.h>
|
|
#include <Roster.h>
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
|
|
#if TEST_MODE
|
|
# include "EventStream.h"
|
|
#endif
|
|
|
|
//#define DEBUG_DESKTOP
|
|
#ifdef DEBUG_DESKTOP
|
|
# define STRACE(a) printf a
|
|
#else
|
|
# define STRACE(a) ;
|
|
#endif
|
|
|
|
#if !USE_MULTI_LOCKER
|
|
# define AutoWriteLocker BAutolock
|
|
#endif
|
|
|
|
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, 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;
|
|
};
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
KeyboardFilter::KeyboardFilter(Desktop* desktop)
|
|
:
|
|
fDesktop(desktop),
|
|
fLastFocus(NULL),
|
|
fTimestamp(0)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
KeyboardFilter::_UpdateFocus(int32 key, EventTarget** _target)
|
|
{
|
|
if (!fDesktop->LockSingleWindow())
|
|
return;
|
|
|
|
EventTarget* focus = fDesktop->KeyboardEventTarget();
|
|
bigtime_t now = system_time();
|
|
|
|
// TODO: this is a try to not steal focus from the current window
|
|
// in case you enter some text and a window pops up you haven't
|
|
// triggered yourself (like a pop-up window in your browser while
|
|
// you're typing a password in another window) - maybe this should
|
|
// be done differently, though (using something like B_LOCK_WINDOW_FOCUS)
|
|
// (at least B_WINDOW_ACTIVATED must be postponed)
|
|
|
|
if (fLastFocus == NULL || (focus != fLastFocus && now - fTimestamp > 100000)) {
|
|
// if the time span between the key presses is very short
|
|
// we keep our previous focus alive - this is save even
|
|
// if the target doesn't exist anymore, as we don't reset
|
|
// it, and the event focus passed in is always valid (or NULL)
|
|
*_target = focus;
|
|
fLastFocus = focus;
|
|
}
|
|
|
|
fDesktop->UnlockSingleWindow();
|
|
|
|
// we always allow to switch focus after the enter key has pressed
|
|
if (key == B_ENTER)
|
|
fTimestamp = 0;
|
|
else
|
|
fTimestamp = now;
|
|
}
|
|
|
|
|
|
filter_result
|
|
KeyboardFilter::Filter(BMessage* message, EventTarget** _target,
|
|
int32* /*_viewToken*/, BMessage* /*latestMouseMoved*/)
|
|
{
|
|
int32 key = 0;
|
|
int32 modifiers;
|
|
|
|
if (message->what == B_KEY_DOWN
|
|
&& message->FindInt32("key", &key) == B_OK
|
|
&& message->FindInt32("modifiers", &modifiers) == B_OK) {
|
|
// Check for safe video mode (cmd + ctrl + escape)
|
|
if (key == 0x01 && (modifiers & B_COMMAND_KEY) != 0
|
|
&& (modifiers & B_CONTROL_KEY) != 0) {
|
|
system("screenmode --fall-back &");
|
|
return B_SKIP_MESSAGE;
|
|
}
|
|
|
|
if (key >= B_F1_KEY && key <= B_F12_KEY) {
|
|
// workspace change
|
|
|
|
#if !TEST_MODE
|
|
if (modifiers & B_COMMAND_KEY)
|
|
#else
|
|
if (modifiers & B_CONTROL_KEY)
|
|
#endif
|
|
{
|
|
STRACE(("Set Workspace %ld\n", key - 1));
|
|
|
|
fDesktop->SetWorkspaceAsync(key - 2);
|
|
return B_SKIP_MESSAGE;
|
|
}
|
|
} if (key == 0x11 && (modifiers & B_COMMAND_KEY) != 0) {
|
|
// switch to previous workspace (command + `)
|
|
fDesktop->SetWorkspaceAsync(-1);
|
|
}
|
|
}
|
|
|
|
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, _target);
|
|
|
|
return B_DISPATCH_MESSAGE;
|
|
}
|
|
|
|
|
|
void
|
|
KeyboardFilter::RemoveTarget(EventTarget* target)
|
|
{
|
|
if (target == fLastFocus)
|
|
fLastFocus = NULL;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
MouseFilter::MouseFilter(Desktop* desktop)
|
|
:
|
|
fDesktop(desktop)
|
|
{
|
|
}
|
|
|
|
|
|
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) {
|
|
// dispatch event to the window
|
|
switch (message->what) {
|
|
case B_MOUSE_DOWN:
|
|
window->MouseDown(message, where, &viewToken);
|
|
break;
|
|
|
|
case B_MOUSE_UP:
|
|
window->MouseUp(message, where, &viewToken);
|
|
fDesktop->SetMouseEventWindow(NULL);
|
|
break;
|
|
|
|
case B_MOUSE_MOVED:
|
|
window->MouseMoved(message, where, &viewToken,
|
|
latestMouseMoved == NULL || latestMouseMoved == message);
|
|
break;
|
|
}
|
|
|
|
if (viewToken != B_NULL_TOKEN) {
|
|
fDesktop->SetViewUnderMouse(window, viewToken);
|
|
|
|
*_viewToken = viewToken;
|
|
*_target = &window->EventTarget();
|
|
}
|
|
}
|
|
|
|
if (window == NULL || viewToken == B_NULL_TOKEN) {
|
|
// mouse is not over a window or over a decorator
|
|
fDesktop->SetViewUnderMouse(window, B_NULL_TOKEN);
|
|
fDesktop->SetCursor(NULL);
|
|
|
|
*_target = NULL;
|
|
}
|
|
|
|
fDesktop->SetLastMouseState(where, buttons, window);
|
|
|
|
fDesktop->UnlockAllWindows();
|
|
|
|
return B_DISPATCH_MESSAGE;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
Desktop::Desktop(uid_t userID)
|
|
: MessageLooper("desktop"),
|
|
|
|
fUserID(userID),
|
|
fSettings(NULL),
|
|
fSharedReadOnlyArea(-1),
|
|
fApplicationsLock("application list"),
|
|
fShutdownSemaphore(-1),
|
|
fCurrentWorkspace(0),
|
|
fPreviousWorkspace(0),
|
|
fAllWindows(kAllWindowList),
|
|
fSubsetWindows(kSubsetList),
|
|
fFocusList(kFocusList),
|
|
fWorkspacesViews(false),
|
|
fWorkspacesLock("workspaces list"),
|
|
fActiveScreen(NULL),
|
|
|
|
fWindowLock("window lock"),
|
|
|
|
fMouseEventWindow(NULL),
|
|
fWindowUnderMouse(NULL),
|
|
fLockedFocusWindow(NULL),
|
|
fViewUnderMouse(B_NULL_TOKEN),
|
|
fLastMousePosition(B_ORIGIN),
|
|
fLastMouseButtons(0),
|
|
|
|
fFocus(NULL),
|
|
fFront(NULL),
|
|
fBack(NULL)
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
Desktop::~Desktop()
|
|
{
|
|
delete fSettings;
|
|
|
|
delete_area(fSharedReadOnlyArea);
|
|
delete_port(fMessagePort);
|
|
gFontManager->DetachUser(fUserID);
|
|
}
|
|
|
|
|
|
status_t
|
|
Desktop::Init()
|
|
{
|
|
if (fMessagePort < B_OK)
|
|
return fMessagePort;
|
|
|
|
// the system palette needs to be initialized before the
|
|
// desktop settings, since it is used there already
|
|
InitializeColorMap();
|
|
|
|
const size_t areaSize = B_PAGE_SIZE;
|
|
char name[B_OS_NAME_LENGTH];
|
|
snprintf(name, sizeof(name), "d:%d:shared read only", /*id*/0);
|
|
fSharedReadOnlyArea = create_area(name, (void **)&fServerReadOnlyMemory,
|
|
B_ANY_ADDRESS, areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
|
|
if (fSharedReadOnlyArea < B_OK)
|
|
return fSharedReadOnlyArea;
|
|
|
|
gFontManager->AttachUser(fUserID);
|
|
|
|
fSettings = new DesktopSettingsPrivate(fServerReadOnlyMemory);
|
|
|
|
for (int32 i = 0; i < kMaxWorkspaces; i++) {
|
|
_Windows(i).SetIndex(i);
|
|
fWorkspaces[i].RestoreConfiguration(*fSettings->WorkspacesMessage(i));
|
|
}
|
|
|
|
fVirtualScreen.RestoreConfiguration(*this, fSettings->WorkspacesMessage(0));
|
|
|
|
// TODO: temporary workaround, fActiveScreen will be removed
|
|
fActiveScreen = fVirtualScreen.ScreenAt(0);
|
|
|
|
if (fVirtualScreen.HWInterface() == NULL) {
|
|
debug_printf("Could not initialize graphics output. Exiting.\n");
|
|
return B_ERROR;
|
|
}
|
|
|
|
fVirtualScreen.HWInterface()->MoveCursorTo(fVirtualScreen.Frame().Width() / 2,
|
|
fVirtualScreen.Frame().Height() / 2);
|
|
|
|
#if TEST_MODE
|
|
gInputManager->AddStream(new InputServerStream);
|
|
#endif
|
|
|
|
fEventDispatcher.SetDesktop(this);
|
|
fEventDispatcher.SetTo(gInputManager->GetStream());
|
|
if (fEventDispatcher.InitCheck() != B_OK)
|
|
_LaunchInputServer();
|
|
|
|
fEventDispatcher.SetHWInterface(fVirtualScreen.HWInterface());
|
|
|
|
fEventDispatcher.SetMouseFilter(new MouseFilter(this));
|
|
fEventDispatcher.SetKeyboardFilter(new KeyboardFilter(this));
|
|
|
|
// draw the background
|
|
|
|
fScreenRegion = fVirtualScreen.Frame();
|
|
|
|
BRegion stillAvailableOnScreen;
|
|
_RebuildClippingForAllWindows(stillAvailableOnScreen);
|
|
_SetBackground(stillAvailableOnScreen);
|
|
|
|
SetCursor(NULL);
|
|
// this will set the default cursor
|
|
|
|
fVirtualScreen.HWInterface()->SetCursorVisible(true);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
// Could not load input_server by signature, try well-known location
|
|
|
|
BEntry entry("/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", /*id*/0, /*name*/"baron");
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::_PrepareQuit()
|
|
{
|
|
// let's kill all remaining applications
|
|
|
|
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);
|
|
}
|
|
|
|
// wait for the last app to die
|
|
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:
|
|
{
|
|
// Create the ServerApp to node monitor a new BApplication
|
|
|
|
// Attached data:
|
|
// 1) port_id - receiver port of a regular app
|
|
// 2) port_id - client looper port - for sending messages to the client
|
|
// 2) team_id - app's team ID
|
|
// 3) int32 - handler token of the regular app
|
|
// 4) char * - signature of the regular app
|
|
|
|
// Find the necessary data
|
|
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;
|
|
|
|
ServerApp *app = new ServerApp(this, clientReplyPort,
|
|
clientLooperPort, clientTeamID, htoken, appSignature);
|
|
if (app->InitCheck() == B_OK
|
|
&& app->Run()) {
|
|
// add the new ServerApp to the known list of ServerApps
|
|
fApplicationsLock.Lock();
|
|
fApplications.AddItem(app);
|
|
fApplicationsLock.Unlock();
|
|
} else {
|
|
delete app;
|
|
|
|
// if everything went well, ServerApp::Run() will notify
|
|
// the client - but since it didn't, we do it here
|
|
BPrivate::LinkSender reply(clientReplyPort);
|
|
reply.StartMessage(B_ERROR);
|
|
reply.Flush();
|
|
}
|
|
|
|
// This is necessary because BPortLink::ReadString allocates memory
|
|
free(appSignature);
|
|
break;
|
|
}
|
|
|
|
case AS_DELETE_APP:
|
|
{
|
|
// Delete a ServerApp. Received only from the respective ServerApp when a
|
|
// BApplication asks it to quit.
|
|
|
|
// Attached Data:
|
|
// 1) thread_id - thread ID of the ServerApp to be deleted
|
|
|
|
thread_id thread = -1;
|
|
if (link.Read<thread_id>(&thread) < B_OK)
|
|
break;
|
|
|
|
fApplicationsLock.Lock();
|
|
|
|
// Run through the list of apps and nuke the proper one
|
|
|
|
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) {
|
|
// wait for the last app to die
|
|
acquire_sem_etc(fShutdownSemaphore, fShutdownCount, B_RELATIVE_TIMEOUT, 500000);
|
|
PostMessage(kMsgQuitLooper);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case AS_ACTIVATE_APP:
|
|
{
|
|
// Someone is requesting to activation of a certain app.
|
|
|
|
// Attached data:
|
|
// 1) port_id reply port
|
|
// 2) team_id team
|
|
|
|
status_t status;
|
|
|
|
// get the parameters
|
|
port_id replyPort;
|
|
team_id team;
|
|
if (link.Read(&replyPort) == B_OK
|
|
&& link.Read(&team) == B_OK)
|
|
status = _ActivateApp(team);
|
|
else
|
|
status = B_ERROR;
|
|
|
|
// send the reply
|
|
BPrivate::PortLink replyLink(replyPort);
|
|
replyLink.StartMessage(status);
|
|
replyLink.Flush();
|
|
break;
|
|
}
|
|
|
|
case AS_APP_CRASHED:
|
|
{
|
|
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(AS_APP_CRASHED);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case AS_EVENT_STREAM_CLOSED:
|
|
_LaunchInputServer();
|
|
break;
|
|
|
|
case B_QUIT_REQUESTED:
|
|
// We've been asked to quit, so (for now) broadcast to all
|
|
// test apps to quit. This situation will occur only when the server
|
|
// is compiled as a regular Be application.
|
|
|
|
fApplicationsLock.Lock();
|
|
fShutdownSemaphore = create_sem(0, "desktop shutdown");
|
|
fShutdownCount = fApplications.CountItems();
|
|
fApplicationsLock.Unlock();
|
|
|
|
fQuitting = true;
|
|
BroadcastToAllApps(AS_QUIT_APP);
|
|
|
|
// We now need to process the remaining AS_DELETE_APP messages and
|
|
// wait for the kMsgShutdownServer message.
|
|
// If an application does not quit as asked, the picasso thread
|
|
// will send us this message in 2-3 seconds.
|
|
|
|
// if there are no apps to quit, shutdown directly
|
|
if (fShutdownCount == 0)
|
|
PostMessage(kMsgQuitLooper);
|
|
break;
|
|
|
|
case AS_ACTIVATE_WORKSPACE:
|
|
{
|
|
int32 index;
|
|
link.Read<int32>(&index);
|
|
|
|
if (index == -1)
|
|
index = fPreviousWorkspace;
|
|
|
|
SetWorkspace(index);
|
|
break;
|
|
}
|
|
|
|
// ToDo: Remove this again. It is a message sent by the
|
|
// invalidate_on_exit kernel debugger add-on to trigger a redraw
|
|
// after exiting a kernel debugger session.
|
|
case 'KDLE':
|
|
{
|
|
BRegion dirty;
|
|
dirty.Include(fVirtualScreen.Frame());
|
|
MarkDirty(dirty);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
printf("Desktop %d:%s received unexpected code %ld\n", 0, "baron", code);
|
|
|
|
if (link.NeedsReply()) {
|
|
// the client is now blocking and waiting for a reply!
|
|
fLink.StartMessage(B_ERROR);
|
|
fLink.Flush();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief activate one of the app's windows.
|
|
*/
|
|
status_t
|
|
Desktop::_ActivateApp(team_id team)
|
|
{
|
|
// search for an unhidden window in the current workspace
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// search for an unhidden window to give focus to
|
|
|
|
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
|
|
window = window->NextWindow(kAllWindowList)) {
|
|
// if window is a normal window of the team, and not hidden,
|
|
// we've found our target
|
|
if (!window->IsHidden() && window->IsNormal()
|
|
&& window->ServerWindow()->ClientTeam() == team) {
|
|
ActivateWindow(window);
|
|
return B_OK;
|
|
}
|
|
}
|
|
|
|
// TODO: we cannot maximize minimized windows here (with the window lock
|
|
// write locked). To work-around this, we could forward the request to
|
|
// the ServerApp of this team - it maintains its own window list, and can
|
|
// therefore call ActivateWindow() without holding the window lock.
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
|
|
/*! \brief Send a quick (no attachments) message to all applications.
|
|
|
|
Quite useful for notification for things like server shutdown, system
|
|
color changes, etc.
|
|
*/
|
|
void
|
|
Desktop::BroadcastToAllApps(int32 code)
|
|
{
|
|
BAutolock locker(fApplicationsLock);
|
|
|
|
for (int32 i = fApplications.CountItems(); i-- > 0;) {
|
|
fApplications.ItemAt(i)->PostMessage(code);
|
|
}
|
|
}
|
|
|
|
|
|
/*! \brief Send a quick (no attachments) message to all windows.
|
|
*/
|
|
void
|
|
Desktop::BroadcastToAllWindows(int32 code)
|
|
{
|
|
AutoWriteLocker _(fWindowLock);
|
|
|
|
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
|
|
window = window->NextWindow(kAllWindowList)) {
|
|
window->ServerWindow()->PostMessage(code);
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
void
|
|
Desktop::SetCursor(ServerCursor* newCursor)
|
|
{
|
|
if (newCursor == NULL)
|
|
newCursor = fCursorManager.GetCursor(B_CURSOR_DEFAULT);
|
|
|
|
ServerCursorReference oldCursor = Cursor();
|
|
if (newCursor == oldCursor.Cursor())
|
|
return;
|
|
|
|
HWInterface()->SetCursor(newCursor);
|
|
}
|
|
|
|
|
|
ServerCursorReference
|
|
Desktop::Cursor() const
|
|
{
|
|
return HWInterface()->Cursor();
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::SetLastMouseState(const BPoint& position, int32 buttons,
|
|
Window* windowUnderMouse)
|
|
{
|
|
// The all-window-lock is write-locked.
|
|
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;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
/*!
|
|
\brief Redraws the background (ie. the desktop window, if any).
|
|
*/
|
|
void
|
|
Desktop::RedrawBackground()
|
|
{
|
|
LockAllWindows();
|
|
|
|
BRegion redraw;
|
|
|
|
Window* window = _CurrentWindows().FirstWindow();
|
|
if (window->Feel() == kDesktopWindowFeel) {
|
|
redraw = window->VisibleContentRegion();
|
|
|
|
// look for desktop background view, and update its background color
|
|
// TODO: is there a better way to do this?
|
|
View* view = window->TopView();
|
|
if (view != NULL)
|
|
view = view->FirstChild();
|
|
|
|
while (view) {
|
|
if (view->IsDesktopBackground()) {
|
|
view->SetViewColor(fWorkspaces[fCurrentWorkspace].Color());
|
|
break;
|
|
}
|
|
view = view->NextSibling();
|
|
}
|
|
|
|
window->ProcessDirtyRegion(redraw);
|
|
} else {
|
|
redraw = BackgroundRegion();
|
|
fBackgroundRegion.MakeEmpty();
|
|
_SetBackground(redraw);
|
|
}
|
|
|
|
_WindowChanged(NULL);
|
|
// update workspaces view as well
|
|
|
|
UnlockAllWindows();
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Store the workspace configuration
|
|
*/
|
|
void
|
|
Desktop::StoreWorkspaceConfiguration(int32 index)
|
|
{
|
|
const BMessage *oldSettings = fSettings->WorkspacesMessage(index);
|
|
// store settings
|
|
BMessage settings;
|
|
if (oldSettings)
|
|
settings = *oldSettings;
|
|
fWorkspaces[index].StoreConfiguration(settings);
|
|
fSettings->SetWorkspacesMessage(index, settings);
|
|
fSettings->Save(kWorkspacesSettings);
|
|
}
|
|
|
|
|
|
status_t
|
|
Desktop::SetWorkspacesCount(int32 newCount)
|
|
{
|
|
if (newCount < 1 || newCount > kMaxWorkspaces)
|
|
return B_BAD_VALUE;
|
|
|
|
if (!LockAllWindows())
|
|
return B_ERROR;
|
|
|
|
fSettings->SetWorkspacesCount(newCount);
|
|
|
|
// either update the workspaces window, or switch to
|
|
// the last available workspace - which will update
|
|
// the workspaces window automatically
|
|
bool workspaceChanged = CurrentWorkspace() >= newCount;
|
|
if (workspaceChanged)
|
|
_SetWorkspace(newCount - 1);
|
|
else
|
|
_WindowChanged(NULL);
|
|
|
|
UnlockAllWindows();
|
|
|
|
if (workspaceChanged)
|
|
_SendFakeMouseMoved();
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
/*!
|
|
Returns the virtual screen frame of the workspace specified by \a index.
|
|
*/
|
|
BRect
|
|
Desktop::WorkspaceFrame(int32 index) const
|
|
{
|
|
BRect frame;
|
|
if (index == fCurrentWorkspace)
|
|
frame = fVirtualScreen.Frame();
|
|
else if (index >= 0 && index < fSettings->WorkspacesCount()) {
|
|
BMessage screenData;
|
|
int32 width;
|
|
int32 height;
|
|
fSettings->WorkspacesMessage(index)->FindMessage("screen", &screenData);
|
|
if (screenData.FindInt32("width", &width) != B_OK || screenData.FindInt32("height", &height) != B_OK)
|
|
frame = fVirtualScreen.Frame();
|
|
else
|
|
frame.Set(0.0, 0.0, width - 1, height - 1);
|
|
}
|
|
return frame;
|
|
}
|
|
|
|
|
|
/*!
|
|
Changes the current workspace to the one specified by \a index.
|
|
*/
|
|
void
|
|
Desktop::SetWorkspaceAsync(int32 index)
|
|
{
|
|
BPrivate::LinkSender link(MessagePort());
|
|
link.StartMessage(AS_ACTIVATE_WORKSPACE);
|
|
link.Attach<int32>(index);
|
|
link.Flush();
|
|
}
|
|
|
|
|
|
/*!
|
|
Changes the current workspace to the one specified by \a index.
|
|
You must not hold any window lock when calling this method.
|
|
*/
|
|
void
|
|
Desktop::SetWorkspace(int32 index)
|
|
{
|
|
LockAllWindows();
|
|
DesktopSettings settings(this);
|
|
|
|
if (index < 0 || index >= settings.WorkspacesCount()
|
|
|| index == fCurrentWorkspace) {
|
|
UnlockAllWindows();
|
|
return;
|
|
}
|
|
|
|
_SetWorkspace(index);
|
|
UnlockAllWindows();
|
|
|
|
_SendFakeMouseMoved();
|
|
}
|
|
|
|
/*! Changes the current workspace to the one specified by \a index.
|
|
You must hold the all window lock when calling this method.
|
|
*/
|
|
void
|
|
Desktop::_SetWorkspace(int32 index)
|
|
{
|
|
int32 previousIndex = fCurrentWorkspace;
|
|
rgb_color previousColor = fWorkspaces[fCurrentWorkspace].Color();
|
|
bool movedMouseEventWindow = false;
|
|
|
|
if (fMouseEventWindow != NULL) {
|
|
if (fMouseEventWindow->IsNormal()) {
|
|
if (!fMouseEventWindow->InWorkspace(index)) {
|
|
// The window currently being dragged will follow us to this
|
|
// workspace if it's not already on it.
|
|
// But only normal windows are following
|
|
uint32 oldWorkspaces = fMouseEventWindow->Workspaces();
|
|
|
|
_Windows(index).AddWindow(fMouseEventWindow);
|
|
_Windows(previousIndex).RemoveWindow(fMouseEventWindow);
|
|
|
|
_UpdateSubsetWorkspaces(fMouseEventWindow, previousIndex,
|
|
index);
|
|
movedMouseEventWindow = true;
|
|
|
|
// send B_WORKSPACES_CHANGED message
|
|
fMouseEventWindow->WorkspacesChanged(oldWorkspaces,
|
|
fMouseEventWindow->Workspaces());
|
|
} else {
|
|
// make sure it's frontmost
|
|
_Windows(index).RemoveWindow(fMouseEventWindow);
|
|
_Windows(index).AddWindow(fMouseEventWindow,
|
|
fMouseEventWindow->Frontmost(_Windows(index).FirstWindow(),
|
|
index));
|
|
}
|
|
}
|
|
|
|
fMouseEventWindow->Anchor(index).position
|
|
= fMouseEventWindow->Frame().LeftTop();
|
|
}
|
|
|
|
// build region of windows that are no longer visible in the new workspace
|
|
|
|
BRegion dirty;
|
|
|
|
for (Window* window = _CurrentWindows().FirstWindow();
|
|
window != NULL; window = window->NextWindow(previousIndex)) {
|
|
// store current position in Workspace anchor
|
|
window->Anchor(previousIndex).position = window->Frame().LeftTop();
|
|
|
|
window->WorkspaceActivated(previousIndex, false);
|
|
|
|
if (window->InWorkspace(index))
|
|
continue;
|
|
|
|
if (!window->IsHidden()) {
|
|
// this window will no longer be visible
|
|
dirty.Include(&window->VisibleRegion());
|
|
}
|
|
|
|
window->SetCurrentWorkspace(-1);
|
|
}
|
|
|
|
fPreviousWorkspace = fCurrentWorkspace;
|
|
fCurrentWorkspace = index;
|
|
|
|
// Change the display mode, if needed
|
|
// TODO: this needs to be done for all screens
|
|
display_mode previousMode, newMode;
|
|
fVirtualScreen.ScreenAt(0)->GetMode(&previousMode);
|
|
fVirtualScreen.RestoreConfiguration(*this,
|
|
fSettings->WorkspacesMessage(index));
|
|
fVirtualScreen.ScreenAt(0)->GetMode(&newMode);
|
|
// We only need to invalidate the entire desktop if we changed display modes
|
|
if (memcmp(&previousMode, &newMode, sizeof(display_mode)))
|
|
ScreenChanged(fVirtualScreen.ScreenAt(0), false);
|
|
|
|
// show windows, and include them in the changed region - but only
|
|
// those that were not visible before (or whose position changed)
|
|
|
|
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) {
|
|
// if you enter a workspace for the first time, the position
|
|
// of the window in the previous workspace is adopted
|
|
position = window->Frame().LeftTop();
|
|
// TODO: make sure the window is still on-screen if it
|
|
// was before!
|
|
}
|
|
|
|
if (!window->InWorkspace(previousIndex)) {
|
|
// This window was not visible before, make sure its frame
|
|
// is up-to-date
|
|
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) {
|
|
// the window was visible before, but its on-screen location changed
|
|
BPoint offset = position - window->Frame().LeftTop();
|
|
MoveWindowBy(window, offset.x, offset.y);
|
|
// TODO: be a bit smarter than this...
|
|
} else {
|
|
// We need to remember the previous visible region of the
|
|
// window if they changed their order
|
|
BRegion* region = new (std::nothrow)
|
|
BRegion(window->VisibleRegion());
|
|
if (region != NULL) {
|
|
if (previousRegions.AddItem(region))
|
|
windows.AddWindow(window);
|
|
else
|
|
delete region;
|
|
}
|
|
}
|
|
}
|
|
|
|
_UpdateFronts(false);
|
|
_UpdateFloating(previousIndex, index,
|
|
movedMouseEventWindow ? fMouseEventWindow : NULL);
|
|
|
|
BRegion stillAvailableOnScreen;
|
|
_RebuildClippingForAllWindows(stillAvailableOnScreen);
|
|
_SetBackground(stillAvailableOnScreen);
|
|
|
|
for (Window* window = _Windows(index).FirstWindow(); window != NULL;
|
|
window = window->NextWindow(index)) {
|
|
// send B_WORKSPACE_ACTIVATED message
|
|
window->WorkspaceActivated(index, true);
|
|
|
|
if (window->InWorkspace(previousIndex) || window->IsHidden()
|
|
|| (window == fMouseEventWindow && fMouseEventWindow->IsNormal())
|
|
|| (!window->IsNormal() && window->HasInSubset(fMouseEventWindow))) {
|
|
// this window was visible before, and is already handled in the above loop
|
|
continue;
|
|
}
|
|
|
|
dirty.Include(&window->VisibleRegion());
|
|
}
|
|
|
|
// Catch order changes in the new workspaces window list
|
|
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;
|
|
}
|
|
|
|
// Set new focus to the front window, but keep focus to a floating
|
|
// window if still visible
|
|
if (!_Windows(index).HasWindow(FocusWindow()) || !FocusWindow()->IsFloating())
|
|
SetFocusWindow(FrontWindow());
|
|
|
|
_WindowChanged(NULL);
|
|
MarkDirty(dirty);
|
|
|
|
#if 0
|
|
// Show the dirty regions of this workspace switch
|
|
if (GetDrawingEngine()->LockParallelAccess()) {
|
|
GetDrawingEngine()->FillRegion(dirty, (rgb_color){255, 0, 0});
|
|
GetDrawingEngine()->UnlockParallelAccess();
|
|
snooze(100000);
|
|
}
|
|
#endif
|
|
|
|
if (previousColor != fWorkspaces[fCurrentWorkspace].Color())
|
|
RedrawBackground();
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::ScreenChanged(Screen* screen, bool makeDefault)
|
|
{
|
|
// TODO: confirm that everywhere this is used,
|
|
// the Window WriteLock is held
|
|
|
|
// the entire screen is dirty, because we're actually
|
|
// operating on an all new buffer in memory
|
|
BRegion dirty(screen->Frame());
|
|
// update our cached screen region
|
|
fScreenRegion.Set(screen->Frame());
|
|
gInputManager->UpdateScreenBounds(screen->Frame());
|
|
|
|
BRegion background;
|
|
_RebuildClippingForAllWindows(background);
|
|
|
|
fBackgroundRegion.MakeEmpty();
|
|
// makes sure that the complete background is redrawn
|
|
_SetBackground(background);
|
|
|
|
// figure out dirty region
|
|
dirty.Exclude(&background);
|
|
_TriggerWindowRedrawing(dirty);
|
|
|
|
// send B_SCREEN_CHANGED to windows on that screen
|
|
BMessage update(B_SCREEN_CHANGED);
|
|
update.AddInt64("when", real_time_clock_usecs());
|
|
update.AddRect("frame", screen->Frame());
|
|
update.AddInt32("mode", screen->ColorSpace());
|
|
|
|
// TODO: currently ignores the screen argument!
|
|
for (Window* window = fAllWindows.FirstWindow(); window != NULL;
|
|
window = window->NextWindow(kAllWindowList)) {
|
|
window->ServerWindow()->SendMessageToClient(&update);
|
|
}
|
|
|
|
fVirtualScreen.UpdateFrame();
|
|
|
|
if (makeDefault) {
|
|
StoreConfiguration(fCurrentWorkspace);
|
|
}
|
|
}
|
|
|
|
|
|
status_t
|
|
Desktop::StoreConfiguration(int32 workspace)
|
|
{
|
|
if (workspace >= 0 && workspace < fSettings->WorkspacesCount()) {
|
|
// store settings
|
|
BMessage settings;
|
|
fVirtualScreen.StoreConfiguration(settings);
|
|
fWorkspaces[workspace].StoreConfiguration(settings);
|
|
|
|
fSettings->SetWorkspacesMessage(workspace, settings);
|
|
fSettings->Save(kWorkspacesSettings);
|
|
return B_OK;
|
|
}
|
|
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
// #pragma mark - Methods for Window manipulation
|
|
|
|
|
|
WindowList&
|
|
Desktop::_CurrentWindows()
|
|
{
|
|
return fWorkspaces[fCurrentWorkspace].Windows();
|
|
}
|
|
|
|
|
|
WindowList&
|
|
Desktop::_Windows(int32 index)
|
|
{
|
|
return fWorkspaces[index].Windows();
|
|
}
|
|
|
|
|
|
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)) {
|
|
// we only care about app/subset floating windows
|
|
if (floating->Feel() != B_FLOATING_SUBSET_WINDOW_FEEL
|
|
&& floating->Feel() != B_FLOATING_APP_WINDOW_FEEL)
|
|
continue;
|
|
|
|
if (fFront != NULL && fFront->IsNormal()
|
|
&& floating->HasInSubset(fFront)) {
|
|
// is now visible
|
|
if (_Windows(previousWorkspace).HasWindow(floating)
|
|
&& previousWorkspace != nextWorkspace
|
|
&& !floating->InSubsetWorkspace(previousWorkspace)) {
|
|
// but no longer on the previous workspace
|
|
_Windows(previousWorkspace).RemoveWindow(floating);
|
|
floating->SetCurrentWorkspace(-1);
|
|
}
|
|
|
|
if (!_Windows(nextWorkspace).HasWindow(floating)) {
|
|
// but wasn't before
|
|
_Windows(nextWorkspace).AddWindow(floating,
|
|
floating->Frontmost(_Windows(nextWorkspace).FirstWindow(), nextWorkspace));
|
|
floating->SetCurrentWorkspace(nextWorkspace);
|
|
if (mouseEventWindow != fFront)
|
|
_ShowWindow(floating);
|
|
|
|
// TODO: put the floating last in the floating window list to
|
|
// preserve the on screen window order
|
|
}
|
|
} else if (_Windows(previousWorkspace).HasWindow(floating)
|
|
&& !floating->InSubsetWorkspace(previousWorkspace)) {
|
|
// was visible, but is no longer
|
|
|
|
_Windows(previousWorkspace).RemoveWindow(floating);
|
|
floating->SetCurrentWorkspace(-1);
|
|
_HideWindow(floating);
|
|
|
|
if (FocusWindow() == floating)
|
|
SetFocusWindow();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*! Search the visible windows for a valid back window
|
|
(only desktop windows can't be back windows)
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/*! Search the visible windows for a valid front window
|
|
(only normal and modal windows can be front windows)
|
|
|
|
The only place where you don't want to update floating windows is
|
|
during a workspace change - because then you'll call _UpdateFloating()
|
|
yourself.
|
|
*/
|
|
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);
|
|
}
|
|
|
|
|
|
/*!
|
|
Returns the current keyboard event target candidate - which is either the
|
|
top-most window (in case it's a menu), or the one having focus.
|
|
The window lock must be held when calling this function.
|
|
*/
|
|
EventTarget*
|
|
Desktop::KeyboardEventTarget()
|
|
{
|
|
Window* window = _CurrentWindows().LastWindow();
|
|
while (window != NULL && window->IsHidden()) {
|
|
window = window->PreviousWindow(fCurrentWorkspace);
|
|
}
|
|
if (window != NULL && window->Feel() == kMenuWindowFeel)
|
|
return &window->EventTarget();
|
|
|
|
if (FocusWindow() != NULL)
|
|
return &FocusWindow()->EventTarget();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
bool
|
|
Desktop::_WindowHasModal(Window* window)
|
|
{
|
|
if (window == NULL)
|
|
return false;
|
|
|
|
for (Window* modal = fSubsetWindows.FirstWindow(); modal != NULL;
|
|
modal = modal->NextWindow(kSubsetList)) {
|
|
// only visible modal windows count
|
|
if (!modal->IsModal() || modal->IsHidden())
|
|
continue;
|
|
|
|
if (modal->HasInSubset(window))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*!
|
|
You must at least hold a single window lock when calling this method.
|
|
*/
|
|
void
|
|
Desktop::_WindowChanged(Window* window)
|
|
{
|
|
BAutolock _(fWorkspacesLock);
|
|
|
|
for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
|
|
WorkspacesView* view = fWorkspacesViews.ItemAt(i);
|
|
view->WindowChanged(window);
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
You must at least hold a single window lock when calling this method.
|
|
*/
|
|
void
|
|
Desktop::_WindowRemoved(Window* window)
|
|
{
|
|
BAutolock _(fWorkspacesLock);
|
|
|
|
for (uint32 i = fWorkspacesViews.CountItems(); i-- > 0;) {
|
|
WorkspacesView* view = fWorkspacesViews.ItemAt(i);
|
|
view->WindowRemoved(window);
|
|
}
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Sends a fake B_MOUSE_MOVED event to the window under the mouse,
|
|
and also updates the current view under the mouse.
|
|
|
|
This has only to be done in case the view changed without user interaction,
|
|
ie. because of a workspace change or a closing window.
|
|
*/
|
|
void
|
|
Desktop::_SendFakeMouseMoved(Window* window)
|
|
{
|
|
int32 viewToken = B_NULL_TOKEN;
|
|
EventTarget* target = NULL;
|
|
|
|
LockAllWindows();
|
|
|
|
if (window == NULL)
|
|
window = MouseEventWindow();
|
|
if (window == NULL)
|
|
window = WindowAt(fLastMousePosition);
|
|
|
|
if (window != NULL) {
|
|
BMessage message;
|
|
window->MouseMoved(&message, fLastMousePosition, &viewToken, 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);
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::SetFocusWindow(Window* focus)
|
|
{
|
|
if (!LockAllWindows())
|
|
return;
|
|
|
|
// test for B_LOCK_WINDOW_FOCUS
|
|
if (fLockedFocusWindow && focus != fLockedFocusWindow) {
|
|
UnlockAllWindows();
|
|
return;
|
|
}
|
|
|
|
bool hasModal = _WindowHasModal(focus);
|
|
|
|
if (focus == fFocus && focus != NULL && !focus->IsHidden()
|
|
&& (focus->Flags() & B_AVOID_FOCUS) == 0 && !hasModal) {
|
|
// the window that is supposed to get focus already has focus
|
|
UnlockAllWindows();
|
|
return;
|
|
}
|
|
|
|
uint32 list = fCurrentWorkspace;
|
|
|
|
if (fSettings->FocusFollowsMouse())
|
|
list = kFocusList;
|
|
|
|
if (focus == NULL || hasModal) {
|
|
if (!fSettings->FocusFollowsMouse()) {
|
|
focus = FrontWindow();
|
|
if (focus == NULL) {
|
|
// there might be no front window in case of only a single
|
|
// window with B_FLOATING_ALL_WINDOW_FEEL
|
|
focus = _CurrentWindows().LastWindow();
|
|
}
|
|
} else
|
|
focus = fFocusList.LastWindow();
|
|
}
|
|
|
|
// make sure no window is chosen that doesn't want focus or cannot have it
|
|
while (focus != NULL
|
|
&& (!focus->InWorkspace(fCurrentWorkspace)
|
|
|| (focus->Flags() & B_AVOID_FOCUS) != 0
|
|
|| _WindowHasModal(focus)
|
|
|| focus->IsHidden())) {
|
|
focus = focus->PreviousWindow(list);
|
|
}
|
|
|
|
if (fFocus == focus) {
|
|
// turns out the window that is supposed to get focus now already has it
|
|
UnlockAllWindows();
|
|
return;
|
|
}
|
|
|
|
team_id oldActiveApp = -1;
|
|
team_id newActiveApp = -1;
|
|
|
|
if (fFocus != NULL) {
|
|
fFocus->SetFocus(false);
|
|
oldActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
|
|
}
|
|
|
|
fFocus = focus;
|
|
|
|
if (fFocus != NULL) {
|
|
fFocus->SetFocus(true);
|
|
newActiveApp = fFocus->ServerWindow()->App()->ClientTeam();
|
|
|
|
// move current focus to the end of the focus list
|
|
fFocusList.RemoveWindow(fFocus);
|
|
fFocusList.AddWindow(fFocus);
|
|
}
|
|
|
|
if (newActiveApp == -1) {
|
|
// make sure the cursor is visible
|
|
HWInterface()->SetCursorVisible(true);
|
|
}
|
|
|
|
UnlockAllWindows();
|
|
|
|
// change the "active" app if appropriate
|
|
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) {
|
|
// Don't allow this to be set when no mouse buttons
|
|
// are pressed. (BView::SetMouseEventMask() should only be called
|
|
// from mouse hooks.)
|
|
if (fLastMouseButtons == 0)
|
|
return;
|
|
}
|
|
|
|
fLockedFocusWindow = window;
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::_BringWindowsToFront(WindowList& windows, int32 list,
|
|
bool wereVisible)
|
|
{
|
|
// we don't need to redraw what is currently
|
|
// visible of the window
|
|
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);
|
|
|
|
// redraw what became visible of the window(s)
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
/*! Returns the last focussed non-hidden subset window belonging to the
|
|
specified \a window.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
|
|
/*! \brief Tries to move the specified window to the front of the screen,
|
|
and make it the focus window.
|
|
|
|
If there are any modal windows on this screen, it might not actually
|
|
become the frontmost window, though, as modal windows stay in front
|
|
of their subset.
|
|
*/
|
|
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 _(fWindowLock);
|
|
|
|
bool windowOnOtherWorkspace = !window->InWorkspace(fCurrentWorkspace);
|
|
if (windowOnOtherWorkspace
|
|
&& (window->Flags() & B_NOT_ANCHORED_ON_ACTIVATE) == 0) {
|
|
if ((window->Flags() & B_NO_WORKSPACE_ACTIVATION) == 0) {
|
|
// Switch to the workspace on which this window is
|
|
// (we'll take the first one that the window is on)
|
|
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()) {
|
|
// Bring a window to front that this floating window belongs to
|
|
Window* front = _LastFocusSubsetWindow(window);
|
|
if (front == NULL) {
|
|
// We can't do anything about those.
|
|
return;
|
|
}
|
|
|
|
ActivateWindow(front);
|
|
|
|
if (!window->InWorkspace(fCurrentWorkspace)) {
|
|
// This window can't be made active
|
|
return;
|
|
}
|
|
} else {
|
|
// Bring the window to the current workspace
|
|
// TODO: what if this window is on multiple workspaces?!?
|
|
uint32 workspaces = workspace_to_workspaces(fCurrentWorkspace);
|
|
SetWindowWorkspaces(window, workspaces);
|
|
}
|
|
}
|
|
|
|
if (window->IsMinimized()) {
|
|
// Unlike WindowAction(), this is called from the application itself,
|
|
// so we will just unminimize the window here.
|
|
window->SetMinimized(false);
|
|
ShowWindow(window);
|
|
}
|
|
|
|
if (window == FrontWindow()) {
|
|
// see if there is a normal B_AVOID_FRONT window still in front of us
|
|
Window* avoidsFront = window->NextWindow(fCurrentWorkspace);
|
|
while (avoidsFront && avoidsFront->IsNormal()
|
|
&& (avoidsFront->Flags() & B_AVOID_FRONT) == 0) {
|
|
avoidsFront = avoidsFront->NextWindow(fCurrentWorkspace);
|
|
}
|
|
|
|
if (avoidsFront == NULL) {
|
|
// we're already the frontmost window, we might just not have focus
|
|
// yet
|
|
if ((window->Flags() & B_AVOID_FOCUS) == 0)
|
|
SetFocusWindow(window);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// we don't need to redraw what is currently
|
|
// visible of the window
|
|
BRegion clean(window->VisibleRegion());
|
|
WindowList windows(kWorkingList);
|
|
|
|
Window* frontmost = window->Frontmost();
|
|
|
|
_CurrentWindows().RemoveWindow(window);
|
|
windows.AddWindow(window);
|
|
|
|
if (frontmost != NULL && frontmost->IsModal()) {
|
|
// all modal windows follow their subsets to the front
|
|
// (ie. they are staying in front of them, but they are
|
|
// not supposed to change their order because of that)
|
|
|
|
Window* nextModal;
|
|
for (Window* modal = frontmost; modal != NULL; modal = nextModal) {
|
|
// get the next modal window
|
|
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);
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::SendWindowBehind(Window* window, Window* behindOf)
|
|
{
|
|
// TODO: should the "not in current workspace" be handled anyway?
|
|
// (the code below would have to be changed then, though)
|
|
if (window == BackWindow()
|
|
|| !window->InWorkspace(fCurrentWorkspace)
|
|
|| (behindOf != NULL && !behindOf->InWorkspace(fCurrentWorkspace))
|
|
|| !LockAllWindows())
|
|
return;
|
|
|
|
// Is this a valid behindOf window?
|
|
if (behindOf != NULL && window->HasInSubset(behindOf))
|
|
behindOf = NULL;
|
|
|
|
// what is currently visible of the window
|
|
// might be dirty after the window is send to back
|
|
BRegion dirty(window->VisibleRegion());
|
|
|
|
// detach window and re-attach at desired position
|
|
Window* backmost = window->Backmost(behindOf);
|
|
|
|
_CurrentWindows().RemoveWindow(window);
|
|
_CurrentWindows().AddWindow(window, backmost
|
|
? backmost->NextWindow(fCurrentWorkspace) : BackWindow());
|
|
|
|
BRegion dummy;
|
|
_RebuildClippingForAllWindows(dummy);
|
|
|
|
// mark everything dirty that is no longer visible
|
|
BRegion clean(window->VisibleRegion());
|
|
dirty.Exclude(&clean);
|
|
MarkDirty(dirty);
|
|
|
|
_UpdateFronts();
|
|
SetFocusWindow(fSettings->FocusFollowsMouse() ?
|
|
WindowAt(fLastMousePosition) : NULL);
|
|
|
|
bool sendFakeMouseMoved = false;
|
|
if (FocusWindow() != window)
|
|
sendFakeMouseMoved = true;
|
|
|
|
_WindowChanged(window);
|
|
|
|
UnlockAllWindows();
|
|
|
|
if (sendFakeMouseMoved)
|
|
_SendFakeMouseMoved();
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::ShowWindow(Window* window)
|
|
{
|
|
if (!window->IsHidden())
|
|
return;
|
|
|
|
AutoWriteLocker locker(fWindowLock);
|
|
|
|
window->SetHidden(false);
|
|
fFocusList.AddWindow(window);
|
|
|
|
// If the window is on the current workspace, we'll show it. Special
|
|
// handling for floating windows, as they can only be shown if their
|
|
// subset is.
|
|
if (window->InWorkspace(fCurrentWorkspace)
|
|
|| (window->IsFloating() && _LastFocusSubsetWindow(window) != NULL)) {
|
|
_ShowWindow(window, true);
|
|
_UpdateSubsetWorkspaces(window);
|
|
ActivateWindow(window);
|
|
} else {
|
|
// then we don't need to send the fake mouse event either
|
|
_WindowChanged(window);
|
|
return;
|
|
}
|
|
|
|
if (window->HasWorkspacesViews()) {
|
|
// find workspaces views in view hierarchy
|
|
BAutolock _(fWorkspacesLock);
|
|
window->FindWorkspacesViews(fWorkspacesViews);
|
|
}
|
|
|
|
// If the mouse cursor is directly over the newly visible window,
|
|
// we'll send a fake mouse moved message to the window, so that
|
|
// it knows the mouse is over it.
|
|
|
|
_SendFakeMouseMoved(window);
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::HideWindow(Window* window)
|
|
{
|
|
if (window->IsHidden())
|
|
return;
|
|
|
|
if (!LockAllWindows())
|
|
return;
|
|
|
|
window->SetHidden(true);
|
|
fFocusList.RemoveWindow(window);
|
|
|
|
if (fMouseEventWindow == window) {
|
|
// Make its decorator lose the current mouse action
|
|
BMessage message;
|
|
int32 viewToken;
|
|
window->MouseUp(&message, fLastMousePosition, &viewToken);
|
|
|
|
fMouseEventWindow = NULL;
|
|
}
|
|
|
|
if (window->InWorkspace(fCurrentWorkspace)) {
|
|
_UpdateSubsetWorkspaces(window);
|
|
_HideWindow(window);
|
|
_UpdateFronts();
|
|
|
|
if (FocusWindow() == window)
|
|
SetFocusWindow();
|
|
} else
|
|
_WindowChanged(window);
|
|
|
|
_WindowRemoved(window);
|
|
|
|
if (window->HasWorkspacesViews()) {
|
|
// remove workspaces views from this window
|
|
BObjectList<WorkspacesView> list(false);
|
|
window->FindWorkspacesViews(list);
|
|
|
|
BAutolock _(fWorkspacesLock);
|
|
|
|
while (WorkspacesView* view = list.RemoveItemAt(0)) {
|
|
fWorkspacesViews.RemoveItem(view);
|
|
}
|
|
}
|
|
|
|
UnlockAllWindows();
|
|
|
|
if (window == fWindowUnderMouse)
|
|
_SendFakeMouseMoved();
|
|
}
|
|
|
|
|
|
/*!
|
|
Shows the window on the screen - it does this independently of the
|
|
Window::IsHidden() state.
|
|
*/
|
|
void
|
|
Desktop::_ShowWindow(Window* window, bool affectsOtherWindows)
|
|
{
|
|
BRegion background;
|
|
_RebuildClippingForAllWindows(background);
|
|
_SetBackground(background);
|
|
_WindowChanged(window);
|
|
|
|
BRegion dirty(window->VisibleRegion());
|
|
|
|
if (!affectsOtherWindows) {
|
|
// everything that is now visible in the
|
|
// window needs a redraw, but other windows
|
|
// are not affected, we can call ProcessDirtyRegion()
|
|
// of the window, and don't have to use MarkDirty()
|
|
window->ProcessDirtyRegion(dirty);
|
|
} else
|
|
MarkDirty(dirty);
|
|
|
|
window->ServerWindow()->HandleDirectConnection(
|
|
B_DIRECT_START | B_BUFFER_RESET);
|
|
}
|
|
|
|
|
|
/*!
|
|
Hides the window from the screen - it does this independently of the
|
|
Window::IsHidden() state.
|
|
*/
|
|
void
|
|
Desktop::_HideWindow(Window* window)
|
|
{
|
|
// after rebuilding the clipping,
|
|
// this window will not have a visible
|
|
// region anymore, so we need to remember
|
|
// it now
|
|
// (actually that's not true, since
|
|
// hidden windows are excluded from the
|
|
// clipping calculation, but anyways)
|
|
BRegion dirty(window->VisibleRegion());
|
|
|
|
BRegion background;
|
|
_RebuildClippingForAllWindows(background);
|
|
_SetBackground(background);
|
|
_WindowChanged(window);
|
|
|
|
MarkDirty(dirty);
|
|
|
|
window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::MoveWindowBy(Window* window, float x, float y, int32 workspace)
|
|
{
|
|
if (!LockAllWindows())
|
|
return;
|
|
|
|
if (workspace == -1)
|
|
workspace = fCurrentWorkspace;
|
|
|
|
if (!window->IsVisible() || workspace != fCurrentWorkspace) {
|
|
if (workspace != fCurrentWorkspace) {
|
|
// move the window on another workspace - this doesn't change it's
|
|
// current position
|
|
if (window->Anchor(workspace).position == kInvalidWindowPosition)
|
|
window->Anchor(workspace).position = window->Frame().LeftTop();
|
|
|
|
window->Anchor(workspace).position += BPoint(x, y);
|
|
_WindowChanged(window);
|
|
} else
|
|
window->MoveBy((int32)x, (int32)y);
|
|
|
|
UnlockAllWindows();
|
|
return;
|
|
}
|
|
|
|
// the dirty region starts with the visible area of the window being moved
|
|
BRegion newDirtyRegion(window->VisibleRegion());
|
|
|
|
// no more drawing for DirectWindows
|
|
window->ServerWindow()->HandleDirectConnection(B_DIRECT_STOP);
|
|
|
|
window->MoveBy((int32)x, (int32)y);
|
|
|
|
BRegion background;
|
|
_RebuildClippingForAllWindows(background);
|
|
|
|
// construct the region that is possible to be blitted
|
|
// to move the contents of the window
|
|
BRegion copyRegion(window->VisibleRegion());
|
|
copyRegion.OffsetBy((int32)-x, (int32)-y);
|
|
copyRegion.IntersectWith(&newDirtyRegion);
|
|
// newDirtyRegion == the windows old visible region
|
|
|
|
// include the the new visible region of the window being
|
|
// moved into the dirty region (for now)
|
|
newDirtyRegion.Include(&window->VisibleRegion());
|
|
|
|
GetDrawingEngine()->CopyRegion(©Region, (int32)x, (int32)y);
|
|
|
|
// allow DirectWindows to draw again after the visual
|
|
// content is at the new location
|
|
window->ServerWindow()->HandleDirectConnection(B_DIRECT_START | B_BUFFER_MOVED);
|
|
|
|
// in the dirty region, exclude the parts that we
|
|
// could move by blitting
|
|
copyRegion.OffsetBy((int32)x, (int32)y);
|
|
newDirtyRegion.Exclude(©Region);
|
|
|
|
MarkDirty(newDirtyRegion);
|
|
_SetBackground(background);
|
|
_WindowChanged(window);
|
|
|
|
UnlockAllWindows();
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::ResizeWindowBy(Window* window, float x, float y)
|
|
{
|
|
if (!LockAllWindows())
|
|
return;
|
|
|
|
if (!window->IsVisible()) {
|
|
window->ResizeBy((int32)x, (int32)y, NULL);
|
|
UnlockAllWindows();
|
|
return;
|
|
}
|
|
|
|
// the dirty region for the inside of the window is
|
|
// constructed by the window itself in ResizeBy()
|
|
BRegion newDirtyRegion;
|
|
// track the dirty region outside the window in case
|
|
// it is shrunk in "previouslyOccupiedRegion"
|
|
BRegion previouslyOccupiedRegion(window->VisibleRegion());
|
|
|
|
window->ResizeBy((int32)x, (int32)y, &newDirtyRegion);
|
|
|
|
BRegion background;
|
|
_RebuildClippingForAllWindows(background);
|
|
|
|
// we just care for the region outside the window
|
|
previouslyOccupiedRegion.Exclude(&window->VisibleRegion());
|
|
|
|
// make sure the window cannot mark stuff dirty outside
|
|
// its visible region...
|
|
newDirtyRegion.IntersectWith(&window->VisibleRegion());
|
|
// ...because we do this outself
|
|
newDirtyRegion.Include(&previouslyOccupiedRegion);
|
|
|
|
MarkDirty(newDirtyRegion);
|
|
_SetBackground(background);
|
|
_WindowChanged(window);
|
|
|
|
UnlockAllWindows();
|
|
}
|
|
|
|
|
|
bool
|
|
Desktop::SetWindowTabLocation(Window* window, float location)
|
|
{
|
|
AutoWriteLocker _(fWindowLock);
|
|
|
|
BRegion dirty;
|
|
bool changed = window->SetTabLocation(location, dirty);
|
|
if (changed)
|
|
_RebuildAndRedrawAfterWindowChange(window, dirty);
|
|
|
|
return changed;
|
|
}
|
|
|
|
|
|
bool
|
|
Desktop::SetWindowDecoratorSettings(Window* window, const BMessage& settings)
|
|
{
|
|
AutoWriteLocker _(fWindowLock);
|
|
|
|
BRegion dirty;
|
|
bool changed = window->SetDecoratorSettings(settings, dirty);
|
|
if (changed)
|
|
_RebuildAndRedrawAfterWindowChange(window, dirty);
|
|
|
|
return changed;
|
|
}
|
|
|
|
|
|
/*! Updates the workspaces of all subset windows with regard to the
|
|
specifed window.
|
|
If newIndex is not -1, it will move all subset windows that belong to
|
|
the specifed window to the new workspace; this form is only called by
|
|
SetWorkspace().
|
|
*/
|
|
void
|
|
Desktop::_UpdateSubsetWorkspaces(Window* window, int32 previousIndex,
|
|
int32 newIndex)
|
|
{
|
|
STRACE(("_UpdateSubsetWorkspaces(window %p, %s)\n", window, window->Title()));
|
|
|
|
// if the window is hidden, the subset windows are up-to-date already
|
|
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) {
|
|
// These windows are always visible on all workspaces,
|
|
// no need to update them.
|
|
continue;
|
|
}
|
|
|
|
if (subset->IsFloating()) {
|
|
// Floating windows are inserted and removed to the current
|
|
// workspace as the need arises - they are not handled here
|
|
// but in _UpdateFront()
|
|
continue;
|
|
}
|
|
|
|
if (subset->HasInSubset(window)) {
|
|
// adopt the workspace change
|
|
SetWindowWorkspaces(subset, subset->SubsetWorkspaces());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*!
|
|
\brief Adds or removes the window to or from the workspaces it's on.
|
|
*/
|
|
void
|
|
Desktop::_ChangeWindowWorkspaces(Window* window, uint32 oldWorkspaces,
|
|
uint32 newWorkspaces)
|
|
{
|
|
if (oldWorkspaces == newWorkspaces)
|
|
return;
|
|
|
|
// apply changes to the workspaces' window lists
|
|
|
|
LockAllWindows();
|
|
|
|
// NOTE: we bypass the anchor-mechanism by intention when switching
|
|
// the workspace programmatically.
|
|
|
|
for (int32 i = 0; i < kMaxWorkspaces; i++) {
|
|
if (workspace_in_workspaces(i, oldWorkspaces)) {
|
|
// window is on this workspace, is it anymore?
|
|
if (!workspace_in_workspaces(i, newWorkspaces)) {
|
|
_Windows(i).RemoveWindow(window);
|
|
|
|
if (i == CurrentWorkspace()) {
|
|
// remove its appearance from the current workspace
|
|
window->SetCurrentWorkspace(-1);
|
|
|
|
if (!window->IsHidden())
|
|
_HideWindow(window);
|
|
}
|
|
}
|
|
} else {
|
|
// window was not on this workspace, is it now?
|
|
if (workspace_in_workspaces(i, newWorkspaces)) {
|
|
_Windows(i).AddWindow(window,
|
|
window->Frontmost(_Windows(i).FirstWindow(), i));
|
|
|
|
if (i == CurrentWorkspace()) {
|
|
// make the window visible in current workspace
|
|
window->SetCurrentWorkspace(fCurrentWorkspace);
|
|
|
|
if (!window->IsHidden()) {
|
|
// this only affects other windows if this windows has floating or
|
|
// modal windows that need to be shown as well
|
|
// TODO: take care of this
|
|
_ShowWindow(window, FrontWindow() == window);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the window is visible only on one workspace, we set it's current
|
|
// position in that workspace (so that WorkspacesView will find us).
|
|
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();
|
|
|
|
// take care about modals and floating windows
|
|
_UpdateSubsetWorkspaces(window);
|
|
|
|
UnlockAllWindows();
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::SetWindowWorkspaces(Window* window, uint32 workspaces)
|
|
{
|
|
LockAllWindows();
|
|
|
|
if (window->IsNormal() && workspaces == B_CURRENT_WORKSPACE)
|
|
workspaces = workspace_to_workspaces(CurrentWorkspace());
|
|
|
|
uint32 oldWorkspaces = window->Workspaces();
|
|
|
|
window->WorkspacesChanged(oldWorkspaces, workspaces);
|
|
_ChangeWindowWorkspaces(window, oldWorkspaces, workspaces);
|
|
|
|
UnlockAllWindows();
|
|
}
|
|
|
|
|
|
/*! \brief Adds the window to the desktop.
|
|
At this point, the window is still hidden and must be shown explicetly
|
|
via ShowWindow().
|
|
*/
|
|
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 {
|
|
// subset windows are visible on all workspaces their subset is on
|
|
window->SetWorkspaces(window->SubsetWorkspaces());
|
|
}
|
|
|
|
_ChangeWindowWorkspaces(window, 0, window->Workspaces());
|
|
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);
|
|
UnlockAllWindows();
|
|
|
|
// make sure this window won't get any events anymore
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::SetWindowLook(Window* window, window_look newLook)
|
|
{
|
|
if (window->Look() == newLook)
|
|
return;
|
|
|
|
AutoWriteLocker _(fWindowLock);
|
|
|
|
BRegion dirty;
|
|
window->SetLook(newLook, &dirty);
|
|
// TODO: test what happens when the window
|
|
// finds out it needs to resize itself...
|
|
|
|
_RebuildAndRedrawAfterWindowChange(window, dirty);
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::SetWindowFeel(Window* window, window_feel newFeel)
|
|
{
|
|
if (window->Feel() == newFeel)
|
|
return;
|
|
|
|
LockAllWindows();
|
|
|
|
bool wasNormal = window->IsNormal();
|
|
|
|
window->SetFeel(newFeel);
|
|
|
|
// move the window out of or into the subset window list as needed
|
|
if (window->IsNormal() && !wasNormal)
|
|
fSubsetWindows.RemoveWindow(window);
|
|
else if (!window->IsNormal() && wasNormal)
|
|
fSubsetWindows.AddWindow(window);
|
|
|
|
// A normal window that was once a floating or modal window will
|
|
// adopt the window's current workspaces
|
|
|
|
if (!window->IsNormal())
|
|
_ChangeWindowWorkspaces(window, window->Workspaces(), window->SubsetWorkspaces());
|
|
|
|
// make sure the window has the correct position in the window lists
|
|
// (ie. all floating windows have to be on the top, ...)
|
|
|
|
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) {
|
|
// check if the backmost window is really behind it
|
|
Window* previous = window->PreviousWindow(i);
|
|
while (previous != NULL) {
|
|
if (previous == backmost)
|
|
break;
|
|
|
|
previous = previous->PreviousWindow(i);
|
|
}
|
|
|
|
if (previous == NULL) {
|
|
// need to reinsert window before its backmost window
|
|
_Windows(i).RemoveWindow(window);
|
|
_Windows(i).AddWindow(window, backmost->NextWindow(i));
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
Window* frontmost = window->Frontmost(_Windows(i).FirstWindow(), i);
|
|
if (frontmost != NULL) {
|
|
// check if the frontmost window is really in front of it
|
|
Window* next = window->NextWindow(i);
|
|
while (next != NULL) {
|
|
if (next == frontmost)
|
|
break;
|
|
|
|
next = next->NextWindow(i);
|
|
}
|
|
|
|
if (next == NULL) {
|
|
// need to reinsert window behind its frontmost window
|
|
_Windows(i).RemoveWindow(window);
|
|
_Windows(i).AddWindow(window, frontmost);
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if (i == fCurrentWorkspace && changed) {
|
|
BRegion dummy;
|
|
_RebuildClippingForAllWindows(dummy);
|
|
|
|
// mark everything dirty that is no longer visible, or
|
|
// is now visible and wasn't before
|
|
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();
|
|
|
|
UnlockAllWindows();
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::SetWindowFlags(Window *window, uint32 newFlags)
|
|
{
|
|
if (window->Flags() == newFlags)
|
|
return;
|
|
|
|
AutoWriteLocker _(fWindowLock);
|
|
|
|
BRegion dirty;
|
|
window->SetFlags(newFlags, &dirty);
|
|
// TODO: test what happens when the window
|
|
// finds out it needs to resize itself...
|
|
|
|
_RebuildAndRedrawAfterWindowChange(window, dirty);
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::SetWindowTitle(Window *window, const char* title)
|
|
{
|
|
AutoWriteLocker _(fWindowLock);
|
|
|
|
BRegion dirty;
|
|
window->SetTitle(title, dirty);
|
|
|
|
_RebuildAndRedrawAfterWindowChange(window, dirty);
|
|
}
|
|
|
|
|
|
/*!
|
|
Returns the window under the mouse cursor.
|
|
You need to have acquired the All Windows lock when calling this method.
|
|
*/
|
|
Window*
|
|
Desktop::WindowAt(BPoint where)
|
|
{
|
|
for (Window* window = _CurrentWindows().LastWindow(); window;
|
|
window = window->PreviousWindow(fCurrentWorkspace)) {
|
|
if (window->IsVisible() && window->VisibleRegion().Contains(where))
|
|
return window;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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::MinimizeApplication(team_id team)
|
|
{
|
|
AutoWriteLocker locker(fWindowLock);
|
|
|
|
// Just minimize all windows of that application
|
|
|
|
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);
|
|
|
|
// TODO: for now, just maximize all windows of that application
|
|
// TODO: have the ability to lock the current workspace
|
|
|
|
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()) {
|
|
// the window is visible, we just need to make it the front window
|
|
ActivateWindow(window);
|
|
} else {
|
|
// if not, ask the window if it wants to be unminimized
|
|
serverWindow->NotifyMinimize(action == B_MINIMIZE_WINDOW);
|
|
}
|
|
|
|
UnlockAllWindows();
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::WriteWindowList(team_id team, BPrivate::LinkSender& sender)
|
|
{
|
|
AutoWriteLocker locker(fWindowLock);
|
|
|
|
// compute the number of windows
|
|
|
|
int32 count = 0;
|
|
|
|
for (Window *window = fAllWindows.FirstWindow(); window != NULL;
|
|
window = window->NextWindow(kAllWindowList)) {
|
|
if (team < B_OK || window->ServerWindow()->ClientTeam() == team)
|
|
count++;
|
|
}
|
|
|
|
// write list
|
|
|
|
sender.StartMessage(B_OK);
|
|
sender.Attach<int32>(count);
|
|
|
|
// first write the windows of the current workspace correctly ordered
|
|
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());
|
|
}
|
|
|
|
// then write all the other windows
|
|
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);
|
|
BAutolock tokenLocker(BPrivate::gDefaultTokens);
|
|
|
|
::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);
|
|
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();
|
|
|
|
// write list
|
|
|
|
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();
|
|
// as long as we hold the window lock, no new window can appear
|
|
|
|
if (workspace < 0)
|
|
workspace = fCurrentWorkspace;
|
|
else if (workspace >= kMaxWorkspaces) {
|
|
sender.StartMessage(B_BAD_VALUE);
|
|
sender.Flush();
|
|
UnlockSingleWindow();
|
|
return;
|
|
}
|
|
|
|
// compute the list of applications on this workspace
|
|
|
|
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) {
|
|
// see if we already have this team
|
|
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();
|
|
|
|
// write list
|
|
|
|
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::MarkDirty(BRegion& region)
|
|
{
|
|
if (region.CountRects() == 0)
|
|
return;
|
|
|
|
if (LockAllWindows()) {
|
|
// send redraw messages to all windows intersecting the dirty region
|
|
_TriggerWindowRedrawing(region);
|
|
|
|
UnlockAllWindows();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::Redraw()
|
|
{
|
|
BRegion dirty(fVirtualScreen.Frame());
|
|
MarkDirty(dirty);
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::_RebuildClippingForAllWindows(BRegion& stillAvailableOnScreen)
|
|
{
|
|
// the available region on screen starts with the entire screen area
|
|
// each window on the screen will take a portion from that area
|
|
|
|
// figure out what the entire screen area is
|
|
stillAvailableOnScreen = fScreenRegion;
|
|
|
|
// set clipping of each window
|
|
for (Window* window = _CurrentWindows().LastWindow(); window != NULL;
|
|
window = window->PreviousWindow(fCurrentWorkspace)) {
|
|
if (!window->IsHidden()) {
|
|
window->SetClipping(&stillAvailableOnScreen);
|
|
// that windows region is not available on screen anymore
|
|
stillAvailableOnScreen.Exclude(&window->VisibleRegion());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::_TriggerWindowRedrawing(BRegion& newDirtyRegion)
|
|
{
|
|
// send redraw messages to all windows intersecting the dirty region
|
|
for (Window* window = _CurrentWindows().LastWindow(); window != NULL;
|
|
window = window->PreviousWindow(fCurrentWorkspace)) {
|
|
if (!window->IsHidden()
|
|
&& newDirtyRegion.Intersects(window->VisibleRegion().Frame()))
|
|
window->ProcessDirtyRegion(newDirtyRegion);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
Desktop::_SetBackground(BRegion& background)
|
|
{
|
|
// NOTE: the drawing operation is caried out
|
|
// in the clipping region rebuild, but it is
|
|
// ok actually, because it also avoids trails on
|
|
// moving windows
|
|
|
|
// remember the region not covered by any windows
|
|
// and redraw the dirty 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! The all window lock must be held when calling this function.
|
|
void
|
|
Desktop::_RebuildAndRedrawAfterWindowChange(Window* window, BRegion& dirty)
|
|
{
|
|
if (!window->IsVisible() || dirty.CountRects() == 0)
|
|
return;
|
|
|
|
BRegion stillAvailableOnScreen;
|
|
_RebuildClippingForAllWindows(stillAvailableOnScreen);
|
|
_SetBackground(stillAvailableOnScreen);
|
|
_WindowChanged(window);
|
|
|
|
_TriggerWindowRedrawing(dirty);
|
|
}
|
|
|