use kwayland for shadows and dialog positioning

use plasmashell interface for moving the dialog,
use kwayland as well for shadows.

this is supposed to replace the waylanddialogfilter
hack in plasmashell.

REVIEW:129148
This commit is contained in:
Marco Martin 2016-10-11 12:09:10 +02:00
parent ba9a8a16ab
commit b0a5bc09ef
6 changed files with 317 additions and 13 deletions

View File

@ -66,6 +66,14 @@ find_package(KF5XmlGui ${KF5_DEP_VERSION} REQUIRED)
find_package(KF5Notifications ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Notifications ${KF5_DEP_VERSION} REQUIRED)
find_package(KF5Package ${KF5_DEP_VERSION} REQUIRED) find_package(KF5Package ${KF5_DEP_VERSION} REQUIRED)
find_package(KF5Wayland ${KF5_DEP_VERSION})
set_package_properties(KF5Wayland PROPERTIES DESCRIPTION "Integration with the Wayland compositor"
TYPE OPTIONAL
)
if(KF5Wayland_FOUND)
set(HAVE_KWAYLAND 1)
endif()
find_package(KF5DocTools ${KF5_DEP_VERSION}) find_package(KF5DocTools ${KF5_DEP_VERSION})
set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Tools to generate documentation" set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Tools to generate documentation"
TYPE OPTIONAL TYPE OPTIONAL

View File

@ -4,5 +4,6 @@
#cmakedefine01 HAVE_X11 #cmakedefine01 HAVE_X11
#cmakedefine01 HAVE_GLX #cmakedefine01 HAVE_GLX
#cmakedefine01 HAVE_EGL #cmakedefine01 HAVE_EGL
#cmakedefine01 HAVE_KWAYLAND
#define PLASMA_RELATIVE_DATA_INSTALL_DIR "@PLASMA_RELATIVE_DATA_INSTALL_DIR@" #define PLASMA_RELATIVE_DATA_INSTALL_DIR "@PLASMA_RELATIVE_DATA_INSTALL_DIR@"

View File

@ -42,6 +42,13 @@ target_link_libraries(KF5PlasmaQuick
KF5::QuickAddons KF5::QuickAddons
) )
if(HAVE_KWAYLAND)
target_link_libraries(KF5PlasmaQuick
PRIVATE
KF5::WaylandClient
)
endif()
if(HAVE_X11) if(HAVE_X11)
target_link_libraries(KF5PlasmaQuick target_link_libraries(KF5PlasmaQuick
PRIVATE PRIVATE

View File

@ -21,6 +21,7 @@
***************************************************************************/ ***************************************************************************/
#include "dialog.h" #include "dialog.h"
#include "config-plasma.h"
#include "../declarativeimports/core/framesvgitem.h" #include "../declarativeimports/core/framesvgitem.h"
#include "dialogshadows_p.h" #include "dialogshadows_p.h"
#include "view.h" #include "view.h"
@ -47,6 +48,11 @@
#include <QDebug> #include <QDebug>
#if HAVE_KWAYLAND
#include <KWayland/Client/plasmashell.h>
#include <KWayland/Client/surface.h>
#endif
#include <config-plasma.h> #include <config-plasma.h>
#if HAVE_XCB_SHAPE #if HAVE_XCB_SHAPE
#include <QX11Info> #include <QX11Info>
@ -128,12 +134,18 @@ public:
bool mainItemContainsPosition(const QPointF &point) const; bool mainItemContainsPosition(const QPointF &point) const;
QPointF positionAdjustedForMainItem(const QPointF &point) const; QPointF positionAdjustedForMainItem(const QPointF &point) const;
void setupWaylandIntegration();
Dialog *q; Dialog *q;
Plasma::Types::Location location; Plasma::Types::Location location;
Plasma::FrameSvgItem *frameSvgItem; Plasma::FrameSvgItem *frameSvgItem;
QPointer<QQuickItem> mainItem; QPointer<QQuickItem> mainItem;
QPointer<QQuickItem> visualParent; QPointer<QQuickItem> visualParent;
QTimer hintsCommitTimer; QTimer hintsCommitTimer;
#if HAVE_KWAYLAND
QPointer<KWayland::Client::PlasmaShellSurface> shellSurface;
#endif
QRect cachedGeometry; QRect cachedGeometry;
bool hasMask; bool hasMask;
@ -664,6 +676,28 @@ QPointF DialogPrivate::positionAdjustedForMainItem(const QPointF &point) const
qBound(itemRect.top(), point.y(), itemRect.bottom())); qBound(itemRect.top(), point.y(), itemRect.bottom()));
} }
void DialogPrivate::setupWaylandIntegration()
{
#if HAVE_KWAYLAND
if (shellSurface) {
// already setup
return;
}
using namespace KWayland::Client;
PlasmaShell *interface = DialogShadows::self()->waylandPlasmaShellInterface();
if (!interface) {
return;
}
Surface *s = Surface::fromWindow(q);
if (!s) {
return;
}
shellSurface = interface->createSurface(s, q);
#endif
}
Dialog::Dialog(QQuickItem *parent) Dialog::Dialog(QQuickItem *parent)
: QQuickWindow(parent ? parent->window() : 0), : QQuickWindow(parent ? parent->window() : 0),
d(new DialogPrivate(this)) d(new DialogPrivate(this))
@ -722,7 +756,6 @@ void Dialog::setMainItem(QQuickItem *mainItem)
disconnect(d->mainItemLayout, 0, this, 0); disconnect(d->mainItemLayout, 0, this, 0);
} }
d->mainItem = mainItem; d->mainItem = mainItem;
if (mainItem) { if (mainItem) {
@ -1089,12 +1122,23 @@ bool Dialog::event(QEvent *event)
if (pSEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) { if (pSEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager);
d->setupWaylandIntegration();
} else if (pSEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
#if HAVE_KWAYLAND
delete d->shellSurface;
d->shellSurface = nullptr;
#endif
} }
#endif #endif
} else if (event->type() == QEvent::Show) { } else if (event->type() == QEvent::Show) {
d->updateVisibility(true); d->updateVisibility(true);
} else if (event->type() == QEvent::Hide) { } else if (event->type() == QEvent::Hide) {
d->updateVisibility(false); d->updateVisibility(false);
} else if (event->type() == QEvent::Move) {
QMoveEvent *me = static_cast<QMoveEvent *>(event);
if (d->shellSurface) {
d->shellSurface->setPosition(me->pos());
}
} }
/*Fitt's law: if the containment has margins, and the mouse cursor clicked /*Fitt's law: if the containment has margins, and the mouse cursor clicked

View File

@ -18,11 +18,11 @@
#include "dialogshadows_p.h" #include "dialogshadows_p.h"
#include <QGlobalStatic>
#include <QWindow> #include <QWindow>
#include <QPainter> #include <QPainter>
#include <config-plasma.h> #include <config-plasma.h>
#include <KWindowSystem>
#if HAVE_X11 #if HAVE_X11
#include <QX11Info> #include <QX11Info>
#include <X11/Xatom.h> #include <X11/Xatom.h>
@ -31,6 +31,15 @@
#include <fixx11h.h> #include <fixx11h.h>
#endif #endif
#if HAVE_KWAYLAND
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/registry.h>
#include <KWayland/Client/shadow.h>
#include <KWayland/Client/shm_pool.h>
#include <KWayland/Client/surface.h>
#include <KWayland/Client/plasmashell.h>
#endif
#include <qdebug.h> #include <qdebug.h>
class DialogShadows::Private class DialogShadows::Private
@ -39,11 +48,12 @@ public:
Private(DialogShadows *shadows) Private(DialogShadows *shadows)
: q(shadows) : q(shadows)
#if HAVE_X11 #if HAVE_X11
, _connection(0x0), ,_connection(0x0),
_gc(0x0) _gc(0x0)
, m_isX11(QX11Info::isPlatformX11()) , m_isX11(KWindowSystem::isPlatformX11())
#endif #endif
{ {
setupWaylandIntegration();
} }
~Private() ~Private()
@ -54,17 +64,24 @@ public:
} }
void freeX11Pixmaps(); void freeX11Pixmaps();
void freeWaylandBuffers();
void clearPixmaps(); void clearPixmaps();
void setupPixmaps(); void setupPixmaps();
Qt::HANDLE createPixmap(const QPixmap &source); Qt::HANDLE createPixmap(const QPixmap &source);
void initPixmap(const QString &element); void initPixmap(const QString &element);
QPixmap initEmptyPixmap(const QSize &size); QPixmap initEmptyPixmap(const QSize &size);
void updateShadow(const QWindow *window, Plasma::FrameSvg::EnabledBorders); void updateShadow(const QWindow *window, Plasma::FrameSvg::EnabledBorders);
void updateShadowX11(const QWindow *window, Plasma::FrameSvg::EnabledBorders);
void updateShadowWayland(const QWindow *window, Plasma::FrameSvg::EnabledBorders);
void clearShadow(const QWindow *window); void clearShadow(const QWindow *window);
void clearShadowX11(const QWindow *window);
void clearShadowWayland(const QWindow *window);
void updateShadows(); void updateShadows();
void windowDestroyed(QObject *deletedObject); void windowDestroyed(QObject *deletedObject);
void setupData(Plasma::FrameSvg::EnabledBorders enabledBorders); void setupData(Plasma::FrameSvg::EnabledBorders enabledBorders);
void setupWaylandIntegration();
DialogShadows *q; DialogShadows *q;
QList<QPixmap> m_shadowPixmaps; QList<QPixmap> m_shadowPixmaps;
@ -85,6 +102,17 @@ public:
bool m_isX11; bool m_isX11;
#endif #endif
#if HAVE_KWAYLAND
struct Wayland {
KWayland::Client::ShadowManager *manager = nullptr;
KWayland::Client::ShmPool *shmPool = nullptr;
KWayland::Client::PlasmaShell *plasmaShell = nullptr;
QList<KWayland::Client::Buffer::Ptr> shadowBuffers;
};
Wayland m_wayland;
#endif
QHash<Plasma::FrameSvg::EnabledBorders, QVector<unsigned long> > data; QHash<Plasma::FrameSvg::EnabledBorders, QVector<unsigned long> > data;
QHash<const QWindow *, Plasma::FrameSvg::EnabledBorders> m_windows; QHash<const QWindow *, Plasma::FrameSvg::EnabledBorders> m_windows;
}; };
@ -146,6 +174,16 @@ void DialogShadows::removeWindow(const QWindow *window)
} }
} }
void DialogShadows::setEnabledBorders(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
{
if (!window || !d->m_windows.contains(window)) {
return;
}
d->updateShadow(window, enabledBorders);
}
void DialogShadows::Private::windowDestroyed(QObject *deletedObject) void DialogShadows::Private::windowDestroyed(QObject *deletedObject)
{ {
m_windows.remove(static_cast<QWindow *>(deletedObject)); m_windows.remove(static_cast<QWindow *>(deletedObject));
@ -269,6 +307,13 @@ void DialogShadows::Private::setupPixmaps()
m_emptyVerticalPix = initEmptyPixmap(QSize(1, q->elementSize(QStringLiteral("shadow-left")).height())); m_emptyVerticalPix = initEmptyPixmap(QSize(1, q->elementSize(QStringLiteral("shadow-left")).height()));
m_emptyHorizontalPix = initEmptyPixmap(QSize(q->elementSize(QStringLiteral("shadow-top")).width(), 1)); m_emptyHorizontalPix = initEmptyPixmap(QSize(q->elementSize(QStringLiteral("shadow-top")).width(), 1));
#if HAVE_KWAYLAND
if (m_wayland.shmPool) {
for (auto it = m_shadowPixmaps.constBegin(); it != m_shadowPixmaps.constEnd(); ++it) {
m_wayland.shadowBuffers << m_wayland.shmPool->createBuffer(it->toImage());
}
}
#endif
} }
void DialogShadows::Private::setupData(Plasma::FrameSvg::EnabledBorders enabledBorders) void DialogShadows::Private::setupData(Plasma::FrameSvg::EnabledBorders enabledBorders)
@ -459,16 +504,35 @@ void DialogShadows::Private::clearPixmaps()
m_emptyVerticalPix = QPixmap(); m_emptyVerticalPix = QPixmap();
m_emptyHorizontalPix = QPixmap(); m_emptyHorizontalPix = QPixmap();
#endif #endif
freeWaylandBuffers();
m_shadowPixmaps.clear(); m_shadowPixmaps.clear();
data.clear(); data.clear();
} }
void DialogShadows::Private::freeWaylandBuffers()
{
#if HAVE_KWAYLAND
m_wayland.shadowBuffers.clear();
#endif
}
void DialogShadows::Private::updateShadow(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders) void DialogShadows::Private::updateShadow(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
{ {
#if HAVE_X11 #if HAVE_X11
if (!m_isX11) { if (m_isX11) {
return; updateShadowX11(window, enabledBorders);
} }
#endif
#if HAVE_KWAYLAND
if (m_wayland.manager) {
updateShadowWayland(window, enabledBorders);
}
#endif
}
void DialogShadows::Private::updateShadowX11(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
{
#if HAVE_X11
if (m_shadowPixmaps.isEmpty()) { if (m_shadowPixmaps.isEmpty()) {
setupPixmaps(); setupPixmaps();
} }
@ -483,23 +547,149 @@ void DialogShadows::Private::updateShadow(const QWindow *window, Plasma::FrameSv
//qDebug() << "going to set the shadow of" << window->winId() << "to" << data; //qDebug() << "going to set the shadow of" << window->winId() << "to" << data;
XChangeProperty(dpy, window->winId(), atom, XA_CARDINAL, 32, PropModeReplace, XChangeProperty(dpy, window->winId(), atom, XA_CARDINAL, 32, PropModeReplace,
reinterpret_cast<const unsigned char *>(data[enabledBorders].constData()), data[enabledBorders].size()); reinterpret_cast<const unsigned char *>(data[enabledBorders].constData()), data[enabledBorders].size());
#else #endif
Q_UNUSED(window) }
Q_UNUSED(enabledBorders)
void DialogShadows::Private::updateShadowWayland(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders)
{
#if HAVE_KWAYLAND
if (!m_wayland.shmPool) {
return;
}
if (m_wayland.shadowBuffers.isEmpty()) {
setupPixmaps();
}
// TODO: check whether the surface already has a shadow
KWayland::Client::Surface *surface = KWayland::Client::Surface::fromWindow(const_cast<QWindow*>(window));
if (!surface) {
return;
}
auto shadow = m_wayland.manager->createShadow(surface, surface);
//shadow-top
if (enabledBorders & Plasma::FrameSvg::TopBorder) {
shadow->attachTop(m_wayland.shadowBuffers.at(0));
}
//shadow-topright
if (enabledBorders & Plasma::FrameSvg::TopBorder &&
enabledBorders & Plasma::FrameSvg::RightBorder) {
shadow->attachTopRight(m_wayland.shadowBuffers.at(1));
}
//shadow-right
if (enabledBorders & Plasma::FrameSvg::RightBorder) {
shadow->attachRight(m_wayland.shadowBuffers.at(2));
}
//shadow-bottomright
if (enabledBorders & Plasma::FrameSvg::BottomBorder &&
enabledBorders & Plasma::FrameSvg::RightBorder) {
shadow->attachBottomRight(m_wayland.shadowBuffers.at(3));
}
//shadow-bottom
if (enabledBorders & Plasma::FrameSvg::BottomBorder) {
shadow->attachBottom(m_wayland.shadowBuffers.at(4));
}
//shadow-bottomleft
if (enabledBorders & Plasma::FrameSvg::BottomBorder &&
enabledBorders & Plasma::FrameSvg::LeftBorder) {
shadow->attachBottomLeft(m_wayland.shadowBuffers.at(5));
}
//shadow-left
if (enabledBorders & Plasma::FrameSvg::LeftBorder) {
shadow->attachLeft(m_wayland.shadowBuffers.at(6));
}
//shadow-topleft
if (enabledBorders & Plasma::FrameSvg::TopBorder &&
enabledBorders & Plasma::FrameSvg::LeftBorder) {
shadow->attachTopLeft(m_wayland.shadowBuffers.at(7));
}
QSize marginHint;
QMarginsF margins = QMarginsF(1, 1, 1, 1);
if (enabledBorders & Plasma::FrameSvg::TopBorder) {
marginHint = q->elementSize(QStringLiteral("shadow-hint-top-margin"));
if (marginHint.isValid()) {
margins.setTop(marginHint.height());
} else {
margins.setTop(m_shadowPixmaps[0].height());
}
}
if (enabledBorders & Plasma::FrameSvg::RightBorder) {
marginHint = q->elementSize(QStringLiteral("shadow-hint-right-margin"));
if (marginHint.isValid()) {
margins.setRight(marginHint.width());
} else {
margins.setRight(m_shadowPixmaps[2].width());
}
}
if (enabledBorders & Plasma::FrameSvg::BottomBorder) {
marginHint = q->elementSize(QStringLiteral("shadow-hint-bottom-margin"));
if (marginHint.isValid()) {
margins.setBottom(marginHint.height());
} else {
margins.setBottom(m_shadowPixmaps[4].height());
}
}
if (enabledBorders & Plasma::FrameSvg::LeftBorder) {
marginHint = q->elementSize(QStringLiteral("shadow-hint-left-margin"));
if (marginHint.isValid()) {
margins.setLeft(marginHint.width());
} else {
margins.setLeft(m_shadowPixmaps[6].width());
}
}
shadow->setOffsets(margins);
shadow->commit();
surface->commit(KWayland::Client::Surface::CommitFlag::None);
#endif #endif
} }
void DialogShadows::Private::clearShadow(const QWindow *window) void DialogShadows::Private::clearShadow(const QWindow *window)
{ {
#if HAVE_X11 if (!static_cast<const QSurface*>(window)->surfaceHandle()) {
if (!m_isX11) { qWarning() << "Cannot clear shadow from window without native surface!";
return; return;
} }
#if HAVE_X11
if (m_isX11) {
clearShadowX11(window);
}
#endif
#if HAVE_KWAYLAND
if (m_wayland.manager) {
clearShadowWayland(window);
}
#endif
}
void DialogShadows::Private::clearShadowX11(const QWindow* window)
{
#if HAVE_X11
Display *dpy = QX11Info::display(); Display *dpy = QX11Info::display();
Atom atom = XInternAtom(dpy, "_KDE_NET_WM_SHADOW", False); Atom atom = XInternAtom(dpy, "_KDE_NET_WM_SHADOW", False);
XDeleteProperty(dpy, window->winId(), atom); XDeleteProperty(dpy, window->winId(), atom);
#else #endif
Q_UNUSED(window) }
void DialogShadows::Private::clearShadowWayland(const QWindow *window)
{
#if HAVE_KWAYLAND
KWayland::Client::Surface *surface = KWayland::Client::Surface::fromWindow(const_cast<QWindow*>(window));
if (!surface) {
return;
}
m_wayland.manager->removeShadow(surface);
surface->commit(KWayland::Client::Surface::CommitFlag::None);
#endif #endif
} }
@ -508,5 +698,47 @@ bool DialogShadows::enabled() const
return hasElement(QStringLiteral("shadow-left")); return hasElement(QStringLiteral("shadow-left"));
} }
KWayland::Client::PlasmaShell *DialogShadows::waylandPlasmaShellInterface() const
{
#if HAVE_KWAYLAND
return d->m_wayland.plasmaShell;
#endif
}
void DialogShadows::Private::setupWaylandIntegration()
{
#if HAVE_KWAYLAND
if (!KWindowSystem::isPlatformWayland()) {
return;
}
using namespace KWayland::Client;
ConnectionThread *connection = ConnectionThread::fromApplication(q);
if (!connection) {
return;
}
Registry *registry = new Registry(q);
registry->create(connection);
connect(registry, &Registry::shadowAnnounced, q,
[this, registry] (quint32 name, quint32 version) {
m_wayland.manager = registry->createShadowManager(name, version, q);
updateShadows();
}, Qt::QueuedConnection
);
connect(registry, &Registry::shmAnnounced, q,
[this, registry] (quint32 name, quint32 version) {
m_wayland.shmPool = registry->createShmPool(name, version, q);
updateShadows();
}, Qt::QueuedConnection
);
connect(registry, &Registry::plasmaShellAnnounced, q,
[this, registry] (quint32 name, quint32 version) {
m_wayland.plasmaShell = registry->createPlasmaShell(name, version, q);
}
);
registry->setup();
connection->roundtrip();
#endif
}
#include "moc_dialogshadows_p.cpp" #include "moc_dialogshadows_p.cpp"

View File

@ -24,6 +24,14 @@
#include "plasma/framesvg.h" #include "plasma/framesvg.h"
#include "plasma/svg.h" #include "plasma/svg.h"
namespace KWayland
{
namespace Client
{
class PlasmaShell;
}
}
class DialogShadows : public Plasma::Svg class DialogShadows : public Plasma::Svg
{ {
Q_OBJECT Q_OBJECT
@ -37,8 +45,12 @@ public:
void addWindow(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders = Plasma::FrameSvg::AllBorders); void addWindow(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders = Plasma::FrameSvg::AllBorders);
void removeWindow(const QWindow *window); void removeWindow(const QWindow *window);
void setEnabledBorders(const QWindow *window, Plasma::FrameSvg::EnabledBorders enabledBorders = Plasma::FrameSvg::AllBorders);
bool enabled() const; bool enabled() const;
KWayland::Client::PlasmaShell *waylandPlasmaShellInterface() const;
private: private:
class Private; class Private;
Private *const d; Private *const d;