From d9aedf620bb022434203970ff6a627a807cabdb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 21 Jan 2013 09:04:06 +0100 Subject: [PATCH] Rewrite of KWin's Screen Edge Handling This rewrite is mostly motivated by the need to handle multi screen setups correctly. That is have edges per screen and not for the combined geometry. Also porting from XLib to XCB has been a motivation for the rewrite. The design of the new ScreenEdge handling is described in the documentation of ScreenEdges in screenedge.h. In addition the following changes have been performed: * move configuration from Options to ScreenEdge * add screen edge information to Workspace::supportInformation (obviously replaces what had been read from Options) * have Workspace hold a pointer to ScreenEdges instead of an object * forward declaration of ScreenEdges in workspaces.h, this explains the seemingly unrelated changes of just another include in some files BUG: 290887 FIXED-IN: 4.11 --- effects.cpp | 3 + events.cpp | 7 +- geometry.cpp | 14 +- layers.cpp | 5 +- options.cpp | 114 ---- options.h | 100 ---- screenedge.cpp | 1078 ++++++++++++++++++++++------------ screenedge.h | 458 ++++++++++++--- scripting/scriptedeffect.cpp | 3 + scripting/scriptingutils.h | 3 + workspace.cpp | 34 +- workspace.h | 8 +- 12 files changed, 1160 insertions(+), 667 deletions(-) diff --git a/effects.cpp b/effects.cpp index 95f87a61d8..a37afc9774 100644 --- a/effects.cpp +++ b/effects.cpp @@ -31,6 +31,9 @@ along with this program. If not, see . #ifdef KWIN_BUILD_TABBOX #include "tabbox.h" #endif +#ifdef KWIN_BUILD_SCREENEDGES +#include "screenedge.h" +#endif #ifdef KWIN_BUILD_SCRIPTING #include "scripting/scriptedeffect.h" #endif diff --git a/events.cpp b/events.cpp index a39aec07fc..b542f3c5bb 100644 --- a/events.cpp +++ b/events.cpp @@ -39,6 +39,9 @@ along with this program. If not, see . #include "unmanaged.h" #include "useractions.h" #include "effects.h" +#ifdef KWIN_BUILD_SCREENEDGES +#include "screenedge.h" +#endif #include "xcbutils.h" #include @@ -383,7 +386,7 @@ bool Workspace::workspaceEvent(XEvent * e) QWhatsThis::leaveWhatsThisMode(); } #ifdef KWIN_BUILD_SCREENEDGES - if (m_screenEdge.isEntered(e)) + if (m_screenEdge->isEntered(e)) return true; #endif break; @@ -437,7 +440,7 @@ bool Workspace::workspaceEvent(XEvent * e) return true; // always eat these, they would tell Qt that KWin is the active app case ClientMessage: #ifdef KWIN_BUILD_SCREENEDGES - if (m_screenEdge.isEntered(e)) + if (m_screenEdge->isEntered(e)) return true; #endif break; diff --git a/geometry.cpp b/geometry.cpp index ae7ad26f22..b9c0728fde 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -40,6 +40,9 @@ along with this program. If not, see . #include "geometrytip.h" #include "rules.h" #include "effects.h" +#ifdef KWIN_BUILD_SCREENEDGES +#include "screenedge.h" +#endif #include #include #include @@ -76,9 +79,12 @@ void Workspace::desktopResized() updateClientArea(); saveOldScreenSizes(); // after updateClientArea(), so that one still uses the previous one + + // TODO: emit a signal instead and remove the deep function calls into edges and effects #ifdef KWIN_BUILD_SCREENEDGES - m_screenEdge.update(true); + m_screenEdge->recreateEdges(); #endif + if (effects) { static_cast(effects)->desktopResized(geom.size()); } @@ -2574,7 +2580,7 @@ bool Client::startMoveResize() Notify::raise(isResize() ? Notify::ResizeStart : Notify::MoveStart); emit clientStartUserMovedResized(this); #ifdef KWIN_BUILD_SCREENEDGES - if (options->electricBorders() == Options::ElectricMoveOnly) + if (workspace()->screenEdge()->isDesktopSwitchingMovingClients()) workspace()->screenEdge()->reserveDesktopSwitching(true, Qt::Vertical|Qt::Horizontal); #endif if (fakeMove) // fix geom_restore position - it HAS to happen at the end, ie. when all moving is set up. inline call will lock focus!! @@ -2656,7 +2662,7 @@ void Client::leaveMoveResize() syncRequest.timeout = NULL; #endif #ifdef KWIN_BUILD_SCREENEDGES - if (options->electricBorders() == Options::ElectricMoveOnly) + if (workspace()->screenEdge()->isDesktopSwitchingMovingClients()) workspace()->screenEdge()->reserveDesktopSwitching(false, Qt::Vertical|Qt::Horizontal); #endif } @@ -3023,7 +3029,7 @@ void Client::handleMoveResize(int x, int y, int x_root, int y_root) if (isMove()) { #ifdef KWIN_BUILD_SCREENEDGES - workspace()->screenEdge()->check(globalPos, xTime()); + workspace()->screenEdge()->check(globalPos, QDateTime::fromMSecsSinceEpoch(xTime())); #endif } } diff --git a/layers.cpp b/layers.cpp index 8d6bf41601..1dab283484 100644 --- a/layers.cpp +++ b/layers.cpp @@ -87,6 +87,9 @@ along with this program. If not, see . #include "effects.h" #include #include "composite.h" +#ifdef KWIN_BUILD_SCREENEDGES +#include "screenedge.h" +#endif namespace KWin { @@ -160,7 +163,7 @@ void Workspace::propagateClients(bool propagate_new_clients) // windows (e.g. popups). newWindowStack << (Window*)supportWindow->winId(); #ifdef KWIN_BUILD_SCREENEDGES - QVectorIterator it(m_screenEdge.windows()); + QVectorIterator it(m_screenEdge->windows()); while (it.hasNext()) { if ((Window)it.next() != None) { newWindowStack << (Window*)⁢ diff --git a/options.cpp b/options.cpp index 41dda403ce..798460f2ac 100644 --- a/options.cpp +++ b/options.cpp @@ -176,18 +176,6 @@ Options::Options(QObject *parent) , CmdAll3(Options::defaultCommandAll3()) , CmdAllWheel(Options::defaultCommandAllWheel()) , CmdAllModKey(Options::defaultKeyCmdAllModKey()) - , electric_border_top(Options::defaultElectricBorderTop()) - , electric_border_top_right(Options::defaultElectricBorderTopRight()) - , electric_border_right(Options::defaultElectricBorderRight()) - , electric_border_bottom_right(Options::defaultElectricBorderBottomRight()) - , electric_border_bottom(Options::defaultElectricBorderBottom()) - , electric_border_bottom_left(Options::defaultElectricBorderBottomLeft()) - , electric_border_left(Options::defaultElectricBorderLeft()) - , electric_border_top_left(Options::defaultElectricBorderTopLeft()) - , electric_borders(Options::defaultElectricBorders()) - , electric_border_delay(Options::defaultElectricBorderDelay()) - , electric_border_cooldown(Options::defaultElectricBorderCooldown()) - , electric_border_pushback_pixels(Options::defaultElectricBorderPushbackPixels()) , electric_border_maximize(Options::defaultElectricBorderMaximize()) , electric_border_tiling(Options::defaultElectricBorderTiling()) , electric_border_corner_ratio(Options::defaultElectricBorderCornerRatio()) @@ -551,33 +539,6 @@ void Options::setCondensedTitle(bool condensedTitle) emit condensedTitleChanged(); } -void Options::setElectricBorderDelay(int electricBorderDelay) -{ - if (electric_border_delay == electricBorderDelay) { - return; - } - electric_border_delay = electricBorderDelay; - emit electricBorderDelayChanged(); -} - -void Options::setElectricBorderCooldown(int electricBorderCooldown) -{ - if (electric_border_cooldown == electricBorderCooldown) { - return; - } - electric_border_cooldown = electricBorderCooldown; - emit electricBorderCooldownChanged(); -} - -void Options::setElectricBorderPushbackPixels(int electricBorderPushbackPixels) -{ - if (electric_border_pushback_pixels == electricBorderPushbackPixels) { - return; - } - electric_border_pushback_pixels = electricBorderPushbackPixels; - emit electricBorderPushbackPixelsChanged(); -} - void Options::setElectricBorderMaximize(bool electricBorderMaximize) { if (electric_border_maximize == electricBorderMaximize) { @@ -803,15 +764,6 @@ void Options::setGlLegacy(bool glLegacy) emit glLegacyChanged(); } -void Options::setElectricBorders(int borders) -{ - if (electric_borders == borders) { - return; - } - electric_borders = borders; - emit electricBordersChanged(); -} - void Options::reparseConfiguration() { KGlobal::config()->reparseConfiguration(); @@ -893,20 +845,6 @@ unsigned long Options::loadConfig() setSnapOnlyWhenOverlapping(config.readEntry("SnapOnlyWhenOverlapping", Options::defaultSnapOnlyWhenOverlapping())); // Electric borders - KConfigGroup borderConfig(_config, "ElectricBorders"); - // TODO: add setters - electric_border_top = electricBorderAction(borderConfig.readEntry("Top", "None")); - electric_border_top_right = electricBorderAction(borderConfig.readEntry("TopRight", "None")); - electric_border_right = electricBorderAction(borderConfig.readEntry("Right", "None")); - electric_border_bottom_right = electricBorderAction(borderConfig.readEntry("BottomRight", "None")); - electric_border_bottom = electricBorderAction(borderConfig.readEntry("Bottom", "None")); - electric_border_bottom_left = electricBorderAction(borderConfig.readEntry("BottomLeft", "None")); - electric_border_left = electricBorderAction(borderConfig.readEntry("Left", "None")); - electric_border_top_left = electricBorderAction(borderConfig.readEntry("TopLeft", "None")); - setElectricBorders(config.readEntry("ElectricBorders", Options::defaultElectricBorders())); - setElectricBorderDelay(config.readEntry("ElectricBorderDelay", Options::defaultElectricBorderDelay())); - setElectricBorderCooldown(config.readEntry("ElectricBorderCooldown", Options::defaultElectricBorderCooldown())); - setElectricBorderPushbackPixels(config.readEntry("ElectricBorderPushbackPixels", Options::defaultElectricBorderPushbackPixels())); setElectricBorderMaximize(config.readEntry("ElectricBorderMaximize", Options::defaultElectricBorderMaximize())); setElectricBorderTiling(config.readEntry("ElectricBorderTiling", Options::defaultElectricBorderTiling())); const float ebr = config.readEntry("ElectricBorderCornerRatio", Options::defaultElectricBorderCornerRatio()); @@ -1052,17 +990,6 @@ void Options::reloadCompositingSettings(bool force) animationSpeed = qBound(0, config.readEntry("AnimationSpeed", Options::defaultAnimationSpeed()), 6); } - -ElectricBorderAction Options::electricBorderAction(const QString& name) -{ - QString lowerName = name.toLower(); - if (lowerName == "dashboard") return ElectricActionDashboard; - else if (lowerName == "showdesktop") return ElectricActionShowDesktop; - else if (lowerName == "lockscreen") return ElectricActionLockScreen; - else if (lowerName == "preventscreenlocking") return ElectricActionPreventScreenLocking; - return ElectricActionNone; -} - // restricted should be true for operations that the user may not be able to repeat // if the window is moved out of the workspace (e.g. if the user moves a window // by the titlebar, and moves it too high beneath Kicker at the top edge, they @@ -1147,47 +1074,6 @@ bool Options::condensedTitle() const return condensed_title; } -ElectricBorderAction Options::electricBorderAction(ElectricBorder edge) const -{ - switch(edge) { - case ElectricTop: - return electric_border_top; - case ElectricTopRight: - return electric_border_top_right; - case ElectricRight: - return electric_border_right; - case ElectricBottomRight: - return electric_border_bottom_right; - case ElectricBottom: - return electric_border_bottom; - case ElectricBottomLeft: - return electric_border_bottom_left; - case ElectricLeft: - return electric_border_left; - case ElectricTopLeft: - return electric_border_top_left; - default: - // fallthrough - break; - } - return ElectricActionNone; -} - -int Options::electricBorders() const -{ - return electric_borders; -} - -int Options::electricBorderDelay() const -{ - return electric_border_delay; -} - -int Options::electricBorderCooldown() const -{ - return electric_border_cooldown; -} - Options::MouseCommand Options::wheelToMouseCommand(MouseWheelCommand com, int delta) const { switch(com) { diff --git a/options.h b/options.h index f458637b7f..f910f55b22 100644 --- a/options.h +++ b/options.h @@ -134,24 +134,6 @@ class Options : public QObject, public KDecorationOptions */ Q_PROPERTY(bool condensedTitle READ condensedTitle WRITE setCondensedTitle NOTIFY condensedTitleChanged) /** - * Whether electric borders are enabled. With electric borders - * you can change desktop by moving the mouse pointer towards the edge - * of the screen - */ - Q_PROPERTY(bool electricBorders READ electricBorders NOTIFY electricBordersChanged) - /** - * the activation delay for electric borders in milliseconds. - */ - Q_PROPERTY(int electricBorderDelay READ electricBorderDelay WRITE setElectricBorderDelay NOTIFY electricBorderDelayChanged) - /** - * the trigger cooldown for electric borders in milliseconds. - */ - Q_PROPERTY(int electricBorderCooldown READ electricBorderCooldown WRITE setElectricBorderCooldown NOTIFY electricBorderCooldownChanged) - /** - * the number of pixels the mouse cursor is pushed back when it reaches the screen edge. - */ - Q_PROPERTY(int electricBorderPushbackPixels READ electricBorderPushbackPixels WRITE setElectricBorderPushbackPixels NOTIFY electricBorderPushbackPixelsChanged) - /** * Whether a window gets maximized when it reaches top screen edge while being moved. */ Q_PROPERTY(bool electricBorderMaximize READ electricBorderMaximize WRITE setElectricBorderMaximize NOTIFY electricBorderMaximizeChanged) @@ -439,7 +421,6 @@ public: return CmdAllModKey; } - static ElectricBorderAction electricBorderAction(const QString& name); static WindowOperation windowOperation(const QString &name, bool restricted); static MouseCommand mouseCommand(const QString &name, bool restricted); static MouseWheelCommand mouseWheelCommand(const QString &name); @@ -454,32 +435,6 @@ public: */ bool condensedTitle() const; - enum { ElectricDisabled = 0, ElectricMoveOnly = 1, ElectricAlways = 2 }; - /** - * @returns The action assigned to the specified electric border - */ - ElectricBorderAction electricBorderAction(ElectricBorder edge) const; - /** - * @returns true if electric borders are enabled. With electric borders - * you can change desktop by moving the mouse pointer towards the edge - * of the screen - */ - int electricBorders() const; - /** - * @returns the activation delay for electric borders in milliseconds. - */ - int electricBorderDelay() const; - /** - * @returns the trigger cooldown for electric borders in milliseconds. - */ - int electricBorderCooldown() const; - /** - * @returns the number of pixels the mouse cursor is pushed back when it - * reaches the screen edge. - */ - int electricBorderPushbackPixels() const { - return electric_border_pushback_pixels; - } /** * @returns true if a window gets maximized when it reaches top screen edge * while being moved. @@ -630,9 +585,6 @@ public: void setKeyCmdAllModKey(uint keyCmdAllModKey); void setShowGeometryTip(bool showGeometryTip); void setCondensedTitle(bool condensedTitle); - void setElectricBorderDelay(int electricBorderDelay); - void setElectricBorderCooldown(int electricBorderCooldown); - void setElectricBorderPushbackPixels(int electricBorderPushbackPixels); void setElectricBorderMaximize(bool electricBorderMaximize); void setElectricBorderTiling(bool electricBorderTiling); void setElectricBorderCornerRatio(float electricBorderCornerRatio); @@ -774,42 +726,6 @@ public: static bool defaultCondensedTitle() { return false; } - static ElectricBorderAction defaultElectricBorderTop() { - return ElectricActionNone; - } - static ElectricBorderAction defaultElectricBorderTopRight() { - return ElectricActionNone; - } - static ElectricBorderAction defaultElectricBorderRight() { - return ElectricActionNone; - } - static ElectricBorderAction defaultElectricBorderBottomRight() { - return ElectricActionNone; - } - static ElectricBorderAction defaultElectricBorderBottom() { - return ElectricActionNone; - } - static ElectricBorderAction defaultElectricBorderBottomLeft() { - return ElectricActionNone; - } - static ElectricBorderAction defaultElectricBorderLeft() { - return ElectricActionNone; - } - static ElectricBorderAction defaultElectricBorderTopLeft() { - return ElectricActionNone; - } - static int defaultElectricBorders() { - return 0; - } - static int defaultElectricBorderDelay() { - return 150; - } - static int defaultElectricBorderCooldown() { - return 350; - } - static int defaultElectricBorderPushbackPixels() { - return 1; - } static bool defaultElectricBorderMaximize() { return true; } @@ -943,10 +859,6 @@ Q_SIGNALS: void keyCmdAllModKeyChanged(); void showGeometryTipChanged(); void condensedTitleChanged(); - void electricBordersChanged(); - void electricBorderDelayChanged(); - void electricBorderCooldownChanged(); - void electricBorderPushbackPixelsChanged(); void electricBorderMaximizeChanged(); void electricBorderTilingChanged(); void electricBorderCornerRatioChanged(); @@ -1041,18 +953,6 @@ private: MouseWheelCommand CmdAllWheel; uint CmdAllModKey; - ElectricBorderAction electric_border_top; - ElectricBorderAction electric_border_top_right; - ElectricBorderAction electric_border_right; - ElectricBorderAction electric_border_bottom_right; - ElectricBorderAction electric_border_bottom; - ElectricBorderAction electric_border_bottom_left; - ElectricBorderAction electric_border_left; - ElectricBorderAction electric_border_top_left; - int electric_borders; - int electric_border_delay; - int electric_border_cooldown; - int electric_border_pushback_pixels; bool electric_border_maximize; bool electric_border_tiling; float electric_border_corner_ratio; diff --git a/screenedge.cpp b/screenedge.cpp index fb6209c199..9524ec26ab 100644 --- a/screenedge.cpp +++ b/screenedge.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2011 Arthur Arlt +Copyright (C) 2013 Martin Gräßlin Since the functionality provided in this class has been moved from class Workspace, it is not clear who exactly has written the code. @@ -32,131 +33,414 @@ along with this program. If not, see . #include "atoms.h" #include "client.h" #include "effects.h" -#include "options.h" #include "utils.h" #include "workspace.h" #include "virtualdesktops.h" - +#include "xcbutils.h" // Qt #include #include #include #include +#include +#include namespace KWin { -ScreenEdge::ScreenEdge() - : QObject(NULL) - , m_screenEdgeWindows(ELECTRIC_COUNT, None) - , m_screenEdgeReserved(ELECTRIC_COUNT, 0) - , m_virtualDesktopSwitching(Options::ElectricDisabled) - , m_virtualDesktopLayout(0) +// Reset timeout +static const int TRESHOLD_RESET = 250; +// Mouse should not move more than this many pixels +static const int DISTANCE_RESET = 30; + +Edge::Edge(ScreenEdges *parent) + : QObject(parent) + , m_edges(parent) + , m_border(ElectricNone) + , m_action(ElectricActionNone) + , m_reserved(0) { } -ScreenEdge::~ScreenEdge() +Edge::~Edge() { } -void ScreenEdge::init() +void Edge::reserve() { - reserveActions(true); - update(); -} - -void ScreenEdge::update(bool force) -{ - m_screenEdgeTimeFirst = xTime(); - m_screenEdgeTimeLast = xTime(); - m_screenEdgeTimeLastTrigger = xTime(); - m_currentScreenEdge = ElectricNone; - QRect r = QRect(0, 0, displayWidth(), displayHeight()); - m_screenEdgeTop = r.top(); - m_screenEdgeBottom = r.bottom(); - m_screenEdgeLeft = r.left(); - m_screenEdgeRight = r.right(); - - for (int pos = 0; pos < ELECTRIC_COUNT; ++pos) { - if (force || m_screenEdgeReserved[pos] == 0) { - if (m_screenEdgeWindows[pos] != None) - XDestroyWindow(display(), m_screenEdgeWindows[pos]); - m_screenEdgeWindows[pos] = None; - } - if (m_screenEdgeReserved[pos] == 0) { - continue; - } - if (m_screenEdgeWindows[pos] != None) - continue; - XSetWindowAttributes attributes; - attributes.override_redirect = True; - attributes.event_mask = EnterWindowMask | LeaveWindowMask; - unsigned long valuemask = CWOverrideRedirect | CWEventMask; - int xywh[ELECTRIC_COUNT][4] = { - { r.left() + 1, r.top(), r.width() - 2, 1 }, // Top - { r.right(), r.top(), 1, 1 }, // Top-right - { r.right(), r.top() + 1, 1, r.height() - 2 }, // Etc. - { r.right(), r.bottom(), 1, 1 }, - { r.left() + 1, r.bottom(), r.width() - 2, 1 }, - { r.left(), r.bottom(), 1, 1 }, - { r.left(), r.top() + 1, 1, r.height() - 2 }, - { r.left(), r.top(), 1, 1 } - }; - m_screenEdgeWindows[pos] = XCreateWindow(display(), rootWindow(), - xywh[pos][0], xywh[pos][1], xywh[pos][2], xywh[pos][3], - 0, CopyFromParent, InputOnly, CopyFromParent, valuemask, &attributes); - XMapWindow(display(), m_screenEdgeWindows[pos]); - - // Set XdndAware on the windows, so that DND enter events are received (#86998) - Atom version = 4; // XDND version - XChangeProperty(display(), m_screenEdgeWindows[pos], atoms->xdnd_aware, XA_ATOM, - 32, PropModeReplace, (unsigned char*)(&version), 1); + m_reserved++; + if (m_reserved == 1) { + // got activated + activate(); } } -void ScreenEdge::restoreSize(ElectricBorder border) +void Edge::unreserve() { - if (m_screenEdgeWindows[border] == None) + m_reserved--; + if (m_reserved == 0) { + // got deactivated + deactivate(); + } +} + +bool Edge::triggersFor(const QPoint &cursorPos) const +{ + if (!m_geometry.contains(cursorPos)) { + return false; + } + if (isLeft() && cursorPos.x() != m_geometry.x()) { + return false; + } + if (isRight() && cursorPos.x() != (m_geometry.x() + m_geometry.width() -1)) { + return false; + } + if (isTop() && cursorPos.y() != m_geometry.y()) { + return false; + } + if (isBottom() && cursorPos.y() != (m_geometry.y() + m_geometry.height() -1)) { + return false; + } + return true; +} + +void Edge::check(const QPoint &cursorPos, const QDateTime &triggerTime, bool forceNoPushBack) +{ + if (!triggersFor(cursorPos)) { return; - QRect r(0, 0, displayWidth(), displayHeight()); - int xywh[ELECTRIC_COUNT][4] = { - { r.left() + 1, r.top(), r.width() - 2, 1 }, // Top - { r.right(), r.top(), 1, 1 }, // Top-right - { r.right(), r.top() + 1, 1, r.height() - 2 }, // Etc. - { r.right(), r.bottom(), 1, 1 }, - { r.left() + 1, r.bottom(), r.width() - 2, 1 }, - { r.left(), r.bottom(), 1, 1 }, - { r.left(), r.top() + 1, 1, r.height() - 2 }, - { r.left(), r.top(), 1, 1 } + } + // no pushback so we have to activate at once + bool directActivate = forceNoPushBack || edges()->cursorPushBackDistance().isNull(); + if (directActivate || canActivate(cursorPos, triggerTime)) { + m_lastTrigger = triggerTime; + m_lastReset = triggerTime; + handle(cursorPos); + } else { + pushCursorBack(cursorPos); + } + m_triggeredPoint = cursorPos; +} + +bool Edge::canActivate(const QPoint &cursorPos, const QDateTime &triggerTime) +{ + if (m_lastReset.msecsTo(triggerTime) > TRESHOLD_RESET) { + m_lastReset = triggerTime; + return false; + } + if (m_lastTrigger.msecsTo(triggerTime) < edges()->reActivationThreshold()) { + return false; + } + if (m_lastReset.msecsTo(triggerTime) < edges()->timeThreshold()) { + return false; + } + // does the check on position make any sense at all? + if ((cursorPos - m_triggeredPoint).manhattanLength() > DISTANCE_RESET) { + return false; + } + return true; +} + +void Edge::handle(const QPoint &cursorPos) +{ + if ((edges()->isDesktopSwitchingMovingClients() && Workspace::self()->getMovingClient()) || + (edges()->isDesktopSwitching() && isScreenEdge())) { + // always switch desktops in case: + // moving a Client and option for switch on client move is enabled + // or switch on screen edge is enabled + switchDesktop(cursorPos); + return; + } + if (handleAction() || handleByEffects()) { + pushCursorBack(cursorPos); + return; + } + if (edges()->isDesktopSwitching() && isCorner()) { + // try again desktop switching for the corner + switchDesktop(cursorPos); + return; + } + // fallback notify world that edge got activated + emit activated(m_border); +} + +bool Edge::handleAction() +{ + switch (m_action) { + case ElectricActionDashboard: { // Display Plasma dashboard + QDBusInterface plasmaApp("org.kde.plasma-desktop", "/App"); + plasmaApp.asyncCall("toggleDashboard"); + return true; + } + case ElectricActionShowDesktop: { + Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); + return true; + } + case ElectricActionLockScreen: { // Lock the screen + QDBusInterface screenSaver("org.kde.screensaver", "/ScreenSaver"); + screenSaver.asyncCall("Lock"); + return true; + } + default: + return false; + } +} + +bool Edge::handleByEffects() +{ + if (!effects) { + return false; + } + return static_cast(effects)->borderActivated(m_border); +} + +void Edge::switchDesktop(const QPoint &cursorPos) +{ + QPoint pos(cursorPos); + VirtualDesktopManager *vds = VirtualDesktopManager::self(); + uint desktop = vds->current(); + const uint oldDesktop = vds->current(); + const int OFFSET = 2; + if (isLeft()) { + desktop = vds->toLeft(desktop, vds->isNavigationWrappingAround()); + pos.setX(displayWidth() - 1 - OFFSET); + } + if (isRight()) { + desktop = vds->toRight(desktop, vds->isNavigationWrappingAround()); + pos.setX(OFFSET); + } + if (isTop()) { + desktop = vds->above(desktop, vds->isNavigationWrappingAround()); + pos.setY(displayHeight() - 1 - OFFSET); + } + if (isBottom()) { + desktop = vds->below(desktop, vds->isNavigationWrappingAround()); + pos.setY(OFFSET); + } + if (Client *c = Workspace::self()->getMovingClient()) { + if (c->rules()->checkDesktop(desktop) != int(desktop)) { + // user attempts to move a client to another desktop where it is ruleforced to not be + return; + } + } + vds->setCurrent(desktop); + if (vds->current() != oldDesktop) { + QCursor::setPos(pos); + } +} + +void Edge::pushCursorBack(const QPoint &cursorPos) +{ + int x = cursorPos.x(); + int y = cursorPos.y(); + const QSize &distance = edges()->cursorPushBackDistance(); + if (isLeft()) { + x += distance.width(); + } + if (isRight()) { + x -= distance.width(); + } + if (isTop()) { + y += distance.height(); + } + if (isBottom()) { + y -= distance.height(); + } + QCursor::setPos(x, y); +} + +void Edge::doGeometryUpdate() +{ +} + +void Edge::activate() +{ +} + +void Edge::deactivate() +{ +} + +/********************************************************** + * ScreenEdges + *********************************************************/ +WindowBasedEdge::WindowBasedEdge(ScreenEdges *parent) + : Edge(parent) + , m_window(XCB_WINDOW_NONE) +{ +} + +WindowBasedEdge::~WindowBasedEdge() +{ + destroyWindow(); +} + +void WindowBasedEdge::activate() +{ + createWindow(); +} + +void WindowBasedEdge::deactivate() +{ + destroyWindow(); +} + +void WindowBasedEdge::createWindow() +{ + if (m_window != XCB_WINDOW_NONE) { + return; + } + const uint32_t mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; + const uint32_t values[] = { + true, + XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW }; - XMoveResizeWindow(display(), m_screenEdgeWindows[border], - xywh[border][0], xywh[border][1], xywh[border][2], xywh[border][3]); + m_window = Xcb::createInputWindow(geometry(), mask, values); + xcb_map_window(connection(), m_window); + // Set XdndAware on the windows, so that DND enter events are received (#86998) + xcb_atom_t version = 4; // XDND version + xcb_change_property(connection(), XCB_PROP_MODE_REPLACE, m_window, + atoms->xdnd_aware, XCB_ATOM_ATOM, 32, 1, (unsigned char*)(&version)); } -void ScreenEdge::reconfigureVirtualDesktopSwitching() +void WindowBasedEdge::destroyWindow() { - const int newMode = options->electricBorders(); - if (m_virtualDesktopSwitching == newMode) { + if (m_window != XCB_WINDOW_NONE) { + xcb_destroy_window(connection(), m_window); + m_window = XCB_WINDOW_NONE; + } +} + +void WindowBasedEdge::doGeometryUpdate() +{ + Xcb::moveResizeWindow(m_window, geometry()); +} + +/********************************************************** + * ScreenEdges + *********************************************************/ +ScreenEdges::ScreenEdges(QObject *parent) + : QObject(parent) + , m_desktopSwitching(false) + , m_desktopSwitchingMovingClients(false) + , m_timeThreshold(0) + , m_reactivateThreshold(0) + , m_virtualDesktopLayout(0) + , m_actionTopLeft(ElectricActionNone) + , m_actionTop(ElectricActionNone) + , m_actionTopRight(ElectricActionNone) + , m_actionRight(ElectricActionNone) + , m_actionBottomRight(ElectricActionNone) + , m_actionBottom(ElectricActionNone) + , m_actionBottomLeft(ElectricActionNone) + , m_actionLeft(ElectricActionNone) +{ + m_externalReservations.insert(ElectricTopLeft, 0); + m_externalReservations.insert(ElectricTop, 0); + m_externalReservations.insert(ElectricTopRight, 0); + m_externalReservations.insert(ElectricRight, 0); + m_externalReservations.insert(ElectricBottomRight, 0); + m_externalReservations.insert(ElectricBottom, 0); + m_externalReservations.insert(ElectricBottomLeft, 0); + m_externalReservations.insert(ElectricLeft, 0); +} + +ScreenEdges::~ScreenEdges() +{ +} + +void ScreenEdges::init() +{ + reconfigure(); + updateLayout(); + recreateEdges(); +} +static ElectricBorderAction electricBorderAction(const QString& name) +{ + QString lowerName = name.toLower(); + if (lowerName == "dashboard") { + return ElectricActionDashboard; + } else if (lowerName == "showdesktop") { + return ElectricActionShowDesktop; + } else if (lowerName == "lockscreen") { + return ElectricActionLockScreen; + } else if (lowerName == "preventscreenlocking") { + return ElectricActionPreventScreenLocking; + } + return ElectricActionNone; +} + +void ScreenEdges::reconfigure() +{ + if (!m_config) { return; } - if (m_virtualDesktopSwitching == Options::ElectricAlways) { - reserveDesktopSwitching(false, m_virtualDesktopLayout); + // TODO: migrate settings to a group ScreenEdges + KConfigGroup windowsConfig = m_config->group("Windows"); + setTimeThreshold(windowsConfig.readEntry("ElectricBorderDelay", 150)); + setReActivationThreshold(windowsConfig.readEntry("ElectricBorderCooldown", 350)); + int desktopSwitching = windowsConfig.readEntry("ElectricBorders", static_cast(ElectricDisabled)); + if (desktopSwitching == ElectricDisabled) { + setDesktopSwitching(false); + setDesktopSwitchingMovingClients(false); + } else if (desktopSwitching == ElectricMoveOnly) { + setDesktopSwitching(false); + setDesktopSwitchingMovingClients(true); + } else if (desktopSwitching == ElectricAlways) { + setDesktopSwitching(true); + setDesktopSwitchingMovingClients(true); } - m_virtualDesktopSwitching = newMode; - if (m_virtualDesktopSwitching == Options::ElectricAlways) { - reserveDesktopSwitching(true, m_virtualDesktopLayout); - } - update(); + const int pushBack = windowsConfig.readEntry("ElectricBorderPushbackPixels", 1); + m_cursorPushBackDistance = QSize(pushBack, pushBack); + + KConfigGroup borderConfig = m_config->group("ElectricBorders"); + setActionForBorder(ElectricTopLeft, &m_actionTopLeft, + electricBorderAction(borderConfig.readEntry("TopLeft", "None"))); + setActionForBorder(ElectricTop, &m_actionTop, + electricBorderAction(borderConfig.readEntry("Top", "None"))); + setActionForBorder(ElectricTopRight, &m_actionTopRight, + electricBorderAction(borderConfig.readEntry("TopRight", "None"))); + setActionForBorder(ElectricRight, &m_actionRight, + electricBorderAction(borderConfig.readEntry("Right", "None"))); + setActionForBorder(ElectricBottomRight, &m_actionBottomRight, + electricBorderAction(borderConfig.readEntry("BottomRight", "None"))); + setActionForBorder(ElectricBottom, &m_actionBottom, + electricBorderAction(borderConfig.readEntry("Bottom", "None"))); + setActionForBorder(ElectricBottomLeft, &m_actionBottomLeft, + electricBorderAction(borderConfig.readEntry("BottomLeft", "None"))); + setActionForBorder(ElectricLeft, &m_actionLeft, + electricBorderAction(borderConfig.readEntry("Left", "None"))); } -void ScreenEdge::reconfigure() +void ScreenEdges::setActionForBorder(ElectricBorder border, ElectricBorderAction *oldValue, ElectricBorderAction newValue) { - reserveActions(true); - reconfigureVirtualDesktopSwitching(); - updateLayout(); - update(); + if (*oldValue == newValue) { + return; + } + if (*oldValue == ElectricActionNone) { + // have to reserve + for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { + if ((*it)->border() == border) { + (*it)->reserve(); + } + } + } + if (newValue == ElectricActionNone) { + // have to unreserve + for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { + if ((*it)->border() == border) { + (*it)->unreserve(); + } + } + } + *oldValue = newValue; + // update action on all Edges for given border + for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { + if ((*it)->border() == border) { + (*it)->setAction(newValue); + } + } } -void ScreenEdge::updateLayout() +void ScreenEdges::updateLayout() { const QSize desktopMatrix = VirtualDesktopManager::self()->grid().size(); Qt::Orientations newLayout = 0; @@ -169,283 +453,332 @@ void ScreenEdge::updateLayout() if (newLayout == m_virtualDesktopLayout) { return; } - if (m_virtualDesktopSwitching == Options::ElectricAlways) { + if (isDesktopSwitching()) { reserveDesktopSwitching(false, m_virtualDesktopLayout); } m_virtualDesktopLayout = newLayout; - if (m_virtualDesktopSwitching == Options::ElectricAlways) { + if (isDesktopSwitching()) { reserveDesktopSwitching(true, m_virtualDesktopLayout); } } -void ScreenEdge::reserveActions(bool isToReserve) +static bool isLeftScreen(const QRect &screen, const QRect &fullArea) { - for (int pos = 0; pos < ELECTRIC_COUNT; ++pos) - if (options->electricBorderAction(static_cast(pos))) { - if (isToReserve) - reserve(static_cast(pos)); - else - unreserve(static_cast(pos)); + const QDesktopWidget *desktop = QApplication::desktop(); + if (desktop->screenCount() == 1) { + return true; + } + if (screen.x() == fullArea.x()) { + return true; + } + // the screen is also on the left in case of a vertical layout with a second screen + // more to the left. In that case no screen ends left of screen's x coord + for (int i=0; iscreenCount(); ++i) { + const QRect otherGeo = desktop->screenGeometry(i); + if (otherGeo == screen) { + // that's our screen to test + continue; } + if (otherGeo.x() + otherGeo.width() <= screen.x()) { + // other screen is completely in the left + return false; + } + } + // did not find a screen left of our current screen, so it is the left most + return true; } -void ScreenEdge::reserveDesktopSwitching(bool isToReserve, Qt::Orientations o) +static bool isRightScreen(const QRect &screen, const QRect &fullArea) +{ + const QDesktopWidget *desktop = QApplication::desktop(); + if (desktop->screenCount() == 1) { + return true; + } + if (screen.x() + screen.width() == fullArea.x() + fullArea.width()) { + return true; + } + // the screen is also on the right in case of a vertical layout with a second screen + // more to the right. In that case no screen starts right of this screen + for (int i=0; iscreenCount(); ++i) { + const QRect otherGeo = desktop->screenGeometry(i); + if (otherGeo == screen) { + // that's our screen to test + continue; + } + if (otherGeo.x() >= screen.x() + screen.width()) { + // other screen is completely in the right + return false; + } + } + // did not find a screen right of our current screen, so it is the right most + return true; +} + +static bool isTopScreen(const QRect &screen, const QRect &fullArea) +{ + const QDesktopWidget *desktop = QApplication::desktop(); + if (desktop->screenCount() == 1) { + return true; + } + if (screen.y() == fullArea.y()) { + return true; + } + // the screen is also top most in case of a horizontal layout with a second screen + // more to the top. In that case no screen ends above screen's y coord + for (int i=0; iscreenCount(); ++i) { + const QRect otherGeo = desktop->screenGeometry(i); + if (otherGeo == screen) { + // that's our screen to test + continue; + } + if (otherGeo.y() + otherGeo.height() <= screen.y()) { + // other screen is completely above + return false; + } + } + // did not find a screen above our current screen, so it is the top most + return true; +} + +static bool isBottomScreen(const QRect &screen, const QRect &fullArea) +{ + const QDesktopWidget *desktop = QApplication::desktop(); + if (desktop->screenCount() == 1) { + return true; + } + if (screen.y() + screen.height() == fullArea.y() + fullArea.height()) { + return true; + } + // the screen is also bottom most in case of a horizontal layout with a second screen + // more below. In that case no screen starts below screen's y coord + height + for (int i=0; iscreenCount(); ++i) { + const QRect otherGeo = desktop->screenGeometry(i); + if (otherGeo == screen) { + // that's our screen to test + continue; + } + if (otherGeo.y() >= screen.y() + screen.height()) { + // other screen is completely below + return false; + } + } + // did not find a screen below our current screen, so it is the bottom most + return true; +} + +void ScreenEdges::recreateEdges() +{ + qDeleteAll(m_edges); + m_edges.clear(); + const QRect fullArea(0, 0, displayWidth(), displayHeight()); + const QDesktopWidget *desktop = QApplication::desktop(); + for (int i=0; iscreenCount(); ++i) { + const QRect screen = desktop->screenGeometry(i); + if (isLeftScreen(screen, fullArea)) { + // left most screen + createVerticalEdge(ElectricLeft, screen, fullArea); + } + if (isRightScreen(screen, fullArea)) { + // right most screen + createVerticalEdge(ElectricRight, screen, fullArea); + } + if (isTopScreen(screen, fullArea)) { + // top most screen + createHorizontalEdge(ElectricTop, screen, fullArea); + } + if (isBottomScreen(screen, fullArea)) { + // bottom most screen + createHorizontalEdge(ElectricBottom, screen, fullArea); + } + } +} + +void ScreenEdges::createVerticalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea) +{ + if (border != ElectricRight && border != KWin::ElectricLeft) { + return; + } + int y = screen.y(); + int height = screen.height(); + const int x = (border == ElectricLeft) ? screen.x() : screen.x() + screen.width() -1; + if (isTopScreen(screen, fullArea)) { + // also top most screen + height--; + y++; + // create top left/right edge + const ElectricBorder edge = (border == ElectricLeft) ? ElectricTopLeft : ElectricTopRight; + m_edges << createEdge(edge, x, screen.y(), 1, 1); + } + if (isBottomScreen(screen, fullArea)) { + // also bottom most screen + height--; + // create bottom left/right edge + const ElectricBorder edge = (border == ElectricLeft) ? ElectricBottomLeft : ElectricBottomRight; + m_edges << createEdge(edge, x, screen.y() + screen.height() -1, 1, 1); + } + // create border + m_edges << createEdge(border, x, y, 1, height); +} + +void ScreenEdges::createHorizontalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea) +{ + if (border != ElectricTop && border != ElectricBottom) { + return; + } + int x = screen.x(); + int width = screen.width(); + if (isLeftScreen(screen, fullArea)) { + // also left most - adjust only x and width + x++; + width--; + } + if (isRightScreen(screen, fullArea)) { + // also right most edge + width--; + } + const int y = (border == ElectricTop) ? screen.y() : screen.y() + screen.height() - 1; + m_edges << createEdge(border, x, y, width, 1); +} + +WindowBasedEdge *ScreenEdges::createEdge(ElectricBorder border, int x, int y, int width, int height) +{ + WindowBasedEdge *edge = new WindowBasedEdge(this); + edge->setBorder(border); + edge->setGeometry(QRect(x, y, width, height)); + const ElectricBorderAction action = actionForEdge(edge); + if (action != KWin::ElectricActionNone) { + edge->reserve(); + edge->setAction(action); + } + if (isDesktopSwitching()) { + if (edge->isCorner()) { + edge->reserve(); + } else { + if ((m_virtualDesktopLayout & Qt::Horizontal) && (edge->isLeft() || edge->isRight())) { + edge->reserve(); + } + if ((m_virtualDesktopLayout & Qt::Vertical) && (edge->isTop() || edge->isBottom())) { + edge->reserve(); + } + } + } + QHash::const_iterator it = m_externalReservations.constFind(border); + if (it != m_externalReservations.constEnd()) { + for (int i=0; ireserve(); + } + } + connect(edge, SIGNAL(activated(ElectricBorder)), SIGNAL(activated(ElectricBorder))); + return edge; +} + +ElectricBorderAction ScreenEdges::actionForEdge(Edge *edge) const +{ + switch (edge->border()) { + case ElectricTopLeft: + return m_actionTopLeft; + case ElectricTop: + return m_actionTop; + case ElectricTopRight: + return m_actionTopRight; + case ElectricRight: + return m_actionRight; + case ElectricBottomRight: + return m_actionBottomRight; + case ElectricBottom: + return m_actionBottom; + case ElectricBottomLeft: + return m_actionBottomLeft; + case ElectricLeft: + return m_actionLeft; + default: + // fall through + break; + } + return ElectricActionNone; +} + +void ScreenEdges::reserveDesktopSwitching(bool isToReserve, Qt::Orientations o) { if (!o) return; - if (isToReserve) { - reserve(ElectricTopLeft); - reserve(ElectricTopRight); - reserve(ElectricBottomRight); - reserve(ElectricBottomLeft); - - if (o & Qt::Horizontal) { - reserve(ElectricLeft); - reserve(ElectricRight); - } - if (o & Qt::Vertical) { - reserve(ElectricTop); - reserve(ElectricBottom); - } - } else { - unreserve(ElectricTopLeft); - unreserve(ElectricTopRight); - unreserve(ElectricBottomRight); - unreserve(ElectricBottomLeft); - - if (o & Qt::Horizontal) { - unreserve(ElectricLeft); - unreserve(ElectricRight); - } - if (o & Qt::Vertical) { - unreserve(ElectricTop); - unreserve(ElectricBottom); - } - } -} - -void ScreenEdge::reserve(ElectricBorder border) -{ - if (border == ElectricNone) - return; - if (m_screenEdgeReserved[border]++ == 0) - QTimer::singleShot(0, this, SLOT(update())); -} - -void ScreenEdge::unreserve(ElectricBorder border) -{ - if (border == ElectricNone) - return; - assert(m_screenEdgeReserved[border] > 0); - if (--m_screenEdgeReserved[border] == 0) - QTimer::singleShot(0, this, SLOT(update())); -} - -void ScreenEdge::check(const QPoint& pos, Time now, bool forceNoPushback) -{ - if ((pos.x() != m_screenEdgeLeft) && - (pos.x() != m_screenEdgeRight) && - (pos.y() != m_screenEdgeTop) && - (pos.y() != m_screenEdgeBottom)) - return; - - bool have_borders = false; - for (int i = 0; i < ELECTRIC_COUNT; ++i) - if (m_screenEdgeWindows[i] != None) - have_borders = true; - if (!have_borders) - return; - - Time treshold_set = options->electricBorderDelay(); // Set timeout - Time treshold_reset = 250; // Reset timeout - Time treshold_trigger = options->electricBorderCooldown(); // Minimum time between triggers - int distance_reset = 30; // Mouse should not move more than this many pixels - int pushback_pixels = forceNoPushback ? 0 : options->electricBorderPushbackPixels(); - - ElectricBorder border; - if (pos.x() == m_screenEdgeLeft && pos.y() == m_screenEdgeTop) - border = ElectricTopLeft; - else if (pos.x() == m_screenEdgeRight && pos.y() == m_screenEdgeTop) - border = ElectricTopRight; - else if (pos.x() == m_screenEdgeLeft && pos.y() == m_screenEdgeBottom) - border = ElectricBottomLeft; - else if (pos.x() == m_screenEdgeRight && pos.y() == m_screenEdgeBottom) - border = ElectricBottomRight; - else if (pos.x() == m_screenEdgeLeft) - border = ElectricLeft; - else if (pos.x() == m_screenEdgeRight) - border = ElectricRight; - else if (pos.y() == m_screenEdgeTop) - border = ElectricTop; - else if (pos.y() == m_screenEdgeBottom) - border = ElectricBottom; - else - abort(); - - if (m_screenEdgeWindows[border] == None) - return; - - if (pushback_pixels == 0) { - // no pushback so we have to activate at once - m_screenEdgeTimeLast = now; - m_currentScreenEdge = border; - m_screenEdgePushPoint = pos; - } - - if ((m_currentScreenEdge == border) && - (timestampDiff(m_screenEdgeTimeLast, now) < treshold_reset) && - (timestampDiff(m_screenEdgeTimeLastTrigger, now) > treshold_trigger) && - ((pos - m_screenEdgePushPoint).manhattanLength() < distance_reset)) { - m_screenEdgeTimeLast = now; - - if (timestampDiff(m_screenEdgeTimeFirst, now) > treshold_set) { - m_currentScreenEdge = ElectricNone; - m_screenEdgeTimeLastTrigger = now; - if (Workspace::self()->getMovingClient()) { - // If moving a client or have force doing the desktop switch - if (m_virtualDesktopSwitching != Options::ElectricDisabled) - switchDesktop(border, pos); - return; // Don't reset cursor position - } else { - if (m_virtualDesktopSwitching == Options::ElectricAlways && - (border == ElectricTop || border == ElectricRight || - border == ElectricBottom || border == ElectricLeft)) { - // If desktop switching is always enabled don't apply it to the corners if - // an effect is applied to it (We will check that later). - switchDesktop(border, pos); - return; // Don't reset cursor position - } - switch(options->electricBorderAction(border)) { - case ElectricActionDashboard: { // Display Plasma dashboard - QDBusInterface plasmaApp("org.kde.plasma-desktop", "/App"); - plasmaApp.call("toggleDashboard"); - } - break; - case ElectricActionShowDesktop: { - Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop()); - break; - } - case ElectricActionLockScreen: { // Lock the screen - QDBusInterface screenSaver("org.kde.screensaver", "/ScreenSaver"); - screenSaver.call("Lock"); - } - break; - case ElectricActionPreventScreenLocking: { - break; - } - case ElectricActionNone: // Either desktop switching or an effect - default: { - if (effects && static_cast(effects)->borderActivated(border)) - {} // Handled by effects - else { - if (m_virtualDesktopSwitching == Options::ElectricAlways) { - switchDesktop(border, pos); - return; // Don't reset cursor position - } - emit activated(border); - } - } - } + for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { + WindowBasedEdge *edge = *it; + if (edge->isCorner()) { + isToReserve ? edge->reserve() : edge->unreserve(); + } else { + if ((m_virtualDesktopLayout & Qt::Horizontal) && (edge->isLeft() || edge->isRight())) { + isToReserve ? edge->reserve() : edge->unreserve(); + } + if ((m_virtualDesktopLayout & Qt::Vertical) && (edge->isTop() || edge->isBottom())) { + isToReserve ? edge->reserve() : edge->unreserve(); } } - } else { - m_currentScreenEdge = border; - m_screenEdgeTimeFirst = now; - m_screenEdgeTimeLast = now; - m_screenEdgePushPoint = pos; } - - // Reset the pointer to find out whether the user is really pushing - // (the direction back from which it came, starting from top clockwise) - const int xdiff[ELECTRIC_COUNT] = { 0, - -pushback_pixels, - -pushback_pixels, - -pushback_pixels, - 0, - pushback_pixels, - pushback_pixels, - pushback_pixels - }; - const int ydiff[ELECTRIC_COUNT] = { pushback_pixels, - pushback_pixels, - 0, - -pushback_pixels, - -pushback_pixels, - -pushback_pixels, - 0, - pushback_pixels - }; - QCursor::setPos(pos.x() + xdiff[border], pos.y() + ydiff[border]); } -void ScreenEdge::switchDesktop(ElectricBorder border, const QPoint& _pos) +void ScreenEdges::reserve(ElectricBorder border) { - QPoint pos = _pos; - VirtualDesktopManager *vds = VirtualDesktopManager::self(); - int desk = vds->current(); - const int OFFSET = 2; - if (border == ElectricLeft || border == ElectricTopLeft || border == ElectricBottomLeft) { - desk = vds->toLeft(desk, options->isRollOverDesktops()); - pos.setX(displayWidth() - 1 - OFFSET); + QHash::iterator it = m_externalReservations.find(border); + if (it != m_externalReservations.end()) { + m_externalReservations.insert(border, it.value() + 1); } - if (border == ElectricRight || border == ElectricTopRight || border == ElectricBottomRight) { - desk = vds->toRight(desk, options->isRollOverDesktops()); - pos.setX(OFFSET); + for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { + if ((*it)->border() == border) { + (*it)->reserve(); + } } - if (border == ElectricTop || border == ElectricTopLeft || border == ElectricTopRight) { - desk = vds->above(desk, options->isRollOverDesktops()); - pos.setY(displayHeight() - 1 - OFFSET); - } - if (border == ElectricBottom || border == ElectricBottomLeft || border == ElectricBottomRight) { - desk = vds->below(desk, options->isRollOverDesktops()); - pos.setY(OFFSET); - } - Client *c = Workspace::self()->getMovingClient(); - if (c && c->rules()->checkDesktop(desk) != desk) - return; // user attempts to move a client to another desktop where it is ruleforced to not be - const uint desk_before = vds->current(); - vds->setCurrent(desk); - if (vds->current() != desk_before) - QCursor::setPos(pos); } -bool ScreenEdge::isEntered(XEvent* e) +void ScreenEdges::unreserve(ElectricBorder border) +{ + QHash::iterator it = m_externalReservations.find(border); + if (it != m_externalReservations.end()) { + m_externalReservations.insert(border, it.value() - 1); + } + for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { + if ((*it)->border() == border) { + (*it)->unreserve(); + } + } +} + +void ScreenEdges::check(const QPoint &pos, const QDateTime &now, bool forceNoPushBack) +{ + for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { + (*it)->check(pos, now, forceNoPushBack); + } +} + +bool ScreenEdges::isEntered(XEvent* e) { if (e->type == EnterNotify) { - for (int i = 0; i < ELECTRIC_COUNT; ++i) - if (m_screenEdgeWindows[i] != None && e->xcrossing.window == m_screenEdgeWindows[i]) { - // The user entered an electric border - check(QPoint(e->xcrossing.x_root, e->xcrossing.y_root), e->xcrossing.time); + for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { + WindowBasedEdge *edge = *it; + if (edge->isReserved() && edge->window() == e->xcrossing.window) { + edge->check(QPoint(e->xcrossing.x_root, e->xcrossing.y_root), QDateTime::fromMSecsSinceEpoch(e->xcrossing.time)); return true; } + } } if (e->type == ClientMessage) { if (e->xclient.message_type == atoms->xdnd_position) { - for (int i = 0; i < ELECTRIC_COUNT; ++i) - if (m_screenEdgeWindows[i] != None && e->xclient.window == m_screenEdgeWindows[i]) { + for (QList::iterator it = m_edges.begin(); it != m_edges.end(); ++it) { + WindowBasedEdge *edge = *it; + if (edge->isReserved() && edge->window() == e->xclient.window) { updateXTime(); - check(QPoint(e->xclient.data.l[2] >> 16, e->xclient.data.l[2] & 0xffff), xTime(), true); + edge->check(QPoint(e->xclient.data.l[2] >> 16, e->xclient.data.l[2] & 0xffff), QDateTime::fromMSecsSinceEpoch(xTime()), true); return true; } + } } } return false; } -void ScreenEdge::ensureOnTop() +void ScreenEdges::ensureOnTop() { - Window* windows = new Window[ 8 ]; // There are up to 8 borders - int pos = 0; - for (int i = 0; i < ELECTRIC_COUNT; ++i) - if (m_screenEdgeWindows[ i ] != None) - windows[ pos++ ] = m_screenEdgeWindows[ i ]; - if (!pos) { - delete [] windows; - return; // No borders at all - } - XRaiseWindow(display(), windows[ 0 ]); - XRestackWindows(display(), windows, pos); - delete [] windows; + Xcb::restackWindowsWithRaise(windows()); } /* @@ -462,45 +795,68 @@ void ScreenEdge::ensureOnTop() * (which raised our electric borders) */ -void ScreenEdge::raisePanelProxies() +void ScreenEdges::raisePanelProxies() { - XWindowAttributes attr; - Window dummy; - Window* windows = NULL; - unsigned int count = 0; + QVector ownWindows = windows(); + Xcb::Tree tree(rootWindow()); + QVector attributes(tree->children_len); + QVector geometries(tree->children_len); + + Xcb::WindowId *windows = tree.children(); QRect screen = QRect(0, 0, displayWidth(), displayHeight()); - QVector proxies; - XQueryTree(display(), rootWindow(), &dummy, &dummy, &windows, &count); - for (unsigned int i = 0; i < count; ++i) { - if (m_screenEdgeWindows.contains(windows[i])) + QVector proxies; + + int count = 0; + for (unsigned int i = 0; i < tree->children_len; ++i) { + if (ownWindows.contains(windows[i])) { + // one of our screen edges continue; - if (XGetWindowAttributes(display(), windows[i], &attr)) { - if (attr.map_state == IsUnmapped) // a thousand Qt group leader dummies ... - continue; - const QRect geo(attr.x, attr.y, attr.width, attr.height); - if (geo.width() < 1 || geo.height() < 1) - continue; - if (!(geo.width() > 1 || geo.height() > 1)) - continue; // random 1x1 dummy windows, all your corners are belong to us >-) - if (attr.c_class != InputOnly && (geo.width() > 3 && geo.height() > 3)) - continue; - if (geo.x() != screen.x() && geo.right() != screen.right() && - geo.y() != screen.y() && geo.bottom() != screen.bottom()) - continue; - proxies << windows[i]; + } + attributes[count] = Xcb::WindowAttributes(windows[i]); + geometries[count] = Xcb::WindowGeometry(windows[i]); + count++; + } + + for (int i=0; imap_state == XCB_MAP_STATE_UNMAPPED) { + continue; + } + Xcb::WindowGeometry geometry(geometries.at(i)); + if (geometry.isNull()) { + continue; + } + const QRect geo(geometry.rect()); + if (geo.width() < 1 || geo.height() < 1) { + continue; + } + if (!(geo.width() > 1 || geo.height() > 1)) { + continue; // random 1x1 dummy windows, all your corners are belong to us >-) + } + if (attr->_class != XCB_WINDOW_CLASS_INPUT_ONLY && (geo.width() > 3 && geo.height() > 3)) { + continue; + } + if (geo.x() != screen.x() && geo.right() != screen.right() && + geo.y() != screen.y() && geo.bottom() != screen.bottom()) { + continue; + } + proxies << attr.window(); + } + Xcb::restackWindowsWithRaise(proxies); +} + +QVector< xcb_window_t > ScreenEdges::windows() const +{ + QVector wins; + for (QList::const_iterator it = m_edges.constBegin(); + it != m_edges.constEnd(); + ++it) { + xcb_window_t w = (*it)->window(); + if (w != XCB_WINDOW_NONE) { + wins.append(w); } } - if (!proxies.isEmpty()) { - XRaiseWindow(display(), proxies.data()[ 0 ]); - XRestackWindows(display(), proxies.data(), proxies.count()); - } - if (windows) - XFree(windows); + return wins; } -const QVector< Window >& ScreenEdge::windows() -{ - return m_screenEdgeWindows; -} } //namespace - diff --git a/screenedge.h b/screenedge.h index 6dd1956332..bd0c69cbfb 100644 --- a/screenedge.h +++ b/screenedge.h @@ -3,6 +3,7 @@ This file is part of the KDE project. Copyright (C) 2011 Arthur Arlt +Copyright (C) 2013 Martin Gräßlin Since the functionality provided in this class has been moved from class Workspace, it is not clear who exactly has written the code. @@ -28,29 +29,162 @@ along with this program. If not, see . #ifndef KWIN_SCREENEDGE_H #define KWIN_SCREENEDGE_H +// KWin +#include "kwinglobals.h" +// KDE includes +#include +// Qt #include #include -#include "kwinglobals.h" - +#include namespace KWin { -/** - * @short This class is used to handle the screen edges - * Screen Edge Window management. Screen Edges allow a user to change the virtual - * desktop or activate other features by moving the mouse pointer to the borders or - * corners of the screen. Technically this is done with input only windows. - * - * @author Arthur Arlt - * @since 4.8 - */ -class ScreenEdge : public QObject { +class ScreenEdges; + +class Edge : public QObject +{ Q_OBJECT public: - ScreenEdge(); - ~ScreenEdge(); + explicit Edge(ScreenEdges *parent); + virtual ~Edge(); + bool isLeft() const; + bool isTop() const; + bool isRight() const; + bool isBottom() const; + bool isCorner() const; + bool isScreenEdge() const; + bool triggersFor(const QPoint &cursorPos) const; + void check(const QPoint &cursorPos, const QDateTime &triggerTime, bool forceNoPushBack = false); + bool isReserved() const; + + ElectricBorder border() const; + +public Q_SLOTS: + void reserve(); + void unreserve(); + void setBorder(ElectricBorder border); + void setAction(ElectricBorderAction action); + void setGeometry(const QRect &geometry); +Q_SIGNALS: + /** + * Emitted when the @p border got activated and there is neither an effect nor a global + * action configured for this @p border. + * @param border The border which got activated + **/ + void activated(ElectricBorder border); +protected: + ScreenEdges *edges(); + const ScreenEdges *edges() const; + const QRect &geometry() const; + virtual void doGeometryUpdate(); + virtual void activate(); + virtual void deactivate(); +private: + bool canActivate(const QPoint &cursorPos, const QDateTime &triggerTime); + void handle(const QPoint &cursorPos); + bool handleAction(); + bool handleByEffects(); + void switchDesktop(const QPoint &cursorPos); + void pushCursorBack(const QPoint &cursorPos); + ScreenEdges *m_edges; + ElectricBorder m_border; + ElectricBorderAction m_action; + int m_reserved; + QRect m_geometry; + QDateTime m_lastTrigger; + QDateTime m_lastReset; + QPoint m_triggeredPoint; +}; + +class WindowBasedEdge : public Edge +{ + Q_OBJECT +public: + explicit WindowBasedEdge(ScreenEdges *parent); + virtual ~WindowBasedEdge(); + + xcb_window_t window() const; + +protected: + virtual void doGeometryUpdate(); + virtual void activate(); + virtual void deactivate(); + +private: + void destroyWindow(); + void createWindow(); + xcb_window_t m_window; +}; + +/** + * @short Class for controlling screen edges. + * + * The screen edge functionality is split into three parts: + * @li This manager class ScreenEdges + * @li abstract class @link Edge + * @li specific implementation of @link Edge, e.g. @link WindowBasedEdge + * + * The ScreenEdges creates an @link Edge for each screen edge which is also an edge in the + * combination of all screens. E.g. if there are two screens, no Edge is created between the screens, + * but at all other edges even if the screens have a different dimension. + * + * In addition at each corner of the overall display geometry an one-pixel large @link Edge is + * created. No matter how many screens there are, there will only be exactly four of these corner + * edges. This is motivated by Fitts's Law which show that it's easy to trigger such a corner, but + * it would be very difficult to trigger a corner between two screens (one pixel target not visually + * outlined). + * + * The ScreenEdges are used for one of the following functionality: + * @li switch virtual desktop (see property @link desktopSwitching) + * @li switch virtual desktop when moving a window (see property @link desktopSwitchingMovingClients) + * @li trigger a pre-defined action (see properties @link actionTop and similar) + * @li trigger an externally configured action (e.g. Effect, Script, see @link reserve, @link unreserve) + * + * An @link Edge is only active if there is at least one of the possible actions "reserved" for this + * edge. The idea is to not block the screen edge if nothing could be triggered there, so that the + * user e.g. can configure nothing on the top edge, which tends to interfere with full screen apps + * having a hidden panel there. On X11 (currently only supported backend) the @link Edge is + * represented by a @link WindowBasedEdge which creates an input only window for the geometry and + * reacts on enter notify events. If the edge gets reserved for the first time a window is created + * and mapped, once the edge gets unreserved again, the window gets destroyed. + * + * When the mouse enters one of the screen edges the following values are used to determine whether + * the action should be triggered or the cursor be pushed back + * @li Time difference between two entering events is not larger than a certain threshold + * @li Time difference between two entering events is larger than @link timeThreshold + * @li Time difference between two activations is larger than @link reActivateThreshold + * @li Distance between two enter events is not larger than a defined pixel distance + * These checks are performed in @link Edge + * + * @todo change way how Effects/Scripts can reserve an edge and are notified. + */ +class ScreenEdges : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool desktopSwitching READ isDesktopSwitching) + Q_PROPERTY(bool desktopSwitchingMovingClients READ isDesktopSwitchingMovingClients) + Q_PROPERTY(QSize cursorPushBackDistance READ cursorPushBackDistance) + Q_PROPERTY(int timeThreshold READ timeThreshold) + Q_PROPERTY(int reActivateThreshold READ reActivationThreshold) + Q_PROPERTY(int actionTopLeft READ actionTopLeft) + Q_PROPERTY(int actionTop READ actionTop) + Q_PROPERTY(int actionTopRight READ actionTopRight) + Q_PROPERTY(int actionRight READ actionRight) + Q_PROPERTY(int actionBottomRight READ actionBottomRight) + Q_PROPERTY(int actionBottom READ actionBottom) + Q_PROPERTY(int actionBottomLeft READ actionBottomLeft) + Q_PROPERTY(int actionLeft READ actionLeft) +public: + explicit ScreenEdges(QObject *parent = 0); + virtual ~ScreenEdges(); + /** + * @internal + **/ + void setConfig(KSharedConfig::Ptr config); /** * Initialize the screen edges. + * @internal */ void init(); /** @@ -60,29 +194,28 @@ public: * @param now the time when the function is called * @param forceNoPushBack needs to be called to workaround some DnD clients, don't use unless you want to chek on a DnD event */ - void check(const QPoint& pos, Time now, bool forceNoPushBack = false); + void check(const QPoint& pos, const QDateTime &now, bool forceNoPushBack = false); /** - * Restore the size of the specified screen edges - * @param border the screen edge to restore the size of - */ - void restoreSize(ElectricBorder border); - /** - * Mark the specified screen edge as reserved in m_screenEdgeReserved + * Mark the specified screen edge as reserved. This method is provided for external activation + * like effects and scripts. When the effect/script does no longer need the edge it is supposed + * to call @link unreserve. * @param border the screen edge to mark as reserved + * @see unreserve + * @todo: add pointer to script/effect */ void reserve(ElectricBorder border); /** - * Mark the specified screen edge as unreserved in m_screenEdgeReserved + * Mark the specified screen edge as unreserved. This method is provided for external activation + * like effects and scripts. This method is only allowed to be called if @link reserve had been + * called before for the same @p border. An unbalanced calling of reserve/unreserve leads to the + * edge never being active or never being able to deactivate again. * @param border the screen edge to mark as unreserved + * @see unreserve + * @todo: add pointer to script/effect */ void unreserve(ElectricBorder border); /** - * Reserve actions for screen edges, if reserve is true. Unreserve otherwise. - * @param reserve indicated weather actions should be reserved or unreseved - */ - void reserveActions(bool isToReserve); - /** - * Reserve desktop switching for screen edges, if reserve is true. Unreserve otherwise. + * Reserve desktop switching for screen edges, if @p isToReserve is @c true. Unreserve otherwise. * @param reserve indicated weather desktop switching should be reserved or unreseved */ void reserveDesktopSwitching(bool isToReserve, Qt::Orientations o); @@ -102,30 +235,43 @@ public: * @param e the X event which is passed to this method. */ bool isEntered(XEvent * e); + /** * Returns a QVector of all existing screen edge windows * @return all existing screen edge windows in a QVector */ - const QVector< Window >& windows(); -public Q_SLOTS: + QVector< xcb_window_t > windows() const; + + bool isDesktopSwitching() const; + bool isDesktopSwitchingMovingClients() const; + const QSize &cursorPushBackDistance() const; /** - * Update the screen edge windows. Add new ones if the user specified - * a new action or enabled desktop switching. Remove, if user deleted - * actions or disabled desktop switching. - */ - void update(bool force=false); - /** - * Reconfigures the screen edges. That is reserves required borders. + * Minimum time between the push back of the cursor and the activation by re-entering the edge. **/ + int timeThreshold() const; + /** + * Minimum time between triggers + **/ + int reActivationThreshold() const; + ElectricBorderAction actionTopLeft() const; + ElectricBorderAction actionTop() const; + ElectricBorderAction actionTopRight() const; + ElectricBorderAction actionRight() const; + ElectricBorderAction actionBottomRight() const; + ElectricBorderAction actionBottom() const; + ElectricBorderAction actionBottomLeft() const; + ElectricBorderAction actionLeft() const; +public Q_SLOTS: void reconfigure(); /** - * Reconfigures for virtual desktop switching, that is updates m_virtualDesktopSwitching. - **/ - void reconfigureVirtualDesktopSwitching(); - /** - * Updates the layout of virtual desktops, that is updates m_virtualDesktopLayout. + * Updates the layout of virtual desktops and adjust the reserved borders in case of + * virtual desktop switching on edges. **/ void updateLayout(); + /** + * Recreates all edges e.g. after the screen size changes. + **/ + void recreateEdges(); Q_SIGNALS: /** * Emitted when the @p border got activated and there is neither an effect nor a global @@ -134,35 +280,207 @@ Q_SIGNALS: **/ void activated(ElectricBorder border); private: - /** - * Switch the desktop if desktop switching is enabled and a screen edge - * is entered to trigger this action. - */ - void switchDesktop(ElectricBorder border, const QPoint& pos); - - QVector< Window > m_screenEdgeWindows; - QVector< int > m_screenEdgeReserved; // Corners/edges used by something - ElectricBorder m_currentScreenEdge; - int m_screenEdgeLeft; - int m_screenEdgeRight; - int m_screenEdgeTop; - int m_screenEdgeBottom; - Time m_screenEdgeTimeFirst; - Time m_screenEdgeTimeLast; - Time m_screenEdgeTimeLastTrigger; - QPoint m_screenEdgePushPoint; - /** - * The virtual desktop switching mode when hitting screen edges. Either: - * @li never enabled - * @li enabled when moving windows - * @li always enabled - **/ - int m_virtualDesktopSwitching; - /** - * Used to know whether desktop switching at top/bottom or left/right borders is supported - * by the layout of virtual desktops. - **/ + enum { ElectricDisabled = 0, ElectricMoveOnly = 1, ElectricAlways = 2 }; + void setDesktopSwitching(bool enable); + void setDesktopSwitchingMovingClients(bool enable); + void setCursorPushBackDistance(const QSize &distance); + void setTimeThreshold(int threshold); + void setReActivationThreshold(int threshold); + void createHorizontalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea); + void createVerticalEdge(ElectricBorder border, const QRect &screen, const QRect &fullArea); + WindowBasedEdge *createEdge(ElectricBorder border, int x, int y, int width, int height); + void setActionForBorder(ElectricBorder border, ElectricBorderAction *oldValue, ElectricBorderAction newValue); + ElectricBorderAction actionForEdge(Edge *edge) const; + bool m_desktopSwitching; + bool m_desktopSwitchingMovingClients; + QSize m_cursorPushBackDistance; + int m_timeThreshold; + int m_reactivateThreshold; Qt::Orientations m_virtualDesktopLayout; + QList m_edges; + KSharedConfig::Ptr m_config; + ElectricBorderAction m_actionTopLeft; + ElectricBorderAction m_actionTop; + ElectricBorderAction m_actionTopRight; + ElectricBorderAction m_actionRight; + ElectricBorderAction m_actionBottomRight; + ElectricBorderAction m_actionBottom; + ElectricBorderAction m_actionBottomLeft; + ElectricBorderAction m_actionLeft; + QHash m_externalReservations; }; + +/********************************************************** + * Inlines Edge + *********************************************************/ + +inline bool Edge::isBottom() const +{ + return m_border == ElectricBottom || m_border == ElectricBottomLeft || m_border == ElectricBottomRight; +} + +inline bool Edge::isLeft() const +{ + return m_border == ElectricLeft || m_border == ElectricTopLeft || m_border == ElectricBottomLeft; +} + +inline bool Edge::isRight() const +{ + return m_border == ElectricRight || m_border == ElectricTopRight || m_border == ElectricBottomRight; +} + +inline bool Edge::isTop() const +{ + return m_border == ElectricTop || m_border == ElectricTopLeft || m_border == ElectricTopRight; +} + +inline bool Edge::isCorner() const +{ + return m_border == ElectricTopLeft + || m_border == ElectricTopRight + || m_border == ElectricBottomRight + || m_border == ElectricBottomLeft; +} + +inline bool Edge::isScreenEdge() const +{ + return m_border == ElectricLeft + || m_border == ElectricRight + || m_border == ElectricTop + || m_border == ElectricBottom; +} + +inline bool Edge::isReserved() const +{ + return m_reserved != 0; +} + +inline void Edge::setAction(ElectricBorderAction action) +{ + m_action = action; +} + +inline void Edge::setBorder(ElectricBorder border) +{ + m_border = border; +} + +inline ScreenEdges *Edge::edges() +{ + return m_edges; +} + +inline const ScreenEdges *Edge::edges() const +{ + return m_edges; +} + +inline const QRect &Edge::geometry() const +{ + return m_geometry; +} + +inline void Edge::setGeometry(const QRect &geometry) +{ + if (m_geometry == geometry) { + return; + } + m_geometry = geometry; + doGeometryUpdate(); +} + +inline ElectricBorder Edge::border() const +{ + return m_border; +} + +/********************************************************** + * Inlines WindowBasedEdge + *********************************************************/ + +inline xcb_window_t WindowBasedEdge::window() const +{ + return m_window; +} + +/********************************************************** + * Inlines ScreenEdges + *********************************************************/ +inline void ScreenEdges::setConfig(KSharedConfig::Ptr config) +{ + m_config = config; +} + +inline const QSize &ScreenEdges::cursorPushBackDistance() const +{ + return m_cursorPushBackDistance; +} + +inline bool ScreenEdges::isDesktopSwitching() const +{ + return m_desktopSwitching; +} + +inline bool ScreenEdges::isDesktopSwitchingMovingClients() const +{ + return m_desktopSwitchingMovingClients; +} + +inline int ScreenEdges::reActivationThreshold() const +{ + return m_reactivateThreshold; +} + +inline int ScreenEdges::timeThreshold() const +{ + return m_timeThreshold; +} + +inline void ScreenEdges::setCursorPushBackDistance(const QSize &distance) +{ + m_cursorPushBackDistance = distance; +} + +inline void ScreenEdges::setDesktopSwitching(bool enable) +{ + if (enable == m_desktopSwitching) { + return; + } + m_desktopSwitching = enable; + reserveDesktopSwitching(enable, m_virtualDesktopLayout); +} + +inline void ScreenEdges::setDesktopSwitchingMovingClients(bool enable) +{ + m_desktopSwitchingMovingClients = enable; +} + +inline void ScreenEdges::setReActivationThreshold(int threshold) +{ + m_reactivateThreshold = threshold; +} + +inline void ScreenEdges::setTimeThreshold(int threshold) +{ + m_timeThreshold = threshold; +} + +#define ACTION( name ) \ +inline ElectricBorderAction ScreenEdges::name() const \ +{ \ + return m_##name; \ +} + +ACTION(actionTopLeft) +ACTION(actionTop) +ACTION(actionTopRight) +ACTION(actionRight) +ACTION(actionBottomRight) +ACTION(actionBottom) +ACTION(actionBottomLeft) +ACTION(actionLeft) + +#undef ACTION + } #endif // KWIN_SCREENEDGE_H diff --git a/scripting/scriptedeffect.cpp b/scripting/scriptedeffect.cpp index e9c0509108..9ec73bf7bc 100644 --- a/scripting/scriptedeffect.cpp +++ b/scripting/scriptedeffect.cpp @@ -22,6 +22,9 @@ along with this program. If not, see . #include "meta.h" #include "scriptingutils.h" #include "workspace_wrapper.h" +#ifdef KWIN_BUILD_SCREENEDGES +#include "../screenedge.h" +#endif // KDE #include #include diff --git a/scripting/scriptingutils.h b/scripting/scriptingutils.h index 3ed7845813..b22f846fe8 100644 --- a/scripting/scriptingutils.h +++ b/scripting/scriptingutils.h @@ -22,6 +22,9 @@ along with this program. If not, see . #define KWIN_SCRIPTINGUTILS_H #include "workspace.h" +#ifdef KWIN_BUILD_SCREENEDGES +#include "screenedge.h" +#endif #include #include diff --git a/workspace.cpp b/workspace.cpp index 9183fd66da..47e84a8abb 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -66,6 +66,9 @@ along with this program. If not, see . #include "xcbutils.h" #include #include +#ifdef KWIN_BUILD_SCREENEDGES +#include "screenedge.h" +#endif #ifdef KWIN_BUILD_SCRIPTING #include "scripting/scripting.h" #endif @@ -96,6 +99,9 @@ Workspace* Workspace::_self = 0; Workspace::Workspace(bool restore) : QObject(0) +#ifdef KWIN_BUILD_SCREENEDGES + , m_screenEdge(new ScreenEdges(this)) +#endif , m_compositor(NULL) // Unsorted , active_popup(NULL) @@ -255,10 +261,10 @@ void Workspace::screenChangeTimeout() void Workspace::init() { #ifdef KWIN_BUILD_SCREENEDGES - m_screenEdge.init(); - connect(options, SIGNAL(configChanged()), &m_screenEdge, SLOT(reconfigure())); - connect(options, SIGNAL(electricBordersChanged()), &m_screenEdge, SLOT(reconfigureVirtualDesktopSwitching())); - connect(VirtualDesktopManager::self(), SIGNAL(layoutChanged(int,int)), &m_screenEdge, SLOT(updateLayout())); + m_screenEdge->setConfig(KGlobal::config()); + m_screenEdge->init(); + connect(options, SIGNAL(configChanged()), m_screenEdge, SLOT(reconfigure())); + connect(VirtualDesktopManager::self(), SIGNAL(layoutChanged(int,int)), m_screenEdge, SLOT(updateLayout())); #endif supportWindow = new QWidget(NULL, Qt::X11BypassWindowManagerHint); @@ -992,10 +998,6 @@ void Workspace::slotReconfigure() kDebug(1212) << "Workspace::slotReconfigure()"; reconfigureTimer.stop(); -#ifdef KWIN_BUILD_SCREENEDGES - m_screenEdge.reserveActions(false); -#endif - bool borderlessMaximizedWindows = options->borderlessMaximizedWindows(); KGlobal::config()->reparseConfiguration(); @@ -1933,9 +1935,9 @@ Outline* Workspace::outline() } #ifdef KWIN_BUILD_SCREENEDGES -ScreenEdge* Workspace::screenEdge() +ScreenEdges* Workspace::screenEdge() { - return &m_screenEdge; + return m_screenEdge; } #endif @@ -1994,6 +1996,18 @@ QString Workspace::supportInformation() const } support.append(QLatin1String(property.name()) % ": " % options->property(property.name()).toString() % '\n'); } +#ifdef KWIN_BUILD_SCREENEDGES + support.append("\nScreen Edges\n"); + support.append( "============\n"); + const QMetaObject *metaScreenEdges = m_screenEdge->metaObject(); + for (int i=0; ipropertyCount(); ++i) { + const QMetaProperty property = metaScreenEdges->property(i); + if (QLatin1String(property.name()) == "objectName") { + continue; + } + support.append(QLatin1String(property.name()) % ": " % m_screenEdge->property(property.name()).toString() % '\n'); + } +#endif support.append("\nScreens\n"); support.append( "=======\n"); support.append("Multi-Head: "); diff --git a/workspace.h b/workspace.h index 203dd344c3..4d7a75ce91 100644 --- a/workspace.h +++ b/workspace.h @@ -41,9 +41,6 @@ along with this program. If not, see . #include "plugins.h" #include "kdecoration.h" #include "kdecorationfactory.h" -#ifdef KWIN_BUILD_SCREENEDGES -#include "screenedge.h" -#endif #include "sm.h" #include @@ -78,6 +75,7 @@ class Scripting; class UserActionsMenu; class WindowRules; class Compositor; +class ScreenEdges; class Workspace : public QObject, public KDecorationDefines { @@ -202,7 +200,7 @@ public: Outline* outline(); #ifdef KWIN_BUILD_SCREENEDGES - ScreenEdge* screenEdge(); + ScreenEdges* screenEdge(); #endif public: @@ -217,7 +215,7 @@ private: Outline* m_outline; #ifdef KWIN_BUILD_SCREENEDGES - ScreenEdge m_screenEdge; + ScreenEdges *m_screenEdge; #endif Compositor *m_compositor;