21#include <config-kwin.h> 
   36#if KWIN_BUILD_SCREENLOCKER 
   37#include "screenlocker_interface.h" 
   40#include <KConfigGroup> 
   42#include <QAbstractEventDispatcher> 
   44#include <QDBusInterface> 
   45#include <QDBusPendingCall> 
   46#include <QFontDatabase> 
   47#include <QFontMetrics> 
   57static const int DISTANCE_RESET = 30;
 
   60static const int TOUCH_TARGET = 3;
 
   63static const int MINIMUM_DELTA = 44;
 
   66    : m_touchUpAction(touchUpAction)
 
   67    , m_progressCallback(progressCallback)
 
 
   77    return m_touchUpAction;
 
 
   82    if (m_progressCallback) {
 
   83        m_progressCallback(border, deltaProgress, output);
 
 
   89    return m_progressCallback != 
nullptr;
 
 
   97    , m_approaching(false)
 
   98    , m_lastApproachingFactor(0)
 
  100    , m_pushBackBlocked(false)
 
  105    m_gesture->setMinimumFingerCount(1);
 
  106    m_gesture->setMaximumFingerCount(1);
 
  111                m_client->showOnScreenEdge();
 
  116            handleTouchCallback();
 
  118        Qt::QueuedConnection);
 
  122        if (!m_touchCallbacks.isEmpty() && m_touchCallbacks.constFirst().hasProgressCallback()) {
 
  123            handleTouchCallback();
 
  126    connect(m_gesture.get(), &SwipeGesture::progress, 
this, [
this](qreal progress) {
 
  127        int factor = progress * 256.0f;
 
  128        if (m_lastApproachingFactor != factor) {
 
  129            m_lastApproachingFactor = factor;
 
  130            Q_EMIT approaching(border(), m_lastApproachingFactor / 256.0f, m_approachGeometry);
 
  133    connect(m_gesture.get(), &SwipeGesture::deltaProgress, 
this, [
this](
const QPointF &progressDelta) {
 
  134        if (!m_touchCallbacks.isEmpty()) {
 
  135            m_touchCallbacks.constFirst().progressCallback(border(), progressDelta, m_output);
 
  138    connect(
this, &Edge::activatesForTouchGestureChanged, 
this, [
this]() {
 
  140            if (activatesForTouchGesture()) {
 
  141                m_edges->gestureRecognizer()->registerSwipeGesture(m_gesture.get());
 
  143                m_edges->gestureRecognizer()->unregisterSwipeGesture(m_gesture.get());
 
 
  157    if (m_reserved == 1) {
 
 
  163void Edge::reserve(QObject *
object, 
const char *slot)
 
  165    connect(
object, &QObject::destroyed, 
this, qOverload<QObject *>(&Edge::unreserve));
 
  166    m_callBacks.insert(
object, QByteArray(slot));
 
 
  172    if (std::any_of(m_touchCallbacks.constBegin(), m_touchCallbacks.constEnd(), [action](
const TouchCallback &c) {
 
  173            return c.touchUpAction() == action;
 
 
  180void Edge::reserveTouchCallBack(
const TouchCallback &callback)
 
  182    if (std::any_of(m_touchCallbacks.constBegin(), m_touchCallbacks.constEnd(), [callback](
const TouchCallback &c) {
 
  183            return c.touchUpAction() == callback.touchUpAction();
 
  187    connect(callback.
touchUpAction(), &QAction::destroyed, 
this, [
this, callback]() {
 
  188        unreserveTouchCallBack(callback.touchUpAction());
 
  190    m_touchCallbacks << callback;
 
  194void Edge::unreserveTouchCallBack(QAction *action)
 
  196    auto it = std::find_if(m_touchCallbacks.begin(), m_touchCallbacks.end(), [action](
const TouchCallback &c) {
 
  197        return c.touchUpAction() == action;
 
  199    if (it != m_touchCallbacks.end()) {
 
  200        m_touchCallbacks.erase(it);
 
 
  205void Edge::unreserve()
 
  208    if (m_reserved == 0) {
 
 
  214void Edge::unreserve(QObject *
object)
 
  216    if (m_callBacks.remove(
object) > 0) {
 
  217        disconnect(
object, &QObject::destroyed, 
this, qOverload<QObject *>(&Edge::unreserve));
 
 
  222bool Edge::activatesForPointer()
 const 
  224    bool isMovingWindow = 
false;
 
  235    } 
else if (
input()->pointer()->areButtonsPressed()) {
 
  236        auto c = Workspace::self()->moveResizeWindow();
 
  237        if (!c || c->isInteractiveResize()) {
 
  240        isMovingWindow = 
true;
 
  246    if (m_edges->isDesktopSwitching()) {
 
  249    if (m_edges->isDesktopSwitchingMovingClients() && isMovingWindow) {
 
  252    if (!m_callBacks.isEmpty()) {
 
 
  261bool Edge::activatesForTouchGesture()
 const 
  263    if (!isScreenEdge()) {
 
  275    if (!m_touchCallbacks.isEmpty()) {
 
 
  281bool Edge::triggersFor(
const QPoint &cursorPos)
 const 
  286    if (!activatesForPointer()) {
 
  289    if (!m_geometry.contains(cursorPos)) {
 
  292    if (isLeft() && cursorPos.x() != m_geometry.x()) {
 
  295    if (isRight() && cursorPos.x() != (m_geometry.x() + m_geometry.width() - 1)) {
 
  298    if (isTop() && cursorPos.y() != m_geometry.y()) {
 
  301    if (isBottom() && cursorPos.y() != (m_geometry.y() + m_geometry.height() - 1)) {
 
 
  307bool Edge::check(
const QPoint &cursorPos, 
const QDateTime &triggerTime, 
bool forceNoPushBack)
 
  309    if (!triggersFor(cursorPos)) {
 
  312    if (m_lastTrigger.isValid() && 
 
  313        m_lastTrigger.msecsTo(triggerTime) < edges()->reActivationThreshold() - edges()->timeThreshold()) {
 
  315        m_lastTrigger = triggerTime;
 
  319    bool directActivate = forceNoPushBack || edges()->cursorPushBackDistance().isNull();
 
  320    if (directActivate || canActivate(cursorPos, triggerTime)) {
 
  321        markAsTriggered(cursorPos, triggerTime);
 
  325        pushCursorBack(cursorPos);
 
  326        m_triggeredPoint = cursorPos;
 
 
  331void Edge::markAsTriggered(
const QPoint &cursorPos, 
const QDateTime &triggerTime)
 
  333    m_lastTrigger = triggerTime;
 
  334    m_lastReset = QDateTime(); 
 
  335    m_triggeredPoint = cursorPos;
 
 
  338bool Edge::canActivate(
const QPoint &cursorPos, 
const QDateTime &triggerTime)
 
  344    if (!m_lastReset.isValid() || m_lastReset.msecsTo(triggerTime) > edges()->reActivationThreshold()) {
 
  345        m_lastReset = triggerTime;
 
  348    if (m_lastTrigger.isValid() && m_lastTrigger.msecsTo(triggerTime) < edges()->reActivationThreshold() - edges()->timeThreshold()) {
 
  351    if (m_lastReset.msecsTo(triggerTime) < edges()->timeThreshold()) {
 
  355    if ((cursorPos - m_triggeredPoint).manhattanLength() > DISTANCE_RESET) {
 
  361void Edge::handle(
const QPoint &cursorPos)
 
  363    Window *movingClient = Workspace::self()->moveResizeWindow();
 
  364    if ((edges()->isDesktopSwitchingMovingClients() && movingClient && !movingClient->isInteractiveResize()) || (edges()->isDesktopSwitching() && isScreenEdge())) {
 
  368        switchDesktop(cursorPos);
 
  379        pushCursorBack(cursorPos);
 
  380        m_client->showOnScreenEdge();
 
  385    if (handlePointerAction() || handleByCallback()) {
 
  386        pushCursorBack(cursorPos);
 
  389    if (edges()->isDesktopSwitching() && isCorner()) {
 
  391        switchDesktop(cursorPos);
 
  395bool Edge::handleAction(ElectricBorderAction action)
 
  399        Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop());
 
  403#if KWIN_BUILD_SCREENLOCKER 
  404        OrgFreedesktopScreenSaverInterface interface(QStringLiteral(
"org.freedesktop.ScreenSaver"),
 
  405                                                     QStringLiteral(
"/ScreenSaver"),
 
  406                                                     QDBusConnection::sessionBus());
 
  407        if (interface.isValid()) {
 
  416        QDBusConnection::sessionBus().asyncCall(
 
  417            QDBusMessage::createMethodCall(QStringLiteral(
"org.kde.krunner"),
 
  418                                           QStringLiteral(
"/App"),
 
  419                                           QStringLiteral(
"org.kde.krunner.App"),
 
  420                                           QStringLiteral(
"display")));
 
  424        QDBusConnection::sessionBus().asyncCall(
 
  425            QDBusMessage::createMethodCall(QStringLiteral(
"org.kde.plasmashell"),
 
  426                                           QStringLiteral(
"/PlasmaShell"),
 
  427                                           QStringLiteral(
"org.kde.PlasmaShell"),
 
  428                                           QStringLiteral(
"toggleActivityManager")));
 
  432        QDBusConnection::sessionBus().asyncCall(
 
  433            QDBusMessage::createMethodCall(QStringLiteral(
"org.kde.plasmashell"),
 
  434                                           QStringLiteral(
"/PlasmaShell"),
 
  435                                           QStringLiteral(
"org.kde.PlasmaShell"),
 
  436                                           QStringLiteral(
"activateLauncherMenu")));
 
  444bool Edge::handleByCallback()
 
  446    if (m_callBacks.isEmpty()) {
 
  449    for (
auto it = m_callBacks.begin(); it != m_callBacks.end(); ++it) {
 
  451        QMetaObject::invokeMethod(it.key(), it.value().constData(), Q_RETURN_ARG(
bool, retVal), Q_ARG(ElectricBorder, m_border));
 
  459void Edge::handleTouchCallback()
 
  461    if (!m_touchCallbacks.isEmpty()) {
 
  462        m_touchCallbacks.constFirst().touchUpAction()->trigger();
 
  466void Edge::switchDesktop(
const QPoint &cursorPos)
 
  468    QPoint pos(cursorPos);
 
  469    VirtualDesktopManager *vds = VirtualDesktopManager::self();
 
  470    VirtualDesktop *oldDesktop = vds->currentDesktop();
 
  471    VirtualDesktop *desktop = oldDesktop;
 
  472    const int OFFSET = 2;
 
  474        const VirtualDesktop *interimDesktop = desktop;
 
  475        desktop = vds->toLeft(desktop, vds->isNavigationWrappingAround());
 
  476        if (desktop != interimDesktop) {
 
  477            pos.setX(
workspace()->geometry().width() - 1 - OFFSET);
 
  479    } 
else if (isRight()) {
 
  480        const VirtualDesktop *interimDesktop = desktop;
 
  481        desktop = vds->toRight(desktop, vds->isNavigationWrappingAround());
 
  482        if (desktop != interimDesktop) {
 
  487        const VirtualDesktop *interimDesktop = desktop;
 
  488        desktop = vds->above(desktop, vds->isNavigationWrappingAround());
 
  489        if (desktop != interimDesktop) {
 
  490            pos.setY(
workspace()->geometry().height() - 1 - OFFSET);
 
  492    } 
else if (isBottom()) {
 
  493        const VirtualDesktop *interimDesktop = desktop;
 
  494        desktop = vds->below(desktop, vds->isNavigationWrappingAround());
 
  495        if (desktop != interimDesktop) {
 
  499    if (
Window *c = Workspace::self()->moveResizeWindow()) {
 
  500        const QList<VirtualDesktop *> desktops{desktop};
 
  501        if (c->rules()->checkDesktops(desktops) != desktops) {
 
  506    vds->setCurrent(desktop);
 
  507    if (vds->currentDesktop() != oldDesktop) {
 
  508        m_pushBackBlocked = 
true;
 
  509        Cursors::self()->mouse()->setPos(pos);
 
  510        auto unblockPush = [
this] {
 
  511            m_pushBackBlocked = 
false;
 
  513        QObject::connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, 
this, unblockPush, Qt::SingleShotConnection);
 
  517void Edge::pushCursorBack(
const QPoint &cursorPos)
 
  519    if (m_pushBackBlocked) {
 
  522    int x = cursorPos.x();
 
  523    int y = cursorPos.y();
 
  524    const QSize &distance = edges()->cursorPushBackDistance();
 
  526        x += distance.width();
 
  529        x -= distance.width();
 
  532        y += distance.height();
 
  535        y -= distance.height();
 
  537    Cursors::self()->mouse()->setPos(QPoint(x, y));
 
  540void Edge::setGeometry(
const QRect &geometry)
 
  542    if (m_geometry == geometry) {
 
  545    m_geometry = geometry;
 
  546    int x = m_geometry.x();
 
  547    int y = m_geometry.y();
 
  548    int width = m_geometry.width();
 
  549    int height = m_geometry.height();
 
  550    const int offset = m_edges->cornerOffset();
 
  553            x = x + width - offset;
 
  556            y = y + height - offset;
 
  563        } 
else if (isRight()) {
 
  564            x = x + width - offset;
 
  566        } 
else if (isTop()) {
 
  568        } 
else if (isBottom()) {
 
  569            y = y + height - offset;
 
  573    m_approachGeometry = QRect(x, y, width, height);
 
  576    if (isScreenEdge()) {
 
  578        m_gesture->setStartGeometry(m_geometry);
 
  579        m_gesture->setMinimumDelta(QPointF(MINIMUM_DELTA, MINIMUM_DELTA) / output->
scale());
 
 
  583void Edge::checkBlocking()
 
  585    Window *client = Workspace::self()->activeWindow();
 
  587    if (newValue == m_blocked) {
 
  590    const bool wasTouch = activatesForTouchGesture();
 
  591    m_blocked = newValue;
 
  592    if (m_blocked && m_approaching) {
 
  595    if (wasTouch != activatesForTouchGesture()) {
 
  596        Q_EMIT activatesForTouchGestureChanged();
 
 
  601void Edge::doUpdateBlocking()
 
 
  605void Edge::doGeometryUpdate()
 
 
  611    if (activatesForTouchGesture()) {
 
  612        m_edges->gestureRecognizer()->registerSwipeGesture(m_gesture.get());
 
  617void Edge::doActivate()
 
 
  621void Edge::deactivate()
 
  623    m_edges->gestureRecognizer()->unregisterSwipeGesture(m_gesture.get());
 
  627void Edge::doDeactivate()
 
 
  631void Edge::startApproaching()
 
  636    m_approaching = 
true;
 
  637    doStartApproaching();
 
  638    m_lastApproachingFactor = 0;
 
  639    Q_EMIT approaching(border(), 0.0, m_approachGeometry);
 
 
  642void Edge::doStartApproaching()
 
 
  646void Edge::stopApproaching()
 
  648    if (!m_approaching) {
 
  651    m_approaching = 
false;
 
  653    m_lastApproachingFactor = 0;
 
  654    Q_EMIT approaching(border(), 0.0, m_approachGeometry);
 
 
  657void Edge::doStopApproaching()
 
 
  661void Edge::updateApproaching(
const QPointF &point)
 
  663    if (exclusiveContains(approachGeometry(), point)) {
 
  665        const int edgeDistance = m_edges->cornerOffset();
 
  666        auto cornerDistance = [=](
const QPointF &corner) {
 
  667            return std::max(std::abs(corner.x() - point.x()), std::abs(corner.y() - point.y()));
 
  669        constexpr double factorScale = 256;
 
  672            factor = (cornerDistance(approachGeometry().topLeft()) * factorScale) / edgeDistance;
 
  675            factor = (cornerDistance(approachGeometry().topRight()) * factorScale) / edgeDistance;
 
  678            factor = (cornerDistance(approachGeometry().bottomRight()) * factorScale) / edgeDistance;
 
  681            factor = (cornerDistance(approachGeometry().bottomLeft()) * factorScale) / edgeDistance;
 
  684            factor = (std::abs(point.y() - approachGeometry().y()) * factorScale) / edgeDistance;
 
  687            factor = (std::abs(point.x() - approachGeometry().right()) * factorScale) / edgeDistance;
 
  690            factor = (std::abs(point.y() - approachGeometry().bottom()) * factorScale) / edgeDistance;
 
  693            factor = (std::abs(point.x() - approachGeometry().x()) * factorScale) / edgeDistance;
 
  698        factor = factorScale - factor;
 
  699        if (m_lastApproachingFactor != factor) {
 
  700            m_lastApproachingFactor = factor;
 
  701            Q_EMIT approaching(border(), m_lastApproachingFactor / factorScale, m_approachGeometry);
 
 
  708quint32 Edge::window()
 const 
 
  713quint32 Edge::approachWindow()
 const 
 
  723        m_gesture->setDirection(SwipeDirection::Down);
 
  726        m_gesture->setDirection(SwipeDirection::Left);
 
  729        m_gesture->setDirection(SwipeDirection::Up);
 
  732        m_gesture->setDirection(SwipeDirection::Right);
 
 
  741    const bool wasTouch = activatesForTouchGesture();
 
  742    m_touchAction = action;
 
  743    if (wasTouch != activatesForTouchGesture()) {
 
  744        Q_EMIT activatesForTouchGestureChanged();
 
 
  750    const bool wasTouch = activatesForTouchGesture();
 
  752    if (wasTouch != activatesForTouchGesture()) {
 
  753        Q_EMIT activatesForTouchGestureChanged();
 
 
  771ScreenEdges::ScreenEdges()
 
  772    : m_desktopSwitching(false)
 
  773    , m_desktopSwitchingMovingClients(false)
 
  775    , m_reactivateThreshold(0)
 
  776    , m_virtualDesktopLayout({})
 
  787    const int gridUnit = QFontMetrics(QFontDatabase::systemFont(QFontDatabase::GeneralFont)).boundingRect(QLatin1Char(
'M')).height();
 
  788    m_cornerOffset = 4 * gridUnit;
 
 
  801    QString lowerName = name.toLower();
 
  802    if (lowerName == QStringLiteral(
"showdesktop")) {
 
  804    } 
else if (lowerName == QStringLiteral(
"lockscreen")) {
 
  806    } 
else if (lowerName == QLatin1String(
"krunner")) {
 
  808    } 
else if (lowerName == QLatin1String(
"activitymanager")) {
 
  810    } 
else if (lowerName == QLatin1String(
"applicationlauncher")) {
 
  821    KConfigGroup screenEdgesConfig = m_config->group(QStringLiteral(
"ScreenEdges"));
 
  822    setRemainActiveOnFullscreen(screenEdgesConfig.readEntry(
"RemainActiveOnFullscreen", 
false));
 
  825    KConfigGroup windowsConfig = m_config->group(QStringLiteral(
"Windows"));
 
  826    setTimeThreshold(windowsConfig.readEntry(
"ElectricBorderDelay", 75));
 
  827    setReActivationThreshold(std::max(
timeThreshold() + 50, windowsConfig.readEntry(
"ElectricBorderCooldown", 350)));
 
  828    int desktopSwitching = windowsConfig.readEntry(
"ElectricBorders", 
static_cast<int>(ElectricDisabled));
 
  830        setDesktopSwitching(
false);
 
  831        setDesktopSwitchingMovingClients(
false);
 
  833        setDesktopSwitching(
false);
 
  834        setDesktopSwitchingMovingClients(
true);
 
  836        setDesktopSwitching(
true);
 
  837        setDesktopSwitchingMovingClients(
true);
 
  839    const int pushBack = windowsConfig.readEntry(
"ElectricBorderPushbackPixels", 1);
 
  840    m_cursorPushBackDistance = QSize(pushBack, pushBack);
 
  842    KConfigGroup borderConfig = m_config->group(QStringLiteral(
"ElectricBorders"));
 
  844                       electricBorderAction(borderConfig.readEntry(
"TopLeft", 
"None")));
 
  846                       electricBorderAction(borderConfig.readEntry(
"Top", 
"None")));
 
  848                       electricBorderAction(borderConfig.readEntry(
"TopRight", 
"None")));
 
  850                       electricBorderAction(borderConfig.readEntry(
"Right", 
"None")));
 
  852                       electricBorderAction(borderConfig.readEntry(
"BottomRight", 
"None")));
 
  854                       electricBorderAction(borderConfig.readEntry(
"Bottom", 
"None")));
 
  856                       electricBorderAction(borderConfig.readEntry(
"BottomLeft", 
"None")));
 
  858                       electricBorderAction(borderConfig.readEntry(
"Left", 
"None")));
 
  860    borderConfig = m_config->group(QStringLiteral(
"TouchEdges"));
 
  861    setActionForTouchBorder(
ElectricTop, electricBorderAction(borderConfig.readEntry(
"Top", 
"None")));
 
  862    setActionForTouchBorder(
ElectricRight, electricBorderAction(borderConfig.readEntry(
"Right", 
"None")));
 
  863    setActionForTouchBorder(
ElectricBottom, electricBorderAction(borderConfig.readEntry(
"Bottom", 
"None")));
 
  864    setActionForTouchBorder(
ElectricLeft, electricBorderAction(borderConfig.readEntry(
"Left", 
"None")));
 
 
  869    if (*oldValue == newValue) {
 
  874        for (
const auto &edge : m_edges) {
 
  875            if (edge->border() == border) {
 
  882        for (
const auto &edge : m_edges) {
 
  883            if (edge->border() == border) {
 
  888    *oldValue = newValue;
 
  890    for (
const auto &edge : m_edges) {
 
  891        if (edge->border() == border) {
 
  892            edge->setAction(newValue);
 
  899    auto it = m_touchCallbacks.find(border);
 
  901    if (it != m_touchCallbacks.end()) {
 
  902        oldValue = it.value();
 
  904    if (oldValue == newValue) {
 
  909        for (
const auto &edge : m_edges) {
 
  910            if (edge->border() == border) {
 
  917        for (
const auto &edge : m_edges) {
 
  918            if (edge->border() == border) {
 
  923        m_touchCallbacks.erase(it);
 
  925        m_touchCallbacks.insert(border, newValue);
 
  928    for (
const auto &edge : m_edges) {
 
  929        if (edge->border() == border) {
 
  930            edge->setTouchAction(newValue);
 
  937    const QSize desktopMatrix = VirtualDesktopManager::self()->grid().size();
 
  938    Qt::Orientations newLayout = {};
 
  939    if (desktopMatrix.width() > 1) {
 
  940        newLayout |= Qt::Horizontal;
 
  942    if (desktopMatrix.height() > 1) {
 
  943        newLayout |= Qt::Vertical;
 
  945    if (newLayout == m_virtualDesktopLayout) {
 
  951    m_virtualDesktopLayout = newLayout;
 
 
  957static bool isLeftScreen(
const QRect &screen, 
const QRect &fullArea)
 
  960    if (outputs.count() == 1) {
 
  963    if (screen.x() == fullArea.x()) {
 
  967    for (
const Output *output : 
outputs) {
 
  968        const QRect otherGeo = output->geometry();
 
  969        if (otherGeo == screen) {
 
  973        if (screen.x() == otherGeo.x() + otherGeo.width()
 
  974            && screen.y() < otherGeo.y() + otherGeo.height()
 
  975            && screen.y() + screen.height() > otherGeo.y()) {
 
  984static bool isRightScreen(
const QRect &screen, 
const QRect &fullArea)
 
  990    if (screen.x() + screen.width() == fullArea.x() + fullArea.width()) {
 
  994    for (
const Output *output : 
outputs) {
 
  995        const QRect otherGeo = output->geometry();
 
  996        if (otherGeo == screen) {
 
 1000        if (screen.x() + screen.width() == otherGeo.x()
 
 1001            && screen.y() < otherGeo.y() + otherGeo.height()
 
 1002            && screen.y() + screen.height() > otherGeo.y()) {
 
 1011static bool isTopScreen(
const QRect &screen, 
const QRect &fullArea)
 
 1017    if (screen.y() == fullArea.y()) {
 
 1021    for (
const Output *output : 
outputs) {
 
 1022        const QRect otherGeo = output->geometry();
 
 1023        if (otherGeo == screen) {
 
 1027        if (screen.y() == otherGeo.y() + otherGeo.height()
 
 1028            && screen.x() < otherGeo.x() + otherGeo.width()
 
 1029            && screen.x() + screen.width() > otherGeo.x()) {
 
 1038static bool isBottomScreen(
const QRect &screen, 
const QRect &fullArea)
 
 1044    if (screen.y() + screen.height() == fullArea.y() + fullArea.height()) {
 
 1048    for (
const Output *output : 
outputs) {
 
 1049        const QRect otherGeo = output->geometry();
 
 1050        if (otherGeo == screen) {
 
 1054        if (screen.y() + screen.height() == otherGeo.y()
 
 1055            && screen.x() < otherGeo.x() + otherGeo.width()
 
 1056            && screen.x() + screen.width() > otherGeo.x()) {
 
 1067    return m_remainActiveOnFullscreen;
 
 
 1072    std::vector<std::unique_ptr<Edge>> oldEdges = std::move(m_edges);
 
 1075    QRegion processedRegion;
 
 1078    for (
Output *output : outputs) {
 
 1079        const QRegion screen = QRegion(output->geometry()).subtracted(processedRegion);
 
 1080        processedRegion += screen;
 
 1081        for (
const QRect &screenPart : screen) {
 
 1082            if (isLeftScreen(screenPart, fullArea)) {
 
 1084                createVerticalEdge(
ElectricLeft, screenPart, fullArea, output);
 
 1086            if (isRightScreen(screenPart, fullArea)) {
 
 1088                createVerticalEdge(
ElectricRight, screenPart, fullArea, output);
 
 1090            if (isTopScreen(screenPart, fullArea)) {
 
 1092                createHorizontalEdge(
ElectricTop, screenPart, fullArea, output);
 
 1094            if (isBottomScreen(screenPart, fullArea)) {
 
 1096                createHorizontalEdge(
ElectricBottom, screenPart, fullArea, output);
 
 1100    auto split = std::partition(oldEdges.begin(), oldEdges.end(), [](
const auto &edge) {
 
 1101        return !edge->client();
 
 1104    for (
const auto &edge : m_edges) {
 
 1105        for (
const auto &oldEdge : std::span(oldEdges.begin(), split)) {
 
 1106            if (oldEdge->border() != edge->border()) {
 
 1109            const QHash<QObject *, QByteArray> &callbacks = oldEdge->callBacks();
 
 1110            for (
auto callback = callbacks.begin(); callback != callbacks.end(); callback++) {
 
 1111                edge->reserve(callback.key(), callback.value().constData());
 
 1113            const auto touchCallBacks = oldEdge->touchCallBacks();
 
 1114            for (
auto c : touchCallBacks) {
 
 1115                edge->reserveTouchCallBack(c);
 
 1120    for (
const auto &oldEdge : std::span(split, oldEdges.end())) {
 
 1121        if (!
reserve(oldEdge->client(), oldEdge->border())) {
 
 1122            oldEdge->client()->showOnScreenEdge();
 
 
 1127void ScreenEdges::createVerticalEdge(
ElectricBorder border, 
const QRect &screen, 
const QRect &fullArea, 
Output *output)
 
 1133    int height = screen.height();
 
 1134    const int x = (border == 
ElectricLeft) ? screen.x() : screen.x() + screen.width() - TOUCH_TARGET;
 
 1135    if (isTopScreen(screen, fullArea)) {
 
 1137        height -= m_cornerOffset;
 
 1138        y += m_cornerOffset;
 
 1141        m_edges.push_back(createEdge(edge, x, screen.y(), TOUCH_TARGET, TOUCH_TARGET, output));
 
 1143    if (isBottomScreen(screen, fullArea)) {
 
 1145        height -= m_cornerOffset;
 
 1148        m_edges.push_back(createEdge(edge, x, screen.y() + screen.height() - TOUCH_TARGET, TOUCH_TARGET, TOUCH_TARGET, output));
 
 1150    if (height <= m_cornerOffset) {
 
 1154    m_edges.push_back(createEdge(border, x, y, TOUCH_TARGET, height, output));
 
 1157void ScreenEdges::createHorizontalEdge(
ElectricBorder border, 
const QRect &screen, 
const QRect &fullArea, Output *output)
 
 1163    int width = screen.width();
 
 1164    if (isLeftScreen(screen, fullArea)) {
 
 1166        x += m_cornerOffset;
 
 1167        width -= m_cornerOffset;
 
 1169    if (isRightScreen(screen, fullArea)) {
 
 1171        width -= m_cornerOffset;
 
 1173    if (width <= m_cornerOffset) {
 
 1177    const int y = (border == 
ElectricTop) ? screen.y() : screen.y() + screen.height() - TOUCH_TARGET;
 
 1178    m_edges.push_back(createEdge(border, x, y, width, TOUCH_TARGET, output));
 
 1181std::unique_ptr<Edge> ScreenEdges::createEdge(
ElectricBorder border, 
int x, 
int y, 
int width, 
int height, Output *output, 
bool createAction)
 
 1183    std::unique_ptr<Edge> edge = kwinApp()->createScreenEdge(
this);
 
 1185    Q_ASSERT(width >= 0);
 
 1186    Q_ASSERT(height >= 0);
 
 1188    edge->setBorder(border);
 
 1189    edge->setGeometry(QRect(x, y, width, height));
 
 1190    edge->setOutput(output);
 
 1195            edge->setAction(action);
 
 1200            edge->setTouchAction(touchAction);
 
 1204        if (edge->isCorner()) {
 
 1207            if ((m_virtualDesktopLayout & Qt::Horizontal) && (edge->isLeft() || edge->isRight())) {
 
 1210            if ((m_virtualDesktopLayout & Qt::Vertical) && (edge->isTop() || edge->isBottom())) {
 
 1222    switch (edge->border()) {
 
 1224        return m_actionTopLeft;
 
 1228        return m_actionTopRight;
 
 1230        return m_actionRight;
 
 1232        return m_actionBottomRight;
 
 1234        return m_actionBottom;
 
 1236        return m_actionBottomLeft;
 
 1238        return m_actionLeft;
 
 1248    auto it = m_touchCallbacks.find(edge->border());
 
 1249    if (it != m_touchCallbacks.end()) {
 
 1257    return m_touchCallbacks.value(border);
 
 
 1265    for (
const auto &edge : m_edges) {
 
 1266        if (edge->isCorner()) {
 
 1267            isToReserve ? edge->reserve() : edge->unreserve();
 
 1269            if ((m_virtualDesktopLayout & Qt::Horizontal) && (edge->isLeft() || edge->isRight())) {
 
 1270                isToReserve ? edge->reserve() : edge->unreserve();
 
 1272            if ((m_virtualDesktopLayout & Qt::Vertical) && (edge->isTop() || edge->isBottom())) {
 
 1273                isToReserve ? edge->reserve() : edge->unreserve();
 
 
 1281    for (
const auto &edge : m_edges) {
 
 1282        if (edge->border() == border) {
 
 1283            edge->reserve(
object, slot);
 
 
 1290    for (
const auto &edge : m_edges) {
 
 1291        if (edge->border() == border) {
 
 1292            edge->unreserve(
object);
 
 
 1299    const auto it = std::remove_if(m_edges.begin(), m_edges.end(), [client](
const auto &edge) {
 
 1300        return edge->client() == client;
 
 1302    const bool hadBorder = it != m_edges.end();
 
 1303    m_edges.erase(it, m_edges.end());
 
 1306        return createEdgeForClient(client, border);
 
 
 1314    for (
const auto &edge : m_edges) {
 
 1315        if (edge->border() == border) {
 
 1316            edge->reserveTouchCallBack(action, callback);
 
 
 1323    for (
const auto &edge : m_edges) {
 
 1324        if (edge->border() == border) {
 
 1325            edge->unreserveTouchCallBack(action);
 
 
 1341    const QRect screen = output->
geometry();
 
 1344        if (!isTopScreen(screen, fullArea)) {
 
 1350        width = geo.width();
 
 1353        if (!isBottomScreen(screen, fullArea)) {
 
 1356        y = screen.y() + screen.height() - 1;
 
 1359        width = geo.width();
 
 1362        if (!isLeftScreen(screen, fullArea)) {
 
 1368        height = geo.height();
 
 1371        if (!isRightScreen(screen, fullArea)) {
 
 1374        x = screen.x() + screen.width() - 1;
 
 1377        height = geo.height();
 
 1383    m_edges.push_back(createEdge(border, x, y, width, height, output, 
false));
 
 1384    Edge *edge = m_edges.back().get();
 
 1385    edge->setClient(client);
 
 1390void ScreenEdges::deleteEdgeForClient(
Window *window)
 
 1392    const auto it = std::remove_if(m_edges.begin(), m_edges.end(), [window](
const auto &edge) {
 
 1393        return edge->client() == window;
 
 1395    m_edges.erase(it, m_edges.end());
 
 1400    bool activatedForClient = 
false;
 
 1401    for (
const auto &edge : m_edges) {
 
 1402        if (!edge->isReserved() || edge->isBlocked()) {
 
 1405        if (!edge->activatesForPointer()) {
 
 1408        if (edge->approachGeometry().contains(pos)) {
 
 1409            edge->startApproaching();
 
 1411        if (edge->client() != 
nullptr && activatedForClient) {
 
 1412            edge->markAsTriggered(pos, now);
 
 1415        if (edge->check(pos, now, forceNoPushBack)) {
 
 1416            if (edge->client()) {
 
 1417                activatedForClient = 
true;
 
 
 1425    if (event->type() != QEvent::MouseMove) {
 
 1428    bool activated = 
false;
 
 1429    bool activatedForClient = 
false;
 
 1430    for (
const auto &edge : m_edges) {
 
 1431        if (!edge->isReserved() || edge->isBlocked()) {
 
 1434        if (!edge->activatesForPointer()) {
 
 1437        if (edge->approachGeometry().contains(event->globalPos())) {
 
 1438            if (!edge->isApproaching()) {
 
 1439                edge->startApproaching();
 
 1441                edge->updateApproaching(event->globalPos());
 
 1444            if (edge->isApproaching()) {
 
 1445                edge->stopApproaching();
 
 1448        if (edge->geometry().contains(event->globalPos())) {
 
 1449            if (edge->check(event->globalPos(), QDateTime::fromMSecsSinceEpoch(event->timestamp(), Qt::UTC))) {
 
 1450                if (edge->client()) {
 
 1451                    activatedForClient = 
true;
 
 1456    if (activatedForClient) {
 
 1457        for (
const auto &edge : m_edges) {
 
 1459                edge->markAsTriggered(event->globalPos(), QDateTime::fromMSecsSinceEpoch(event->timestamp(), Qt::UTC));
 
 
 1468    bool activated = 
false;
 
 1469    bool activatedForClient = 
false;
 
 1470    for (
const auto &edge : m_edges) {
 
 1471        if (!edge || edge->window() == XCB_WINDOW_NONE) {
 
 1474        if (!edge->isReserved() || edge->isBlocked()) {
 
 1477        if (!edge->activatesForPointer()) {
 
 1480        if (edge->window() == window) {
 
 1481            if (edge->check(point, timestamp)) {
 
 1482                if (edge->client()) {
 
 1483                    activatedForClient = 
true;
 
 1489        if (edge->approachWindow() == window) {
 
 1490            edge->startApproaching();
 
 1495    if (activatedForClient) {
 
 1496        for (
const auto &edge : m_edges) {
 
 1497            if (edge->client()) {
 
 1498                edge->markAsTriggered(point, timestamp);
 
 
 1507    for (
const auto &edge : m_edges) {
 
 1508        if (!edge || edge->window() == XCB_WINDOW_NONE) {
 
 1511        if (edge->isReserved() && edge->window() == window) {
 
 1512            kwinApp()->updateXTime();
 
 1513            edge->check(point, QDateTime::fromMSecsSinceEpoch(
xTime(), Qt::UTC), 
true);
 
 
 1522    Xcb::restackWindowsWithRaise(
windows());
 
 
 1527    QList<xcb_window_t> wins;
 
 1528    for (
const auto &edge : m_edges) {
 
 1529        xcb_window_t w = edge->window();
 
 1530        if (w != XCB_WINDOW_NONE) {
 
 1534        w = edge->approachWindow();
 
 1535        if (w != XCB_WINDOW_NONE) {
 
 
 1542void ScreenEdges::setRemainActiveOnFullscreen(
bool remainActive)
 
 1544    m_remainActiveOnFullscreen = remainActive;
 
 1554#include "moc_screenedge.cpp" 
Edge(ScreenEdges *parent)
void approaching(ElectricBorder border, qreal factor, const QRect &geometry)
bool hasActiveFullScreenEffect
Class for controlling screen edges.
void unreserveTouch(ElectricBorder border, QAction *action)
void approaching(ElectricBorder border, qreal factor, const QRect &geometry)
void reserve(ElectricBorder border, QObject *object, const char *callback)
bool isEntered(QMouseEvent *event)
void check(const QPoint &pos, const QDateTime &now, bool forceNoPushBack=false)
const std::vector< std::unique_ptr< Edge > > & edges() const
bool handleEnterNotifiy(xcb_window_t window, const QPoint &point, const QDateTime ×tamp)
bool remainActiveOnFullscreen() const
bool handleDndNotify(xcb_window_t window, const QPoint &point)
void reserveDesktopSwitching(bool isToReserve, Qt::Orientations o)
void unreserve(ElectricBorder border, QObject *object)
ElectricBorderAction actionForTouchBorder(ElectricBorder border) const
void reserveTouch(ElectricBorder border, QAction *action, TouchCallback::CallbackFunction callback=nullptr)
QList< xcb_window_t > windows() const
bool isDesktopSwitching() const
TouchCallback(QAction *touchUpAction, TouchCallback::CallbackFunction progressCallback)
std::function< void(ElectricBorder border, const QPointF &, Output *output)> CallbackFunction
void progressCallback(ElectricBorder border, const QPointF &deltaProgress, Output *output) const
bool hasProgressCallback() const
QAction * touchUpAction() const
virtual bool isFullScreen() const
void windowRemoved(KWin::Window *)
QList< Output * > outputs() const
Output * outputAt(const QPointF &pos) const
QList< KWayland::Client::Output * > outputs
KWIN_EXPORT xcb_timestamp_t xTime()
WaylandServer * waylandServer()
@ ElectricActionActivityManager
@ ElectricActionLockScreen
@ ElectricActionApplicationLauncher
@ ElectricActionShowDesktop
InputRedirection * input()