haiku/src/servers/app/RootLayer.cpp
Axel Dörfler a631158a62 Reenabled most of the workspaces functionality - the Workspaces window doesn't
show any windows, but everything else seems to work fine.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15241 a95241bf-73f2-0310-859d-f6bbb57e9c96
2005-11-30 15:47:01 +00:00

681 lines
14 KiB
C++

/*
* Copyright 2001-2005, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Gabe Yoder <gyoder@stny.rr.com>
* DarkWyrm <bpmagic@columbus.rr.com>
* Adrian Oanca <adioanca@gmail.com>
* Stephan Aßmus <superstippi@gmx.de>
* Axel Dörfler, axeld@pinc-software.de
*/
/** Class used for the top layer of each workspace's Layer tree */
#include "Decorator.h"
#include "DrawingEngine.h"
#include "HWInterface.h"
#include "Layer.h"
#include "RootLayer.h"
#include "ServerApp.h"
#include "ServerConfig.h"
#include "ServerProtocol.h"
#include "ServerScreen.h"
#include "ServerWindow.h"
#include "WindowLayer.h"
#include "Workspace.h"
#include "WorkspacesLayer.h"
#include <File.h>
#include <List.h>
#include <Message.h>
#include <PortLink.h>
#include <Window.h>
#include <stdio.h>
#if DISPLAY_HAIKU_LOGO
#include "ServerBitmap.h"
#include "HaikuLogo.h"
static const float kGoldenProportion = (sqrtf(5.0) - 1.0) / 2.0;
#endif
//#define TRACE_ROOT_LAYER
#ifdef TRACE_ROOT_LAYER
# define STRACE(a) printf a
#else
# define STRACE(a) ;
#endif
RootLayer::RootLayer(const char *name, Desktop *desktop, DrawingEngine *driver)
: Layer(BRect(0, 0, 0, 0), name, 0, B_FOLLOW_ALL, B_WILL_DRAW, driver),
fDesktop(desktop),
fDragMessage(NULL),
fMouseEventWindow(NULL),
fAllRegionsLock("root layer region lock"),
fDirtyForRedraw(),
fWorkspace(0),
fWorkspacesLayer(NULL),
fFocus(NULL),
fFront(NULL),
fBack(NULL)
{
//NOTE: be careful about this one.
fRootLayer = this;
// Some stuff that will go away in the future but fills some gaps right now
#if ON_SCREEN_DEBUGGING_INFO
fDebugInfo.SetTo("");
#endif
fDrawState->SetHighColor(RGBColor(255, 255, 255));
#if DISPLAY_HAIKU_LOGO
fLogoBitmap = new UtilityBitmap(BRect(0.0, 0.0, kLogoWidth - 1.0,
kLogoHeight - 1.0),
kLogoFormat, 0);
if (fLogoBitmap->IsValid()) {
int32 size = min_c(sizeof(kLogoBits), fLogoBitmap->BitsLength());
memcpy(fLogoBitmap->Bits(), kLogoBits, size);
} else {
delete fLogoBitmap;
fLogoBitmap = NULL;
}
#endif // DISPLAY_HAIKU_LOGO
fHidden = false;
#if ON_SCREEN_DEBUGGING_INFO
DebugInfoManager::Default()->SetRootLayer(this);
#endif
fFrame = desktop->VirtualScreen().Frame();
// RootLayer starts with valid visible regions
fFullVisible.Set(Bounds());
fVisible.Set(Bounds());
// first make sure we are actualy visible
MarkForRebuild(Bounds());
MarkForRedraw(Bounds());
_UpdateWorkspace(fDesktop->WorkspaceAt(fDesktop->CurrentWorkspace()));
TriggerRebuild();
TriggerRedraw();
}
RootLayer::~RootLayer()
{
delete fDragMessage;
fFirstChild = NULL;
// this prevents the Layer destructor from freeing our children again
// (the Desktop destructor already took care of that)
// RootLayer object just uses Screen objects, it is not allowed to delete them.
#if DISPLAY_HAIKU_LOGO
delete fLogoBitmap;
#endif
}
void
RootLayer::MoveBy(float x, float y)
{
}
void
RootLayer::ResizeBy(float x, float y)
{
if (x == 0 && y == 0)
return;
// TODO: we should be able to update less here
//BRect previous = fFrame;
fFrame.right += x;
fFrame.bottom += y;
fFullVisible.Set(Bounds());
fVisible.Set(Bounds());
BRegion region(fFrame);
MarkForRebuild(region);
TriggerRebuild();
//region.Exclude(previous);
MarkForRedraw(region);
TriggerRedraw();
}
bool
RootLayer::_SetFocus(WindowLayer* focus, BRegion& update)
{
// TODO: test for FFM and B_LOCK_WINDOW_FOCUS
if (focus == fFocus)
return true;
if (fFocus != NULL) {
update.Include(&fFocus->VisibleRegion());
fFocus->SetFocus(false);
}
fFocus = focus;
if (focus != NULL) {
update.Include(&focus->VisibleRegion());
focus->SetFocus(true);
}
return true;
}
bool
RootLayer::SetFocus(WindowLayer* focus)
{
BAutolock _(fAllRegionsLock);
BRegion update;
bool success = _SetFocus(focus, update);
if (!success)
return false;
MarkForRedraw(update);
TriggerRedraw();
return true;
}
void
RootLayer::_SetFront(WindowLayer* windowLayer, BRegion& update)
{
if (windowLayer == NULL) {
fBack = NULL;
fFront = NULL;
return;
}
if (!windowLayer->SupportsFront() && fFront != NULL)
return;
BRegion previous = windowLayer->FullVisible();
_RemoveChildFromList(windowLayer);
_AddChildToList(windowLayer);
windowLayer->GetOnScreenRegion(update);
update.Exclude(&previous);
fFront = windowLayer;
if (windowLayer == fBack || fBack == NULL)
_UpdateBack();
}
/*! search the visible windows for a valid back window
(only normal windows can be back windows)
*/
void
RootLayer::_UpdateBack()
{
fBack = NULL;
for (WindowLayer* window = (WindowLayer*)FirstChild();
window != NULL; window = (WindowLayer*)window->NextLayer()) {
if (window->IsHidden() || !window->SupportsFront())
continue;
fBack = window;
break;
}
}
/*! search the visible windows for a valid front window
(only normal windows can be front windows)
*/
void
RootLayer::_UpdateFront()
{
// TODO: for now, just choose the top window
fFront = NULL;
for (WindowLayer* window = (WindowLayer*)LastChild();
window != NULL; window = (WindowLayer*)window->PreviousLayer()) {
if (window->IsHidden() || !window->SupportsFront())
continue;
fFront = window;
break;
}
}
void
RootLayer::_UpdateFronts()
{
_UpdateBack();
_UpdateFront();
}
void
RootLayer::ActivateWindow(WindowLayer* windowLayer)
{
BAutolock _(fAllRegionsLock);
BRegion changed;
_SetFront(windowLayer, changed);
// TODO: if this window has any floating windows, add them here
MarkForRebuild(changed);
TriggerRebuild();
_SetFocus(windowLayer, changed);
_WindowsChanged(changed);
MarkForRedraw(changed);
TriggerRedraw();
}
void
RootLayer::SendBehindWindow(WindowLayer* windowLayer, WindowLayer* behindOf)
{
BAutolock _(fAllRegionsLock);
if (windowLayer == Back())
return;
bool wasFocus = windowLayer == Focus();
BRegion changed = windowLayer->FullVisible();
_RemoveChildFromList(windowLayer);
if (behindOf == NULL)
behindOf = Back();
_AddChildToList(windowLayer, behindOf);
// TODO: if this window has any floating windows, remove them here
MarkForRebuild(changed);
TriggerRebuild();
_UpdateFronts();
_SetFocus(Front(), changed);
changed.Exclude(&windowLayer->FullVisible());
if (wasFocus != (Focus() == windowLayer))
changed.Include(&windowLayer->VisibleRegion());
_WindowsChanged(changed);
MarkForRedraw(changed);
TriggerRedraw();
}
void
RootLayer::AddWindowLayer(WindowLayer* windowLayer)
{
STRACE(("AddWindowLayer(%p \"%s\")\n", windowLayer, windowLayer->Name()));
if (windowLayer->SupportsFront())
_AddChildToList(windowLayer);
else
_AddChildToList(windowLayer, Back());
windowLayer->SetRootLayer(this);
if (!windowLayer->IsHidden())
ActivateWindow(windowLayer);
}
void
RootLayer::RemoveWindowLayer(WindowLayer* windowLayer)
{
if (!windowLayer->IsHidden())
HideWindowLayer(windowLayer);
_RemoveChildFromList(windowLayer);
LayerRemoved(windowLayer);
windowLayer->SetRootLayer(NULL);
}
void
RootLayer::_UpdateWorkspace(Workspace& workspace)
{
fColor = workspace.Color();
#if ON_SCREEN_DEBUGGING_INFO
fDrawState->SetLowColor(fColor);
#endif
}
void
RootLayer::SetWorkspace(int32 index, Workspace& previousWorkspace,
Workspace& workspace)
{
BAutolock _(fAllRegionsLock);
int32 previousIndex = fWorkspace;
// build region of windows that are no longer visible in the new workspace
// and move windows to the previous workspace's window list
BRegion changed;
while (WindowLayer* window = (WindowLayer*)FirstChild()) {
// move the window into the workspace's window list
_RemoveChildFromList(window);
BPoint position = window->Frame().LeftTop();
previousWorkspace.AddWindow(window, &position);
if (window->IsHidden())
continue;
if (!window->OnWorkspace(index)) {
// this window will no longer be visible
changed.Include(&window->FullVisible());
}
}
fWorkspace = index;
_UpdateWorkspace(workspace);
// add new windows, and include them in the changed region - but only
// those that were not visible before (or whose position changed)
while (workspace.CountWindows() != 0) {
window_layer_info* info = workspace.WindowAt(0);
WindowLayer* window = info->window;
BPoint position = info->position;
// move window into the RootLayer's list
workspace.RemoveWindowAt(0);
_AddChildToList(window);
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->OnWorkspace(previousIndex)) {
// this window was not visible before
// TODO: what we really want here is the visible region of the window
BRegion region;
window->GetOnScreenRegion(region);
changed.Include(&region);
} else if (window->Frame().LeftTop() != position) {
// the window was visible before, but its on-screen location changed
BPoint offset = position - window->Frame().LeftTop();
window->MoveBy(offset.x, offset.y);
// TODO: we're playing dumb here - what we need is a MoveBy() that
// gives us a dirty region back, and since the new clipping code
// will give us just that, we don't take any special measurement
// here
GetOnScreenRegion(changed);
} else {
// the window is still visible and on the same location
continue;
}
}
_UpdateFronts();
_SetFocus(Front(), changed);
MarkForRebuild(changed);
TriggerRebuild();
_WindowsChanged(changed);
MarkForRedraw(changed);
TriggerRedraw();
}
void
RootLayer::HideWindowLayer(WindowLayer* windowLayer)
{
BAutolock _(fAllRegionsLock);
BRegion changed = windowLayer->FullVisible();
windowLayer->Hide();
_UpdateFronts();
if (dynamic_cast<WorkspacesLayer*>(windowLayer->TopLayer()) != NULL)
fWorkspacesLayer = NULL;
// TODO: if this window has any floating windows, remove them here
MarkForRebuild(changed);
TriggerRebuild();
if (windowLayer == Focus())
_SetFocus(Front(), changed);
_WindowsChanged(changed);
MarkForRedraw(changed);
TriggerRedraw();
}
void
RootLayer::ShowWindowLayer(WindowLayer* windowLayer, bool toFront)
{
STRACE(("ShowWindowLayer(%p)\n", windowLayer));
if (!windowLayer->IsHidden())
return;
BAutolock _(fAllRegionsLock);
windowLayer->Show();
BRegion changed = windowLayer->FullVisible();
if (toFront)
_SetFront(windowLayer, changed);
else
_UpdateFront();
// TODO: if this window has any floating windows, remove them here
// TODO: support FFM and B_AVOID_FOCUS
if (Front() == windowLayer || Focus() == NULL)
_SetFocus(windowLayer, changed);
_WindowsChanged(changed);
MarkForRedraw(changed);
TriggerRedraw();
if (dynamic_cast<WorkspacesLayer*>(windowLayer->TopLayer()) != NULL)
fWorkspacesLayer = windowLayer->TopLayer();
}
void
RootLayer::SetWindowLayerFeel(WindowLayer *windowLayer, int32 newFeel)
{
BAutolock _(fAllRegionsLock);
// TODO
}
void
RootLayer::SetWindowLayerLook(WindowLayer *windowLayer, int32 newLook)
{
BAutolock _(fAllRegionsLock);
// TODO
}
void
RootLayer::_WindowsChanged(BRegion& region)
{
if (fWorkspacesLayer == NULL)
return;
region.Include(&fWorkspacesLayer->VisibleRegion());
}
WindowLayer*
RootLayer::WindowAt(BPoint where)
{
if (VisibleRegion().Contains(where))
return NULL;
for (Layer* child = LastChild(); child; child = child->PreviousLayer()) {
if (child->FullVisible().Contains(where))
return dynamic_cast<WindowLayer*>(child);
}
return NULL;
}
// #pragma mark - Input related methods
void
RootLayer::SetMouseEventWindow(WindowLayer* window)
{
fMouseEventWindow = window;
}
void
RootLayer::LayerRemoved(Layer* layer)
{
if (fMouseEventWindow == layer)
fMouseEventWindow = NULL;
if (fWorkspacesLayer == layer)
fWorkspacesLayer = NULL;
}
void
RootLayer::SetDragMessage(BMessage* msg)
{
if (fDragMessage) {
delete fDragMessage;
fDragMessage = NULL;
}
if (msg)
fDragMessage = new BMessage(*msg);
}
BMessage *
RootLayer::DragMessage() const
{
return fDragMessage;
}
void
RootLayer::Draw(const BRect& rect)
{
fDriver->FillRect(rect, fColor);
#if ON_SCREEN_DEBUGGING_INFO
BPoint location(5, 40);
float textHeight = 15.0; // be lazy
const char* p = fDebugInfo.String();
const char* start = p;
while (*p) {
p++;
if (*p == 0 || *p == '\n') {
fDriver->DrawString(start, p - start, location,
fDrawState);
// NOTE: Eventually, this will be below the screen!
location.y += textHeight;
start = p + 1;
}
}
#endif // ON_SCREEN_DEBUGGING_INFO
#if DISPLAY_HAIKU_LOGO
if (fLogoBitmap) {
BPoint logoPos;
logoPos.x = floorf(min_c(fFrame.left + fFrame.Width() * kGoldenProportion,
fFrame.right - (kLogoWidth + kLogoHeight / 2.0)));
logoPos.y = floorf(fFrame.bottom - kLogoHeight * 1.5);
BRect bitmapBounds = fLogoBitmap->Bounds();
fDriver->DrawBitmap(fLogoBitmap, bitmapBounds,
bitmapBounds.OffsetToCopy(logoPos), fDrawState);
}
#endif // DISPLAY_HAIKU_LOGO
}
#if ON_SCREEN_DEBUGGING_INFO
void
RootLayer::AddDebugInfo(const char* string)
{
if (Lock()) {
fDebugInfo << string;
MarkForRedraw(VisibleRegion());
TriggerRedraw();
Unlock();
}
}
#endif // ON_SCREEN_DEBUGGING_INFO
void
RootLayer::MarkForRedraw(const BRegion &dirty)
{
BAutolock locker(fAllRegionsLock);
fDirtyForRedraw.Include(&dirty);
}
void
RootLayer::TriggerRedraw()
{
BAutolock locker(fAllRegionsLock);
#if 0
// for debugging
GetDrawingEngine()->ConstrainClippingRegion(&fDirtyForRedraw);
RGBColor color(rand()%255, rand()%255, rand()%255);
GetDrawingEngine()->FillRect(Bounds(), color);
snooze(200000);
GetDrawingEngine()->ConstrainClippingRegion(NULL);
#endif
_AllRedraw(fDirtyForRedraw);
fDirtyForRedraw.MakeEmpty();
}