diff --git a/outline.cpp b/outline.cpp index 5ad303bb1..69a8a5462 100644 --- a/outline.cpp +++ b/outline.cpp @@ -1,311 +1,186 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2011 Arthur Arlt Copyright (C) 2013 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ // own #include "outline.h" // KWin #include "composite.h" +#include "main.h" +#include "platform.h" #include "scripting/scripting.h" -// KWin libs -#include -#include "workspace.h" +#include "utils.h" // Frameworks #include // Qt #include #include #include #include #include #include -// xcb -#include namespace KWin { KWIN_SINGLETON_FACTORY(Outline) Outline::Outline(QObject *parent) : QObject(parent) , m_active(false) { connect(Compositor::self(), SIGNAL(compositingToggled(bool)), SLOT(compositingChanged())); } Outline::~Outline() { } void Outline::show() { if (m_visual.isNull()) { createHelper(); } if (m_visual.isNull()) { // something went wrong return; } m_visual->show(); m_active = true; emit activeChanged(); } void Outline::hide() { if (!m_active) { return; } m_active = false; emit activeChanged(); if (m_visual.isNull()) { return; } m_visual->hide(); } void Outline::show(const QRect& outlineGeometry) { show(outlineGeometry, QRect()); } void Outline::show(const QRect &outlineGeometry, const QRect &visualParentGeometry) { setGeometry(outlineGeometry); setVisualParentGeometry(visualParentGeometry); show(); } void Outline::setGeometry(const QRect& outlineGeometry) { if (m_outlineGeometry == outlineGeometry) { return; } m_outlineGeometry = outlineGeometry; emit geometryChanged(); emit unifiedGeometryChanged(); } void Outline::setVisualParentGeometry(const QRect &visualParentGeometry) { if (m_visualParentGeometry == visualParentGeometry) { return; } m_visualParentGeometry = visualParentGeometry; emit visualParentGeometryChanged(); emit unifiedGeometryChanged(); } QRect Outline::unifiedGeometry() const { return m_outlineGeometry | m_visualParentGeometry; } void Outline::createHelper() { if (!m_visual.isNull()) { return; } - if (Compositor::compositing()) { - m_visual.reset(new CompositedOutlineVisual(this)); - } else { - m_visual.reset(new NonCompositedOutlineVisual(this)); - } + m_visual.reset(kwinApp()->platform()->createOutline(this)); } void Outline::compositingChanged() { m_visual.reset(); if (m_active) { show(); } } OutlineVisual::OutlineVisual(Outline *outline) : m_outline(outline) { } OutlineVisual::~OutlineVisual() { } CompositedOutlineVisual::CompositedOutlineVisual(Outline *outline) : OutlineVisual(outline) , m_qmlContext() , m_qmlComponent() , m_mainItem() { } CompositedOutlineVisual::~CompositedOutlineVisual() { } void CompositedOutlineVisual::hide() { if (QQuickWindow *w = qobject_cast(m_mainItem.data())) { w->hide(); w->destroy(); } } void CompositedOutlineVisual::show() { if (m_qmlContext.isNull()) { m_qmlContext.reset(new QQmlContext(Scripting::self()->qmlEngine())); m_qmlContext->setContextProperty(QStringLiteral("outline"), outline()); } if (m_qmlComponent.isNull()) { m_qmlComponent.reset(new QQmlComponent(Scripting::self()->qmlEngine())); const QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, kwinApp()->config()->group(QStringLiteral("Outline")).readEntry("QmlPath", QStringLiteral(KWIN_NAME "/outline/plasma/outline.qml"))); if (fileName.isEmpty()) { qCDebug(KWIN_CORE) << "Could not locate outline.qml"; return; } m_qmlComponent->loadUrl(QUrl::fromLocalFile(fileName)); if (m_qmlComponent->isError()) { qCDebug(KWIN_CORE) << "Component failed to load: " << m_qmlComponent->errors(); } else { m_mainItem.reset(m_qmlComponent->create(m_qmlContext.data())); } } } -NonCompositedOutlineVisual::NonCompositedOutlineVisual(Outline *outline) - : OutlineVisual(outline) - , m_initialized(false) -{ -} - -NonCompositedOutlineVisual::~NonCompositedOutlineVisual() -{ -} - -void NonCompositedOutlineVisual::show() -{ - if (!m_initialized) { - const QRect geo(0, 0, 1, 1); - const uint32_t values[] = {true}; - // TODO: use template variant - m_leftOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); - m_rightOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); - m_topOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); - m_bottomOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); - m_initialized = true; - } - - const int defaultDepth = Xcb::defaultDepth(); - - const QRect &outlineGeometry = outline()->geometry(); - // left/right parts are between top/bottom, they don't reach as far as the corners - const uint16_t verticalWidth = 5; - const uint16_t verticalHeight = outlineGeometry.height() - 10; - const uint16_t horizontalWidth = outlineGeometry.width(); - const uint horizontalHeight = 5; - m_leftOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y() + 5, verticalWidth, verticalHeight); - m_rightOutline.setGeometry(outlineGeometry.x() + outlineGeometry.width() - 5, outlineGeometry.y() + 5, verticalWidth, verticalHeight); - m_topOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y(), horizontalWidth, horizontalHeight); - m_bottomOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y() + outlineGeometry.height() - 5, horizontalWidth, horizontalHeight); - - const xcb_render_color_t white = {0xffff, 0xffff, 0xffff, 0xffff}; - QColor qGray(Qt::gray); - const xcb_render_color_t gray = { - uint16_t(0xffff * qGray.redF()), - uint16_t(0xffff * qGray.greenF()), - uint16_t(0xffff * qGray.blueF()), - 0xffff - }; - const xcb_render_color_t black = {0, 0, 0, 0xffff}; - { - xcb_pixmap_t xpix = xcb_generate_id(connection()); - xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), verticalWidth, verticalHeight); - XRenderPicture pic(xpix, defaultDepth); - - xcb_rectangle_t rect = {0, 0, 5, verticalHeight}; - xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); - rect.x = 1; - rect.width = 3; - xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 1, &rect); - rect.x = 2; - rect.width = 1; - xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 1, &rect); - - m_leftOutline.setBackgroundPixmap(xpix); - m_rightOutline.setBackgroundPixmap(xpix); - // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. - xcb_free_pixmap(connection(), xpix); - } - { - xcb_pixmap_t xpix = xcb_generate_id(connection()); - xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), horizontalWidth, horizontalHeight); - XRenderPicture pic(xpix, defaultDepth); - - xcb_rectangle_t rect = {0, 0, horizontalWidth, horizontalHeight}; - xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); - xcb_rectangle_t grayRects[] = { - {1, 1, uint16_t(horizontalWidth -2), 3}, - {1, 4, 3, 1}, - {int16_t(horizontalWidth - 4), 4, 3, 1} - }; - xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 3, grayRects); - xcb_rectangle_t blackRects[] = { - {2, 2, uint16_t(horizontalWidth -4), 1}, - {2, 3, 1, 2}, - {int16_t(horizontalWidth - 3), 3, 1, 2} - }; - xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 3, blackRects); - m_topOutline.setBackgroundPixmap(xpix); - // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. - xcb_free_pixmap(connection(), xpix); - } - { - xcb_pixmap_t xpix = xcb_generate_id(connection()); - xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), outlineGeometry.width(), 5); - XRenderPicture pic(xpix, defaultDepth); - - xcb_rectangle_t rect = {0, 0, horizontalWidth, horizontalHeight}; - xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); - xcb_rectangle_t grayRects[] = { - {1, 1, uint16_t(horizontalWidth -2), 3}, - {1, 0, 3, 1}, - {int16_t(horizontalWidth - 4), 0, 3, 1} - }; - xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 3, grayRects); - xcb_rectangle_t blackRects[] = { - {2, 2, uint16_t(horizontalWidth -4), 1}, - {2, 0, 1, 2}, - {int16_t(horizontalWidth - 3), 0, 1, 2} - }; - xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 3, blackRects); - m_bottomOutline.setBackgroundPixmap(xpix); - // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. - xcb_free_pixmap(connection(), xpix); - } - forEachWindow(&Xcb::Window::clear); - forEachWindow(&Xcb::Window::map); -} - -void NonCompositedOutlineVisual::hide() -{ - forEachWindow(&Xcb::Window::unmap); -} - } // namespace diff --git a/outline.h b/outline.h index 3771cd789..982c97f11 100644 --- a/outline.h +++ b/outline.h @@ -1,222 +1,194 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2011 Arthur Arlt This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_OUTLINE_H #define KWIN_OUTLINE_H -#include "xcbutils.h" #include #include -#include +#include + +#include class QQmlContext; class QQmlComponent; namespace KWin { class OutlineVisual; /** * @short This class is used to show the outline of a given geometry. * * The class renders an outline by using four windows. One for each border of * the geometry. It is possible to replace the outline with an effect. If an * effect is available the effect will be used, otherwise the outline will be * rendered by using the X implementation. * * @author Arthur Arlt * @since 4.7 */ class Outline : public QObject { Q_OBJECT Q_PROPERTY(QRect geometry READ geometry NOTIFY geometryChanged) Q_PROPERTY(QRect visualParentGeometry READ visualParentGeometry NOTIFY visualParentGeometryChanged) Q_PROPERTY(QRect unifiedGeometry READ unifiedGeometry NOTIFY unifiedGeometryChanged) Q_PROPERTY(bool active READ isActive NOTIFY activeChanged) public: ~Outline(); /** * Set the outline geometry. * To show the outline use @link showOutline. * @param outlineGeometry The geometry of the outline to be shown * @see showOutline */ void setGeometry(const QRect &outlineGeometry); /** * Set the visual parent geometry. * This is the geometry from which the will emerge. * @param visualParentGeometry The visual geometry of the visual parent * @see showOutline */ void setVisualParentGeometry(const QRect &visualParentGeometry); /** * Shows the outline of a window using either an effect or the X implementation. * To stop the outline process use @link hideOutline. * @see hideOutline */ void show(); /** * Shows the outline for the given @p outlineGeometry. * This is the same as setOutlineGeometry followed by showOutline directly. * To stop the outline process use @link hideOutline. * @param outlineGeometry The geometry of the outline to be shown * @see hideOutline */ void show(const QRect &outlineGeometry); /** * Shows the outline for the given @p outlineGeometry animated from @p visualParentGeometry. * This is the same as setOutlineGeometry followed by setVisualParentGeometry * and then showOutline. * To stop the outline process use @link hideOutline. * @param outlineGeometry The geometry of the outline to be shown * @param visualParentGeometry The geometry from where the outline should emerge * @see hideOutline * @since 5.10 */ void show(const QRect &outlineGeometry, const QRect &visualParentGeometry); /** * Hides shown outline. * @see showOutline */ void hide(); const QRect &geometry() const; const QRect &visualParentGeometry() const; QRect unifiedGeometry() const; bool isActive() const; private Q_SLOTS: void compositingChanged(); Q_SIGNALS: void activeChanged(); void geometryChanged(); void unifiedGeometryChanged(); void visualParentGeometryChanged(); private: void createHelper(); QScopedPointer m_visual; QRect m_outlineGeometry; QRect m_visualParentGeometry; bool m_active; KWIN_SINGLETON(Outline) }; -class OutlineVisual +class KWIN_EXPORT OutlineVisual { public: OutlineVisual(Outline *outline); virtual ~OutlineVisual(); virtual void show() = 0; virtual void hide() = 0; protected: Outline *outline(); const Outline *outline() const; private: Outline *m_outline; }; class CompositedOutlineVisual : public OutlineVisual { public: CompositedOutlineVisual(Outline *outline); virtual ~CompositedOutlineVisual(); virtual void show(); virtual void hide(); private: QScopedPointer m_qmlContext; QScopedPointer m_qmlComponent; QScopedPointer m_mainItem; }; -class NonCompositedOutlineVisual : public OutlineVisual -{ -public: - NonCompositedOutlineVisual(Outline *outline); - virtual ~NonCompositedOutlineVisual(); - virtual void show(); - virtual void hide(); - -private: - // TODO: variadic template arguments for adding method arguments - template - void forEachWindow(T method); - bool m_initialized; - Xcb::Window m_topOutline; - Xcb::Window m_rightOutline; - Xcb::Window m_bottomOutline; - Xcb::Window m_leftOutline; -}; - inline bool Outline::isActive() const { return m_active; } inline const QRect &Outline::geometry() const { return m_outlineGeometry; } inline const QRect &Outline::visualParentGeometry() const { return m_visualParentGeometry; } inline Outline *OutlineVisual::outline() { return m_outline; } inline const Outline *OutlineVisual::outline() const { return m_outline; } -template -inline -void NonCompositedOutlineVisual::forEachWindow(T method) -{ - (m_topOutline.*method)(); - (m_rightOutline.*method)(); - (m_bottomOutline.*method)(); - (m_leftOutline.*method)(); -} - inline Outline *outline() { return Outline::self(); } } #endif diff --git a/platform.cpp b/platform.cpp index 8472b56cd..d9ac03206 100644 --- a/platform.cpp +++ b/platform.cpp @@ -1,473 +1,482 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "platform.h" #include #include "abstract_egl_backend.h" #include "composite.h" #include "cursor.h" #include "input.h" #include "overlaywindow.h" +#include "outline.h" #include "pointer_input.h" #include "scene_opengl.h" #include "screenedge.h" #include "wayland_server.h" namespace KWin { Platform::Platform(QObject *parent) : QObject(parent) , m_eglDisplay(EGL_NO_DISPLAY) { } Platform::~Platform() { if (m_eglDisplay != EGL_NO_DISPLAY) { eglTerminate(m_eglDisplay); } } QImage Platform::softwareCursor() const { return input()->pointer()->cursorImage(); } QPoint Platform::softwareCursorHotspot() const { return input()->pointer()->cursorHotSpot(); } PlatformCursorImage Platform::cursorImage() const { return PlatformCursorImage(softwareCursor(), softwareCursorHotspot()); } void Platform::hideCursor() { m_hideCursorCounter++; if (m_hideCursorCounter == 1) { doHideCursor(); } } void Platform::doHideCursor() { } void Platform::showCursor() { m_hideCursorCounter--; if (m_hideCursorCounter == 0) { doShowCursor(); } } void Platform::doShowCursor() { } Screens *Platform::createScreens(QObject *parent) { Q_UNUSED(parent) return nullptr; } OpenGLBackend *Platform::createOpenGLBackend() { return nullptr; } QPainterBackend *Platform::createQPainterBackend() { return nullptr; } Edge *Platform::createScreenEdge(ScreenEdges *edges) { return new Edge(edges); } void Platform::createPlatformCursor(QObject *parent) { new InputRedirectionCursor(parent); } void Platform::configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *config) { Q_UNUSED(config) qCWarning(KWIN_CORE) << "This backend does not support configuration changes."; } void Platform::setSoftWareCursor(bool set) { if (m_softWareCursor == set) { return; } m_softWareCursor = set; if (m_softWareCursor) { connect(Cursor::self(), &Cursor::posChanged, this, &Platform::triggerCursorRepaint); connect(this, &Platform::cursorChanged, this, &Platform::triggerCursorRepaint); } else { disconnect(Cursor::self(), &Cursor::posChanged, this, &Platform::triggerCursorRepaint); disconnect(this, &Platform::cursorChanged, this, &Platform::triggerCursorRepaint); } } void Platform::triggerCursorRepaint() { if (!Compositor::self()) { return; } Compositor::self()->addRepaint(m_cursor.lastRenderedGeometry); Compositor::self()->addRepaint(QRect(Cursor::pos() - softwareCursorHotspot(), softwareCursor().size())); } void Platform::markCursorAsRendered() { if (m_softWareCursor) { m_cursor.lastRenderedGeometry = QRect(Cursor::pos() - softwareCursorHotspot(), softwareCursor().size()); } if (input()->pointer()) { input()->pointer()->markCursorAsRendered(); } } void Platform::keyboardKeyPressed(quint32 key, quint32 time) { if (!input()) { return; } input()->processKeyboardKey(key, InputRedirection::KeyboardKeyPressed, time); } void Platform::keyboardKeyReleased(quint32 key, quint32 time) { if (!input()) { return; } input()->processKeyboardKey(key, InputRedirection::KeyboardKeyReleased, time); } void Platform::keyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) { if (!input()) { return; } input()->processKeyboardModifiers(modsDepressed, modsLatched, modsLocked, group); } void Platform::keymapChange(int fd, uint32_t size) { if (!input()) { return; } input()->processKeymapChange(fd, size); } void Platform::pointerAxisHorizontal(qreal delta, quint32 time) { if (!input()) { return; } input()->processPointerAxis(InputRedirection::PointerAxisHorizontal, delta, time); } void Platform::pointerAxisVertical(qreal delta, quint32 time) { if (!input()) { return; } input()->processPointerAxis(InputRedirection::PointerAxisVertical, delta, time); } void Platform::pointerButtonPressed(quint32 button, quint32 time) { if (!input()) { return; } input()->processPointerButton(button, InputRedirection::PointerButtonPressed, time); } void Platform::pointerButtonReleased(quint32 button, quint32 time) { if (!input()) { return; } input()->processPointerButton(button, InputRedirection::PointerButtonReleased, time); } void Platform::pointerMotion(const QPointF &position, quint32 time) { if (!input()) { return; } input()->processPointerMotion(position, time); } void Platform::touchCancel() { if (!input()) { return; } input()->cancelTouch(); } void Platform::touchDown(qint32 id, const QPointF &pos, quint32 time) { if (!input()) { return; } input()->processTouchDown(id, pos, time); } void Platform::touchFrame() { if (!input()) { return; } input()->touchFrame(); } void Platform::touchMotion(qint32 id, const QPointF &pos, quint32 time) { if (!input()) { return; } input()->processTouchMotion(id, pos, time); } void Platform::touchUp(qint32 id, quint32 time) { if (!input()) { return; } input()->processTouchUp(id, time); } void Platform::processSwipeGestureBegin(int fingerCount, quint32 time) { if (!input()) { return; } input()->pointer()->processSwipeGestureBegin(fingerCount, time); } void Platform::processSwipeGestureUpdate(const QSizeF &delta, quint32 time) { if (!input()) { return; } input()->pointer()->processSwipeGestureUpdate(delta, time); } void Platform::processSwipeGestureEnd(quint32 time) { if (!input()) { return; } input()->pointer()->processSwipeGestureEnd(time); } void Platform::processSwipeGestureCancelled(quint32 time) { if (!input()) { return; } input()->pointer()->processSwipeGestureCancelled(time); } void Platform::processPinchGestureBegin(int fingerCount, quint32 time) { if (!input()) { return; } input()->pointer()->processPinchGestureBegin(fingerCount, time); } void Platform::processPinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time) { if (!input()) { return; } input()->pointer()->processPinchGestureUpdate(scale, angleDelta, delta, time); } void Platform::processPinchGestureEnd(quint32 time) { if (!input()) { return; } input()->pointer()->processPinchGestureEnd(time); } void Platform::processPinchGestureCancelled(quint32 time) { if (!input()) { return; } input()->pointer()->processPinchGestureCancelled(time); } void Platform::repaint(const QRect &rect) { if (!Compositor::self()) { return; } Compositor::self()->addRepaint(rect); } void Platform::setReady(bool ready) { if (m_ready == ready) { return; } m_ready = ready; emit readyChanged(m_ready); } void Platform::warpPointer(const QPointF &globalPos) { Q_UNUSED(globalPos) } bool Platform::supportsQpaContext() const { if (Compositor *c = Compositor::self()) { if (SceneOpenGL *s = dynamic_cast(c->scene())) { return s->backend()->hasExtension(QByteArrayLiteral("EGL_KHR_surfaceless_context")); } } return false; } EGLDisplay KWin::Platform::sceneEglDisplay() const { return m_eglDisplay; } void Platform::setSceneEglDisplay(EGLDisplay display) { m_eglDisplay = display; } EGLContext Platform::sceneEglContext() const { if (Compositor *c = Compositor::self()) { if (SceneOpenGL *s = dynamic_cast(c->scene())) { return static_cast(s->backend())->context(); } } return EGL_NO_CONTEXT; } EGLSurface Platform::sceneEglSurface() const { if (Compositor *c = Compositor::self()) { if (SceneOpenGL *s = dynamic_cast(c->scene())) { return static_cast(s->backend())->surface(); } } return EGL_NO_SURFACE; } EGLConfig Platform::sceneEglConfig() const { if (Compositor *c = Compositor::self()) { if (SceneOpenGL *s = dynamic_cast(c->scene())) { return static_cast(s->backend())->config(); } } return nullptr; } QSize Platform::screenSize() const { return QSize(); } QVector Platform::screenGeometries() const { return QVector({QRect(QPoint(0, 0), screenSize())}); } QVector Platform::screenScales() const { return QVector({1}); } bool Platform::requiresCompositing() const { return true; } bool Platform::compositingPossible() const { return true; } QString Platform::compositingNotPossibleReason() const { return QString(); } bool Platform::openGLCompositingIsBroken() const { return false; } void Platform::createOpenGLSafePoint(OpenGLSafePoint safePoint) { Q_UNUSED(safePoint) } void Platform::startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName) { if (!input()) { callback(nullptr); return; } input()->startInteractiveWindowSelection(callback, cursorName); } void Platform::startInteractivePositionSelection(std::function callback) { if (!input()) { callback(QPoint(-1, -1)); return; } input()->startInteractivePositionSelection(callback); } void Platform::setupActionForGlobalAccel(QAction *action) { Q_UNUSED(action) } OverlayWindow *Platform::createOverlayWindow() { return nullptr; } void Platform::updateXTime() { } +OutlineVisual *Platform::createOutline(Outline *outline) +{ + if (Compositor::compositing()) { + return new CompositedOutlineVisual(outline); + } + return nullptr; +} + } diff --git a/platform.h b/platform.h index 1d56933c4..80b5072a1 100644 --- a/platform.h +++ b/platform.h @@ -1,419 +1,427 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_PLATFORM_H #define KWIN_PLATFORM_H #include #include #include #include #include #include #include class QAction; namespace KWayland { namespace Server { class OutputConfigurationInterface; } } namespace KWin { class Edge; class OverlayWindow; class OpenGLBackend; +class Outline; +class OutlineVisual; class QPainterBackend; class Screens; class ScreenEdges; class Toplevel; class WaylandCursorTheme; class KWIN_EXPORT Platform : public QObject { Q_OBJECT public: virtual ~Platform(); virtual void init() = 0; virtual Screens *createScreens(QObject *parent = nullptr); virtual OpenGLBackend *createOpenGLBackend(); virtual QPainterBackend *createQPainterBackend(); /** * Allows the platform to create a platform specific screen edge. * The default implementation creates a Edge. **/ virtual Edge *createScreenEdge(ScreenEdges *parent); /** * Allows the platform to create a platform specific Cursor. * The default implementation creates an InputRedirectionCursor. **/ virtual void createPlatformCursor(QObject *parent = nullptr); virtual void warpPointer(const QPointF &globalPos); /** * Whether our Compositing EGL display allows a surface less context * so that a sharing context could be created. **/ virtual bool supportsQpaContext() const; /** * The EGLDisplay used by the compositing scene. **/ EGLDisplay sceneEglDisplay() const; void setSceneEglDisplay(EGLDisplay display); /** * The EGLContext used by the compositing scene. **/ virtual EGLContext sceneEglContext() const; /** * The first (in case of multiple) EGLSurface used by the compositing scene. **/ EGLSurface sceneEglSurface() const; /** * The EglConfig used by the compositing scene. **/ EGLConfig sceneEglConfig() const; /** * Implementing subclasses should provide a size in case the backend represents * a basic screen and uses the BasicScreens. * * Base implementation returns an invalid size. **/ virtual QSize screenSize() const; /** * Implementing subclasses should provide all geometries in case the backend represents * a basic screen and uses the BasicScreens. * * Base implementation returns one QRect positioned at 0/0 with screenSize() as size. **/ virtual QVector screenGeometries() const; /** * Implementing subclasses should provide all geometries in case the backend represents * a basic screen and uses the BasicScreens. * * Base implementation returns a screen with a scale of 1. **/ virtual QVector screenScales() const; /** * Implement this method to receive configuration change requests through KWayland's * OutputManagement interface. * * Base implementation warns that the current backend does not implement this * functionality. */ virtual void configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *config); /** * Whether the Platform requires compositing for rendering. * Default implementation returns @c true. If the implementing Platform allows to be used * without compositing (e.g. rendering is done by the windowing system), re-implement this method. **/ virtual bool requiresCompositing() const; /** * Whether Compositing is possible in the Platform. * Returning @c false in this method makes only sense if @link{requiresCompositing} returns @c false. * * The default implementation returns @c true. * @see requiresCompositing **/ virtual bool compositingPossible() const; /** * Returns a user facing text explaining why compositing is not possible in case * @link{compositingPossible} returns @c false. * * The default implementation returns an empty string. * @see compositingPossible **/ virtual QString compositingNotPossibleReason() const; /** * Whether OpenGL compositing is broken. * The Platform can implement this method if it is able to detect whether OpenGL compositing * broke (e.g. triggered a crash in a previous run). * * Default implementation returns @c false. * @see createOpenGLSafePoint **/ virtual bool openGLCompositingIsBroken() const; enum class OpenGLSafePoint { PreInit, PostInit, PreFrame, PostFrame, PostLastGuardedFrame }; /** * This method is invoked before and after creating the OpenGL rendering Scene. * An implementing Platform can use it to detect crashes triggered by the OpenGL implementation. * This can be used for @link{openGLCompositingIsBroken}. * * The default implementation does nothing. * @see openGLCompositingIsBroken. **/ virtual void createOpenGLSafePoint(OpenGLSafePoint safePoint); /** * Starts an interactive window selection process. * * Once the user selected a window the @p callback is invoked with the selected Toplevel as * argument. In case the user cancels the interactive window selection or selecting a window is currently * not possible (e.g. screen locked) the @p callback is invoked with a @c nullptr argument. * * During the interactive window selection the cursor is turned into a crosshair cursor unless * @p cursorName is provided. The argument @p cursorName is a QByteArray instead of Qt::CursorShape * to support the "pirate" cursor for kill window which is not wrapped by Qt::CursorShape. * * The default implementation forwards to InputRedirection. * * @param callback The function to invoke once the interactive window selection ends * @param cursorName The optional name of the cursor shape to use, default is crosshair **/ virtual void startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName = QByteArray()); /** * Starts an interactive position selection process. * * Once the user selected a position on the screen the @p callback is invoked with * the selected point as argument. In case the user cancels the interactive position selection * or selecting a position is currently not possible (e.g. screen locked) the @p callback * is invoked with a point at @c -1 as x and y argument. * * During the interactive window selection the cursor is turned into a crosshair cursor. * * The default implementation forwards to InputRedirection. * * @param callback The function to invoke once the interactive position selection ends **/ virtual void startInteractivePositionSelection(std::function callback); /** * Platform specific preparation for an @p action which is used for KGlobalAccel. * * A platform might need to do preparation for an @p action before * it can be used with KGlobalAccel. * * Code using KGlobalAccel should invoke this method for the @p action * prior to setting up any shortcuts and connections. * * The default implementation does nothing. * * @param action The action which will be used with KGlobalAccel. * @since 5.10 **/ virtual void setupActionForGlobalAccel(QAction *action); bool usesSoftwareCursor() const { return m_softWareCursor; } QImage softwareCursor() const; QPoint softwareCursorHotspot() const; void markCursorAsRendered(); /** * Returns a PlatformCursorImage. By default this is created by softwareCursor and * softwareCursorHotspot. An implementing subclass can use this to provide a better * suited PlatformCursorImage. * * @see softwareCursor * @see softwareCursorHotspot * @since 5.9 **/ virtual PlatformCursorImage cursorImage() const; /** * The Platform cursor image should be hidden. * @see showCursor * @see doHideCursor * @see isCursorHidden * @since 5.9 **/ void hideCursor(); /** * The Platform cursor image should be shown again. * @see hideCursor * @see doShowCursor * @see isCursorHidden * @since 5.9 **/ void showCursor(); /** * Whether the cursor is currently hidden. * @see showCursor * @see hideCursor * @since 5.9 **/ bool isCursorHidden() const { return m_hideCursorCounter > 0; } bool handlesOutputs() const { return m_handlesOutputs; } bool isReady() const { return m_ready; } void setInitialWindowSize(const QSize &size) { m_initialWindowSize = size; } void setDeviceIdentifier(const QByteArray &identifier) { m_deviceIdentifier = identifier; } bool supportsPointerWarping() const { return m_pointerWarping; } bool areOutputsEnabled() const { return m_outputsEnabled; } void setOutputsEnabled(bool enabled) { m_outputsEnabled = enabled; } int initialOutputCount() const { return m_initialOutputCount; } void setInitialOutputCount(int count) { m_initialOutputCount = count; } qreal initialOutputScale() const { return m_initialOutputScale; } void setInitialOutputScale(qreal scale) { m_initialOutputScale = scale; } /** * Creates the OverlayWindow required for X11 based compositors. * Default implementation returns @c nullptr. **/ virtual OverlayWindow *createOverlayWindow(); /** * Allows a platform to update the X11 timestamp. * Mostly for the X11 standalone platform to interact with QX11Info. * * Default implementation does nothing. This means code relying on the X timestamp being up to date, * might not be working. E.g. synced X11 window resizing **/ virtual void updateXTime(); + /** + * Creates the OutlineVisual for the given @p outline. + * Default implementation creates an OutlineVisual suited for composited usage. + **/ + virtual OutlineVisual *createOutline(Outline *outline); + public Q_SLOTS: void pointerMotion(const QPointF &position, quint32 time); void pointerButtonPressed(quint32 button, quint32 time); void pointerButtonReleased(quint32 button, quint32 time); void pointerAxisHorizontal(qreal delta, quint32 time); void pointerAxisVertical(qreal delta, quint32 time); void keyboardKeyPressed(quint32 key, quint32 time); void keyboardKeyReleased(quint32 key, quint32 time); void keyboardModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group); void keymapChange(int fd, uint32_t size); void touchDown(qint32 id, const QPointF &pos, quint32 time); void touchUp(qint32 id, quint32 time); void touchMotion(qint32 id, const QPointF &pos, quint32 time); void touchCancel(); void touchFrame(); void processSwipeGestureBegin(int fingerCount, quint32 time); void processSwipeGestureUpdate(const QSizeF &delta, quint32 time); void processSwipeGestureEnd(quint32 time); void processSwipeGestureCancelled(quint32 time); void processPinchGestureBegin(int fingerCount, quint32 time); void processPinchGestureUpdate(qreal scale, qreal angleDelta, const QSizeF &delta, quint32 time); void processPinchGestureEnd(quint32 time); void processPinchGestureCancelled(quint32 time); Q_SIGNALS: void screensQueried(); void initFailed(); void cursorChanged(); void readyChanged(bool); /** * Emitted by backends using a one screen (nested window) approach and when the size of that changes. **/ void screenSizeChanged(); protected: explicit Platform(QObject *parent = nullptr); void setSoftWareCursor(bool set); void handleOutputs() { m_handlesOutputs = true; } void repaint(const QRect &rect); void setReady(bool ready); QSize initialWindowSize() const { return m_initialWindowSize; } QByteArray deviceIdentifier() const { return m_deviceIdentifier; } void setSupportsPointerWarping(bool set) { m_pointerWarping = set; } /** * Actual platform specific way to hide the cursor. * Sub-classes need to implement if they support hiding the cursor. * * This method is invoked by hideCursor if the cursor needs to be hidden. * The default implementation does nothing. * * @see doShowCursor * @see hideCursor * @see showCursor **/ virtual void doHideCursor(); /** * Actual platform specific way to show the cursor. * Sub-classes need to implement if they support showing the cursor. * * This method is invoked by showCursor if the cursor needs to be shown again. * * @see doShowCursor * @see hideCursor * @see showCursor **/ virtual void doShowCursor(); private: void triggerCursorRepaint(); bool m_softWareCursor = false; struct { QRect lastRenderedGeometry; } m_cursor; bool m_handlesOutputs = false; bool m_ready = false; QSize m_initialWindowSize; QByteArray m_deviceIdentifier; bool m_pointerWarping = false; bool m_outputsEnabled = true; int m_initialOutputCount = 1; qreal m_initialOutputScale = 1; EGLDisplay m_eglDisplay; int m_hideCursorCounter = 0; }; } Q_DECLARE_INTERFACE(KWin::Platform, "org.kde.kwin.Platform") #endif diff --git a/plugins/platforms/x11/standalone/CMakeLists.txt b/plugins/platforms/x11/standalone/CMakeLists.txt index 582af8c04..1f398bc6f 100644 --- a/plugins/platforms/x11/standalone/CMakeLists.txt +++ b/plugins/platforms/x11/standalone/CMakeLists.txt @@ -1,35 +1,36 @@ set(X11PLATFORM_SOURCES edge.cpp logging.cpp x11cursor.cpp x11_platform.cpp screens_xrandr.cpp windowselector.cpp overlaywindow_x11.cpp screenedges_filter.cpp + non_composited_outline.cpp ) if(X11_Xinput_FOUND) set(X11PLATFORM_SOURCES ${X11PLATFORM_SOURCES} xinputintegration.cpp) endif() if(HAVE_EPOXY_GLX) set(X11PLATFORM_SOURCES ${X11PLATFORM_SOURCES} glxbackend.cpp glx_context_attribute_builder.cpp) endif() add_library(KWinX11Platform MODULE ${X11PLATFORM_SOURCES}) target_link_libraries(KWinX11Platform eglx11common kwin Qt5::X11Extras XCB::CURSOR) if(X11_Xinput_FOUND) target_link_libraries(KWinX11Platform ${X11_Xinput_LIB}) endif() if(HAVE_DL_LIBRARY) target_link_libraries(KWinX11Platform ${DL_LIBRARY}) endif() install( TARGETS KWinX11Platform DESTINATION ${PLUGIN_INSTALL_DIR}/org.kde.kwin.platforms/ ) diff --git a/outline.cpp b/plugins/platforms/x11/standalone/non_composited_outline.cpp similarity index 62% copy from outline.cpp copy to plugins/platforms/x11/standalone/non_composited_outline.cpp index 5ad303bb1..dd03bbaee 100644 --- a/outline.cpp +++ b/plugins/platforms/x11/standalone/non_composited_outline.cpp @@ -1,311 +1,150 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2011 Arthur Arlt Copyright (C) 2013 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ // own -#include "outline.h" -// KWin -#include "composite.h" -#include "scripting/scripting.h" +#include "non_composited_outline.h" // KWin libs #include -#include "workspace.h" -// Frameworks -#include -// Qt -#include -#include -#include -#include -#include -#include // xcb #include -namespace KWin { - -KWIN_SINGLETON_FACTORY(Outline) - -Outline::Outline(QObject *parent) - : QObject(parent) - , m_active(false) -{ - connect(Compositor::self(), SIGNAL(compositingToggled(bool)), SLOT(compositingChanged())); -} - -Outline::~Outline() -{ -} - -void Outline::show() -{ - if (m_visual.isNull()) { - createHelper(); - } - if (m_visual.isNull()) { - // something went wrong - return; - } - m_visual->show(); - m_active = true; - emit activeChanged(); -} - -void Outline::hide() -{ - if (!m_active) { - return; - } - m_active = false; - emit activeChanged(); - if (m_visual.isNull()) { - return; - } - m_visual->hide(); -} - -void Outline::show(const QRect& outlineGeometry) -{ - show(outlineGeometry, QRect()); -} - -void Outline::show(const QRect &outlineGeometry, const QRect &visualParentGeometry) -{ - setGeometry(outlineGeometry); - setVisualParentGeometry(visualParentGeometry); - show(); -} - -void Outline::setGeometry(const QRect& outlineGeometry) -{ - if (m_outlineGeometry == outlineGeometry) { - return; - } - m_outlineGeometry = outlineGeometry; - emit geometryChanged(); - emit unifiedGeometryChanged(); -} - -void Outline::setVisualParentGeometry(const QRect &visualParentGeometry) -{ - if (m_visualParentGeometry == visualParentGeometry) { - return; - } - m_visualParentGeometry = visualParentGeometry; - emit visualParentGeometryChanged(); - emit unifiedGeometryChanged(); -} - -QRect Outline::unifiedGeometry() const +namespace KWin { - return m_outlineGeometry | m_visualParentGeometry; -} - -void Outline::createHelper() -{ - if (!m_visual.isNull()) { - return; - } - if (Compositor::compositing()) { - m_visual.reset(new CompositedOutlineVisual(this)); - } else { - m_visual.reset(new NonCompositedOutlineVisual(this)); - } -} - -void Outline::compositingChanged() -{ - m_visual.reset(); - if (m_active) { - show(); - } -} - -OutlineVisual::OutlineVisual(Outline *outline) - : m_outline(outline) -{ -} - -OutlineVisual::~OutlineVisual() -{ -} - -CompositedOutlineVisual::CompositedOutlineVisual(Outline *outline) - : OutlineVisual(outline) - , m_qmlContext() - , m_qmlComponent() - , m_mainItem() -{ -} - -CompositedOutlineVisual::~CompositedOutlineVisual() -{ -} - -void CompositedOutlineVisual::hide() -{ - if (QQuickWindow *w = qobject_cast(m_mainItem.data())) { - w->hide(); - w->destroy(); - } -} - -void CompositedOutlineVisual::show() -{ - if (m_qmlContext.isNull()) { - m_qmlContext.reset(new QQmlContext(Scripting::self()->qmlEngine())); - m_qmlContext->setContextProperty(QStringLiteral("outline"), outline()); - } - if (m_qmlComponent.isNull()) { - m_qmlComponent.reset(new QQmlComponent(Scripting::self()->qmlEngine())); - const QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, - kwinApp()->config()->group(QStringLiteral("Outline")).readEntry("QmlPath", QStringLiteral(KWIN_NAME "/outline/plasma/outline.qml"))); - if (fileName.isEmpty()) { - qCDebug(KWIN_CORE) << "Could not locate outline.qml"; - return; - } - m_qmlComponent->loadUrl(QUrl::fromLocalFile(fileName)); - if (m_qmlComponent->isError()) { - qCDebug(KWIN_CORE) << "Component failed to load: " << m_qmlComponent->errors(); - } else { - m_mainItem.reset(m_qmlComponent->create(m_qmlContext.data())); - } - } -} NonCompositedOutlineVisual::NonCompositedOutlineVisual(Outline *outline) : OutlineVisual(outline) , m_initialized(false) { } NonCompositedOutlineVisual::~NonCompositedOutlineVisual() { } void NonCompositedOutlineVisual::show() { if (!m_initialized) { const QRect geo(0, 0, 1, 1); const uint32_t values[] = {true}; // TODO: use template variant m_leftOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); m_rightOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); m_topOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); m_bottomOutline.create(geo, XCB_CW_OVERRIDE_REDIRECT, values); m_initialized = true; } const int defaultDepth = Xcb::defaultDepth(); const QRect &outlineGeometry = outline()->geometry(); // left/right parts are between top/bottom, they don't reach as far as the corners const uint16_t verticalWidth = 5; const uint16_t verticalHeight = outlineGeometry.height() - 10; const uint16_t horizontalWidth = outlineGeometry.width(); const uint horizontalHeight = 5; m_leftOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y() + 5, verticalWidth, verticalHeight); m_rightOutline.setGeometry(outlineGeometry.x() + outlineGeometry.width() - 5, outlineGeometry.y() + 5, verticalWidth, verticalHeight); m_topOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y(), horizontalWidth, horizontalHeight); m_bottomOutline.setGeometry(outlineGeometry.x(), outlineGeometry.y() + outlineGeometry.height() - 5, horizontalWidth, horizontalHeight); const xcb_render_color_t white = {0xffff, 0xffff, 0xffff, 0xffff}; QColor qGray(Qt::gray); const xcb_render_color_t gray = { uint16_t(0xffff * qGray.redF()), uint16_t(0xffff * qGray.greenF()), uint16_t(0xffff * qGray.blueF()), 0xffff }; const xcb_render_color_t black = {0, 0, 0, 0xffff}; { xcb_pixmap_t xpix = xcb_generate_id(connection()); xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), verticalWidth, verticalHeight); XRenderPicture pic(xpix, defaultDepth); xcb_rectangle_t rect = {0, 0, 5, verticalHeight}; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); rect.x = 1; rect.width = 3; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 1, &rect); rect.x = 2; rect.width = 1; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 1, &rect); m_leftOutline.setBackgroundPixmap(xpix); m_rightOutline.setBackgroundPixmap(xpix); // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. xcb_free_pixmap(connection(), xpix); } { xcb_pixmap_t xpix = xcb_generate_id(connection()); xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), horizontalWidth, horizontalHeight); XRenderPicture pic(xpix, defaultDepth); xcb_rectangle_t rect = {0, 0, horizontalWidth, horizontalHeight}; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); xcb_rectangle_t grayRects[] = { {1, 1, uint16_t(horizontalWidth -2), 3}, {1, 4, 3, 1}, {int16_t(horizontalWidth - 4), 4, 3, 1} }; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 3, grayRects); xcb_rectangle_t blackRects[] = { {2, 2, uint16_t(horizontalWidth -4), 1}, {2, 3, 1, 2}, {int16_t(horizontalWidth - 3), 3, 1, 2} }; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 3, blackRects); m_topOutline.setBackgroundPixmap(xpix); // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. xcb_free_pixmap(connection(), xpix); } { xcb_pixmap_t xpix = xcb_generate_id(connection()); xcb_create_pixmap(connection(), defaultDepth, xpix, rootWindow(), outlineGeometry.width(), 5); XRenderPicture pic(xpix, defaultDepth); xcb_rectangle_t rect = {0, 0, horizontalWidth, horizontalHeight}; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, white, 1, &rect); xcb_rectangle_t grayRects[] = { {1, 1, uint16_t(horizontalWidth -2), 3}, {1, 0, 3, 1}, {int16_t(horizontalWidth - 4), 0, 3, 1} }; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, gray, 3, grayRects); xcb_rectangle_t blackRects[] = { {2, 2, uint16_t(horizontalWidth -4), 1}, {2, 0, 1, 2}, {int16_t(horizontalWidth - 3), 0, 1, 2} }; xcb_render_fill_rectangles(connection(), XCB_RENDER_PICT_OP_SRC, pic, black, 3, blackRects); m_bottomOutline.setBackgroundPixmap(xpix); // According to the XSetWindowBackgroundPixmap documentation the pixmap can be freed. xcb_free_pixmap(connection(), xpix); } forEachWindow(&Xcb::Window::clear); forEachWindow(&Xcb::Window::map); } void NonCompositedOutlineVisual::hide() { forEachWindow(&Xcb::Window::unmap); } } // namespace diff --git a/plugins/platforms/x11/standalone/non_composited_outline.h b/plugins/platforms/x11/standalone/non_composited_outline.h new file mode 100644 index 000000000..1744ff96a --- /dev/null +++ b/plugins/platforms/x11/standalone/non_composited_outline.h @@ -0,0 +1,59 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2011 Arthur Arlt + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_NON_COMPOSITED_OUTLINE_H +#define KWIN_NON_COMPOSITED_OUTLINE_H +#include "outline.h" +#include "xcbutils.h" + +namespace KWin +{ + +class NonCompositedOutlineVisual : public OutlineVisual +{ +public: + NonCompositedOutlineVisual(Outline *outline); + virtual ~NonCompositedOutlineVisual(); + virtual void show(); + virtual void hide(); + +private: + // TODO: variadic template arguments for adding method arguments + template + void forEachWindow(T method); + bool m_initialized; + Xcb::Window m_topOutline; + Xcb::Window m_rightOutline; + Xcb::Window m_bottomOutline; + Xcb::Window m_leftOutline; +}; + +template +inline +void NonCompositedOutlineVisual::forEachWindow(T method) +{ + (m_topOutline.*method)(); + (m_rightOutline.*method)(); + (m_bottomOutline.*method)(); + (m_leftOutline.*method)(); +} + +} + +#endif diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp index e52545fee..59bd8dd90 100644 --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -1,339 +1,349 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "x11_platform.h" #include "x11cursor.h" #include "edge.h" #include "windowselector.h" #include #include #if HAVE_EPOXY_GLX #include "glxbackend.h" #endif #if HAVE_X11_XINPUT #include "xinputintegration.h" #endif #include "eglonxbackend.h" #include "keyboard_input.h" #include "logging.h" #include "screens_xrandr.h" #include "screenedges_filter.h" #include "options.h" #include "overlaywindow_x11.h" +#include "non_composited_outline.h" #include #include #include #include #include namespace KWin { X11StandalonePlatform::X11StandalonePlatform(QObject *parent) : Platform(parent) , m_x11Display(QX11Info::display()) { #if HAVE_X11_XINPUT if (!qEnvironmentVariableIsSet("KWIN_NO_XI2")) { m_xinputIntegration = new XInputIntegration(m_x11Display, this); m_xinputIntegration->init(); if (!m_xinputIntegration->hasXinput()) { delete m_xinputIntegration; m_xinputIntegration = nullptr; } else { connect(kwinApp(), &Application::workspaceCreated, m_xinputIntegration, &XInputIntegration::startListening); } } #endif } X11StandalonePlatform::~X11StandalonePlatform() { if (m_openGLFreezeProtectionThread) { m_openGLFreezeProtectionThread->quit(); m_openGLFreezeProtectionThread->wait(); delete m_openGLFreezeProtectionThread; } } void X11StandalonePlatform::init() { if (!QX11Info::isPlatformX11()) { emit initFailed(); return; } setReady(true); emit screensQueried(); } Screens *X11StandalonePlatform::createScreens(QObject *parent) { return new XRandRScreens(parent); } OpenGLBackend *X11StandalonePlatform::createOpenGLBackend() { switch (options->glPlatformInterface()) { #if HAVE_EPOXY_GLX case GlxPlatformInterface: if (hasGlx()) { return new GlxBackend(m_x11Display); } else { qCWarning(KWIN_X11STANDALONE) << "Glx not available, trying EGL instead."; // no break, needs fall-through #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) Q_FALLTHROUGH(); #endif } #endif case EglPlatformInterface: return new EglOnXBackend(m_x11Display); default: // no backend available return nullptr; } } Edge *X11StandalonePlatform::createScreenEdge(ScreenEdges *edges) { if (m_screenEdgesFilter.isNull()) { m_screenEdgesFilter.reset(new ScreenEdgesFilter); } return new WindowBasedEdge(edges); } void X11StandalonePlatform::createPlatformCursor(QObject *parent) { auto c = new X11Cursor(parent, m_xinputIntegration != nullptr); #if HAVE_X11_XINPUT if (m_xinputIntegration) { m_xinputIntegration->setCursor(c); // we know we have xkb already auto xkb = input()->keyboard()->xkb(); xkb->reconfigure(); } #endif } bool X11StandalonePlatform::requiresCompositing() const { return false; } bool X11StandalonePlatform::openGLCompositingIsBroken() const { const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); return KConfigGroup(kwinApp()->config(), "Compositing").readEntry(unsafeKey, false); } QString X11StandalonePlatform::compositingNotPossibleReason() const { // first off, check whether we figured that we'll crash on detection because of a buggy driver KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && gl_workaround_group.readEntry(unsafeKey, false)) return i18n("OpenGL compositing (the default) has crashed KWin in the past.
" "This was most likely due to a driver bug." "

If you think that you have meanwhile upgraded to a stable driver,
" "you can reset this protection but be aware that this might result in an immediate crash!

" "

Alternatively, you might want to use the XRender backend instead.

"); if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) { return i18n("Required X extensions (XComposite and XDamage) are not available."); } #if !defined( KWIN_HAVE_XRENDER_COMPOSITING ) if (!hasGlx()) return i18n("GLX/OpenGL are not available and only OpenGL support is compiled."); #else if (!(hasGlx() || (Xcb::Extensions::self()->isRenderAvailable() && Xcb::Extensions::self()->isFixesAvailable()))) { return i18n("GLX/OpenGL and XRender/XFixes are not available."); } #endif return QString(); } bool X11StandalonePlatform::compositingPossible() const { // first off, check whether we figured that we'll crash on detection because of a buggy driver KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing"); const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && gl_workaround_group.readEntry(unsafeKey, false)) return false; if (!Xcb::Extensions::self()->isCompositeAvailable()) { qCDebug(KWIN_CORE) << "No composite extension available"; return false; } if (!Xcb::Extensions::self()->isDamageAvailable()) { qCDebug(KWIN_CORE) << "No damage extension available"; return false; } if (hasGlx()) return true; #ifdef KWIN_HAVE_XRENDER_COMPOSITING if (Xcb::Extensions::self()->isRenderAvailable() && Xcb::Extensions::self()->isFixesAvailable()) return true; #endif if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { return true; } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { return true; } qCDebug(KWIN_CORE) << "No OpenGL or XRender/XFixes support"; return false; } bool X11StandalonePlatform::hasGlx() { return Xcb::Extensions::self()->hasGlx(); } void X11StandalonePlatform::createOpenGLSafePoint(OpenGLSafePoint safePoint) { const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); auto group = KConfigGroup(kwinApp()->config(), "Compositing"); switch (safePoint) { case OpenGLSafePoint::PreInit: group.writeEntry(unsafeKey, true); group.sync(); // Deliberately continue with PreFrame #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) Q_FALLTHROUGH(); #endif case OpenGLSafePoint::PreFrame: if (m_openGLFreezeProtectionThread == nullptr) { Q_ASSERT(m_openGLFreezeProtection == nullptr); m_openGLFreezeProtectionThread = new QThread(this); m_openGLFreezeProtectionThread->setObjectName("FreezeDetector"); m_openGLFreezeProtectionThread->start(); m_openGLFreezeProtection = new QTimer; m_openGLFreezeProtection->setInterval(15000); m_openGLFreezeProtection->setSingleShot(true); m_openGLFreezeProtection->start(); m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread); connect(m_openGLFreezeProtection, &QTimer::timeout, m_openGLFreezeProtection, [] { const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); auto group = KConfigGroup(kwinApp()->config(), "Compositing"); group.writeEntry(unsafeKey, true); group.sync(); qFatal("Freeze in OpenGL initialization detected"); }, Qt::DirectConnection); } else { Q_ASSERT(m_openGLFreezeProtection); QMetaObject::invokeMethod(m_openGLFreezeProtection, "start", Qt::QueuedConnection); } break; case OpenGLSafePoint::PostInit: group.writeEntry(unsafeKey, false); group.sync(); // Deliberately continue with PostFrame #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) Q_FALLTHROUGH(); #endif case OpenGLSafePoint::PostFrame: QMetaObject::invokeMethod(m_openGLFreezeProtection, "stop", Qt::QueuedConnection); break; case OpenGLSafePoint::PostLastGuardedFrame: m_openGLFreezeProtection->deleteLater(); m_openGLFreezeProtection = nullptr; m_openGLFreezeProtectionThread->quit(); m_openGLFreezeProtectionThread->wait(); delete m_openGLFreezeProtectionThread; m_openGLFreezeProtectionThread = nullptr; break; } } PlatformCursorImage X11StandalonePlatform::cursorImage() const { auto c = kwinApp()->x11Connection(); QScopedPointer cursor( xcb_xfixes_get_cursor_image_reply(c, xcb_xfixes_get_cursor_image_unchecked(c), nullptr)); if (cursor.isNull()) { return PlatformCursorImage(); } QImage qcursorimg((uchar *) xcb_xfixes_get_cursor_image_cursor_image(cursor.data()), cursor->width, cursor->height, QImage::Format_ARGB32_Premultiplied); // deep copy of image as the data is going to be freed return PlatformCursorImage(qcursorimg.copy(), QPoint(cursor->xhot, cursor->yhot)); } void X11StandalonePlatform::doHideCursor() { xcb_xfixes_hide_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); } void X11StandalonePlatform::doShowCursor() { xcb_xfixes_show_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); } void X11StandalonePlatform::startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName) { if (m_windowSelector.isNull()) { m_windowSelector.reset(new WindowSelector); } m_windowSelector->start(callback, cursorName); } void X11StandalonePlatform::setupActionForGlobalAccel(QAction *action) { connect(action, &QAction::triggered, kwinApp(), [action] { QVariant timestamp = action->property("org.kde.kglobalaccel.activationTimestamp"); bool ok = false; const quint32 t = timestamp.toULongLong(&ok); if (ok) { kwinApp()->setX11Time(t); } }); } OverlayWindow *X11StandalonePlatform::createOverlayWindow() { return new OverlayWindowX11(); } /* Updates xTime(). This used to simply fetch current timestamp from the server, but that can cause xTime() to be newer than timestamp of events that are still in our events queue, thus e.g. making XSetInputFocus() caused by such event to be ignored. Therefore events queue is searched for first event with timestamp, and extra PropertyNotify is generated in order to make sure such event is found. */ void X11StandalonePlatform::updateXTime() { // NOTE: QX11Info::getTimestamp does not yet search the event queue as the old // solution did. This means there might be regressions currently. See the // documentation above on how it should be done properly. kwinApp()->setX11Time(QX11Info::getTimestamp(), Application::TimestampUpdate::Always); } +OutlineVisual *X11StandalonePlatform::createOutline(Outline *outline) +{ + // first try composited Outline + auto ret = Platform::createOutline(outline); + if (!ret) { + ret = new NonCompositedOutlineVisual(outline); + } + return ret; +} } diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h index eb79276f3..9fce72a0a 100644 --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -1,90 +1,91 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_X11STANDALONE_PLATFORM_H #define KWIN_X11STANDALONE_PLATFORM_H #include "platform.h" #include #include namespace KWin { class XInputIntegration; class WindowSelector; class X11EventFilter; class KWIN_EXPORT X11StandalonePlatform : public Platform { Q_OBJECT Q_INTERFACES(KWin::Platform) Q_PLUGIN_METADATA(IID "org.kde.kwin.Platform" FILE "x11.json") public: X11StandalonePlatform(QObject *parent = nullptr); virtual ~X11StandalonePlatform(); void init() override; Screens *createScreens(QObject *parent = nullptr) override; OpenGLBackend *createOpenGLBackend() override; Edge *createScreenEdge(ScreenEdges *parent) override; void createPlatformCursor(QObject *parent = nullptr) override; bool requiresCompositing() const override; bool compositingPossible() const override; QString compositingNotPossibleReason() const override; bool openGLCompositingIsBroken() const override; void createOpenGLSafePoint(OpenGLSafePoint safePoint) override; void startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName = QByteArray()) override; PlatformCursorImage cursorImage() const override; void setupActionForGlobalAccel(QAction *action) override; OverlayWindow *createOverlayWindow() override; void updateXTime() override; + OutlineVisual *createOutline(Outline *outline) override; protected: void doHideCursor() override; void doShowCursor() override; private: /** * Tests whether GLX is supported and returns @c true * in case KWin is compiled with OpenGL support and GLX * is available. * * If KWin is compiled with OpenGL ES or without OpenGL at * all, @c false is returned. * @returns @c true if GLX is available, @c false otherwise and if not build with OpenGL support. **/ static bool hasGlx(); XInputIntegration *m_xinputIntegration = nullptr; QThread *m_openGLFreezeProtectionThread = nullptr; QTimer *m_openGLFreezeProtection = nullptr; Display *m_x11Display; QScopedPointer m_windowSelector; QScopedPointer m_screenEdgesFilter; }; } #endif