/* KWin - the KDE window manager This file is part of the KDE project. SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich SPDX-FileCopyrightText: 2003 Lubos Lunak SPDX-FileCopyrightText: 2009 Lucas Murray SPDX-FileCopyrightText: 2019 Vlad Zahorodnii SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef KWIN_WORKSPACE_H #define KWIN_WORKSPACE_H // kwin #include "options.h" #include "sm.h" #include "utils/common.h" // Qt #include #include #include // std #include #include class KConfig; class KConfigGroup; class KStartupInfo; class KStartupInfoData; class KStartupInfoId; namespace KWin { namespace Xcb { class Tree; class Window; } class Window; class Output; class ColorMapper; class Compositor; class Deleted; class Group; class InternalWindow; class KillWindow; class ShortcutDialog; class Unmanaged; class UserActionsMenu; class VirtualDesktop; class X11Window; class X11EventFilter; enum class Predicate; class KWIN_EXPORT Workspace : public QObject { Q_OBJECT public: explicit Workspace(); ~Workspace() override; static Workspace *self() { return _self; } bool workspaceEvent(xcb_generic_event_t *); bool workspaceEvent(QEvent *); bool hasWindow(const Window *); /** * @brief Finds the first Client matching the condition expressed by passed in @p func. * * Internally findClient uses the std::find_if algorithm and that determines how the function * needs to be implemented. An example usage for finding a Client with a matching windowId * @code * xcb_window_t w; // our test window * X11Window *client = findClient([w](const X11Window *c) -> bool { * return c->window() == w; * }); * @endcode * * For the standard cases of matching the window id with one of the Client's windows use * the simplified overload method findClient(Predicate, xcb_window_t). Above example * can be simplified to: * @code * xcb_window_t w; // our test window * X11Window *client = findClient(Predicate::WindowMatch, w); * @endcode * * @param func Unary function that accepts a X11Window *as argument and * returns a value convertible to bool. The value returned indicates whether the * X11Window *is considered a match in the context of this function. * The function shall not modify its argument. * This can either be a function pointer or a function object. * @return KWin::X11Window *The found Client or @c null * @see findClient(Predicate, xcb_window_t) */ X11Window *findClient(std::function func) const; Window *findAbstractClient(std::function func) const; Window *findAbstractClient(const QUuid &internalId) const; /** * @brief Finds the Client matching the given match @p predicate for the given window. * * @param predicate Which window should be compared * @param w The window id to test against * @return KWin::X11Window *The found Client or @c null * @see findClient(std::function) */ X11Window *findClient(Predicate predicate, xcb_window_t w) const; void forEachClient(std::function func); void forEachAbstractClient(std::function func); Unmanaged *findUnmanaged(std::function func) const; /** * @brief Finds the Unmanaged with the given window id. * * @param w The window id to search for * @return KWin::Unmanaged* Found Unmanaged or @c null if there is no Unmanaged with given Id. */ Unmanaged *findUnmanaged(xcb_window_t w) const; void forEachUnmanaged(std::function func); Window *findToplevel(std::function func) const; void forEachToplevel(std::function func); Window *findToplevel(const QUuid &internalId) const; /** * @brief Finds a Window for the internal window @p w. * * Internal window means a window created by KWin itself. On X11 this is an Unmanaged * and mapped by the window id, on Wayland a XdgShellClient mapped on the internal window id. * * @returns Window */ Window *findInternal(QWindow *w) const; QRect clientArea(clientAreaOption, const Output *output, const VirtualDesktop *desktop) const; QRect clientArea(clientAreaOption, const Window *window) const; QRect clientArea(clientAreaOption, const Window *window, const Output *output) const; QRect clientArea(clientAreaOption, const Window *window, const QPoint &pos) const; /** * Returns the geometry of this Workspace, i.e. the bounding rectangle of all outputs. */ QRect geometry() const; QRegion restrictedMoveArea(const VirtualDesktop *desktop, StrutAreas areas = StrutAreaAll) const; bool initializing() const; Output *activeOutput() const; void setActiveOutput(Output *output); void setActiveOutput(const QPoint &pos); /** * Returns the active window, i.e. the window that has the focus (or None * if no window has the focus) */ Window *activeWindow() const; /** * Window that was activated, but it's not yet really activeWindow(), because * we didn't process yet the matching FocusIn event. Used mostly in focus * stealing prevention code. */ Window *mostRecentlyActivatedWindow() const; Window *windowUnderMouse(Output *output) const; void activateWindow(Window *window, bool force = false); bool requestFocus(Window *window, bool force = false); enum ActivityFlag { ActivityFocus = 1 << 0, // focus the window ActivityFocusForce = 1 << 1 | ActivityFocus, // focus even if Dock etc. ActivityRaise = 1 << 2 // raise the window }; Q_DECLARE_FLAGS(ActivityFlags, ActivityFlag) bool takeActivity(Window *window, ActivityFlags flags); bool allowWindowActivation(const Window *window, xcb_timestamp_t time = -1U, bool focus_in = false, bool ignore_desktop = false); bool restoreFocus(); void gotFocusIn(const Window *window); void setShouldGetFocus(Window *window); bool activateNextWindow(Window *window); bool focusChangeEnabled() { return block_focus == 0; } /** * Indicates that the given @a window is being moved or resized by the user. */ void setMoveResizeWindow(Window *window); QRect adjustClientArea(Window *window, const QRect &area) const; QPoint adjustWindowPosition(Window *window, QPoint pos, bool unrestricted, double snapAdjust = 1.0); QRect adjustWindowSize(Window *window, QRect moveResizeGeom, Gravity gravity); void raiseWindow(Window *window, bool nogroup = false); void lowerWindow(Window *window, bool nogroup = false); void raiseWindowRequest(Window *window, NET::RequestSource src = NET::FromApplication, xcb_timestamp_t timestamp = 0); void lowerWindowRequest(X11Window *window, NET::RequestSource src, xcb_timestamp_t timestamp); void lowerWindowRequest(Window *window); void restackWindowUnderActive(Window *window); void restack(Window *window, Window *under, bool force = false); void raiseOrLowerWindow(Window *window); void resetUpdateToolWindowsTimer(); void restoreSessionStackingOrder(X11Window *window); void updateStackingOrder(bool propagate_new_windows = false); void forceRestacking(); void constrain(Window *below, Window *above); void unconstrain(Window *below, Window *above); void windowHidden(Window *); void windowAttentionChanged(Window *, bool set); /** * @return List of windows currently managed by Workspace */ const QList &clientList() const { return m_x11Clients; } /** * @return List of unmanaged "windows" currently registered in Workspace */ const QList &unmanagedList() const { return m_unmanaged; } /** * @return List of deleted "windows" currently managed by Workspace */ const QList &deletedList() const { return deleted; } /** * @returns List of all windows (either X11 or Wayland) currently managed by Workspace */ const QList allClientList() const { return m_allClients; } /** * @returns List of all internal windows currently managed by Workspace */ const QList &internalWindows() const { return m_internalWindows; } void stackScreenEdgesUnderOverrideRedirect(); SessionManager *sessionManager() const; public: QPoint cascadeOffset(const Window *c) const; private: QTimer *m_quickTileCombineTimer; QuickTileMode m_lastTilingMode; //------------------------------------------------- // Unsorted public: bool isOnCurrentHead(); // True when performing Workspace::updateClientArea(). // The calls below are valid only in that case. bool inUpdateClientArea() const; QRegion previousRestrictedMoveArea(const VirtualDesktop *desktop, StrutAreas areas = StrutAreaAll) const; QHash previousScreenSizes() const; int oldDisplayWidth() const; int oldDisplayHeight() const; /** * Returns the list of windows sorted in stacking order, with topmost window * at the last position */ const QList &stackingOrder() const; QList xStackingOrder() const; QList unconstrainedStackingOrder() const; QList ensureStackingOrder(const QList &windows) const; QList ensureStackingOrder(const QList &windows) const; Window *topWindowOnDesktop(VirtualDesktop *desktop, Output *output = nullptr, bool unconstrained = false, bool only_normal = true) const; Window *findDesktop(bool topmost, VirtualDesktop *desktop) const; void sendWindowToDesktop(Window *window, int desktop, bool dont_activate); void windowToPreviousDesktop(Window *window); void windowToNextDesktop(Window *window); void sendWindowToOutput(Window *window, Output *output); void addManualOverlay(xcb_window_t id) { manual_overlays << id; } void removeManualOverlay(xcb_window_t id) { manual_overlays.removeOne(id); } /** * Shows the menu operations menu for the window and makes it active if * it's not already. */ void showWindowMenu(const QRect &pos, Window *cl); const UserActionsMenu *userActionsMenu() const { return m_userActionsMenu; } void showApplicationMenu(const QRect &pos, Window *window, int actionId); void updateMinimizedOfTransients(Window *); void updateOnAllDesktopsOfTransients(Window *); void checkTransients(xcb_window_t w); SessionInfo *takeSessionInfo(X11Window *); // D-Bus interface QString supportInformation() const; Output *nextOutput(Output *reference) const; Output *previousOutput(Output *reference) const; void switchToOutput(Output *output); /** * Set "Show Desktop" status * * @param showing @c true to show the desktop, @c false to restore the window positions * @param animated @c true if the "Show Desktop Animation" should be played, otherwise @c false */ void setShowingDesktop(bool showing, bool animated = true); bool showingDesktop() const; void removeX11Window(X11Window *); // Only called from X11Window::destroyWindow() or X11Window::releaseWindow() void setActiveWindow(Window *window); Group *findGroup(xcb_window_t leader) const; void addGroup(Group *group); void removeGroup(Group *group); Group *findClientLeaderGroup(const X11Window *c) const; int unconstainedStackingOrderIndex(const X11Window *c) const; void removeUnmanaged(Unmanaged *); // Only called from Unmanaged::release() void removeDeleted(Deleted *); void addDeleted(Deleted *, Window *); bool checkStartupNotification(xcb_window_t w, KStartupInfoId &id, KStartupInfoData &data); void focusToNull(); // SELI TODO: Public? void windowShortcutUpdated(Window *window); bool shortcutAvailable(const QKeySequence &cut, Window *ignore = nullptr) const; bool globalShortcutsDisabled() const; void disableGlobalShortcutsForClient(bool disable); void setWasUserInteraction(); bool wasUserInteraction() const; int packPositionLeft(const Window *window, int oldX, bool leftEdge) const; int packPositionRight(const Window *window, int oldX, bool rightEdge) const; int packPositionUp(const Window *window, int oldY, bool topEdge) const; int packPositionDown(const Window *window, int oldY, bool bottomEdge) const; void cancelDelayFocus(); void requestDelayFocus(Window *); /** * updates the mouse position to track whether a focus follow mouse focus change was caused by * an actual mouse move * is esp. called on enter/motion events of inactive windows * since an active window doesn't receive mouse events, it must also be invoked if a (potentially) * active window might be moved/resize away from the cursor (causing a leave event) */ void updateFocusMousePosition(const QPoint &pos); QPoint focusMousePosition() const; /** * Returns a window that is currently being moved or resized by the user. * * If none of windows is being moved or resized, @c null will be returned. */ Window *moveResizeWindow() { return m_moveResizeWindow; } void markXStackingOrderAsDirty(); void quickTileWindow(QuickTileMode mode); enum Direction { DirectionNorth, DirectionEast, DirectionSouth, DirectionWest }; void switchWindow(Direction direction); ShortcutDialog *shortcutDialog() const { return m_windowKeysDialog; } void addInternalWindow(InternalWindow *window); void removeInternalWindow(InternalWindow *window); /** * @internal * Used by session management */ void setInitialDesktop(int desktop); public Q_SLOTS: void performWindowOperation(KWin::Window *window, Options::WindowOperation op); // Keybindings // void slotSwitchToWindow( int ); void slotWindowToDesktop(VirtualDesktop *desktop); // void slotWindowToListPosition( int ); void slotSwitchToScreen(); void slotWindowToScreen(); void slotSwitchToNextScreen(); void slotWindowToNextScreen(); void slotSwitchToPrevScreen(); void slotWindowToPrevScreen(); void slotToggleShowDesktop(); void slotWindowMaximize(); void slotWindowMaximizeVertical(); void slotWindowMaximizeHorizontal(); void slotWindowMinimize(); void slotWindowShade(); void slotWindowRaise(); void slotWindowLower(); void slotWindowRaiseOrLower(); void slotActivateAttentionWindow(); void slotWindowCenter(); void slotWindowMoveLeft(); void slotWindowMoveRight(); void slotWindowMoveUp(); void slotWindowMoveDown(); void slotWindowExpandHorizontal(); void slotWindowExpandVertical(); void slotWindowShrinkHorizontal(); void slotWindowShrinkVertical(); void slotIncreaseWindowOpacity(); void slotLowerWindowOpacity(); void slotWindowOperations(); void slotWindowClose(); void slotWindowMove(); void slotWindowResize(); void slotWindowAbove(); void slotWindowBelow(); void slotWindowOnAllDesktops(); void slotWindowFullScreen(); void slotWindowNoBorder(); void slotWindowToNextDesktop(); void slotWindowToPreviousDesktop(); void slotWindowToDesktopRight(); void slotWindowToDesktopLeft(); void slotWindowToDesktopUp(); void slotWindowToDesktopDown(); void reconfigure(); void slotReconfigure(); void slotKillWindow(); void slotSetupWindowShortcut(); void setupWindowShortcutDone(bool); void updateClientArea(); private Q_SLOTS: void desktopResized(); void selectWmInputEventMask(); void slotUpdateToolWindows(); void delayFocus(); void slotReloadConfig(); void updateCurrentActivity(const QString &new_activity); // virtual desktop handling void slotCurrentDesktopChanged(uint oldDesktop, uint newDesktop); void slotCurrentDesktopChanging(uint currentDesktop, QPointF delta); void slotCurrentDesktopChangingCancelled(); void slotDesktopAdded(VirtualDesktop *desktop); void slotDesktopRemoved(VirtualDesktop *desktop); void slotOutputEnabled(Output *output); void slotOutputDisabled(Output *output); Q_SIGNALS: /** * Emitted after the Workspace has setup the complete initialization process. * This can be used to connect to for performing post-workspace initialization. */ void workspaceInitialized(); void geometryChanged(); // Signals required for the scripting interface void desktopPresenceChanged(KWin::Window *, int); void currentActivityChanged(); void currentDesktopChanged(int, KWin::Window *); void currentDesktopChanging(uint currentDesktop, QPointF delta, KWin::Window *); // for realtime animations void currentDesktopChangingCancelled(); void windowAdded(KWin::Window *); void windowRemoved(KWin::Window *); void windowActivated(KWin::Window *); void windowDemandsAttentionChanged(KWin::Window *, bool); void windowMinimizedChanged(KWin::Window *); void groupAdded(KWin::Group *); void unmanagedAdded(KWin::Unmanaged *); void unmanagedRemoved(KWin::Unmanaged *); void deletedRemoved(KWin::Deleted *); void configChanged(); void showingDesktopChanged(bool showing, bool animated); /** * This signal is emitted when the stacking order changed, i.e. a window is risen * or lowered */ void stackingOrderChanged(); /** * This signal is emitted whenever an internal window is created. */ void internalWindowAdded(KWin::InternalWindow *window); /** * This signal is emitted whenever an internal window gets removed. */ void internalWindowRemoved(KWin::InternalWindow *window); private: void init(); void initializeX11(); void cleanupX11(); void initShortcuts(); template void initShortcut(const QString &actionName, const QString &description, const QKeySequence &shortcut, Slot slot, const QVariant &data = QVariant()); template void initShortcut(const QString &actionName, const QString &description, const QKeySequence &shortcut, T *receiver, Slot slot, const QVariant &data = QVariant()); void setupWindowShortcut(Window *window); bool switchWindow(Window *window, Direction direction, QPoint curPos, VirtualDesktop *desktop); void propagateWindows(bool propagate_new_windows); // Called only from updateStackingOrder QList constrainedStackingOrder(); void raiseWindowWithinApplication(Window *window); void lowerWindowWithinApplication(Window *window); bool allowFullClientRaising(const Window *window, xcb_timestamp_t timestamp); void blockStackingUpdates(bool block); void updateToolWindows(bool also_hide); void fixPositionAfterCrash(xcb_window_t w, const xcb_get_geometry_reply_t *geom); void saveOldScreenSizes(); void addToStack(Window *window); void replaceInStack(Window *original, Deleted *deleted); void removeFromStack(Window *window); /// This is the right way to create a new X11 window X11Window *createX11Window(xcb_window_t windowId, bool is_mapped); void addX11Window(X11Window *c); void setupWindowConnections(Window *window); Unmanaged *createUnmanaged(xcb_window_t windowId); void addUnmanaged(Unmanaged *c); void addWaylandWindow(Window *window); void removeWaylandWindow(Window *window); //--------------------------------------------------------------------- void closeActivePopup(); void updateWindowVisibilityOnDesktopChange(VirtualDesktop *newDesktop); void activateWindowOnNewDesktop(VirtualDesktop *desktop); Window *findWindowToActivateOnDesktop(VirtualDesktop *desktop); void removeAbstractClient(Window *client); struct Constraint { Window *below; Window *above; // All constraints above our "below" window QList parents; // All constraints below our "above" window QList children; // Used to prevent cycles. bool enqueued = false; }; QList m_constraints; QWidget *active_popup; Window *m_activePopupWindow; int m_initialDesktop; void updateXStackingOrder(); void updateTabbox(); Output *m_activeOutput = nullptr; Window *m_activeWindow; Window *m_lastActiveWindow; Window *m_moveResizeWindow; // Delay(ed) window focus timer and window QTimer *delayFocusTimer; Window *m_delayFocusWindow; QPoint focusMousePos; QList m_x11Clients; QList m_allClients; QList m_unmanaged; QList deleted; QList m_internalWindows; QList unconstrained_stacking_order; // Topmost last QList stacking_order; // Topmost last QVector manual_overlays; // Topmost last bool force_restacking; QList x_stacking; // From XQueryTree() std::unique_ptr m_xStackingQueryTree; bool m_xStackingDirty = false; QList should_get_focus; // Last is most recent QList attention_chain; bool showing_desktop; QList groups; bool was_user_interaction; QScopedPointer m_wasUserInteractionFilter; int block_focus; /** * Holds the menu containing the user actions which is shown * on e.g. right click the window decoration. */ UserActionsMenu *m_userActionsMenu; void modalActionsSwitch(bool enabled); ShortcutDialog *m_windowKeysDialog = nullptr; Window *m_windowKeysWindow = nullptr; bool m_globalShortcutsDisabledForWindow = false; // Timer to collect requests for 'reconfigure' QTimer reconfigureTimer; QTimer updateToolWindowsTimer; static Workspace *_self; bool workspaceInit; QScopedPointer m_startup; QScopedPointer m_colorMapper; QHash m_workAreas; QHash m_restrictedAreas; QHash> m_screenAreas; QRect m_geometry; QHash m_oldScreenGeometries; QSize olddisplaysize; // previous sizes od displayWidth()/displayHeight() QHash m_oldRestrictedAreas; bool m_inUpdateClientArea = false; int m_setActiveWindowRecursion = 0; int m_blockStackingUpdates = 0; // When > 0, stacking updates are temporarily disabled bool m_blockedPropagatingNewWindows; // Propagate also new windows after enabling stacking updates? QScopedPointer m_nullFocus; friend class StackingUpdatesBlocker; QScopedPointer m_windowKiller; QScopedPointer m_movingClientFilter; QScopedPointer m_syncAlarmFilter; SessionManager *m_sessionManager; private: friend bool performTransiencyCheck(); friend Workspace *workspace(); }; /** * Helper for Workspace::blockStackingUpdates() being called in pairs (True/false) */ class StackingUpdatesBlocker { public: explicit StackingUpdatesBlocker(Workspace *w) : ws(w) { ws->blockStackingUpdates(true); } ~StackingUpdatesBlocker() { ws->blockStackingUpdates(false); } private: Workspace *ws; }; class ColorMapper : public QObject { Q_OBJECT public: ColorMapper(QObject *parent); ~ColorMapper() override; public Q_SLOTS: void update(); private: xcb_colormap_t m_default; xcb_colormap_t m_installed; }; //--------------------------------------------------------- // Unsorted inline bool Workspace::initializing() const { return workspaceInit; } inline Window *Workspace::activeWindow() const { return m_activeWindow; } inline Window *Workspace::mostRecentlyActivatedWindow() const { return should_get_focus.count() > 0 ? should_get_focus.last() : m_activeWindow; } inline void Workspace::addGroup(Group *group) { Q_EMIT groupAdded(group); groups.append(group); } inline void Workspace::removeGroup(Group *group) { groups.removeAll(group); } inline const QList &Workspace::stackingOrder() const { // TODO: Q_ASSERT( block_stacking_updates == 0 ); return stacking_order; } inline bool Workspace::wasUserInteraction() const { return was_user_interaction; } inline SessionManager *Workspace::sessionManager() const { return m_sessionManager; } inline bool Workspace::showingDesktop() const { return showing_desktop; } inline bool Workspace::globalShortcutsDisabled() const { return m_globalShortcutsDisabledForWindow; } inline void Workspace::forceRestacking() { force_restacking = true; StackingUpdatesBlocker blocker(this); // Do restacking if not blocked } inline void Workspace::updateFocusMousePosition(const QPoint &pos) { focusMousePos = pos; } inline QPoint Workspace::focusMousePosition() const { return focusMousePos; } inline void Workspace::forEachClient(std::function func) { std::for_each(m_x11Clients.constBegin(), m_x11Clients.constEnd(), func); } inline void Workspace::forEachUnmanaged(std::function func) { std::for_each(m_unmanaged.constBegin(), m_unmanaged.constEnd(), func); } inline Workspace *workspace() { return Workspace::_self; } } // namespace Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::Workspace::ActivityFlags) #endif