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