File indexing completed on 2024-10-13 13:14:45
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2011 Arthur Arlt <a.arlt@stud.uni-heidelberg.de> 0006 SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org> 0007 0008 Since the functionality provided in this class has been moved from 0009 class Workspace, it is not clear who exactly has written the code. 0010 The list below contains the copyright holders of the class Workspace. 0011 0012 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org> 0013 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org> 0014 SPDX-FileCopyrightText: 2009 Lucas Murray <lmurray@undefinedfire.com> 0015 0016 SPDX-License-Identifier: GPL-2.0-or-later 0017 */ 0018 0019 #pragma once 0020 // KWin 0021 #include "kwinglobals.h" 0022 // KDE includes 0023 #include <KSharedConfig> 0024 // Qt 0025 #include <QDateTime> 0026 #include <QObject> 0027 #include <QRect> 0028 #include <QVector> 0029 #include <memory> 0030 0031 class QAction; 0032 class QMouseEvent; 0033 0034 namespace KWin 0035 { 0036 0037 class Window; 0038 class Output; 0039 class GestureRecognizer; 0040 class ScreenEdges; 0041 class SwipeGesture; 0042 0043 class TouchCallback 0044 { 0045 public: 0046 using CallbackFunction = std::function<void(ElectricBorder border, const QPointF &, Output *output)>; 0047 explicit TouchCallback(QAction *touchUpAction, TouchCallback::CallbackFunction progressCallback); 0048 ~TouchCallback(); 0049 0050 QAction *touchUpAction() const; 0051 void progressCallback(ElectricBorder border, const QPointF &deltaProgress, Output *output) const; 0052 bool hasProgressCallback() const; 0053 0054 private: 0055 QAction *m_touchUpAction = nullptr; 0056 TouchCallback::CallbackFunction m_progressCallback; 0057 }; 0058 0059 class KWIN_EXPORT Edge : public QObject 0060 { 0061 Q_OBJECT 0062 public: 0063 explicit Edge(ScreenEdges *parent); 0064 ~Edge() override; 0065 bool isLeft() const; 0066 bool isTop() const; 0067 bool isRight() const; 0068 bool isBottom() const; 0069 bool isCorner() const; 0070 bool isScreenEdge() const; 0071 bool triggersFor(const QPoint &cursorPos) const; 0072 bool check(const QPoint &cursorPos, const QDateTime &triggerTime, bool forceNoPushBack = false); 0073 void markAsTriggered(const QPoint &cursorPos, const QDateTime &triggerTime); 0074 bool isReserved() const; 0075 const QRect &approachGeometry() const; 0076 0077 ElectricBorder border() const; 0078 void reserve(QObject *object, const char *slot); 0079 const QHash<QObject *, QByteArray> &callBacks() const; 0080 void reserveTouchCallBack(QAction *action, TouchCallback::CallbackFunction callback = nullptr); 0081 void unreserveTouchCallBack(QAction *action); 0082 void startApproaching(); 0083 void stopApproaching(); 0084 bool isApproaching() const; 0085 void setClient(Window *client); 0086 Window *client() const; 0087 void setOutput(Output *output); 0088 Output *output() const; 0089 const QRect &geometry() const; 0090 void setTouchAction(ElectricBorderAction action); 0091 0092 bool activatesForPointer() const; 0093 bool activatesForTouchGesture() const; 0094 0095 /** 0096 * The window id of the native window representing the edge. 0097 * Default implementation returns @c 0, which means no window. 0098 */ 0099 virtual quint32 window() const; 0100 /** 0101 * The approach window is a special window to notice when get close to the screen border but 0102 * not yet triggering the border. 0103 * 0104 * The default implementation returns @c 0, which means no window. 0105 */ 0106 virtual quint32 approachWindow() const; 0107 0108 public Q_SLOTS: 0109 void reserve(); 0110 void unreserve(); 0111 void unreserve(QObject *object); 0112 void setBorder(ElectricBorder border); 0113 void setAction(ElectricBorderAction action); 0114 void setGeometry(const QRect &geometry); 0115 void updateApproaching(const QPoint &point); 0116 void checkBlocking(); 0117 Q_SIGNALS: 0118 void approaching(ElectricBorder border, qreal factor, const QRect &geometry); 0119 void activatesForTouchGestureChanged(); 0120 0121 protected: 0122 ScreenEdges *edges(); 0123 const ScreenEdges *edges() const; 0124 bool isBlocked() const; 0125 virtual void doGeometryUpdate(); 0126 virtual void doActivate(); 0127 virtual void doDeactivate(); 0128 virtual void doStartApproaching(); 0129 virtual void doStopApproaching(); 0130 virtual void doUpdateBlocking(); 0131 0132 private: 0133 void activate(); 0134 void deactivate(); 0135 bool canActivate(const QPoint &cursorPos, const QDateTime &triggerTime); 0136 void handle(const QPoint &cursorPos); 0137 bool handleAction(ElectricBorderAction action); 0138 bool handlePointerAction() 0139 { 0140 return handleAction(m_action); 0141 } 0142 bool handleTouchAction() 0143 { 0144 return handleAction(m_touchAction); 0145 } 0146 bool handleByCallback(); 0147 void handleTouchCallback(); 0148 void switchDesktop(const QPoint &cursorPos); 0149 void pushCursorBack(const QPoint &cursorPos); 0150 void reserveTouchCallBack(const TouchCallback &callback); 0151 QVector<TouchCallback> touchCallBacks() const 0152 { 0153 return m_touchCallbacks; 0154 } 0155 ScreenEdges *m_edges; 0156 ElectricBorder m_border; 0157 ElectricBorderAction m_action; 0158 ElectricBorderAction m_touchAction = ElectricActionNone; 0159 int m_reserved; 0160 QRect m_geometry; 0161 QRect m_approachGeometry; 0162 QDateTime m_lastTrigger; 0163 QDateTime m_lastReset; 0164 QPoint m_triggeredPoint; 0165 QHash<QObject *, QByteArray> m_callBacks; 0166 bool m_approaching; 0167 int m_lastApproachingFactor; 0168 bool m_blocked; 0169 bool m_pushBackBlocked; 0170 Window *m_client; 0171 Output *m_output; 0172 std::unique_ptr<SwipeGesture> m_gesture; 0173 QVector<TouchCallback> m_touchCallbacks; 0174 friend class ScreenEdges; 0175 }; 0176 0177 /** 0178 * @short Class for controlling screen edges. 0179 * 0180 * The screen edge functionality is split into three parts: 0181 * @li This manager class ScreenEdges 0182 * @li abstract class @ref Edge 0183 * @li specific implementation of @ref Edge, e.g. WindowBasedEdge 0184 * 0185 * The ScreenEdges creates an @ref Edge for each screen edge which is also an edge in the 0186 * combination of all screens. E.g. if there are two screens, no Edge is created between the screens, 0187 * but at all other edges even if the screens have a different dimension. 0188 * 0189 * In addition at each corner of the overall display geometry an one-pixel large @ref Edge is 0190 * created. No matter how many screens there are, there will only be exactly four of these corner 0191 * edges. This is motivated by Fitts's Law which show that it's easy to trigger such a corner, but 0192 * it would be very difficult to trigger a corner between two screens (one pixel target not visually 0193 * outlined). 0194 * 0195 * The ScreenEdges are used for one of the following functionality: 0196 * @li switch virtual desktop (see property @ref desktopSwitching) 0197 * @li switch virtual desktop when moving a window (see property @ref desktopSwitchingMovingClients) 0198 * @li trigger a pre-defined action (see properties @ref actionTop and similar) 0199 * @li trigger an externally configured action (e.g. Effect, Script, see @ref reserve, @ref unreserve) 0200 * 0201 * An @ref Edge is only active if there is at least one of the possible actions "reserved" for this 0202 * edge. The idea is to not block the screen edge if nothing could be triggered there, so that the 0203 * user e.g. can configure nothing on the top edge, which tends to interfere with full screen apps 0204 * having a hidden panel there. On X11 (currently only supported backend) the @ref Edge is 0205 * represented by a WindowBasedEdge which creates an input only window for the geometry and 0206 * reacts on enter notify events. If the edge gets reserved for the first time a window is created 0207 * and mapped, once the edge gets unreserved again, the window gets destroyed. 0208 * 0209 * When the mouse enters one of the screen edges the following values are used to determine whether 0210 * the action should be triggered or the cursor be pushed back 0211 * @li Time difference between two entering events is not larger than a certain threshold 0212 * @li Time difference between two entering events is larger than @ref timeThreshold 0213 * @li Time difference between two activations is larger than @ref reActivateThreshold 0214 * @li Distance between two enter events is not larger than a defined pixel distance 0215 * These checks are performed in @ref Edge 0216 * 0217 * @todo change way how Effects/Scripts can reserve an edge and are notified. 0218 */ 0219 class KWIN_EXPORT ScreenEdges : public QObject 0220 { 0221 Q_OBJECT 0222 Q_PROPERTY(bool desktopSwitching READ isDesktopSwitching) 0223 Q_PROPERTY(bool desktopSwitchingMovingClients READ isDesktopSwitchingMovingClients) 0224 Q_PROPERTY(QSize cursorPushBackDistance READ cursorPushBackDistance) 0225 Q_PROPERTY(int timeThreshold READ timeThreshold) 0226 Q_PROPERTY(int reActivateThreshold READ reActivationThreshold) 0227 Q_PROPERTY(int actionTopLeft READ actionTopLeft) 0228 Q_PROPERTY(int actionTop READ actionTop) 0229 Q_PROPERTY(int actionTopRight READ actionTopRight) 0230 Q_PROPERTY(int actionRight READ actionRight) 0231 Q_PROPERTY(int actionBottomRight READ actionBottomRight) 0232 Q_PROPERTY(int actionBottom READ actionBottom) 0233 Q_PROPERTY(int actionBottomLeft READ actionBottomLeft) 0234 Q_PROPERTY(int actionLeft READ actionLeft) 0235 public: 0236 explicit ScreenEdges(); 0237 /** 0238 * @internal 0239 */ 0240 void setConfig(KSharedConfig::Ptr config); 0241 /** 0242 * Initialize the screen edges. 0243 * @internal 0244 */ 0245 void init(); 0246 /** 0247 * Check, if a screen edge is entered and trigger the appropriate action 0248 * if one is enabled for the current region and the timeout is satisfied 0249 * @param pos the position of the mouse pointer 0250 * @param now the time when the function is called 0251 * @param forceNoPushBack needs to be called to workaround some DnD clients, don't use unless you want to chek on a DnD event 0252 */ 0253 void check(const QPoint &pos, const QDateTime &now, bool forceNoPushBack = false); 0254 /** 0255 * The (dpi dependent) length, reserved for the active corners of each edge - 1/3" 0256 */ 0257 int cornerOffset() const; 0258 /** 0259 * Mark the specified screen edge as reserved. This method is provided for external activation 0260 * like effects and scripts. When the effect/script does no longer need the edge it is supposed 0261 * to call @ref unreserve. 0262 * @param border the screen edge to mark as reserved 0263 * @param object The object on which the @p callback needs to be invoked 0264 * @param callback The method name to be invoked - uses QMetaObject::invokeMethod 0265 * @see unreserve 0266 * @todo: add pointer to script/effect 0267 */ 0268 void reserve(ElectricBorder border, QObject *object, const char *callback); 0269 /** 0270 * Mark the specified screen edge as unreserved. This method is provided for external activation 0271 * like effects and scripts. This method is only allowed to be called if @ref reserve had been 0272 * called before for the same @p border. An unbalanced calling of reserve/unreserve leads to the 0273 * edge never being active or never being able to deactivate again. 0274 * @param border the screen edge to mark as unreserved 0275 * @param object the object on which the callback had been invoked 0276 * @see reserve 0277 * @todo: add pointer to script/effect 0278 */ 0279 void unreserve(ElectricBorder border, QObject *object); 0280 /** 0281 * Reserves an edge for the @p client. The idea behind this is to show the @p client if the 0282 * screen edge which the @p client borders gets triggered. 0283 * 0284 * When first called it is tried to create an Edge for the client. This is only done if the 0285 * client borders with a screen edge specified by @p border. If the client doesn't border the 0286 * screen edge, no Edge gets created and the client is shown again. Otherwise there would not 0287 * be a possibility to show the client again. 0288 * 0289 * On subsequent calls for the client no new Edge is created, but the existing one gets reused 0290 * and if the client is already hidden, the Edge gets reserved. 0291 * 0292 * Once the Edge for the client triggers, the client gets shown again and the Edge unreserved. 0293 * The idea is that the Edge can only get activated if the client is currently hidden. 0294 * 0295 * To make sure that the client can always be shown again the implementation also starts to 0296 * track geometry changes and shows the Client again. The same for screen geometry changes. 0297 * 0298 * The Edge gets automatically destroyed if the client gets released. 0299 * @param client The Client for which an Edge should be reserved 0300 * @param border The border which the client wants to use, only proper borders are supported (no corners) 0301 */ 0302 void reserve(KWin::Window *client, ElectricBorder border); 0303 0304 /** 0305 * Mark the specified screen edge as reserved for touch gestures. This method is provided for 0306 * external activation like effects and scripts. 0307 * When the effect/script does no longer need the edge it is supposed 0308 * to call @ref unreserveTouch. 0309 * @param border the screen edge to mark as reserved 0310 * @param action The action which gets triggered 0311 * @see unreserveTouch 0312 * @since 5.10 0313 */ 0314 void reserveTouch(ElectricBorder border, QAction *action, TouchCallback::CallbackFunction callback = nullptr); 0315 /** 0316 * Unreserves the specified @p border from activating the @p action for touch gestures. 0317 * @see reserveTouch 0318 * @since 5.10 0319 */ 0320 void unreserveTouch(ElectricBorder border, QAction *action); 0321 0322 /** 0323 * Reserve desktop switching for screen edges, if @p isToReserve is @c true. Unreserve otherwise. 0324 * @param isToReserve indicated whether desktop switching should be reserved or unreseved 0325 * @param o Qt orientations 0326 */ 0327 void reserveDesktopSwitching(bool isToReserve, Qt::Orientations o); 0328 /** 0329 * Raise electric border windows to the real top of the screen. We only need 0330 * to do this if an effect input window is active. 0331 */ 0332 void ensureOnTop(); 0333 bool isEntered(QMouseEvent *event); 0334 0335 /** 0336 * Returns a QVector of all existing screen edge windows 0337 * @return all existing screen edge windows in a QVector 0338 */ 0339 QVector<xcb_window_t> windows() const; 0340 0341 bool isDesktopSwitching() const; 0342 bool isDesktopSwitchingMovingClients() const; 0343 const QSize &cursorPushBackDistance() const; 0344 /** 0345 * Minimum time between the push back of the cursor and the activation by re-entering the edge. 0346 */ 0347 int timeThreshold() const; 0348 /** 0349 * Minimum time between triggers 0350 */ 0351 int reActivationThreshold() const; 0352 ElectricBorderAction actionTopLeft() const; 0353 ElectricBorderAction actionTop() const; 0354 ElectricBorderAction actionTopRight() const; 0355 ElectricBorderAction actionRight() const; 0356 ElectricBorderAction actionBottomRight() const; 0357 ElectricBorderAction actionBottom() const; 0358 ElectricBorderAction actionBottomLeft() const; 0359 ElectricBorderAction actionLeft() const; 0360 0361 ElectricBorderAction actionForTouchBorder(ElectricBorder border) const; 0362 0363 GestureRecognizer *gestureRecognizer() const 0364 { 0365 return m_gestureRecognizer; 0366 } 0367 0368 bool handleDndNotify(xcb_window_t window, const QPoint &point); 0369 bool handleEnterNotifiy(xcb_window_t window, const QPoint &point, const QDateTime ×tamp); 0370 bool remainActiveOnFullscreen() const; 0371 const std::vector<std::unique_ptr<Edge>> &edges() const; 0372 0373 public Q_SLOTS: 0374 void reconfigure(); 0375 /** 0376 * Updates the layout of virtual desktops and adjust the reserved borders in case of 0377 * virtual desktop switching on edges. 0378 */ 0379 void updateLayout(); 0380 /** 0381 * Recreates all edges e.g. after the screen size changes. 0382 */ 0383 void recreateEdges(); 0384 0385 Q_SIGNALS: 0386 /** 0387 * Signal emitted during approaching of mouse towards @p border. The @p factor indicates how 0388 * far away the mouse is from the approaching area. The values are clamped into [0.0,1.0] with 0389 * @c 0.0 meaning far away from the border, @c 1.0 in trigger distance. 0390 */ 0391 void approaching(ElectricBorder border, qreal factor, const QRect &geometry); 0392 void checkBlocking(); 0393 0394 private: 0395 enum { 0396 ElectricDisabled = 0, 0397 ElectricMoveOnly = 1, 0398 ElectricAlways = 2, 0399 }; 0400 void setDesktopSwitching(bool enable); 0401 void setDesktopSwitchingMovingClients(bool enable); 0402 void setCursorPushBackDistance(const QSize &distance); 0403 void setTimeThreshold(int threshold); 0404 void setReActivationThreshold(int threshold); 0405 void createHorizontalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea, Output *output); 0406 void createVerticalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea, Output *output); 0407 std::unique_ptr<Edge> createEdge(ElectricBorder border, int x, int y, int width, int height, Output *output, bool createAction = true); 0408 void setActionForBorder(ElectricBorder border, ElectricBorderAction *oldValue, ElectricBorderAction newValue); 0409 void setActionForTouchBorder(ElectricBorder border, ElectricBorderAction newValue); 0410 void setRemainActiveOnFullscreen(bool remainActive); 0411 ElectricBorderAction actionForEdge(Edge *edge) const; 0412 ElectricBorderAction actionForTouchEdge(Edge *edge) const; 0413 void createEdgeForClient(Window *client, ElectricBorder border); 0414 void deleteEdgeForClient(Window *client); 0415 bool m_desktopSwitching; 0416 bool m_desktopSwitchingMovingClients; 0417 QSize m_cursorPushBackDistance; 0418 int m_timeThreshold; 0419 int m_reactivateThreshold; 0420 Qt::Orientations m_virtualDesktopLayout; 0421 std::vector<std::unique_ptr<Edge>> m_edges; 0422 KSharedConfig::Ptr m_config; 0423 ElectricBorderAction m_actionTopLeft; 0424 ElectricBorderAction m_actionTop; 0425 ElectricBorderAction m_actionTopRight; 0426 ElectricBorderAction m_actionRight; 0427 ElectricBorderAction m_actionBottomRight; 0428 ElectricBorderAction m_actionBottom; 0429 ElectricBorderAction m_actionBottomLeft; 0430 ElectricBorderAction m_actionLeft; 0431 QMap<ElectricBorder, ElectricBorderAction> m_touchCallbacks; 0432 int m_cornerOffset; 0433 GestureRecognizer *m_gestureRecognizer; 0434 bool m_remainActiveOnFullscreen = false; 0435 }; 0436 0437 /********************************************************** 0438 * Inlines Edge 0439 *********************************************************/ 0440 0441 inline bool Edge::isBottom() const 0442 { 0443 return m_border == ElectricBottom || m_border == ElectricBottomLeft || m_border == ElectricBottomRight; 0444 } 0445 0446 inline bool Edge::isLeft() const 0447 { 0448 return m_border == ElectricLeft || m_border == ElectricTopLeft || m_border == ElectricBottomLeft; 0449 } 0450 0451 inline bool Edge::isRight() const 0452 { 0453 return m_border == ElectricRight || m_border == ElectricTopRight || m_border == ElectricBottomRight; 0454 } 0455 0456 inline bool Edge::isTop() const 0457 { 0458 return m_border == ElectricTop || m_border == ElectricTopLeft || m_border == ElectricTopRight; 0459 } 0460 0461 inline bool Edge::isCorner() const 0462 { 0463 return m_border == ElectricTopLeft 0464 || m_border == ElectricTopRight 0465 || m_border == ElectricBottomRight 0466 || m_border == ElectricBottomLeft; 0467 } 0468 0469 inline bool Edge::isScreenEdge() const 0470 { 0471 return m_border == ElectricLeft 0472 || m_border == ElectricRight 0473 || m_border == ElectricTop 0474 || m_border == ElectricBottom; 0475 } 0476 0477 inline bool Edge::isReserved() const 0478 { 0479 return m_reserved != 0; 0480 } 0481 0482 inline void Edge::setAction(ElectricBorderAction action) 0483 { 0484 m_action = action; 0485 } 0486 0487 inline ScreenEdges *Edge::edges() 0488 { 0489 return m_edges; 0490 } 0491 0492 inline const ScreenEdges *Edge::edges() const 0493 { 0494 return m_edges; 0495 } 0496 0497 inline const QRect &Edge::geometry() const 0498 { 0499 return m_geometry; 0500 } 0501 0502 inline const QRect &Edge::approachGeometry() const 0503 { 0504 return m_approachGeometry; 0505 } 0506 0507 inline ElectricBorder Edge::border() const 0508 { 0509 return m_border; 0510 } 0511 0512 inline const QHash<QObject *, QByteArray> &Edge::callBacks() const 0513 { 0514 return m_callBacks; 0515 } 0516 0517 inline bool Edge::isBlocked() const 0518 { 0519 return m_blocked; 0520 } 0521 0522 inline Window *Edge::client() const 0523 { 0524 return m_client; 0525 } 0526 0527 inline bool Edge::isApproaching() const 0528 { 0529 return m_approaching; 0530 } 0531 0532 /********************************************************** 0533 * Inlines ScreenEdges 0534 *********************************************************/ 0535 inline void ScreenEdges::setConfig(KSharedConfig::Ptr config) 0536 { 0537 m_config = config; 0538 } 0539 0540 inline int ScreenEdges::cornerOffset() const 0541 { 0542 return m_cornerOffset; 0543 } 0544 0545 inline const QSize &ScreenEdges::cursorPushBackDistance() const 0546 { 0547 return m_cursorPushBackDistance; 0548 } 0549 0550 inline bool ScreenEdges::isDesktopSwitching() const 0551 { 0552 return m_desktopSwitching; 0553 } 0554 0555 inline bool ScreenEdges::isDesktopSwitchingMovingClients() const 0556 { 0557 return m_desktopSwitchingMovingClients; 0558 } 0559 0560 inline int ScreenEdges::reActivationThreshold() const 0561 { 0562 return m_reactivateThreshold; 0563 } 0564 0565 inline int ScreenEdges::timeThreshold() const 0566 { 0567 return m_timeThreshold; 0568 } 0569 0570 inline void ScreenEdges::setCursorPushBackDistance(const QSize &distance) 0571 { 0572 m_cursorPushBackDistance = distance; 0573 } 0574 0575 inline void ScreenEdges::setDesktopSwitching(bool enable) 0576 { 0577 if (enable == m_desktopSwitching) { 0578 return; 0579 } 0580 m_desktopSwitching = enable; 0581 reserveDesktopSwitching(enable, m_virtualDesktopLayout); 0582 } 0583 0584 inline void ScreenEdges::setDesktopSwitchingMovingClients(bool enable) 0585 { 0586 m_desktopSwitchingMovingClients = enable; 0587 } 0588 0589 inline void ScreenEdges::setReActivationThreshold(int threshold) 0590 { 0591 Q_ASSERT(threshold >= m_timeThreshold); 0592 m_reactivateThreshold = threshold; 0593 } 0594 0595 inline void ScreenEdges::setTimeThreshold(int threshold) 0596 { 0597 m_timeThreshold = threshold; 0598 } 0599 0600 #define ACTION(name) \ 0601 inline ElectricBorderAction ScreenEdges::name() const \ 0602 { \ 0603 return m_##name; \ 0604 } 0605 0606 ACTION(actionTopLeft) 0607 ACTION(actionTop) 0608 ACTION(actionTopRight) 0609 ACTION(actionRight) 0610 ACTION(actionBottomRight) 0611 ACTION(actionBottom) 0612 ACTION(actionBottomLeft) 0613 ACTION(actionLeft) 0614 0615 #undef ACTION 0616 0617 }