/*
    KWin - the KDE window manager
    This file is part of the KDE project.

    SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#ifndef KWIN_TOPLEVEL_H
#define KWIN_TOPLEVEL_H

// kwin
#include "input.h"
#include "utils.h"
#include "virtualdesktops.h"
#include "xcbutils.h"
// KDE
#include <NETWM>
// Qt
#include <QObject>
#include <QMatrix4x4>
#include <QUuid>
// xcb
#include <xcb/damage.h>
#include <xcb/xfixes.h>
// c++
#include <functional>

class QOpenGLFramebufferObject;

namespace KWaylandServer
{
class SurfaceInterface;
}

namespace KWin
{

class ClientMachine;
class Deleted;
class EffectWindowImpl;
class Shadow;

/**
 * Enum to describe the reason why a Toplevel has to be released.
 */
enum class ReleaseReason {
    Release, ///< Normal Release after e.g. an Unmap notify event (window still valid)
    Destroyed, ///< Release after an Destroy notify event (window no longer valid)
    KWinShutsDown ///< Release on KWin Shutdown (window still valid)
};

class KWIN_EXPORT Toplevel : public QObject
{
    Q_OBJECT

    Q_PROPERTY(bool alpha READ hasAlpha NOTIFY hasAlphaChanged)
    Q_PROPERTY(qulonglong frameId READ frameId)

    /**
     * This property holds the geometry of the Toplevel, excluding invisible
     * portions, e.g. client-side and server-side drop-shadows, etc.
     *
     * @deprecated Use frameGeometry property instead.
     */
    Q_PROPERTY(QRect geometry READ frameGeometry NOTIFY frameGeometryChanged)

    /**
     * This property holds rectangle that the pixmap or buffer of this Toplevel
     * occupies on the screen. This rectangle includes invisible portions of the
     * client, e.g. client-side drop shadows, etc.
     */
    Q_PROPERTY(QRect bufferGeometry READ bufferGeometry)

    /**
     * This property holds the geometry of the Toplevel, excluding invisible
     * portions, e.g. server-side and client-side drop-shadows, etc.
     */
    Q_PROPERTY(QRect frameGeometry READ frameGeometry NOTIFY frameGeometryChanged)

    /**
     * This property holds the position of the Toplevel's frame geometry.
     */
    Q_PROPERTY(QPoint pos READ pos)

    /**
     * This property holds the size of the Toplevel's frame geometry.
     */
    Q_PROPERTY(QSize size READ size)

    /**
     * This property holds the x position of the Toplevel's frame geometry.
     */
    Q_PROPERTY(int x READ x)

    /**
     * This property holds the y position of the Toplevel's frame geometry.
     */
    Q_PROPERTY(int y READ y)

    /**
     * This property holds the width of the Toplevel's frame geometry.
     */
    Q_PROPERTY(int width READ width)

    /**
     * This property holds the height of the Toplevel's frame geometry.
     */
    Q_PROPERTY(int height READ height)

    Q_PROPERTY(QRect visibleRect READ visibleRect)
    Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged)
    Q_PROPERTY(int screen READ screen NOTIFY screenChanged)
    Q_PROPERTY(qulonglong windowId READ windowId CONSTANT)
    Q_PROPERTY(int desktop READ desktop)

    /**
     * Whether the window is on all desktops. That is desktop is -1.
     */
    Q_PROPERTY(bool onAllDesktops READ isOnAllDesktops)

    Q_PROPERTY(QRect rect READ rect)
    Q_PROPERTY(QPoint clientPos READ clientPos)
    Q_PROPERTY(QSize clientSize READ clientSize)
    Q_PROPERTY(QByteArray resourceName READ resourceName NOTIFY windowClassChanged)
    Q_PROPERTY(QByteArray resourceClass READ resourceClass NOTIFY windowClassChanged)
    Q_PROPERTY(QByteArray windowRole READ windowRole NOTIFY windowRoleChanged)

    /**
     * Returns whether the window is a desktop background window (the one with wallpaper).
     * See _NET_WM_WINDOW_TYPE_DESKTOP at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool desktopWindow READ isDesktop)

    /**
     * Returns whether the window is a dock (i.e. a panel).
     * See _NET_WM_WINDOW_TYPE_DOCK at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool dock READ isDock)

    /**
     * Returns whether the window is a standalone (detached) toolbar window.
     * See _NET_WM_WINDOW_TYPE_TOOLBAR at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool toolbar READ isToolbar)

    /**
     * Returns whether the window is a torn-off menu.
     * See _NET_WM_WINDOW_TYPE_MENU at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool menu READ isMenu)

    /**
     * Returns whether the window is a "normal" window, i.e. an application or any other window
     * for which none of the specialized window types fit.
     * See _NET_WM_WINDOW_TYPE_NORMAL at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool normalWindow READ isNormalWindow)

    /**
     * Returns whether the window is a dialog window.
     * See _NET_WM_WINDOW_TYPE_DIALOG at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool dialog READ isDialog)

    /**
     * Returns whether the window is a splashscreen. Note that many (especially older) applications
     * do not support marking their splash windows with this type.
     * See _NET_WM_WINDOW_TYPE_SPLASH at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool splash READ isSplash)

    /**
     * Returns whether the window is a utility window, such as a tool window.
     * See _NET_WM_WINDOW_TYPE_UTILITY at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool utility READ isUtility)

    /**
     * Returns whether the window is a dropdown menu (i.e. a popup directly or indirectly open
     * from the applications menubar).
     * See _NET_WM_WINDOW_TYPE_DROPDOWN_MENU at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool dropdownMenu READ isDropdownMenu)

    /**
     * Returns whether the window is a popup menu (that is not a torn-off or dropdown menu).
     * See _NET_WM_WINDOW_TYPE_POPUP_MENU at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool popupMenu READ isPopupMenu)

    /**
     * Returns whether the window is a tooltip.
     * See _NET_WM_WINDOW_TYPE_TOOLTIP at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool tooltip READ isTooltip)

    /**
     * Returns whether the window is a window with a notification.
     * See _NET_WM_WINDOW_TYPE_NOTIFICATION at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool notification READ isNotification)

    /**
     * Returns whether the window is a window with a critical notification.
     */
    Q_PROPERTY(bool criticalNotification READ isCriticalNotification)

    /**
     * Returns whether the window is an On Screen Display.
     */
    Q_PROPERTY(bool onScreenDisplay READ isOnScreenDisplay)

    /**
     * Returns whether the window is a combobox popup.
     * See _NET_WM_WINDOW_TYPE_COMBO at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool comboBox READ isComboBox)

    /**
     * Returns whether the window is a Drag&Drop icon.
     * See _NET_WM_WINDOW_TYPE_DND at https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(bool dndIcon READ isDNDIcon)

    /**
     * Returns the NETWM window type
     * See https://standards.freedesktop.org/wm-spec/wm-spec-latest.html .
     */
    Q_PROPERTY(int windowType READ windowType)

    Q_PROPERTY(QStringList activities READ activities)

    /**
     * Whether this Toplevel is managed by KWin (it has control over its placement and other
     * aspects, as opposed to override-redirect windows that are entirely handled by the application).
     */
    Q_PROPERTY(bool managed READ isClient CONSTANT)

    /**
     * Whether this Toplevel represents an already deleted window and only kept for the compositor for animations.
     */
    Q_PROPERTY(bool deleted READ isDeleted CONSTANT)

    /**
     * Whether the window has an own shape
     */
    Q_PROPERTY(bool shaped READ shape NOTIFY shapedChanged)

    /**
     * Whether the window does not want to be animated on window close.
     * There are legit reasons for this like a screenshot application which does not want it's
     * window being captured.
     */
    Q_PROPERTY(bool skipsCloseAnimation READ skipsCloseAnimation WRITE setSkipCloseAnimation NOTIFY skipCloseAnimationChanged)

    /**
     * The Id of the Wayland Surface associated with this Toplevel.
     * On X11 only setups the value is @c 0.
     */
    Q_PROPERTY(quint32 surfaceId READ surfaceId NOTIFY surfaceIdChanged)

    /**
     * Interface to the Wayland Surface.
     * Relevant only in Wayland, in X11 it will be nullptr
     */
    Q_PROPERTY(KWaylandServer::SurfaceInterface *surface READ surface)

    /**
     * Whether the window is a popup.
     */
    Q_PROPERTY(bool popupWindow READ isPopupWindow)

    /**
     * Whether this Toplevel represents the outline.
     *
     * @note It's always @c false if compositing is turned off.
     */
    Q_PROPERTY(bool outline READ isOutline)

    /**
     * This property holds a UUID to uniquely identify this Toplevel.
     */
    Q_PROPERTY(QUuid internalId READ internalId CONSTANT)

    /**
     * The pid of the process owning this window.
     *
     * @since 5.20
     */
    Q_PROPERTY(int pid READ pid CONSTANT)

public:
    explicit Toplevel();
    virtual xcb_window_t frameId() const;
    xcb_window_t window() const;
    /**
     * @return a unique identifier for the Toplevel. On X11 same as @ref window
     */
    virtual quint32 windowId() const;
    /**
     * Returns the geometry of the pixmap or buffer attached to this Toplevel.
     *
     * For X11 clients, this method returns server-side geometry of the Toplevel.
     *
     * For Wayland clients, this method returns rectangle that the main surface
     * occupies on the screen, in global screen coordinates.
     */
    virtual QRect bufferGeometry() const = 0;
    /**
     * Returns the extents of invisible portions in the pixmap.
     *
     * An X11 pixmap may contain invisible space around the actual contents of the
     * client. That space is reserved for server-side decoration, which we usually
     * want to skip when building contents window quads.
     *
     * Default implementation returns a margins object with all margins set to 0.
     */
    virtual QMargins bufferMargins() const;
    /**
     * Returns the geometry of the Toplevel, excluding invisible portions, e.g.
     * server-side and client-side drop shadows, etc.
     */
    QRect frameGeometry() const;
    /**
     * Returns the geometry of the client window, in global screen coordinates.
     */
    QRect clientGeometry() const;
    /**
     * Returns the extents of the server-side decoration.
     *
     * Note that the returned margins object will have all margins set to 0 if
     * the client doesn't have a server-side decoration.
     *
     * Default implementation returns a margins object with all margins set to 0.
     */
    virtual QMargins frameMargins() const;
    /**
     * The geometry of the Toplevel which accepts input events. This might be larger
     * than the actual geometry, e.g. to support resizing outside the window.
     *
     * Default implementation returns same as geometry.
     */
    virtual QRect inputGeometry() const;
    QSize size() const;
    QPoint pos() const;
    QRect rect() const;
    int x() const;
    int y() const;
    int width() const;
    int height() const;
    bool isOnScreen(int screen) const;   // true if it's at least partially there
    bool isOnActiveScreen() const;
    int screen() const; // the screen where the center is
    /**
     * The scale of the screen this window is currently on
     * @note The buffer scale can be different.
     * @since 5.12
     */
    qreal screenScale() const; //
    /**
     * Returns the ratio between physical pixels and device-independent pixels for
     * the attached buffer (or pixmap).
     *
     * For X11 clients, this method always returns 1.
     */
    virtual qreal bufferScale() const;
    virtual QPoint clientPos() const = 0; // inside of geometry()
    /**
     * Describes how the client's content maps to the window geometry including the frame.
     * The default implementation is a 1:1 mapping meaning the frame is part of the content.
     */
    virtual QPoint clientContentPos() const;
    QSize clientSize() const;
    /**
     * Returns a rectangle that the window occupies on the screen, including drop-shadows.
     */
    virtual QRect visibleRect() const;
    virtual QRect transparentRect() const = 0;
    virtual bool isClient() const;
    virtual bool isDeleted() const;

    // prefer isXXX() instead
    // 0 for supported types means default for managed/unmanaged types
    virtual NET::WindowType windowType(bool direct = false, int supported_types = 0) const = 0;
    bool hasNETSupport() const;
    bool isDesktop() const;
    bool isDock() const;
    bool isToolbar() const;
    bool isMenu() const;
    bool isNormalWindow() const; // normal as in 'NET::Normal or NET::Unknown non-transient'
    bool isDialog() const;
    bool isSplash() const;
    bool isUtility() const;
    bool isDropdownMenu() const;
    bool isPopupMenu() const; // a context popup, not dropdown, not torn-off
    bool isTooltip() const;
    bool isNotification() const;
    bool isCriticalNotification() const;
    bool isOnScreenDisplay() const;
    bool isComboBox() const;
    bool isDNDIcon() const;

    virtual bool isLockScreen() const;
    virtual bool isInputMethod() const;
    virtual bool isOutline() const;

    /**
     * Returns the virtual desktop within the workspace() the client window
     * is located in, 0 if it isn't located on any special desktop (not mapped yet),
     * or NET::OnAllDesktops. Do not use desktop() directly, use
     * isOnDesktop() instead.
     */
    virtual int desktop() const = 0;
    virtual QVector<VirtualDesktop *> desktops() const = 0;
    virtual QStringList activities() const = 0;
    bool isOnDesktop(int d) const;
    bool isOnActivity(const QString &activity) const;
    bool isOnCurrentDesktop() const;
    bool isOnCurrentActivity() const;
    bool isOnAllDesktops() const;
    bool isOnAllActivities() const;

    virtual QByteArray windowRole() const;
    QByteArray sessionId() const;
    QByteArray resourceName() const;
    QByteArray resourceClass() const;
    QByteArray wmCommand();
    QByteArray wmClientMachine(bool use_localhost) const;
    const ClientMachine *clientMachine() const;
    virtual bool isLocalhost() const;
    xcb_window_t wmClientLeader() const;
    virtual pid_t pid() const;
    static bool resourceMatch(const Toplevel* c1, const Toplevel* c2);

    bool readyForPainting() const; // true if the window has been already painted its contents
    xcb_visualid_t visual() const;
    bool shape() const;
    QRegion inputShape() const;
    virtual void setOpacity(double opacity);
    virtual double opacity() const;
    int depth() const;
    bool hasAlpha() const;
    virtual bool setupCompositing();
    virtual void finishCompositing(ReleaseReason releaseReason = ReleaseReason::Release);
    Q_INVOKABLE void addRepaint(const QRect& r);
    Q_INVOKABLE void addRepaint(const QRegion& r);
    Q_INVOKABLE void addRepaint(int x, int y, int w, int h);
    Q_INVOKABLE void addLayerRepaint(const QRect& r);
    Q_INVOKABLE void addLayerRepaint(const QRegion& r);
    Q_INVOKABLE void addLayerRepaint(int x, int y, int w, int h);
    Q_INVOKABLE virtual void addRepaintFull();
    // these call workspace->addRepaint(), but first transform the damage if needed
    void addWorkspaceRepaint(const QRect& r);
    void addWorkspaceRepaint(int x, int y, int w, int h);
    QRegion repaints() const;
    void resetRepaints();
    QRegion damage() const;
    void resetDamage();
    EffectWindowImpl* effectWindow();
    const EffectWindowImpl* effectWindow() const;
    /**
     * Window will be temporarily painted as if being at the top of the stack.
     * Only available if Compositor is active, if not active, this method is a no-op.
     */
    void elevate(bool elevate);

    /**
     * Returns the pointer to the Toplevel's Shadow. A Shadow
     * is only available if Compositing is enabled and the corresponding X window
     * has the Shadow property set.
     * @returns The Shadow belonging to this Toplevel, @c null if there's no Shadow.
     */
    const Shadow *shadow() const;
    Shadow *shadow();
    /**
     * Updates the Shadow associated with this Toplevel from X11 Property.
     * Call this method when the Property changes or Compositing is started.
     */
    void updateShadow();
    /**
     * Whether the Toplevel currently wants the shadow to be rendered. Default
     * implementation always returns @c true.
     */
    virtual bool wantsShadowToBeRendered() const;

    /**
     * This method returns the area that the Toplevel window reports to be opaque.
     * It is supposed to only provide valuable information if hasAlpha is @c true .
     * @see hasAlpha
     */
    const QRegion& opaqueRegion() const;

    virtual Layer layer() const = 0;

    /**
     * Resets the damage state and sends a request for the damage region.
     * A call to this function must be followed by a call to getDamageRegionReply(),
     * or the reply will be leaked.
     *
     * Returns true if the window was damaged, and false otherwise.
     */
    bool resetAndFetchDamage();

    /**
     * Gets the reply from a previous call to resetAndFetchDamage().
     * Calling this function is a no-op if there is no pending reply.
     * Call damage() to return the fetched region.
     */
    void getDamageRegionReply();

    bool skipsCloseAnimation() const;
    void setSkipCloseAnimation(bool set);

    quint32 surfaceId() const;
    KWaylandServer::SurfaceInterface *surface() const;
    void setSurface(KWaylandServer::SurfaceInterface *surface);

    const QSharedPointer<QOpenGLFramebufferObject> &internalFramebufferObject() const;
    QImage internalImageObject() const;

    /**
     * @returns Transformation to map from global to window coordinates.
     *
     * Default implementation returns a translation on negative pos().
     * @see pos
     */
    virtual QMatrix4x4 inputTransformation() const;

    /**
     * The window has a popup grab. This means that when it got mapped the
     * parent window had an implicit (pointer) grab.
     *
     * Normally this is only relevant for transient windows.
     *
     * Once the popup grab ends (e.g. pointer press outside of any Toplevel of
     * the client), the method popupDone should be invoked.
     *
     * The default implementation returns @c false.
     * @see popupDone
     * @since 5.10
     */
    virtual bool hasPopupGrab() const {
        return false;
    }
    /**
     * This method should be invoked for Toplevels with a popup grab when
     * the grab ends.
     *
     * The default implementation does nothing.
     * @see hasPopupGrab
     * @since 5.10
     */
    virtual void popupDone() {};

    /**
     * @brief Finds the Toplevel matching the condition expressed in @p func in @p list.
     *
     * The method is templated to operate on either a list of Toplevels or on a list of
     * a subclass type of Toplevel.
     * @param list The list to search in
     * @param func The condition function (compare std::find_if)
     * @return T* The found Toplevel or @c null if there is no matching Toplevel
     */
    template <class T, class U>
    static T *findInList(const QList<T*> &list, std::function<bool (const U*)> func);

    /**
     * Whether the window is a popup.
     *
     * Popups can be used to implement popup menus, tooltips, combo boxes, etc.
     *
     * @since 5.15
     */
    virtual bool isPopupWindow() const;

    /**
     * A UUID to uniquely identify this Toplevel independent of windowing system.
     */
    QUuid internalId() const
    {
        return m_internalId;
    }

Q_SIGNALS:
    void opacityChanged(KWin::Toplevel* toplevel, qreal oldOpacity);
    void damaged(KWin::Toplevel* toplevel, const QRegion& damage);
    void inputTransformationChanged();
    /**
     * This signal is emitted when the Toplevel's frame geometry changes.
     * @deprecated since 5.19, use frameGeometryChanged instead
     */
    void geometryChanged();
    void geometryShapeChanged(KWin::Toplevel* toplevel, const QRect& old);
    void paddingChanged(KWin::Toplevel* toplevel, const QRect& old);
    void windowClosed(KWin::Toplevel* toplevel, KWin::Deleted* deleted);
    void windowShown(KWin::Toplevel* toplevel);
    void windowHidden(KWin::Toplevel* toplevel);
    /**
     * Signal emitted when the window's shape state changed. That is if it did not have a shape
     * and received one or if the shape was withdrawn. Think of Chromium enabling/disabling KWin's
     * decoration.
     */
    void shapedChanged();
    /**
     * Emitted whenever the state changes in a way, that the Compositor should
     * schedule a repaint of the scene.
     */
    void needsRepaint();
    /**
     * Emitted whenever the Toplevel's screen changes. This can happen either in consequence to
     * a screen being removed/added or if the Toplevel's geometry changes.
     * @since 4.11
     */
    void screenChanged();
    void skipCloseAnimationChanged();
    /**
     * Emitted whenever the window role of the window changes.
     * @since 5.0
     */
    void windowRoleChanged();
    /**
     * Emitted whenever the window class name or resource name of the window changes.
     * @since 5.0
     */
    void windowClassChanged();
    /**
     * Emitted when a Wayland Surface gets associated with this Toplevel.
     * @since 5.3
     */
    void surfaceIdChanged(quint32);
    /**
     * @since 5.4
     */
    void hasAlphaChanged();

    /**
     * Emitted whenever the Surface for this Toplevel changes.
     */
    void surfaceChanged();

    /*
     * Emitted when the client's screen changes onto a screen of a different scale
     * or the screen we're on changes
     * @since 5.12
     */
    void screenScaleChanged();

    /**
     * Emitted whenever the client's shadow changes.
     * @since 5.15
     */
    void shadowChanged();

    /**
     * This signal is emitted when the Toplevel's buffer geometry changes.
     */
    void bufferGeometryChanged(KWin::Toplevel *toplevel, const QRect &oldGeometry);
    /**
     * This signal is emitted when the Toplevel's frame geometry changes.
     */
    void frameGeometryChanged(KWin::Toplevel *toplevel, const QRect &oldGeometry);
    /**
     * This signal is emitted when the Toplevel's client geometry has changed.
     */
    void clientGeometryChanged(KWin::Toplevel *toplevel, const QRect &oldGeometry);

protected Q_SLOTS:
    /**
     * Checks whether the screen number for this Toplevel changed and updates if needed.
     * Any method changing the geometry of the Toplevel should call this method.
     */
    void checkScreen();
    void setupCheckScreenConnection();
    void removeCheckScreenConnection();
    void setReadyForPainting();

protected:
    ~Toplevel() override;
    void setWindowHandles(xcb_window_t client);
    void detectShape(xcb_window_t id);
    virtual void propertyNotifyEvent(xcb_property_notify_event_t *e);
    virtual void damageNotifyEvent();
    virtual void clientMessageEvent(xcb_client_message_event_t *e);
    void discardWindowPixmap();
    void addDamageFull();
    virtual void addDamage(const QRegion &damage);
    Xcb::Property fetchWmClientLeader() const;
    void readWmClientLeader(Xcb::Property &p);
    void getWmClientLeader();
    void getWmClientMachine();
    /**
     * @returns Whether there is a compositor and it is active.
     */
    bool compositing() const;

    /**
     * This function fetches the opaque region from this Toplevel.
     * Will only be called on corresponding property changes and for initialization.
     */
    void getWmOpaqueRegion();

    void getResourceClass();
    void setResourceClass(const QByteArray &name, const QByteArray &className = QByteArray());
    Xcb::Property fetchSkipCloseAnimation() const;
    void readSkipCloseAnimation(Xcb::Property &prop);
    void getSkipCloseAnimation();
    void copyToDeleted(Toplevel* c);
    void disownDataPassedToDeleted();
    void deleteEffectWindow();
    void setDepth(int depth);
    QRect m_frameGeometry;
    QRect m_clientGeometry;
    xcb_visualid_t m_visual;
    int bit_depth;
    NETWinInfo* info;
    bool ready_for_painting;
    QRegion repaints_region; // updating, repaint just requires repaint of that area
    QRegion layer_repaints_region;
    /**
     * An FBO object KWin internal windows might render to.
     */
    QSharedPointer<QOpenGLFramebufferObject> m_internalFBO;
    QImage m_internalImage;

protected:
    bool m_isDamaged;

private:
    // when adding new data members, check also copyToDeleted()
    QUuid m_internalId;
    Xcb::Window m_client;
    xcb_damage_damage_t damage_handle;
    QRegion damage_region; // damage is really damaged window (XDamage) and texture needs
    bool is_shape;
    EffectWindowImpl* effect_window;
    QByteArray resource_name;
    QByteArray resource_class;
    ClientMachine *m_clientMachine;
    xcb_window_t m_wmClientLeader;
    bool m_damageReplyPending;
    QRegion opaque_region;
    xcb_xfixes_fetch_region_cookie_t m_regionCookie;
    int m_screen;
    bool m_skipCloseAnimation;
    quint32 m_surfaceId = 0;
    KWaylandServer::SurfaceInterface *m_surface = nullptr;
    // when adding new data members, check also copyToDeleted()
    qreal m_screenScale = 1.0;
};

inline xcb_window_t Toplevel::window() const
{
    return m_client;
}

inline void Toplevel::setWindowHandles(xcb_window_t w)
{
    Q_ASSERT(!m_client.isValid() && w != XCB_WINDOW_NONE);
    m_client.reset(w, false);
}

inline QRect Toplevel::clientGeometry() const
{
    return m_clientGeometry;
}

inline QSize Toplevel::clientSize() const
{
    return m_clientGeometry.size();
}

inline QRect Toplevel::frameGeometry() const
{
    return m_frameGeometry;
}

inline QSize Toplevel::size() const
{
    return m_frameGeometry.size();
}

inline QPoint Toplevel::pos() const
{
    return m_frameGeometry.topLeft();
}

inline int Toplevel::x() const
{
    return m_frameGeometry.x();
}

inline int Toplevel::y() const
{
    return m_frameGeometry.y();
}

inline int Toplevel::width() const
{
    return m_frameGeometry.width();
}

inline int Toplevel::height() const
{
    return m_frameGeometry.height();
}

inline QRect Toplevel::rect() const
{
    return QRect(0, 0, width(), height());
}

inline bool Toplevel::readyForPainting() const
{
    return ready_for_painting;
}

inline xcb_visualid_t Toplevel::visual() const
{
    return m_visual;
}

inline bool Toplevel::isDesktop() const
{
    return windowType() == NET::Desktop;
}

inline bool Toplevel::isDock() const
{
    return windowType() == NET::Dock;
}

inline bool Toplevel::isMenu() const
{
    return windowType() == NET::Menu;
}

inline bool Toplevel::isToolbar() const
{
    return windowType() == NET::Toolbar;
}

inline bool Toplevel::isSplash() const
{
    return windowType() == NET::Splash;
}

inline bool Toplevel::isUtility() const
{
    return windowType() == NET::Utility;
}

inline bool Toplevel::isDialog() const
{
    return windowType() == NET::Dialog;
}

inline bool Toplevel::isNormalWindow() const
{
    return windowType() == NET::Normal;
}

inline bool Toplevel::isDropdownMenu() const
{
    return windowType() == NET::DropdownMenu;
}

inline bool Toplevel::isPopupMenu() const
{
    return windowType() == NET::PopupMenu;
}

inline bool Toplevel::isTooltip() const
{
    return windowType() == NET::Tooltip;
}

inline bool Toplevel::isNotification() const
{
    return windowType() == NET::Notification;
}

inline bool Toplevel::isCriticalNotification() const
{
    return windowType() == NET::CriticalNotification;
}

inline bool Toplevel::isOnScreenDisplay() const
{
    return windowType() == NET::OnScreenDisplay;
}

inline bool Toplevel::isComboBox() const
{
    return windowType() == NET::ComboBox;
}

inline bool Toplevel::isDNDIcon() const
{
    return windowType() == NET::DNDIcon;
}

inline bool Toplevel::isLockScreen() const
{
    return false;
}

inline bool Toplevel::isInputMethod() const
{
    return false;
}

inline bool Toplevel::isOutline() const
{
    return false;
}

inline QRegion Toplevel::damage() const
{
    return damage_region;
}

inline QRegion Toplevel::repaints() const
{
    return repaints_region.translated(pos()) | layer_repaints_region;
}

inline bool Toplevel::shape() const
{
    return is_shape;
}

inline int Toplevel::depth() const
{
    return bit_depth;
}

inline bool Toplevel::hasAlpha() const
{
    return depth() == 32;
}

inline const QRegion& Toplevel::opaqueRegion() const
{
    return opaque_region;
}

inline
EffectWindowImpl* Toplevel::effectWindow()
{
    return effect_window;
}

inline
const EffectWindowImpl* Toplevel::effectWindow() const
{
    return effect_window;
}

inline bool Toplevel::isOnAllDesktops() const
{
    return kwinApp()->operationMode() == Application::OperationModeWaylandOnly ||
           kwinApp()->operationMode() == Application::OperationModeXwayland
        //Wayland
        ? desktops().isEmpty()
        //X11
        : desktop() == NET::OnAllDesktops;
}

inline bool Toplevel::isOnAllActivities() const
{
    return activities().isEmpty();
}

inline bool Toplevel::isOnDesktop(int d) const
{
    return (kwinApp()->operationMode() == Application::OperationModeWaylandOnly ||
            kwinApp()->operationMode() == Application::OperationModeXwayland
            ? desktops().contains(VirtualDesktopManager::self()->desktopForX11Id(d))
            : desktop() == d
           ) || isOnAllDesktops();
}

inline bool Toplevel::isOnActivity(const QString &activity) const
{
    return activities().isEmpty() || activities().contains(activity);
}

inline bool Toplevel::isOnCurrentDesktop() const
{
    return isOnDesktop(VirtualDesktopManager::self()->current());
}

inline QByteArray Toplevel::resourceName() const
{
    return resource_name; // it is always lowercase
}

inline QByteArray Toplevel::resourceClass() const
{
    return resource_class; // it is always lowercase
}

inline const ClientMachine *Toplevel::clientMachine() const
{
    return m_clientMachine;
}

inline quint32 Toplevel::surfaceId() const
{
    return m_surfaceId;
}

inline KWaylandServer::SurfaceInterface *Toplevel::surface() const
{
    return m_surface;
}

inline const QSharedPointer<QOpenGLFramebufferObject> &Toplevel::internalFramebufferObject() const
{
    return m_internalFBO;
}

inline QImage Toplevel::internalImageObject() const
{
    return m_internalImage;
}

inline QPoint Toplevel::clientContentPos() const
{
    return QPoint(0, 0);
}

template <class T, class U>
inline T *Toplevel::findInList(const QList<T*> &list, std::function<bool (const U*)> func)
{
    static_assert(std::is_base_of<U, T>::value,
                 "U must be derived from T");
    const auto it = std::find_if(list.begin(), list.end(), func);
    if (it == list.end()) {
        return nullptr;
    }
    return *it;
}

inline bool Toplevel::isPopupWindow() const
{
    switch (windowType()) {
    case NET::ComboBox:
    case NET::DropdownMenu:
    case NET::PopupMenu:
    case NET::Tooltip:
        return true;

    default:
        return false;
    }
}

QDebug operator<<(QDebug debug, const Toplevel *toplevel);

} // namespace
Q_DECLARE_METATYPE(KWin::Toplevel*)

#endif