Support passing generic QIcon through PlasmaWindow interface

Summary:
Especially for Xwayland windows the compositor might not have a themed
icon name. Resulting in a task manager not having dedicated icons for
Xwayland windows.

This change deprecates the way how a compositor is supposed to set the
window icon. Instead of passing the themed icon name, it is now supposed to
pass the QIcon. In case it's a themed icon the existing way to pass to
the client is used.

Otherwise a new event is used to inform the client that there is an icon
- no data is transmitted at this point. The client can then create a
file descriptor and pass it to the compositor. The compositor serializes
the icon into the file descriptor and the client can read from it. This
all happens transparently on client side there is no api change at all.

The writing and reading of the icon is done in a thread. Due to that
Qt5::Concurrent is now a required dependency instead of an optional
dependency.

Reviewers: #plasma_on_wayland, hein

Subscribers: plasma-devel

Tags: #plasma_on_wayland

Differential Revision: https://phabricator.kde.org/D3049
This commit is contained in:
Martin Gräßlin 2016-10-13 13:39:27 +02:00
parent bd34a6d828
commit d92592a291
6 changed files with 118 additions and 22 deletions

View file

@ -145,6 +145,7 @@ target_link_libraries(KF5WaylandServer
PRIVATE
Wayland::Server
EGL::EGL
Qt5::Concurrent
)
if(IS_ABSOLUTE "${KF5_INCLUDE_INSTALL_DIR}")

View file

@ -66,6 +66,7 @@ private Q_SLOTS:
void testRequestShowingDesktop();
void testParentWindow();
void testGeometry();
void testIcon();
void cleanup();
@ -545,5 +546,40 @@ void TestWindowManagement::testGeometry()
QCOMPARE(window->geometry(), QRect(0, 0, 35, 45));
}
QTEST_GUILESS_MAIN(TestWindowManagement)
void TestWindowManagement::testIcon()
{
using namespace KWayland::Client;
QVERIFY(m_window);
QSignalSpy iconChangedSpy(m_window, &PlasmaWindow::iconChanged);
QVERIFY(iconChangedSpy.isValid());
m_windowInterface->setIcon(QIcon());
// first goes from themed name to empty
QVERIFY(iconChangedSpy.wait());
if (!QIcon::hasThemeIcon(QStringLiteral("wayland"))) {
QEXPECT_FAIL("", "no icon", Continue);
}
QCOMPARE(m_window->icon().name(), QStringLiteral("wayland"));
QVERIFY(iconChangedSpy.wait());
if (!QIcon::hasThemeIcon(QStringLiteral("wayland"))) {
QEXPECT_FAIL("", "no icon", Continue);
}
QCOMPARE(m_window->icon().name(), QStringLiteral("wayland"));
// create an icon with a pixmap
QPixmap p(32, 32);
p.fill(Qt::red);
m_windowInterface->setIcon(p);
QVERIFY(iconChangedSpy.wait());
QCOMPARE(m_window->icon().pixmap(32, 32), p);
// let's set a themed icon
m_windowInterface->setIcon(QIcon::fromTheme(QStringLiteral("xorg")));
QVERIFY(iconChangedSpy.wait());
if (!QIcon::hasThemeIcon(QStringLiteral("xorg"))) {
QEXPECT_FAIL("", "no icon", Continue);
}
QCOMPARE(m_window->icon().name(), QStringLiteral("xorg"));
}
QTEST_MAIN(TestWindowManagement)
#include "test_wayland_windowmanagement.moc"

View file

@ -23,6 +23,9 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include "display.h"
#include "surface_interface.h"
#include <QtConcurrentRun>
#include <QFile>
#include <QIcon>
#include <QList>
#include <QVector>
#include <QRect>
@ -70,6 +73,7 @@ public:
void setTitle(const QString &title);
void setAppId(const QString &appId);
void setThemedIconName(const QString &iconName);
void setIcon(const QIcon &icon);
void setVirtualDesktop(quint32 desktop);
void unmap();
void setState(org_kde_plasma_window_management_state flag, bool set);
@ -97,6 +101,7 @@ private:
static void setMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel, uint32_t x, uint32_t y, uint32_t width, uint32_t height);
static void unsetMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel);
static void destroyCallback(wl_client *client, wl_resource *resource);
static void getIconCallback(wl_client *client, wl_resource *resource, int32_t fd);
static Private *cast(wl_resource *resource) {
return reinterpret_cast<Private*>(wl_resource_get_user_data(resource));
}
@ -105,13 +110,14 @@ private:
QString m_title;
QString m_appId;
QString m_themedIconName;
QIcon m_icon;
quint32 m_virtualDesktop = 0;
quint32 m_state = 0;
wl_listener listener;
static const struct org_kde_plasma_window_interface s_interface;
};
const quint32 PlasmaWindowManagementInterface::Private::s_version = 6;
const quint32 PlasmaWindowManagementInterface::Private::s_version = 7;
PlasmaWindowManagementInterface::Private::Private(PlasmaWindowManagementInterface *q, Display *d)
: Global::Private(d, &org_kde_plasma_window_management_interface, s_version)
@ -267,7 +273,8 @@ const struct org_kde_plasma_window_interface PlasmaWindowInterface::Private::s_i
closeCallback,
requestMoveCallback,
requestResizeCallback,
destroyCallback
destroyCallback,
getIconCallback
};
#endif
@ -326,7 +333,13 @@ void PlasmaWindowInterface::Private::createResource(wl_resource *parent, uint32_
org_kde_plasma_window_send_title_changed(resource, m_title.toUtf8().constData());
}
org_kde_plasma_window_send_state_changed(resource, m_state);
org_kde_plasma_window_send_themed_icon_name_changed(resource, m_themedIconName.toUtf8().constData());
if (!m_themedIconName.isEmpty()) {
org_kde_plasma_window_send_themed_icon_name_changed(resource, m_themedIconName.toUtf8().constData());
} else {
if (wl_resource_get_version(resource) >= ORG_KDE_PLASMA_WINDOW_ICON_CHANGED_SINCE_VERSION) {
org_kde_plasma_window_send_icon_changed(resource);
}
}
org_kde_plasma_window_send_parent_window(resource, resourceForParent(parentWindow, resource));
@ -368,6 +381,34 @@ void PlasmaWindowInterface::Private::setThemedIconName(const QString &iconName)
}
}
void PlasmaWindowInterface::Private::setIcon(const QIcon &icon)
{
m_icon = icon;
setThemedIconName(m_icon.name());
if (m_icon.name().isEmpty()) {
for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
if (wl_resource_get_version(*it) >= ORG_KDE_PLASMA_WINDOW_ICON_CHANGED_SINCE_VERSION) {
org_kde_plasma_window_send_icon_changed(*it);
}
}
}
}
void PlasmaWindowInterface::Private::getIconCallback(wl_client *client, wl_resource *resource, int32_t fd)
{
Q_UNUSED(client)
Private *p = cast(resource);
QtConcurrent::run(
[fd] (const QIcon &icon) {
QFile file;
file.open(fd, QIODevice::WriteOnly, QFileDevice::AutoCloseHandle);
QDataStream ds(&file);
ds << icon;
file.close();
}, p->m_icon
);
}
void PlasmaWindowInterface::Private::setTitle(const QString &title)
{
if (m_title == title) {
@ -699,10 +740,17 @@ void PlasmaWindowInterface::setSkipTaskbar(bool set)
d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR, set);
}
#ifndef KWAYLANDSERVER_NO_DEPRECATED
void PlasmaWindowInterface::setThemedIconName(const QString &iconName)
{
d->setThemedIconName(iconName);
}
#endif
void PlasmaWindowInterface::setIcon(const QIcon &icon)
{
d->setIcon(icon);
}
void PlasmaWindowInterface::setShadeable(bool set)
{

View file

@ -101,7 +101,13 @@ public:
void setMaximizeable(bool set);
void setFullscreenable(bool set);
void setSkipTaskbar(bool skip);
void setThemedIconName(const QString &iconName);
/**
* @deprecated since 5.28 use setIcon
* @see setIcon
**/
#ifndef KWAYLANDSERVER_NO_DEPRECATED
void KWAYLANDSERVER_DEPRECATED setThemedIconName(const QString &iconName);
#endif
/**
* @since 5.22
*/
@ -155,6 +161,19 @@ public:
**/
void setGeometry(const QRect &geometry);
/**
* Set the icon of the PlasmaWindowInterface.
*
* In case the icon has a themed name, only the name is sent to the client.
* Otherwise the client is only informed that there is an icon and the client
* can request the icon in an asynchronous way by passing a file descriptor
* into which the icon will be serialized.
*
* @param icon The new icon
* @since 5.28
**/
void setIcon(const QIcon &icon);
Q_SIGNALS:
void closeRequested();
/**

View file

@ -10,8 +10,7 @@ target_link_libraries(testServer KF5::WaylandServer)
ecm_mark_as_test(testServer)
find_package(Qt5Widgets ${QT_MIN_VERSION} CONFIG QUIET)
find_package(Qt5Concurrent ${QT_MIN_VERSION} CONFIG QUIET)
if (Qt5Widgets_FOUND AND Qt5Concurrent_FOUND)
if (Qt5Widgets_FOUND)
set(testRenderingServer_SRCS
renderingservertest.cpp
)
@ -24,11 +23,9 @@ add_executable(copyClient copyclient.cpp)
target_link_libraries(copyClient KF5::WaylandClient)
ecm_mark_as_test(copyClient)
if (Qt5Concurrent_FOUND)
add_executable(pasteClient pasteclient.cpp)
target_link_libraries(pasteClient Qt5::Concurrent KF5::WaylandClient)
ecm_mark_as_test(pasteClient)
endif()
add_executable(pasteClient pasteclient.cpp)
target_link_libraries(pasteClient Qt5::Concurrent KF5::WaylandClient)
ecm_mark_as_test(pasteClient)
if (HAVE_LINUX_INPUT_H)
add_executable(touchClientTest touchclienttest.cpp)

View file

@ -2,14 +2,9 @@ add_subdirectory(testserver)
include(ECMMarkAsTest)
find_package(Qt5Concurrent ${QT_MIN_VERSION} CONFIG QUIET)
set(scannerSRCS generator.cpp)
if (Qt5Concurrent_FOUND)
set(scannerSRCS generator.cpp)
add_definitions(-DMAPPING_FILE="${CMAKE_CURRENT_SOURCE_DIR}/mapping.txt")
add_executable(kwaylandScanner ${scannerSRCS})
target_link_libraries(kwaylandScanner Qt5::Core Qt5::Concurrent)
ecm_mark_as_test(kwaylandScanner)
endif()
add_definitions(-DMAPPING_FILE="${CMAKE_CURRENT_SOURCE_DIR}/mapping.txt")
add_executable(kwaylandScanner ${scannerSRCS})
target_link_libraries(kwaylandScanner Qt5::Core Qt5::Concurrent)
ecm_mark_as_test(kwaylandScanner)