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(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})
set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Tools to generate documentation"
TYPE OPTIONAL

View File

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

View File

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

View File

@ -21,6 +21,7 @@
***************************************************************************/
#include "dialog.h"
#include "config-plasma.h"
#include "../declarativeimports/core/framesvgitem.h"
#include "dialogshadows_p.h"
#include "view.h"
@ -47,6 +48,11 @@
#include <QDebug>
#if HAVE_KWAYLAND
#include <KWayland/Client/plasmashell.h>
#include <KWayland/Client/surface.h>
#endif
#include <config-plasma.h>
#if HAVE_XCB_SHAPE
#include <QX11Info>
@ -128,12 +134,18 @@ public:
bool mainItemContainsPosition(const QPointF &point) const;
QPointF positionAdjustedForMainItem(const QPointF &point) const;
void setupWaylandIntegration();
Dialog *q;
Plasma::Types::Location location;
Plasma::FrameSvgItem *frameSvgItem;
QPointer<QQuickItem> mainItem;
QPointer<QQuickItem> visualParent;
QTimer hintsCommitTimer;
#if HAVE_KWAYLAND
QPointer<KWayland::Client::PlasmaShellSurface> shellSurface;
#endif
QRect cachedGeometry;
bool hasMask;
@ -664,6 +676,28 @@ QPointF DialogPrivate::positionAdjustedForMainItem(const QPointF &point) const
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)
: QQuickWindow(parent ? parent->window() : 0),
d(new DialogPrivate(this))
@ -722,7 +756,6 @@ void Dialog::setMainItem(QQuickItem *mainItem)
disconnect(d->mainItemLayout, 0, this, 0);
}
d->mainItem = mainItem;
if (mainItem) {
@ -1089,12 +1122,23 @@ bool Dialog::event(QEvent *event)
if (pSEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
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
} else if (event->type() == QEvent::Show) {
d->updateVisibility(true);
} else if (event->type() == QEvent::Hide) {
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

View File

@ -18,11 +18,11 @@
#include "dialogshadows_p.h"
#include <QGlobalStatic>
#include <QWindow>
#include <QPainter>
#include <config-plasma.h>
#include <KWindowSystem>
#if HAVE_X11
#include <QX11Info>
#include <X11/Xatom.h>
@ -31,6 +31,15 @@
#include <fixx11h.h>
#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>
class DialogShadows::Private
@ -39,11 +48,12 @@ public:
Private(DialogShadows *shadows)
: q(shadows)
#if HAVE_X11
, _connection(0x0),
,_connection(0x0),
_gc(0x0)
, m_isX11(QX11Info::isPlatformX11())
, m_isX11(KWindowSystem::isPlatformX11())
#endif
{
setupWaylandIntegration();
}
~Private()
@ -54,17 +64,24 @@ public:
}
void freeX11Pixmaps();
void freeWaylandBuffers();
void clearPixmaps();
void setupPixmaps();
Qt::HANDLE createPixmap(const QPixmap &source);
void initPixmap(const QString &element);
QPixmap initEmptyPixmap(const QSize &size);
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 clearShadowX11(const QWindow *window);
void clearShadowWayland(const QWindow *window);
void updateShadows();
void windowDestroyed(QObject *deletedObject);
void setupData(Plasma::FrameSvg::EnabledBorders enabledBorders);
void setupWaylandIntegration();
DialogShadows *q;
QList<QPixmap> m_shadowPixmaps;
@ -85,6 +102,17 @@ public:
bool m_isX11;
#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<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)
{
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_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)
@ -459,16 +504,35 @@ void DialogShadows::Private::clearPixmaps()
m_emptyVerticalPix = QPixmap();
m_emptyHorizontalPix = QPixmap();
#endif
freeWaylandBuffers();
m_shadowPixmaps.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)
{
#if HAVE_X11
if (!m_isX11) {
return;
if (m_isX11) {
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()) {
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;
XChangeProperty(dpy, window->winId(), atom, XA_CARDINAL, 32, PropModeReplace,
reinterpret_cast<const unsigned char *>(data[enabledBorders].constData()), data[enabledBorders].size());
#else
Q_UNUSED(window)
Q_UNUSED(enabledBorders)
#endif
}
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
}
void DialogShadows::Private::clearShadow(const QWindow *window)
{
#if HAVE_X11
if (!m_isX11) {
if (!static_cast<const QSurface*>(window)->surfaceHandle()) {
qWarning() << "Cannot clear shadow from window without native surface!";
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();
Atom atom = XInternAtom(dpy, "_KDE_NET_WM_SHADOW", False);
XDeleteProperty(dpy, window->winId(), atom);
#else
Q_UNUSED(window)
#endif
}
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
}
@ -508,5 +698,47 @@ bool DialogShadows::enabled() const
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"

View File

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