root/src/servers/app/Window.h
/*
 * Copyright 2001-2020, Haiku, Inc.
 * Distributed under the terms of the MIT license.
 *
 * Authors:
 *              DarkWyrm, bpmagic@columbus.rr.com
 *              Adi Oanca, adioanca@gmail.com
 *              Stephan Aßmus, superstippi@gmx.de
 *              Axel Dörfler, axeld@pinc-software.de
 *              Brecht Machiels, brecht@mos6581.org
 *              Clemens Zeidler, haiku@clemens-zeidler.de
 *              Tri-Edge AI
 *              Jacob Secunda, secundja@gmail.com
 */
#ifndef WINDOW_H
#define WINDOW_H


#include "RegionPool.h"
#include "ServerWindow.h"
#include "View.h"
#include "WindowList.h"

#include <AutoDeleter.h>
#include <ObjectList.h>
#include <Referenceable.h>
#include <Region.h>
#include <String.h>


class Window;


typedef BObjectList<Window>     StackWindows;


class WindowStack : public BReferenceable {
public:
                                                                WindowStack(::Decorator* decorator);
                                                                ~WindowStack();

                        void                            SetDecorator(::Decorator* decorator);
                        ::Decorator*            Decorator();

        const   StackWindows&           WindowList() const { return fWindowList; }
        const   StackWindows&           LayerOrder() const { return fWindowLayerOrder; }

                        Window*                         TopLayerWindow() const;

                        int32                           CountWindows();
                        Window*                         WindowAt(int32 index);
                        bool                            AddWindow(Window* window,
                                                                        int32 position = -1);
                        bool                            RemoveWindow(Window* window);

                        bool                            MoveToTopLayer(Window* window);
                        bool                            Move(int32 from, int32 to);
private:
                        ObjectDeleter< ::Decorator>
                                                                fDecorator;

                        StackWindows            fWindowList;
                        StackWindows            fWindowLayerOrder;
};


namespace BPrivate {
        class PortLink;
};

class ClickTarget;
class ClientLooper;
class Decorator;
class Desktop;
class DrawingEngine;
class EventDispatcher;
class Screen;
class WindowBehaviour;
class WorkspacesView;

// TODO: move this into a proper place
#define AS_REDRAW 'rdrw'


class Window {
public:
                                                                Window(const BRect& frame, const char *name,
                                                                        window_look look, window_feel feel,
                                                                        uint32 flags, uint32 workspaces,
                                                                        ::ServerWindow* window,
                                                                        DrawingEngine* drawingEngine);
        virtual                                         ~Window();

                        status_t                        InitCheck() const;

                        BRect                           Frame() const { return fFrame; }
                        const char*                     Title() const { return fTitle.String(); }

                        window_anchor&          Anchor(int32 index);
                        Window*                         NextWindow(int32 index) const;
                        Window*                         PreviousWindow(int32 index) const;

                        ::Desktop*                      Desktop() const { return fDesktop; }
                        ::Decorator*            Decorator() const;
                        ::ServerWindow*         ServerWindow() const { return fWindow; }
                        ::EventTarget&          EventTarget() const
                                                                        { return fWindow->EventTarget(); }

                        bool                            ReloadDecor();

                        void                            SetScreen(const ::Screen* screen);
                        const ::Screen*         Screen() const;

                        // setting and getting the "hard" clipping, you need to have
                        // WriteLock()ed the clipping!
                        void                            SetClipping(BRegion* stillAvailableOnScreen);
                        // you need to have ReadLock()ed the clipping!
        inline  BRegion&                        VisibleRegion() { return fVisibleRegion; }
                        BRegion&                        VisibleContentRegion();

                        // TODO: not protected by a lock, but noone should need this anyways
                        // make private? when used inside Window, it has the ReadLock()
                        void                            GetFullRegion(BRegion* region);
                        void                            GetBorderRegion(BRegion* region);
                        void                            GetContentRegion(BRegion* region);

                        void                            MoveBy(int32 x, int32 y, bool moveStack = true);
                        void                            ResizeBy(int32 x, int32 y,
                                                                        BRegion* dirtyRegion,
                                                                        bool resizeStack = true);
                        void                            SetOutlinesDelta(BPoint delta,
                                                                        BRegion* dirtyRegion);

                        void                            ScrollViewBy(View* view, int32 dx, int32 dy);

                        void                            SetTopView(View* topView);
                        View*                           TopView() const { return fTopView.Get(); }
                        View*                           ViewAt(const BPoint& where);

        virtual bool                            IsOffscreenWindow() const { return false; }

                        void                            GetEffectiveDrawingRegion(View* view,
                                                                        BRegion& region);
                        bool                            DrawingRegionChanged(View* view) const;

                        // generic version, used by the Desktop
                        void                            ProcessDirtyRegion(const BRegion& dirtyRegion,
                                                                        const BRegion& exposeRegion);
                        void                            ProcessDirtyRegion(const BRegion& exposeRegion)
                                                                        { ProcessDirtyRegion(exposeRegion, exposeRegion); }
                        void                            RedrawDirtyRegion();

                        // can be used from inside classes that don't
                        // need to know about Desktop (first version uses Desktop)
                        void                            MarkDirty(BRegion& regionOnScreen);
                        // these versions do not use the Desktop
                        void                            MarkContentDirty(BRegion& dirtyRegion,
                                                                        BRegion& exposeRegion);
                        void                            MarkContentDirtyAsync(BRegion& dirtyRegion);
                        // shortcut for invalidating just one view
                        void                            InvalidateView(View* view, BRegion& viewRegion);

                        void                            DisableUpdateRequests();
                        void                            EnableUpdateRequests();

                        void                            BeginUpdate(BPrivate::PortLink& link);
                        void                            EndUpdate();
                        bool                            InUpdate() const
                                                                        { return fInUpdate; }

                        bool                            NeedsUpdate() const
                                                                        { return fUpdateRequested; }

                        DrawingEngine*          GetDrawingEngine() const
                                                                        { return fDrawingEngine.Get(); }

                        // managing a region pool
                        ::RegionPool*           RegionPool()
                                                                        { return &fRegionPool; }
        inline  BRegion*                        GetRegion()
                                                                        { return fRegionPool.GetRegion(); }
        inline  BRegion*                        GetRegion(const BRegion& copy)
                                                                        { return fRegionPool.GetRegion(copy); }
        inline  void                            RecycleRegion(BRegion* region)
                                                                        { fRegionPool.Recycle(region); }

                        void                            CopyContents(BRegion* region,
                                                                        int32 xOffset, int32 yOffset);

                        void                            MouseDown(BMessage* message, BPoint where,
                                                                        const ClickTarget& lastClickTarget,
                                                                        int32& clickCount,
                                                                        ClickTarget& _clickTarget);
                        void                            MouseUp(BMessage* message, BPoint where,
                                                                        int32* _viewToken);
                        void                            MouseMoved(BMessage* message, BPoint where,
                                                                        int32* _viewToken, bool isLatestMouseMoved,
                                                                        bool isFake);

                        void                            ModifiersChanged(int32 modifiers);

                        // some hooks to inform the client window
                        // TODO: move this to ServerWindow maybe?
                        void                            WorkspaceActivated(int32 index, bool active);
                        void                            WorkspacesChanged(uint32 oldWorkspaces,
                                                                        uint32 newWorkspaces);
                        void                            Activated(bool active);

                        // changing some properties
                        void                            SetTitle(const char* name, BRegion& dirty);

                        void                            SetFocus(bool focus);
                        bool                            IsFocus() const { return fIsFocus; }

                        void                            SetHidden(bool hidden);
        inline  bool                            IsHidden() const { return fHidden; }

                        void                            SetShowLevel(int32 showLevel);
        inline  int32                           ShowLevel() const { return fShowLevel; }

                        void                            SetMinimized(bool minimized);
        inline  bool                            IsMinimized() const { return fMinimized; }

                        void                            SetCurrentWorkspace(int32 index)
                                                                        { fCurrentWorkspace = index; fPriorWorkspace = index; }
                        int32                           CurrentWorkspace() const
                                                                        { return fCurrentWorkspace; }
                        bool                            IsVisible() const;

                        void                            SetPriorWorkspace(int32 index)
                                                                        { fPriorWorkspace = index; }
                        int32                           PriorWorkspace() const
                                                                        { return fPriorWorkspace; }

                        bool                            IsDragging() const;
                        bool                            IsResizing() const;

                        void                            SetSizeLimits(int32 minWidth, int32 maxWidth,
                                                                        int32 minHeight, int32 maxHeight);

                        void                            GetSizeLimits(int32* minWidth, int32* maxWidth,
                                                                        int32* minHeight, int32* maxHeight) const;

                                                                // 0.0 -> left .... 1.0 -> right
                        bool                            SetTabLocation(float location, bool isShifting,
                                                                        BRegion& dirty);
                        float                           TabLocation() const;

                        bool                            SetDecoratorSettings(const BMessage& settings,
                                                                                                         BRegion& dirty);
                        bool                            GetDecoratorSettings(BMessage* settings);

                        void                            HighlightDecorator(bool active);

                        void                            FontsChanged(BRegion* updateRegion);
                        void                            ColorsChanged(BRegion* updateRegion);

                        void                            SetLook(window_look look,
                                                                        BRegion* updateRegion);
                        void                            SetFeel(window_feel feel);
                        void                            SetFlags(uint32 flags, BRegion* updateRegion);

                        window_look                     Look() const { return fLook; }
                        window_feel                     Feel() const { return fFeel; }
                        uint32                          Flags() const { return fFlags; }

                        // window manager stuff
                        uint32                          Workspaces() const { return fWorkspaces; }
                        void                            SetWorkspaces(uint32 workspaces)
                                                                        { fWorkspaces = workspaces; }
                        bool                            InWorkspace(int32 index) const;

                        bool                            SupportsFront();

                        bool                            IsModal() const;
                        bool                            IsFloating() const;
                        bool                            IsNormal() const;

                        bool                            HasModal() const;

                        Window*                         Frontmost(Window* first = NULL,
                                                                        int32 workspace = -1);
                        Window*                         Backmost(Window* first = NULL,
                                                                        int32 workspace = -1);

                        bool                            AddToSubset(Window* window);
                        void                            RemoveFromSubset(Window* window);
                        bool                            HasInSubset(const Window* window) const;
                        bool                            SameSubset(Window* window);
                        uint32                          SubsetWorkspaces() const;
                        bool                            InSubsetWorkspace(int32 index) const;

                        bool                            HasWorkspacesViews() const
                                                                        { return fWorkspacesViewCount != 0; }
                        void                            AddWorkspacesView()
                                                                        { fWorkspacesViewCount++; }
                        void                            RemoveWorkspacesView()
                                                                        { fWorkspacesViewCount--; }
                        void                            FindWorkspacesViews(
                                                                        BObjectList<WorkspacesView>& list) const;

        static  bool                            IsValidLook(window_look look);
        static  bool                            IsValidFeel(window_feel feel);
        static  bool                            IsModalFeel(window_feel feel);
        static  bool                            IsFloatingFeel(window_feel feel);

        static  uint32                          ValidWindowFlags();
        static  uint32                          ValidWindowFlags(window_feel feel);

                        // Window stack methods.
                        WindowStack*            GetWindowStack();

                        bool                            DetachFromWindowStack(
                                                                        bool ownStackNeeded = true);
                        bool                            AddWindowToStack(Window* window);
                        Window*                         StackedWindowAt(const BPoint& where);
                        Window*                         TopLayerStackWindow();

                        int32                           PositionInStack() const;
                        bool                            MoveToTopStackLayer();
                        bool                            MoveToStackPosition(int32 index,
                                                                        bool isMoving);
protected:
                        void                            _ShiftPartOfRegion(BRegion* region,
                                                                        BRegion* regionToShift, int32 xOffset,
                                                                        int32 yOffset);

                        // different types of drawing
                        void                            _TriggerContentRedraw(BRegion& dirty,
                                                                        const BRegion& expose = BRegion());
                        void                            _DrawBorder();

                        // handling update sessions
                        void                            _TransferToUpdateSession(
                                                                        BRegion* contentDirtyRegion);
                        void                            _SendUpdateMessage();

                        void                            _UpdateContentRegion();

                        void                            _ObeySizeLimits();
                        void                            _PropagatePosition();

                        BString                         fTitle;
                        // TODO: no fp rects anywhere
                        BRect                           fFrame;
                        const ::Screen*         fScreen;

                        window_anchor           fAnchor[kListCount];

                        // the visible region is only recalculated from the
                        // Desktop thread, when using it, Desktop::ReadLockClipping()
                        // has to be called

                        BRegion                         fVisibleRegion;
                        BRegion                         fVisibleContentRegion;

                        // Our part of the "global" dirty region (what needs to be redrawn).
                        // It is calculated from the desktop thread, but we can write to it when we read locked
                        // the clipping, since it is local and the desktop thread is blocked.
                        BRegion                         fDirtyRegion;

                        // Subset of the dirty region that is newly exposed. While the dirty region is merely
                        // showing out of date data on screen, this subset of it is showing remains of other
                        // windows. To avoid glitches, it must be set to a reasonable state as fast as possible,
                        // without waiting for a roundtrip to the window's Draw() methods. So it will be filled
                        // using background color and view bitmap, which can all be done without leaving
                        // app_server.
                        BRegion                         fExposeRegion;

                        // caching local regions
                        BRegion                         fContentRegion;
                        BRegion                         fEffectiveDrawingRegion;

                        bool                            fVisibleContentRegionValid : 1;
                        bool                            fContentRegionValid : 1;
                        bool                            fEffectiveDrawingRegionValid : 1;

                        ::RegionPool            fRegionPool;

                        BObjectList<Window> fSubsets;

                        ObjectDeleter<WindowBehaviour>
                                                                fWindowBehaviour;
                        ObjectDeleter<View>     fTopView;
                        ::ServerWindow*         fWindow;
                        ObjectDeleter<DrawingEngine>
                                                                fDrawingEngine;
                        ::Desktop*                      fDesktop;

                        // The synchronization, which client drawing commands
                        // belong to the redraw of which dirty region is handled
                        // through an UpdateSession. When the client has
                        // been informed that it should redraw stuff, then
                        // this is the current update session. All new
                        // redraw requests from the Desktop will go
                        // into the pending update session.
        class UpdateSession {
        public:
                                                                        UpdateSession();

                                void                            Include(BRegion* additionalDirty);
                                void                            Exclude(BRegion* dirtyInNextSession);

                inline  BRegion&                        DirtyRegion()
                                                                                { return fDirtyRegion; }

                                void                            MoveBy(int32 x, int32 y);

                                void                            SetUsed(bool used);
                inline  bool                            IsUsed() const
                                                                                { return fInUse; }

        private:
                                BRegion                         fDirtyRegion;
                                bool                            fInUse;
        };

                        UpdateSession           fUpdateSessions[2];
                        UpdateSession*          fCurrentUpdateSession;
                        UpdateSession*          fPendingUpdateSession;
                        // these two flags are supposed to ensure a sane
                        // and consistent update session
                        bool                            fUpdateRequested : 1;
                        bool                            fInUpdate : 1;
                        bool                            fUpdatesEnabled : 1;

                        bool                            fHidden : 1;
                        int32                           fShowLevel;
                        bool                            fMinimized : 1;
                        bool                            fIsFocus : 1;

                        window_look                     fLook;
                        window_feel                     fFeel;
                        uint32                          fOriginalFlags;
                        uint32                          fFlags;
                        uint32                          fWorkspaces;
                        int32                           fCurrentWorkspace;
                        int32                           fPriorWorkspace;

                        int32                           fMinWidth;
                        int32                           fMaxWidth;
                        int32                           fMinHeight;
                        int32                           fMaxHeight;

                        int32                           fWorkspacesViewCount;

                friend class DecorManager;

private:
                        WindowStack*            _InitWindowStack();

                        BReference<WindowStack>         fCurrentStack;
};


#endif // WINDOW_H