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()