File indexing completed on 2024-04-28 05:30:33

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #pragma once
0010 // KWin
0011 #include "effect/globals.h"
0012 #include <kwin_export.h>
0013 // Qt includes
0014 #include <QAction>
0015 #include <QObject>
0016 #include <QPoint>
0017 #include <QPointer>
0018 #include <QSize>
0019 
0020 // KDE includes
0021 #include <KConfig>
0022 #include <KSharedConfig>
0023 
0024 class KLocalizedString;
0025 class NETRootInfo;
0026 class QAction;
0027 
0028 namespace KWin
0029 {
0030 
0031 class Options;
0032 class PlasmaVirtualDesktopManagementInterface;
0033 
0034 class KWIN_EXPORT VirtualDesktop : public QObject
0035 {
0036     Q_OBJECT
0037     Q_PROPERTY(QString id READ id CONSTANT)
0038     Q_PROPERTY(uint x11DesktopNumber READ x11DesktopNumber NOTIFY x11DesktopNumberChanged)
0039     Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
0040 public:
0041     explicit VirtualDesktop(QObject *parent = nullptr);
0042     ~VirtualDesktop() override;
0043 
0044     void setId(const QString &id);
0045     QString id() const
0046     {
0047         return m_id;
0048     }
0049 
0050     void setName(const QString &name);
0051     QString name() const
0052     {
0053         return m_name;
0054     }
0055 
0056     void setX11DesktopNumber(uint number);
0057     uint x11DesktopNumber() const
0058     {
0059         return m_x11DesktopNumber;
0060     }
0061 
0062 Q_SIGNALS:
0063     void nameChanged();
0064     void x11DesktopNumberChanged();
0065     /**
0066      * Emitted just before the desktop gets destroyed.
0067      */
0068     void aboutToBeDestroyed();
0069 
0070 private:
0071     QString m_id;
0072     QString m_name;
0073     int m_x11DesktopNumber = 0;
0074 };
0075 
0076 /**
0077  * @brief Two dimensional grid containing the ID of the virtual desktop at a specific position
0078  * in the grid.
0079  *
0080  * The VirtualDesktopGrid represents a visual layout of the Virtual Desktops as they are in e.g.
0081  * a Pager. This grid is used for getting a desktop next to a given desktop in any direction by
0082  * making use of the layout information. This allows navigation like move to desktop on left.
0083  */
0084 class VirtualDesktopGrid
0085 {
0086 public:
0087     VirtualDesktopGrid();
0088     ~VirtualDesktopGrid();
0089     void update(const QSize &size, const QList<VirtualDesktop *> &desktops);
0090     /**
0091      * @returns The coords of desktop @a id in grid units.
0092      */
0093     QPoint gridCoords(uint id) const;
0094     /**
0095      * @returns The coords of desktop @a vd in grid units.
0096      */
0097     QPoint gridCoords(VirtualDesktop *vd) const;
0098     /**
0099      * @returns The desktop at the point @a coords or 0 if no desktop exists at that
0100      * point. @a coords is to be in grid units.
0101      */
0102     VirtualDesktop *at(const QPoint &coords) const;
0103     int width() const;
0104     int height() const;
0105     const QSize &size() const;
0106 
0107 private:
0108     QSize m_size;
0109     QList<QList<VirtualDesktop *>> m_grid;
0110 };
0111 
0112 /**
0113  * @brief Manages the number of available virtual desktops, the layout of those and which virtual
0114  * desktop is the current one.
0115  *
0116  * This manager is responsible for Virtual Desktop handling inside KWin. It has a property for the
0117  * count of available virtual desktops and a property for the currently active virtual desktop. All
0118  * changes to the number of virtual desktops and the current virtual desktop need to go through this
0119  * manager.
0120  *
0121  * On all changes a signal is emitted and interested parties should connect to the signal. The manager
0122  * itself does not interact with other parts of the system. E.g. it does not hide/show windows of
0123  * desktop changes. This is outside the scope of this manager.
0124  *
0125  * Internally the manager organizes the virtual desktops in a grid allowing to navigate over the
0126  * virtual desktops. For this a set of convenient methods are available which allow to get the id
0127  * of an adjacent desktop or to switch to an adjacent desktop. Interested parties should make use of
0128  * these methods and not replicate the logic to switch to the next desktop.
0129  */
0130 class KWIN_EXPORT VirtualDesktopManager : public QObject
0131 {
0132     Q_OBJECT
0133     /**
0134      * The number of virtual desktops currently available.
0135      * The ids of the virtual desktops are in the range [1, VirtualDesktopManager::maximum()].
0136      */
0137     Q_PROPERTY(uint count READ count WRITE setCount NOTIFY countChanged)
0138     /**
0139      * The id of the virtual desktop which is currently in use.
0140      */
0141     Q_PROPERTY(uint current READ current WRITE setCurrent NOTIFY currentChanged)
0142     /**
0143      * Whether navigation in the desktop layout wraps around at the borders.
0144      */
0145     Q_PROPERTY(bool navigationWrappingAround READ isNavigationWrappingAround WRITE setNavigationWrappingAround NOTIFY navigationWrappingAroundChanged)
0146 public:
0147     ~VirtualDesktopManager() override;
0148     /**
0149      * @internal, for X11 case
0150      */
0151     void setRootInfo(NETRootInfo *info);
0152     /**
0153      * @internal, for Wayland case
0154      */
0155     void setVirtualDesktopManagement(PlasmaVirtualDesktopManagementInterface *management);
0156     /**
0157      * @internal
0158      */
0159     void setConfig(KSharedConfig::Ptr config);
0160     /**
0161      * @returns Total number of desktops currently in existence.
0162      * @see setCount
0163      * @see countChanged
0164      */
0165     uint count() const;
0166     /**
0167      * @returns the number of rows the layout has.
0168      * @see setRows
0169      * @see rowsChanged
0170      */
0171     uint rows() const;
0172     /**
0173      * @returns The ID of the current desktop.
0174      * @see setCurrent
0175      * @see currentChanged
0176      */
0177     uint current() const;
0178     /**
0179      * @returns The current desktop
0180      * @see setCurrent
0181      * @see currentChanged
0182      */
0183     VirtualDesktop *currentDesktop() const;
0184     /**
0185      * Moves to the desktop through the algorithm described by Direction.
0186      * @param wrap If @c true wraps around to the other side of the layout
0187      * @see setCurrent
0188      */
0189     template<typename Direction>
0190     void moveTo(bool wrap = false);
0191 
0192     /**
0193      * @returns @c true if navigation at borders of layout wraps around, @c false otherwise
0194      * @see setNavigationWrappingAround
0195      * @see navigationWrappingAroundChanged
0196      */
0197     bool isNavigationWrappingAround() const;
0198 
0199     /**
0200      * @returns The layout aware virtual desktop grid used by this manager.
0201      */
0202     const VirtualDesktopGrid &grid() const;
0203 
0204     enum class Direction {
0205         Up,
0206         Down,
0207         Right,
0208         Left,
0209         Next,
0210         Previous
0211     };
0212     VirtualDesktop *inDirection(VirtualDesktop *desktop, Direction direction, bool wrap = true);
0213     uint inDirection(uint desktop, Direction direction, bool wrap = true);
0214     void moveTo(Direction direction, bool wrap = true);
0215 
0216     /**
0217      * @returns The desktop above desktop @a desktop. Wraps around to the bottom of
0218      * the layout if @a wrap is set. If @a desktop is @c null use the current one.
0219      */
0220     VirtualDesktop *above(VirtualDesktop *desktop, bool wrap = true) const;
0221     /**
0222      * @returns The desktop to the right of desktop @a desktop. Wraps around to the
0223      * left of the layout if @a wrap is set. If @a desktop is @c null use the current one.
0224      */
0225     VirtualDesktop *toRight(VirtualDesktop *desktop, bool wrap = true) const;
0226     /**
0227      * @returns The desktop below desktop @a desktop. Wraps around to the top of the
0228      * layout if @a wrap is set. If @a desktop is @c null use the current one.
0229      */
0230     VirtualDesktop *below(VirtualDesktop *desktop, bool wrap = true) const;
0231     /**
0232      * @returns The desktop to the left of desktop @a desktop. Wraps around to the
0233      * right of the layout if @a wrap is set. If @a desktop is @c null use the current one.
0234      */
0235     VirtualDesktop *toLeft(VirtualDesktop *desktop, bool wrap = true) const;
0236     /**
0237      * @returns The desktop after the desktop @a desktop. Wraps around to the first
0238      * desktop if @a wrap is set. If @a desktop is @c null use the current desktop.
0239      */
0240     VirtualDesktop *next(VirtualDesktop *desktop = nullptr, bool wrap = true) const;
0241     /**
0242      * @returns The desktop in front of the desktop @a desktop. Wraps around to the
0243      * last desktop if @a wrap is set. If @a desktop is @c null use the current desktop.
0244      */
0245     VirtualDesktop *previous(VirtualDesktop *desktop = nullptr, bool wrap = true) const;
0246 
0247     void initShortcuts();
0248 
0249     /**
0250      * @returns all currently managed VirtualDesktops
0251      */
0252     QList<VirtualDesktop *> desktops() const
0253     {
0254         return m_desktops;
0255     }
0256 
0257     /**
0258      * @returns The VirtualDesktop for the x11 @p id, if no such VirtualDesktop @c null is returned
0259      */
0260     VirtualDesktop *desktopForX11Id(uint id) const;
0261 
0262     /**
0263      * @returns The VirtualDesktop for the internal desktop string @p id, if no such VirtualDesktop @c null is returned
0264      */
0265     VirtualDesktop *desktopForId(const QString &id) const;
0266 
0267     /**
0268      * Create a new virtual desktop at the requested position.
0269      * The difference with setCount is that setCount always adds new desktops at the end of the chain. The Id is automatically generated.
0270      * @param position The position of the desktop. It should be in range [0, count].
0271      * @param name The name for the new desktop, if empty the default name will be used.
0272      * @returns the new VirtualDesktop, nullptr if we reached the maximum number of desktops
0273      */
0274     VirtualDesktop *createVirtualDesktop(uint position, const QString &name = QString());
0275 
0276     /**
0277      * Remove the virtual desktop identified by id, if it exists
0278      * difference with setCount is that is possible to remove an arbitrary desktop,
0279      * not only the last one.
0280      * @param id the string id of the desktop to remove
0281      */
0282     void removeVirtualDesktop(const QString &id);
0283     void removeVirtualDesktop(VirtualDesktop *desktop);
0284 
0285     /**
0286      * Updates the net root info for new number of desktops
0287      */
0288     void updateRootInfo();
0289 
0290     /**
0291      * @returns The maximum number of desktops that KWin supports.
0292      */
0293     static uint maximum();
0294 
0295 public Q_SLOTS:
0296     /**
0297      * Set the number of available desktops to @a count. This function overrides any previous
0298      * grid layout.
0299      * There needs to be at least one virtual desktop and the new value is capped at the maximum
0300      * number of desktops. A caller of this function cannot expect that the change has been applied.
0301      * It is the callers responsibility to either check the numberOfDesktops or connect to the
0302      * countChanged signal.
0303      *
0304      * In case the @ref current desktop is on a desktop higher than the new count, the current desktop
0305      * is changed to be the new desktop with highest id. In that situation the signal desktopRemoved
0306      * is emitted.
0307      * @param count The new number of desktops to use
0308      * @see count
0309      * @see maximum
0310      * @see countChanged
0311      * @see desktopCreated
0312      * @see desktopRemoved
0313      */
0314     void setCount(uint count);
0315     /**
0316      * Set the current desktop to @a current.
0317      * @returns True on success, false otherwise.
0318      * @see current
0319      * @see currentChanged
0320      * @see moveTo
0321      */
0322     bool setCurrent(uint current);
0323     /**
0324      * Set the current desktop to @a current.
0325      * @returns True on success, false otherwise.
0326      * @see current
0327      * @see currentChanged
0328      * @see moveTo
0329      */
0330     bool setCurrent(VirtualDesktop *current);
0331     /**
0332      * Updates the layout to a new number of rows. The number of columns will be calculated accordingly
0333      */
0334     void setRows(uint rows);
0335     /**
0336      * Called from within setCount() to ensure the desktop layout is still valid.
0337      */
0338     void updateLayout();
0339     /**
0340      * @param enabled wrapping around borders for navigation in desktop layout
0341      * @see isNavigationWrappingAround
0342      * @see navigationWrappingAroundChanged
0343      */
0344     void setNavigationWrappingAround(bool enabled);
0345     /**
0346      * Loads number of desktops and names from configuration file
0347      */
0348     void load();
0349     /**
0350      * Saves number of desktops and names to configuration file
0351      */
0352     void save();
0353 
0354 Q_SIGNALS:
0355     /**
0356      * Signal emitted whenever the number of virtual desktops changes.
0357      * @param previousCount The number of desktops prior to the change
0358      * @param newCount The new current number of desktops
0359      */
0360     void countChanged(uint previousCount, uint newCount);
0361 
0362     /**
0363      * Signal when the number of rows in the layout changes
0364      * @param rows number of rows
0365      */
0366     void rowsChanged(uint rows);
0367 
0368     /**
0369      * A new desktop has been created
0370      * @param desktop the new just crated desktop
0371      */
0372     void desktopAdded(KWin::VirtualDesktop *desktop);
0373 
0374     /**
0375      * A desktop has been removed and is about to be deleted
0376      * @param desktop the desktop that has been removed.
0377      *          It's guaranteed to stil la valid pointer when the signal arrives,
0378      *          but it's about to be deleted.
0379      */
0380     void desktopRemoved(KWin::VirtualDesktop *desktop);
0381 
0382     /**
0383      * Signal emitted whenever the current desktop changes.
0384      * @param previousDesktop The virtual desktop changed from
0385      * @param newDesktop The virtual desktop changed to
0386      */
0387     void currentChanged(KWin::VirtualDesktop *previousDesktop, KWin::VirtualDesktop *newDesktop);
0388 
0389     /**
0390      * Signal emmitted for realtime desktop switching animations.
0391      * @param currentDesktop The current virtual desktop
0392      * @param offset The current total change in desktop coordinate
0393      * Offset x and y are negative if switching Left and Down.
0394      * Example: x = 0.6 means 60% of the way to the desktop to the right.
0395      */
0396     void currentChanging(KWin::VirtualDesktop *currentDesktop, QPointF offset);
0397     void currentChangingCancelled();
0398 
0399     /**
0400      * Signal emitted whenever the desktop layout changes.
0401      * @param columns The new number of columns in the layout
0402      * @param rows The new number of rows in the layout
0403      */
0404     void layoutChanged(int columns, int rows);
0405     /**
0406      * Signal emitted whenever the navigationWrappingAround property changes.
0407      */
0408     void navigationWrappingAroundChanged();
0409 
0410 private Q_SLOTS:
0411     /**
0412      * Common slot for all "Switch to Desktop n" shortcuts.
0413      * This method uses the sender() method to access some data.
0414      * DO NOT CALL DIRECTLY! ONLY TO BE USED FROM AN ACTION!
0415      */
0416     void slotSwitchTo();
0417     /**
0418      * Slot for switch to next desktop action.
0419      */
0420     void slotNext();
0421     /**
0422      * Slot for switch to previous desktop action.
0423      */
0424     void slotPrevious();
0425     /**
0426      * Slot for switch to right desktop action.
0427      */
0428     void slotRight();
0429     /**
0430      * Slot for switch to left desktop action.
0431      */
0432     void slotLeft();
0433     /**
0434      * Slot for switch to desktop above action.
0435      */
0436     void slotUp();
0437     /**
0438      * Slot for switch to desktop below action.
0439      */
0440     void slotDown();
0441 
0442     /* For gestured desktopSwitching
0443      * Called when gesture ended, the thing that actually switches the desktop.
0444      */
0445     void gestureReleasedY();
0446     void gestureReleasedX();
0447 
0448 private:
0449     /**
0450      * @returns A default name for the given @p desktop
0451      */
0452     QString defaultName(int desktop) const;
0453     /**
0454      * Creates all the global keyboard shortcuts for "Switch To Desktop n" actions.
0455      */
0456     void initSwitchToShortcuts();
0457     /**
0458      * Creates an action and connects it to the @p slot in this Manager. This method is
0459      * meant to be used for the case that an additional information needs to be stored in
0460      * the action and the label.
0461      * @param name The name of the action to be created
0462      * @param label The localized name for the action to be created
0463      * @param value An additional value added to the label and to the created action
0464      * @param key The global shortcut for the action
0465      * @param slot The slot to invoke when the action is triggered
0466      */
0467     QAction *addAction(const QString &name, const KLocalizedString &label, uint value, const QKeySequence &key, void (VirtualDesktopManager::*slot)());
0468     /**
0469      * Creates an action and connects it to the @p slot in this Manager.
0470      * Overloaded method for the case that no additional value needs to be passed to the action.
0471      * @param name The name of the action to be created
0472      * @param label The localized name for the action to be created
0473      * @param key The global shortcut for the action. If an empty QKeySequence is passed, no global shortcut is defined by default.
0474      * @param slot The slot to invoke when the action is triggered
0475      */
0476     QAction *addAction(const QString &name, const QString &label, const QKeySequence &key, void (VirtualDesktopManager::*slot)());
0477 
0478     QList<VirtualDesktop *> m_desktops;
0479     QPointer<VirtualDesktop> m_current;
0480     quint32 m_rows = 2;
0481     bool m_navigationWrapsAround;
0482     VirtualDesktopGrid m_grid;
0483     // TODO: QPointer
0484     NETRootInfo *m_rootInfo;
0485     PlasmaVirtualDesktopManagementInterface *m_virtualDesktopManagement = nullptr;
0486     KSharedConfig::Ptr m_config;
0487 
0488     std::unique_ptr<QAction> m_swipeGestureReleasedY;
0489     std::unique_ptr<QAction> m_swipeGestureReleasedX;
0490     QPointF m_currentDesktopOffset = QPointF(0, 0);
0491 
0492     KWIN_SINGLETON_VARIABLE(VirtualDesktopManager, s_manager)
0493 };
0494 
0495 inline int VirtualDesktopGrid::width() const
0496 {
0497     return m_size.width();
0498 }
0499 
0500 inline int VirtualDesktopGrid::height() const
0501 {
0502     return m_size.height();
0503 }
0504 
0505 inline const QSize &VirtualDesktopGrid::size() const
0506 {
0507     return m_size;
0508 }
0509 
0510 inline uint VirtualDesktopManager::maximum()
0511 {
0512     return 20;
0513 }
0514 
0515 inline uint VirtualDesktopManager::count() const
0516 {
0517     return m_desktops.count();
0518 }
0519 
0520 inline bool VirtualDesktopManager::isNavigationWrappingAround() const
0521 {
0522     return m_navigationWrapsAround;
0523 }
0524 
0525 inline void VirtualDesktopManager::setConfig(KSharedConfig::Ptr config)
0526 {
0527     m_config = std::move(config);
0528 }
0529 
0530 inline const VirtualDesktopGrid &VirtualDesktopManager::grid() const
0531 {
0532     return m_grid;
0533 }
0534 
0535 } // namespace KWin