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;