2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2016-06-30 11:32:54 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
|
2016-06-30 11:32:54 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2022-02-23 13:27:05 +00:00
|
|
|
#include <config-kwin.h>
|
2016-06-30 11:32:54 +00:00
|
|
|
#include "kwin_wayland_test.h"
|
2022-02-28 18:58:35 +00:00
|
|
|
#if KWIN_BUILD_SCREENLOCKER
|
2016-08-16 08:23:37 +00:00
|
|
|
#include "screenlockerwatcher.h"
|
2022-02-23 13:27:05 +00:00
|
|
|
#endif
|
2016-07-01 07:54:44 +00:00
|
|
|
#include "wayland_server.h"
|
2020-07-22 18:35:41 +00:00
|
|
|
#include "workspace.h"
|
2020-09-29 14:46:32 +00:00
|
|
|
#include "inputmethod.h"
|
2016-06-30 11:32:54 +00:00
|
|
|
|
|
|
|
#include <KWayland/Client/compositor.h>
|
|
|
|
#include <KWayland/Client/connection_thread.h>
|
|
|
|
#include <KWayland/Client/event_queue.h>
|
|
|
|
#include <KWayland/Client/registry.h>
|
|
|
|
#include <KWayland/Client/plasmashell.h>
|
|
|
|
#include <KWayland/Client/plasmawindowmanagement.h>
|
2016-11-25 06:17:43 +00:00
|
|
|
#include <KWayland/Client/pointerconstraints.h>
|
2016-06-30 11:32:54 +00:00
|
|
|
#include <KWayland/Client/seat.h>
|
|
|
|
#include <KWayland/Client/server_decoration.h>
|
2018-06-07 09:08:15 +00:00
|
|
|
#include <KWayland/Client/shadow.h>
|
2016-06-30 11:32:54 +00:00
|
|
|
#include <KWayland/Client/shm_pool.h>
|
2017-08-18 14:15:19 +00:00
|
|
|
#include <KWayland/Client/output.h>
|
2019-02-21 22:25:19 +00:00
|
|
|
#include <KWayland/Client/subcompositor.h>
|
|
|
|
#include <KWayland/Client/subsurface.h>
|
2016-06-30 11:32:54 +00:00
|
|
|
#include <KWayland/Client/surface.h>
|
2020-09-01 08:58:46 +00:00
|
|
|
#include <KWayland/Client/textinput.h>
|
2017-12-22 14:22:24 +00:00
|
|
|
#include <KWayland/Client/appmenu.h>
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/display.h>
|
2016-06-30 11:32:54 +00:00
|
|
|
|
2016-08-16 06:19:45 +00:00
|
|
|
//screenlocker
|
2022-02-28 18:58:35 +00:00
|
|
|
#if KWIN_BUILD_SCREENLOCKER
|
2016-08-16 06:19:45 +00:00
|
|
|
#include <KScreenLocker/KsldApp>
|
2022-02-23 13:27:05 +00:00
|
|
|
#endif
|
2016-08-16 06:19:45 +00:00
|
|
|
|
2016-06-30 11:32:54 +00:00
|
|
|
#include <QThread>
|
|
|
|
|
2016-12-03 13:31:14 +00:00
|
|
|
// system
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2016-06-30 11:32:54 +00:00
|
|
|
using namespace KWayland::Client;
|
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
namespace Test
|
|
|
|
{
|
|
|
|
|
2020-08-07 19:01:42 +00:00
|
|
|
LayerShellV1::~LayerShellV1()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
LayerSurfaceV1::~LayerSurfaceV1()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LayerSurfaceV1::zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height)
|
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT configureRequested(serial, QSize(width, height));
|
2020-08-07 19:01:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LayerSurfaceV1::zwlr_layer_surface_v1_closed()
|
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT closeRequested();
|
2020-08-07 19:01:42 +00:00
|
|
|
}
|
|
|
|
|
2020-09-02 09:27:08 +00:00
|
|
|
XdgShell::~XdgShell()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
XdgSurface::XdgSurface(XdgShell *shell, KWayland::Client::Surface *surface, QObject *parent)
|
2020-09-02 09:27:08 +00:00
|
|
|
: QObject(parent)
|
|
|
|
, QtWayland::xdg_surface(shell->get_xdg_surface(*surface))
|
|
|
|
, m_surface(surface)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgSurface::~XdgSurface()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
KWayland::Client::Surface *XdgSurface::surface() const
|
2020-09-02 09:27:08 +00:00
|
|
|
{
|
|
|
|
return m_surface;
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgSurface::xdg_surface_configure(uint32_t serial)
|
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT configureRequested(serial);
|
2020-09-02 09:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
XdgToplevel::XdgToplevel(XdgSurface *surface, QObject *parent)
|
|
|
|
: QObject(parent)
|
|
|
|
, QtWayland::xdg_toplevel(surface->get_toplevel())
|
|
|
|
, m_xdgSurface(surface)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgToplevel::~XdgToplevel()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgSurface *XdgToplevel::xdgSurface() const
|
|
|
|
{
|
|
|
|
return m_xdgSurface.data();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevel::xdg_toplevel_configure(int32_t width, int32_t height, wl_array *states)
|
|
|
|
{
|
|
|
|
States requestedStates;
|
|
|
|
|
|
|
|
const uint32_t *stateData = static_cast<const uint32_t *>(states->data);
|
|
|
|
const size_t stateCount = states->size / sizeof(uint32_t);
|
|
|
|
|
|
|
|
for (size_t i = 0; i < stateCount; ++i) {
|
|
|
|
switch (stateData[i]) {
|
|
|
|
case QtWayland::xdg_toplevel::state_maximized:
|
|
|
|
requestedStates |= State::Maximized;
|
|
|
|
break;
|
|
|
|
case QtWayland::xdg_toplevel::state_fullscreen:
|
|
|
|
requestedStates |= State::Fullscreen;
|
|
|
|
break;
|
|
|
|
case QtWayland::xdg_toplevel::state_resizing:
|
|
|
|
requestedStates |= State::Resizing;
|
|
|
|
break;
|
|
|
|
case QtWayland::xdg_toplevel::state_activated:
|
|
|
|
requestedStates |= State::Activated;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT configureRequested(QSize(width, height), requestedStates);
|
2020-09-02 09:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevel::xdg_toplevel_close()
|
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT closeRequested();
|
2020-09-02 09:27:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
XdgPositioner::XdgPositioner(XdgShell *shell)
|
|
|
|
: QtWayland::xdg_positioner(shell->create_positioner())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgPositioner::~XdgPositioner()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgPopup::XdgPopup(XdgSurface *surface, XdgSurface *parentSurface, XdgPositioner *positioner, QObject *parent)
|
|
|
|
: QObject(parent)
|
|
|
|
, QtWayland::xdg_popup(surface->get_popup(parentSurface->object(), positioner->object()))
|
|
|
|
, m_xdgSurface(surface)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgPopup::~XdgPopup()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgSurface *XdgPopup::xdgSurface() const
|
|
|
|
{
|
|
|
|
return m_xdgSurface.data();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgPopup::xdg_popup_configure(int32_t x, int32_t y, int32_t width, int32_t height)
|
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT configureRequested(QRect(x, y, width, height));
|
2020-09-02 09:27:08 +00:00
|
|
|
}
|
|
|
|
|
2021-05-11 05:26:51 +00:00
|
|
|
void XdgPopup::xdg_popup_popup_done()
|
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT doneReceived();
|
2021-05-11 05:26:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
XdgDecorationManagerV1::~XdgDecorationManagerV1()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgToplevelDecorationV1::XdgToplevelDecorationV1(XdgDecorationManagerV1 *manager,
|
|
|
|
XdgToplevel *toplevel, QObject *parent)
|
|
|
|
: QObject(parent)
|
|
|
|
, QtWayland::zxdg_toplevel_decoration_v1(manager->get_toplevel_decoration(toplevel->object()))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgToplevelDecorationV1::~XdgToplevelDecorationV1()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
void XdgToplevelDecorationV1::zxdg_toplevel_decoration_v1_configure(uint32_t m)
|
|
|
|
{
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT configureRequested(mode(m));
|
2021-05-11 05:26:51 +00:00
|
|
|
}
|
|
|
|
|
2021-05-13 19:59:22 +00:00
|
|
|
IdleInhibitManagerV1::~IdleInhibitManagerV1()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
IdleInhibitorV1::IdleInhibitorV1(IdleInhibitManagerV1 *manager, KWayland::Client::Surface *surface)
|
|
|
|
: QtWayland::zwp_idle_inhibitor_v1(manager->create_inhibitor(*surface))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
IdleInhibitorV1::~IdleInhibitorV1()
|
|
|
|
{
|
|
|
|
destroy();
|
|
|
|
}
|
|
|
|
|
2016-06-30 11:32:54 +00:00
|
|
|
static struct {
|
|
|
|
ConnectionThread *connection = nullptr;
|
|
|
|
EventQueue *queue = nullptr;
|
2020-07-22 18:35:41 +00:00
|
|
|
KWayland::Client::Compositor *compositor = nullptr;
|
2019-02-21 22:25:19 +00:00
|
|
|
SubCompositor *subCompositor = nullptr;
|
2016-06-30 11:32:54 +00:00
|
|
|
ServerSideDecorationManager *decoration = nullptr;
|
2018-06-07 09:08:15 +00:00
|
|
|
ShadowManager *shadowManager = nullptr;
|
2020-09-02 09:27:08 +00:00
|
|
|
XdgShell *xdgShell = nullptr;
|
2016-06-30 11:32:54 +00:00
|
|
|
ShmPool *shm = nullptr;
|
|
|
|
Seat *seat = nullptr;
|
|
|
|
PlasmaShell *plasmaShell = nullptr;
|
|
|
|
PlasmaWindowManagement *windowManagement = nullptr;
|
2016-11-25 06:17:43 +00:00
|
|
|
PointerConstraints *pointerConstraints = nullptr;
|
2017-08-18 14:15:19 +00:00
|
|
|
Registry *registry = nullptr;
|
2021-09-08 09:04:44 +00:00
|
|
|
WaylandOutputManagementV2 *outputManagementV2 = nullptr;
|
2016-06-30 11:32:54 +00:00
|
|
|
QThread *thread = nullptr;
|
2017-08-18 14:15:19 +00:00
|
|
|
QVector<Output*> outputs;
|
2021-09-08 09:04:44 +00:00
|
|
|
QVector<WaylandOutputDeviceV2 *> outputDevicesV2;
|
2021-05-13 19:59:22 +00:00
|
|
|
IdleInhibitManagerV1 *idleInhibitManagerV1 = nullptr;
|
2017-12-22 14:22:24 +00:00
|
|
|
AppMenuManager *appMenu = nullptr;
|
2021-05-11 05:26:51 +00:00
|
|
|
XdgDecorationManagerV1 *xdgDecorationManagerV1 = nullptr;
|
2020-09-01 08:58:46 +00:00
|
|
|
TextInputManager *textInputManager = nullptr;
|
|
|
|
QtWayland::zwp_input_panel_v1 *inputPanelV1 = nullptr;
|
|
|
|
MockInputMethod *inputMethodV1 = nullptr;
|
|
|
|
QtWayland::zwp_input_method_context_v1 *inputMethodContextV1 = nullptr;
|
2020-08-07 19:01:42 +00:00
|
|
|
LayerShellV1 *layerShellV1 = nullptr;
|
2020-09-25 06:51:04 +00:00
|
|
|
TextInputManagerV3 *textInputManagerV3 = nullptr;
|
2016-06-30 11:32:54 +00:00
|
|
|
} s_waylandConnection;
|
|
|
|
|
2021-07-20 23:37:58 +00:00
|
|
|
AbstractClient *inputPanelClient()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.inputMethodV1->client();
|
|
|
|
}
|
|
|
|
|
2021-12-23 08:42:00 +00:00
|
|
|
MockInputMethod *inputMethod()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.inputMethodV1;
|
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
KWayland::Client::Surface *inputPanelSurface()
|
2021-07-20 23:37:58 +00:00
|
|
|
{
|
|
|
|
return s_waylandConnection.inputMethodV1->inputPanelSurface();
|
|
|
|
}
|
|
|
|
|
2020-09-01 08:58:46 +00:00
|
|
|
MockInputMethod::MockInputMethod(struct wl_registry *registry, int id, int version)
|
|
|
|
: QtWayland::zwp_input_method_v1(registry, id, version)
|
|
|
|
{
|
2020-11-27 19:57:24 +00:00
|
|
|
|
2020-09-01 08:58:46 +00:00
|
|
|
}
|
|
|
|
MockInputMethod::~MockInputMethod()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void MockInputMethod::zwp_input_method_v1_activate(struct ::zwp_input_method_context_v1 *context)
|
|
|
|
{
|
2020-09-10 07:10:31 +00:00
|
|
|
Q_UNUSED(context)
|
2020-09-01 08:58:46 +00:00
|
|
|
if (!m_inputSurface) {
|
|
|
|
m_inputSurface = Test::createSurface();
|
|
|
|
m_inputMethodSurface = Test::createInputPanelSurfaceV1(m_inputSurface, s_waylandConnection.outputs.first());
|
|
|
|
}
|
2021-12-23 08:42:00 +00:00
|
|
|
m_context = context;
|
2022-01-19 01:50:33 +00:00
|
|
|
m_client = Test::renderAndWaitForShown(m_inputSurface, QSize(1280, 400), Qt::blue);
|
2021-12-23 08:42:00 +00:00
|
|
|
|
|
|
|
Q_EMIT activate();
|
2020-09-01 08:58:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MockInputMethod::zwp_input_method_v1_deactivate(struct ::zwp_input_method_context_v1 *context)
|
|
|
|
{
|
2021-12-23 08:42:00 +00:00
|
|
|
QCOMPARE(context, m_context);
|
2020-09-01 08:58:46 +00:00
|
|
|
zwp_input_method_context_v1_destroy(context);
|
2021-12-23 08:42:00 +00:00
|
|
|
m_context = nullptr;
|
2020-09-01 08:58:46 +00:00
|
|
|
|
|
|
|
if (m_inputSurface) {
|
|
|
|
m_inputSurface->release();
|
|
|
|
m_inputSurface->destroy();
|
|
|
|
delete m_inputSurface;
|
|
|
|
m_inputSurface = nullptr;
|
|
|
|
delete m_inputMethodSurface;
|
|
|
|
m_inputMethodSurface = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-03 13:31:14 +00:00
|
|
|
bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
|
2016-06-30 11:32:54 +00:00
|
|
|
{
|
|
|
|
if (s_waylandConnection.connection) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-12-03 13:31:14 +00:00
|
|
|
|
|
|
|
int sx[2];
|
|
|
|
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
KWin::waylandServer()->display()->createClient(sx[0]);
|
2016-06-30 11:32:54 +00:00
|
|
|
// setup connection
|
|
|
|
s_waylandConnection.connection = new ConnectionThread;
|
|
|
|
QSignalSpy connectedSpy(s_waylandConnection.connection, &ConnectionThread::connected);
|
|
|
|
if (!connectedSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-12-03 13:31:14 +00:00
|
|
|
s_waylandConnection.connection->setSocketFd(sx[1]);
|
2016-06-30 11:32:54 +00:00
|
|
|
|
|
|
|
s_waylandConnection.thread = new QThread(kwinApp());
|
|
|
|
s_waylandConnection.connection->moveToThread(s_waylandConnection.thread);
|
|
|
|
s_waylandConnection.thread->start();
|
|
|
|
|
|
|
|
s_waylandConnection.connection->initConnection();
|
|
|
|
if (!connectedSpy.wait()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
s_waylandConnection.queue = new EventQueue;
|
|
|
|
s_waylandConnection.queue->setup(s_waylandConnection.connection);
|
|
|
|
if (!s_waylandConnection.queue->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-08-18 14:15:19 +00:00
|
|
|
Registry *registry = new Registry;
|
|
|
|
s_waylandConnection.registry = registry;
|
|
|
|
registry->setEventQueue(s_waylandConnection.queue);
|
|
|
|
|
|
|
|
QObject::connect(registry, &Registry::outputAnnounced, [=](quint32 name, quint32 version) {
|
2021-01-14 08:21:59 +00:00
|
|
|
Output* output = registry->createOutput(name, version, s_waylandConnection.registry);
|
2017-08-18 14:15:19 +00:00
|
|
|
s_waylandConnection.outputs << output;
|
|
|
|
QObject::connect(output, &Output::removed, [=]() {
|
|
|
|
output->deleteLater();
|
|
|
|
s_waylandConnection.outputs.removeOne(output);
|
2020-08-20 08:53:33 +00:00
|
|
|
});
|
|
|
|
QObject::connect(output, &Output::destroyed, [=]() {
|
|
|
|
s_waylandConnection.outputs.removeOne(output);
|
2017-08-18 14:15:19 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-01 08:58:46 +00:00
|
|
|
QObject::connect(registry, &Registry::interfaceAnnounced, [=](const QByteArray &interface, quint32 name, quint32 version) {
|
|
|
|
if (flags & AdditionalWaylandInterface::InputMethodV1) {
|
|
|
|
if (interface == QByteArrayLiteral("zwp_input_method_v1")) {
|
|
|
|
s_waylandConnection.inputMethodV1 = new MockInputMethod(*registry, name, version);
|
|
|
|
} else if (interface == QByteArrayLiteral("zwp_input_panel_v1")) {
|
|
|
|
s_waylandConnection.inputPanelV1 = new QtWayland::zwp_input_panel_v1(*registry, name, version);
|
|
|
|
}
|
|
|
|
}
|
2020-08-07 19:01:42 +00:00
|
|
|
if (flags & AdditionalWaylandInterface::LayerShellV1) {
|
|
|
|
if (interface == QByteArrayLiteral("zwlr_layer_shell_v1")) {
|
|
|
|
s_waylandConnection.layerShellV1 = new LayerShellV1();
|
|
|
|
s_waylandConnection.layerShellV1->init(*registry, name, version);
|
|
|
|
}
|
|
|
|
}
|
2020-09-25 06:51:04 +00:00
|
|
|
if (flags & AdditionalWaylandInterface::TextInputManagerV3) {
|
|
|
|
// do something
|
|
|
|
if (interface == QByteArrayLiteral("zwp_text_input_manager_v3")) {
|
|
|
|
s_waylandConnection.textInputManagerV3 = new TextInputManagerV3();
|
|
|
|
s_waylandConnection.textInputManagerV3->init(*registry, name, version);
|
|
|
|
}
|
|
|
|
}
|
2020-09-02 09:27:08 +00:00
|
|
|
if (interface == QByteArrayLiteral("xdg_wm_base")) {
|
|
|
|
s_waylandConnection.xdgShell = new XdgShell();
|
|
|
|
s_waylandConnection.xdgShell->init(*registry, name, version);
|
|
|
|
}
|
2021-05-11 05:26:51 +00:00
|
|
|
if (flags & AdditionalWaylandInterface::XdgDecorationV1) {
|
|
|
|
if (interface == zxdg_decoration_manager_v1_interface.name) {
|
|
|
|
s_waylandConnection.xdgDecorationManagerV1 = new XdgDecorationManagerV1();
|
|
|
|
s_waylandConnection.xdgDecorationManagerV1->init(*registry, name, version);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2021-05-13 19:59:22 +00:00
|
|
|
if (flags & AdditionalWaylandInterface::IdleInhibitV1) {
|
|
|
|
if (interface == zwp_idle_inhibit_manager_v1_interface.name) {
|
|
|
|
s_waylandConnection.idleInhibitManagerV1 = new IdleInhibitManagerV1();
|
|
|
|
s_waylandConnection.idleInhibitManagerV1->init(*registry, name, version);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2021-09-08 09:04:44 +00:00
|
|
|
if (flags & AdditionalWaylandInterface::OutputDeviceV2) {
|
|
|
|
if (interface == kde_output_device_v2_interface.name) {
|
|
|
|
WaylandOutputDeviceV2 *device = new WaylandOutputDeviceV2(name);
|
|
|
|
device->init(*registry, name, version);
|
|
|
|
|
|
|
|
s_waylandConnection.outputDevicesV2 << device;
|
|
|
|
|
|
|
|
QObject::connect(device, &WaylandOutputDeviceV2::destroyed, [=]() {
|
|
|
|
s_waylandConnection.outputDevicesV2.removeOne(device);
|
|
|
|
device->deleteLater();
|
|
|
|
});
|
|
|
|
|
|
|
|
QObject::connect(registry, &KWayland::Client::Registry::interfaceRemoved, device, [name, device](const quint32 &interfaceName) {
|
|
|
|
if (name == interfaceName) {
|
|
|
|
s_waylandConnection.outputDevicesV2.removeOne(device);
|
|
|
|
device->deleteLater();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (flags & AdditionalWaylandInterface::OutputManagementV2) {
|
|
|
|
if (interface == kde_output_management_v2_interface.name) {
|
|
|
|
s_waylandConnection.outputManagementV2 = new WaylandOutputManagementV2(*registry, name, version);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2020-09-01 08:58:46 +00:00
|
|
|
});
|
|
|
|
|
2017-08-18 14:15:19 +00:00
|
|
|
QSignalSpy allAnnounced(registry, &Registry::interfacesAnnounced);
|
2016-06-30 11:32:54 +00:00
|
|
|
if (!allAnnounced.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-08-18 14:15:19 +00:00
|
|
|
registry->create(s_waylandConnection.connection);
|
|
|
|
if (!registry->isValid()) {
|
2016-06-30 11:32:54 +00:00
|
|
|
return false;
|
|
|
|
}
|
2017-08-18 14:15:19 +00:00
|
|
|
registry->setup();
|
2016-06-30 11:32:54 +00:00
|
|
|
if (!allAnnounced.wait()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-08-18 14:15:19 +00:00
|
|
|
s_waylandConnection.compositor = registry->createCompositor(registry->interface(Registry::Interface::Compositor).name, registry->interface(Registry::Interface::Compositor).version);
|
2016-06-30 11:32:54 +00:00
|
|
|
if (!s_waylandConnection.compositor->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-02-21 22:25:19 +00:00
|
|
|
s_waylandConnection.subCompositor = registry->createSubCompositor(registry->interface(Registry::Interface::SubCompositor).name, registry->interface(Registry::Interface::SubCompositor).version);
|
|
|
|
if (!s_waylandConnection.subCompositor->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-08-18 14:15:19 +00:00
|
|
|
s_waylandConnection.shm = registry->createShmPool(registry->interface(Registry::Interface::Shm).name, registry->interface(Registry::Interface::Shm).version);
|
2016-06-30 11:32:54 +00:00
|
|
|
if (!s_waylandConnection.shm->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (flags.testFlag(AdditionalWaylandInterface::Seat)) {
|
2017-08-18 14:15:19 +00:00
|
|
|
s_waylandConnection.seat = registry->createSeat(registry->interface(Registry::Interface::Seat).name, registry->interface(Registry::Interface::Seat).version);
|
2016-06-30 11:32:54 +00:00
|
|
|
if (!s_waylandConnection.seat->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2018-06-07 09:08:15 +00:00
|
|
|
if (flags.testFlag(AdditionalWaylandInterface::ShadowManager)) {
|
|
|
|
s_waylandConnection.shadowManager = registry->createShadowManager(registry->interface(Registry::Interface::Shadow).name,
|
|
|
|
registry->interface(Registry::Interface::Shadow).version);
|
|
|
|
if (!s_waylandConnection.shadowManager->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-06-30 11:32:54 +00:00
|
|
|
if (flags.testFlag(AdditionalWaylandInterface::Decoration)) {
|
2017-08-18 14:15:19 +00:00
|
|
|
s_waylandConnection.decoration = registry->createServerSideDecorationManager(registry->interface(Registry::Interface::ServerSideDecorationManager).name,
|
|
|
|
registry->interface(Registry::Interface::ServerSideDecorationManager).version);
|
2016-06-30 11:32:54 +00:00
|
|
|
if (!s_waylandConnection.decoration->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (flags.testFlag(AdditionalWaylandInterface::PlasmaShell)) {
|
2017-08-18 14:15:19 +00:00
|
|
|
s_waylandConnection.plasmaShell = registry->createPlasmaShell(registry->interface(Registry::Interface::PlasmaShell).name,
|
|
|
|
registry->interface(Registry::Interface::PlasmaShell).version);
|
2016-06-30 11:32:54 +00:00
|
|
|
if (!s_waylandConnection.plasmaShell->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (flags.testFlag(AdditionalWaylandInterface::WindowManagement)) {
|
2017-08-18 14:15:19 +00:00
|
|
|
s_waylandConnection.windowManagement = registry->createPlasmaWindowManagement(registry->interface(Registry::Interface::PlasmaWindowManagement).name,
|
|
|
|
registry->interface(Registry::Interface::PlasmaWindowManagement).version);
|
2016-06-30 11:32:54 +00:00
|
|
|
if (!s_waylandConnection.windowManagement->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-11-25 06:17:43 +00:00
|
|
|
if (flags.testFlag(AdditionalWaylandInterface::PointerConstraints)) {
|
2017-08-18 14:15:19 +00:00
|
|
|
s_waylandConnection.pointerConstraints = registry->createPointerConstraints(registry->interface(Registry::Interface::PointerConstraintsUnstableV1).name,
|
|
|
|
registry->interface(Registry::Interface::PointerConstraintsUnstableV1).version);
|
2016-11-25 06:17:43 +00:00
|
|
|
if (!s_waylandConnection.pointerConstraints->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2017-12-22 14:22:24 +00:00
|
|
|
if (flags.testFlag(AdditionalWaylandInterface::AppMenu)) {
|
|
|
|
s_waylandConnection.appMenu = registry->createAppMenuManager(registry->interface(Registry::Interface::AppMenu).name, registry->interface(Registry::Interface::AppMenu).version);
|
|
|
|
if (!s_waylandConnection.appMenu->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-09-01 08:58:46 +00:00
|
|
|
if (flags.testFlag(AdditionalWaylandInterface::TextInputManagerV2)) {
|
|
|
|
s_waylandConnection.textInputManager = registry->createTextInputManager(registry->interface(Registry::Interface::TextInputManagerUnstableV2).name, registry->interface(Registry::Interface::TextInputManagerUnstableV2).version);
|
|
|
|
if (!s_waylandConnection.textInputManager->isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2016-06-30 11:32:54 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void destroyWaylandConnection()
|
|
|
|
{
|
|
|
|
delete s_waylandConnection.compositor;
|
|
|
|
s_waylandConnection.compositor = nullptr;
|
2019-02-21 22:25:19 +00:00
|
|
|
delete s_waylandConnection.subCompositor;
|
|
|
|
s_waylandConnection.subCompositor = nullptr;
|
2016-06-30 11:32:54 +00:00
|
|
|
delete s_waylandConnection.windowManagement;
|
|
|
|
s_waylandConnection.windowManagement = nullptr;
|
|
|
|
delete s_waylandConnection.plasmaShell;
|
|
|
|
s_waylandConnection.plasmaShell = nullptr;
|
|
|
|
delete s_waylandConnection.decoration;
|
|
|
|
s_waylandConnection.decoration = nullptr;
|
|
|
|
delete s_waylandConnection.decoration;
|
|
|
|
s_waylandConnection.decoration = nullptr;
|
|
|
|
delete s_waylandConnection.seat;
|
|
|
|
s_waylandConnection.seat = nullptr;
|
2016-11-25 06:17:43 +00:00
|
|
|
delete s_waylandConnection.pointerConstraints;
|
|
|
|
s_waylandConnection.pointerConstraints = nullptr;
|
2020-09-02 09:27:08 +00:00
|
|
|
delete s_waylandConnection.xdgShell;
|
|
|
|
s_waylandConnection.xdgShell = nullptr;
|
2018-06-07 09:08:15 +00:00
|
|
|
delete s_waylandConnection.shadowManager;
|
|
|
|
s_waylandConnection.shadowManager = nullptr;
|
2021-05-13 19:59:22 +00:00
|
|
|
delete s_waylandConnection.idleInhibitManagerV1;
|
|
|
|
s_waylandConnection.idleInhibitManagerV1 = nullptr;
|
2016-06-30 11:32:54 +00:00
|
|
|
delete s_waylandConnection.shm;
|
|
|
|
s_waylandConnection.shm = nullptr;
|
|
|
|
delete s_waylandConnection.queue;
|
|
|
|
s_waylandConnection.queue = nullptr;
|
2017-08-18 14:15:19 +00:00
|
|
|
delete s_waylandConnection.registry;
|
|
|
|
s_waylandConnection.registry = nullptr;
|
2017-12-22 14:22:24 +00:00
|
|
|
delete s_waylandConnection.appMenu;
|
|
|
|
s_waylandConnection.appMenu = nullptr;
|
2021-05-11 05:26:51 +00:00
|
|
|
delete s_waylandConnection.xdgDecorationManagerV1;
|
|
|
|
s_waylandConnection.xdgDecorationManagerV1 = nullptr;
|
2020-09-01 08:58:46 +00:00
|
|
|
delete s_waylandConnection.textInputManager;
|
|
|
|
s_waylandConnection.textInputManager = nullptr;
|
|
|
|
delete s_waylandConnection.inputPanelV1;
|
|
|
|
s_waylandConnection.inputPanelV1 = nullptr;
|
2020-08-07 19:01:42 +00:00
|
|
|
delete s_waylandConnection.layerShellV1;
|
|
|
|
s_waylandConnection.layerShellV1 = nullptr;
|
2021-09-08 09:04:44 +00:00
|
|
|
delete s_waylandConnection.outputManagementV2;
|
|
|
|
s_waylandConnection.outputManagementV2 = nullptr;
|
2016-06-30 11:32:54 +00:00
|
|
|
if (s_waylandConnection.thread) {
|
|
|
|
QSignalSpy spy(s_waylandConnection.connection, &QObject::destroyed);
|
|
|
|
s_waylandConnection.connection->deleteLater();
|
2016-08-08 08:10:26 +00:00
|
|
|
if (spy.isEmpty()) {
|
|
|
|
QVERIFY(spy.wait());
|
|
|
|
}
|
2016-06-30 11:32:54 +00:00
|
|
|
s_waylandConnection.thread->quit();
|
|
|
|
s_waylandConnection.thread->wait();
|
|
|
|
delete s_waylandConnection.thread;
|
|
|
|
s_waylandConnection.thread = nullptr;
|
|
|
|
s_waylandConnection.connection = nullptr;
|
|
|
|
}
|
2021-01-14 08:21:59 +00:00
|
|
|
s_waylandConnection.outputs.clear();
|
2021-09-08 09:04:44 +00:00
|
|
|
s_waylandConnection.outputDevicesV2.clear();
|
2016-06-30 11:32:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ConnectionThread *waylandConnection()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.connection;
|
|
|
|
}
|
|
|
|
|
2020-07-22 18:35:41 +00:00
|
|
|
KWayland::Client::Compositor *waylandCompositor()
|
2016-06-30 11:32:54 +00:00
|
|
|
{
|
|
|
|
return s_waylandConnection.compositor;
|
|
|
|
}
|
|
|
|
|
2019-02-21 22:25:19 +00:00
|
|
|
SubCompositor *waylandSubCompositor()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.subCompositor;
|
|
|
|
}
|
|
|
|
|
2018-06-07 09:08:15 +00:00
|
|
|
ShadowManager *waylandShadowManager()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.shadowManager;
|
|
|
|
}
|
|
|
|
|
2016-06-30 11:32:54 +00:00
|
|
|
ShmPool *waylandShmPool()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.shm;
|
|
|
|
}
|
|
|
|
|
|
|
|
Seat *waylandSeat()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.seat;
|
|
|
|
}
|
|
|
|
|
|
|
|
ServerSideDecorationManager *waylandServerSideDecoration()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.decoration;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlasmaShell *waylandPlasmaShell()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.plasmaShell;
|
|
|
|
}
|
|
|
|
|
|
|
|
PlasmaWindowManagement *waylandWindowManagement()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.windowManagement;
|
|
|
|
}
|
|
|
|
|
2016-11-25 06:17:43 +00:00
|
|
|
PointerConstraints *waylandPointerConstraints()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.pointerConstraints;
|
|
|
|
}
|
|
|
|
|
2017-12-22 14:22:24 +00:00
|
|
|
AppMenuManager* waylandAppMenuManager()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.appMenu;
|
|
|
|
}
|
|
|
|
|
2021-09-08 09:04:44 +00:00
|
|
|
KWin::Test::WaylandOutputManagementV2 *waylandOutputManagementV2()
|
2020-06-24 09:17:42 +00:00
|
|
|
{
|
2021-09-08 09:04:44 +00:00
|
|
|
return s_waylandConnection.outputManagementV2;
|
2020-06-24 09:17:42 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 08:58:46 +00:00
|
|
|
TextInputManager *waylandTextInputManager()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.textInputManager;
|
|
|
|
}
|
|
|
|
|
2020-09-25 06:51:04 +00:00
|
|
|
TextInputManagerV3 *waylandTextInputManagerV3()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.textInputManagerV3;
|
|
|
|
}
|
|
|
|
|
2020-08-07 19:01:42 +00:00
|
|
|
QVector<KWayland::Client::Output *> waylandOutputs()
|
|
|
|
{
|
|
|
|
return s_waylandConnection.outputs;
|
|
|
|
}
|
2019-01-01 17:37:18 +00:00
|
|
|
|
2021-09-08 09:04:44 +00:00
|
|
|
QVector<KWin::Test::WaylandOutputDeviceV2 *> waylandOutputDevicesV2()
|
2021-01-14 08:21:59 +00:00
|
|
|
{
|
2021-09-08 09:04:44 +00:00
|
|
|
return s_waylandConnection.outputDevicesV2;
|
2021-01-14 08:21:59 +00:00
|
|
|
}
|
|
|
|
|
2021-05-15 08:43:37 +00:00
|
|
|
bool waitForWaylandSurface(AbstractClient *client)
|
|
|
|
{
|
|
|
|
if (client->surface()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
QSignalSpy surfaceChangedSpy(client, &Toplevel::surfaceChanged);
|
|
|
|
return surfaceChangedSpy.wait();
|
|
|
|
}
|
|
|
|
|
2016-06-30 11:32:54 +00:00
|
|
|
bool waitForWaylandPointer()
|
|
|
|
{
|
|
|
|
if (!s_waylandConnection.seat) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
QSignalSpy hasPointerSpy(s_waylandConnection.seat, &Seat::hasPointerChanged);
|
|
|
|
if (!hasPointerSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return hasPointerSpy.wait();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool waitForWaylandTouch()
|
|
|
|
{
|
|
|
|
if (!s_waylandConnection.seat) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
QSignalSpy hasTouchSpy(s_waylandConnection.seat, &Seat::hasTouchChanged);
|
|
|
|
if (!hasTouchSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return hasTouchSpy.wait();
|
|
|
|
}
|
|
|
|
|
2016-08-11 09:17:09 +00:00
|
|
|
bool waitForWaylandKeyboard()
|
|
|
|
{
|
|
|
|
if (!s_waylandConnection.seat) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
QSignalSpy hasKeyboardSpy(s_waylandConnection.seat, &Seat::hasKeyboardChanged);
|
|
|
|
if (!hasKeyboardSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return hasKeyboardSpy.wait();
|
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
void render(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format)
|
2016-06-30 11:32:54 +00:00
|
|
|
{
|
|
|
|
QImage img(size, format);
|
|
|
|
img.fill(color);
|
2017-07-31 14:12:37 +00:00
|
|
|
render(surface, img);
|
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
void render(KWayland::Client::Surface *surface, const QImage &img)
|
2017-07-31 14:12:37 +00:00
|
|
|
{
|
2016-06-30 11:32:54 +00:00
|
|
|
surface->attachBuffer(s_waylandConnection.shm->createBuffer(img));
|
2017-07-31 14:12:37 +00:00
|
|
|
surface->damage(QRect(QPoint(0, 0), img.size()));
|
2021-09-03 17:54:03 +00:00
|
|
|
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
2016-06-30 11:32:54 +00:00
|
|
|
}
|
|
|
|
|
2020-03-04 07:55:26 +00:00
|
|
|
AbstractClient *waitForWaylandWindowShown(int timeout)
|
2016-07-01 07:54:44 +00:00
|
|
|
{
|
2020-07-22 18:35:41 +00:00
|
|
|
QSignalSpy clientAddedSpy(workspace(), &Workspace::clientAdded);
|
2016-07-01 07:54:44 +00:00
|
|
|
if (!clientAddedSpy.isValid()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (!clientAddedSpy.wait(timeout)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2020-03-04 07:55:26 +00:00
|
|
|
return clientAddedSpy.first().first().value<AbstractClient *>();
|
2016-07-01 07:54:44 +00:00
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
AbstractClient *renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format, int timeout)
|
2016-07-01 07:54:44 +00:00
|
|
|
{
|
2020-07-22 18:35:41 +00:00
|
|
|
QSignalSpy clientAddedSpy(workspace(), &Workspace::clientAdded);
|
2016-07-01 07:54:44 +00:00
|
|
|
if (!clientAddedSpy.isValid()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
render(surface, size, color, format);
|
|
|
|
flushWaylandConnection();
|
|
|
|
if (!clientAddedSpy.wait(timeout)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2020-03-04 07:55:26 +00:00
|
|
|
return clientAddedSpy.first().first().value<AbstractClient *>();
|
2016-07-01 07:54:44 +00:00
|
|
|
}
|
|
|
|
|
2016-06-30 11:32:54 +00:00
|
|
|
void flushWaylandConnection()
|
|
|
|
{
|
|
|
|
if (s_waylandConnection.connection) {
|
|
|
|
s_waylandConnection.connection->flush();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
KWayland::Client::Surface *createSurface(QObject *parent)
|
2016-06-30 11:32:54 +00:00
|
|
|
{
|
|
|
|
if (!s_waylandConnection.compositor) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
auto s = s_waylandConnection.compositor->createSurface(parent);
|
|
|
|
if (!s->isValid()) {
|
|
|
|
delete s;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
SubSurface *createSubSurface(KWayland::Client::Surface *surface, KWayland::Client::Surface *parentSurface, QObject *parent)
|
2019-02-21 22:25:19 +00:00
|
|
|
{
|
|
|
|
if (!s_waylandConnection.subCompositor) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
auto s = s_waylandConnection.subCompositor->createSubSurface(surface, parentSurface, parent);
|
|
|
|
if (!s->isValid()) {
|
|
|
|
delete s;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
LayerSurfaceV1 *createLayerSurfaceV1(KWayland::Client::Surface *surface, const QString &scope, Output *output, LayerShellV1::layer layer)
|
2020-08-07 19:01:42 +00:00
|
|
|
{
|
|
|
|
LayerShellV1 *shell = s_waylandConnection.layerShellV1;
|
|
|
|
if (!shell) {
|
|
|
|
qWarning() << "Could not create a layer surface because the layer shell global is not bound";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ::wl_output *nativeOutput = nullptr;
|
|
|
|
if (output) {
|
|
|
|
nativeOutput = *output;
|
|
|
|
}
|
|
|
|
|
|
|
|
LayerSurfaceV1 *shellSurface = new LayerSurfaceV1();
|
|
|
|
shellSurface->init(shell->get_layer_surface(*surface, nativeOutput, layer, scope));
|
|
|
|
|
|
|
|
return shellSurface;
|
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(KWayland::Client::Surface *surface, Output *output)
|
2020-09-01 08:58:46 +00:00
|
|
|
{
|
|
|
|
if (!s_waylandConnection.inputPanelV1) {
|
|
|
|
qWarning() << "Unable to create the input panel surface. The interface input_panel global is not bound";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
QtWayland::zwp_input_panel_surface_v1 *s = new QtWayland::zwp_input_panel_surface_v1(s_waylandConnection.inputPanelV1->get_input_panel_surface(*surface));
|
|
|
|
|
|
|
|
if (!s->isInitialized()) {
|
|
|
|
delete s;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
s->set_toplevel(output->output(), QtWayland::zwp_input_panel_surface_v1::position_center_bottom);
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2020-09-02 09:27:08 +00:00
|
|
|
static void waitForConfigured(XdgSurface *shellSurface)
|
|
|
|
{
|
|
|
|
QSignalSpy surfaceConfigureRequestedSpy(shellSurface, &XdgSurface::configureRequested);
|
|
|
|
QVERIFY(surfaceConfigureRequestedSpy.isValid());
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
shellSurface->surface()->commit(KWayland::Client::Surface::CommitFlag::None);
|
2020-09-02 09:27:08 +00:00
|
|
|
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
|
|
|
|
|
|
|
shellSurface->ack_configure(surfaceConfigureRequestedSpy.last().first().toUInt());
|
|
|
|
}
|
|
|
|
|
2021-12-09 07:47:55 +00:00
|
|
|
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent)
|
|
|
|
{
|
|
|
|
return createXdgToplevelSurface(surface, CreationSetup::CreateAndConfigure, parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, CreationSetup configureMode, QObject *parent)
|
2020-09-02 09:27:08 +00:00
|
|
|
{
|
|
|
|
XdgShell *shell = s_waylandConnection.xdgShell;
|
|
|
|
|
|
|
|
if (!shell) {
|
|
|
|
qWarning() << "Could not create an xdg_toplevel surface because xdg_wm_base global is not bound";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-05-11 05:26:51 +00:00
|
|
|
XdgSurface *xdgSurface = new XdgSurface(shell, surface);
|
2020-09-02 09:27:08 +00:00
|
|
|
XdgToplevel *xdgToplevel = new XdgToplevel(xdgSurface, parent);
|
|
|
|
|
|
|
|
if (configureMode == CreationSetup::CreateAndConfigure) {
|
|
|
|
waitForConfigured(xdgSurface);
|
|
|
|
}
|
|
|
|
|
|
|
|
return xdgToplevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
XdgPositioner *createXdgPositioner()
|
|
|
|
{
|
|
|
|
XdgShell *shell = s_waylandConnection.xdgShell;
|
|
|
|
|
|
|
|
if (!shell) {
|
|
|
|
qWarning() << "Could not create an xdg_positioner object because xdg_wm_base global is not bound";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new XdgPositioner(shell);
|
|
|
|
}
|
|
|
|
|
2021-09-03 17:54:03 +00:00
|
|
|
XdgPopup *createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface, XdgPositioner *positioner,
|
2021-12-09 07:47:55 +00:00
|
|
|
CreationSetup configureMode, QObject *parent)
|
2020-09-02 09:27:08 +00:00
|
|
|
{
|
|
|
|
XdgShell *shell = s_waylandConnection.xdgShell;
|
|
|
|
|
|
|
|
if (!shell) {
|
|
|
|
qWarning() << "Could not create an xdg_popup surface because xdg_wm_base global is not bound";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-05-11 05:26:51 +00:00
|
|
|
XdgSurface *xdgSurface = new XdgSurface(shell, surface);
|
2020-09-02 09:27:08 +00:00
|
|
|
XdgPopup *xdgPopup = new XdgPopup(xdgSurface, parentSurface, positioner, parent);
|
|
|
|
|
|
|
|
if (configureMode == CreationSetup::CreateAndConfigure) {
|
|
|
|
waitForConfigured(xdgSurface);
|
|
|
|
}
|
|
|
|
|
|
|
|
return xdgPopup;
|
|
|
|
}
|
|
|
|
|
2021-05-11 05:26:51 +00:00
|
|
|
XdgToplevelDecorationV1 *createXdgToplevelDecorationV1(XdgToplevel *toplevel, QObject *parent)
|
|
|
|
{
|
|
|
|
XdgDecorationManagerV1 *manager = s_waylandConnection.xdgDecorationManagerV1;
|
|
|
|
|
|
|
|
if (!manager) {
|
|
|
|
qWarning() << "Could not create an xdg_toplevel_decoration_v1 because xdg_decoration_manager_v1 global is not bound";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new XdgToplevelDecorationV1(manager, toplevel, parent);
|
|
|
|
}
|
|
|
|
|
2021-05-13 19:59:22 +00:00
|
|
|
IdleInhibitorV1 *createIdleInhibitorV1(KWayland::Client::Surface *surface)
|
|
|
|
{
|
|
|
|
IdleInhibitManagerV1 *manager = s_waylandConnection.idleInhibitManagerV1;
|
|
|
|
if (!manager) {
|
|
|
|
qWarning() << "Could not create an idle_inhibitor_v1 because idle_inhibit_manager_v1 global is not bound";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return new IdleInhibitorV1(manager, surface);
|
|
|
|
}
|
|
|
|
|
2016-07-01 10:37:09 +00:00
|
|
|
bool waitForWindowDestroyed(AbstractClient *client)
|
|
|
|
{
|
|
|
|
QSignalSpy destroyedSpy(client, &QObject::destroyed);
|
|
|
|
if (!destroyedSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return destroyedSpy.wait();
|
|
|
|
}
|
|
|
|
|
2022-02-28 18:58:35 +00:00
|
|
|
#if KWIN_BUILD_SCREENLOCKER
|
2016-08-16 06:19:45 +00:00
|
|
|
bool lockScreen()
|
|
|
|
{
|
|
|
|
if (waylandServer()->isScreenLocked()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
QSignalSpy lockStateChangedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged);
|
|
|
|
if (!lockStateChangedSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ScreenLocker::KSldApp::self()->lock(ScreenLocker::EstablishLock::Immediate);
|
|
|
|
if (lockStateChangedSpy.count() != 1) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-08-16 08:23:37 +00:00
|
|
|
if (!waylandServer()->isScreenLocked()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!ScreenLockerWatcher::self()->isLocked()) {
|
|
|
|
QSignalSpy lockedSpy(ScreenLockerWatcher::self(), &ScreenLockerWatcher::locked);
|
|
|
|
if (!lockedSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!lockedSpy.wait()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!ScreenLockerWatcher::self()->isLocked()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2016-08-16 06:19:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool unlockScreen()
|
|
|
|
{
|
|
|
|
QSignalSpy lockStateChangedSpy(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged);
|
|
|
|
if (!lockStateChangedSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
using namespace ScreenLocker;
|
|
|
|
const auto children = KSldApp::self()->children();
|
|
|
|
for (auto it = children.begin(); it != children.end(); ++it) {
|
|
|
|
if (qstrcmp((*it)->metaObject()->className(), "LogindIntegration") != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
QMetaObject::invokeMethod(*it, "requestUnlock");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (waylandServer()->isScreenLocked()) {
|
|
|
|
lockStateChangedSpy.wait();
|
|
|
|
}
|
2016-08-16 08:23:37 +00:00
|
|
|
if (waylandServer()->isScreenLocked()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (ScreenLockerWatcher::self()->isLocked()) {
|
|
|
|
QSignalSpy lockedSpy(ScreenLockerWatcher::self(), &ScreenLockerWatcher::locked);
|
|
|
|
if (!lockedSpy.isValid()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!lockedSpy.wait()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (ScreenLockerWatcher::self()->isLocked()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2016-08-16 06:19:45 +00:00
|
|
|
}
|
2022-02-23 13:27:05 +00:00
|
|
|
#endif // KWIN_BUILD_LOCKSCREEN
|
2016-08-16 06:19:45 +00:00
|
|
|
|
2021-05-08 00:08:22 +00:00
|
|
|
void initWaylandWorkspace()
|
|
|
|
{
|
|
|
|
QSignalSpy workspaceInitializedSpy(waylandServer(), &WaylandServer::initialized);
|
|
|
|
waylandServer()->initWorkspace();
|
|
|
|
QVERIFY(workspaceInitializedSpy.count() || workspaceInitializedSpy.wait());
|
|
|
|
}
|
|
|
|
|
2021-09-08 09:04:44 +00:00
|
|
|
WaylandOutputManagementV2::WaylandOutputManagementV2(struct ::wl_registry *registry, int id, int version)
|
|
|
|
: QObject()
|
|
|
|
, QtWayland::kde_output_management_v2()
|
|
|
|
{
|
|
|
|
init(registry, id, version);
|
|
|
|
}
|
|
|
|
|
|
|
|
WaylandOutputConfigurationV2 *WaylandOutputManagementV2::createConfiguration()
|
|
|
|
{
|
|
|
|
return new WaylandOutputConfigurationV2(create_configuration());
|
|
|
|
}
|
|
|
|
|
|
|
|
WaylandOutputConfigurationV2::WaylandOutputConfigurationV2(struct ::kde_output_configuration_v2 *object)
|
|
|
|
: QObject()
|
|
|
|
, QtWayland::kde_output_configuration_v2()
|
|
|
|
{
|
|
|
|
init(object);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputConfigurationV2::kde_output_configuration_v2_applied()
|
|
|
|
{
|
|
|
|
Q_EMIT applied();
|
|
|
|
}
|
|
|
|
void WaylandOutputConfigurationV2::kde_output_configuration_v2_failed()
|
|
|
|
{
|
|
|
|
Q_EMIT failed();
|
|
|
|
}
|
|
|
|
|
|
|
|
WaylandOutputDeviceV2Mode::WaylandOutputDeviceV2Mode(struct ::kde_output_device_mode_v2 *object)
|
|
|
|
: QtWayland::kde_output_device_mode_v2(object)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
WaylandOutputDeviceV2Mode::~WaylandOutputDeviceV2Mode()
|
|
|
|
{
|
|
|
|
kde_output_device_mode_v2_destroy(object());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2Mode::kde_output_device_mode_v2_size(int32_t width, int32_t height)
|
|
|
|
{
|
|
|
|
m_size = QSize(width, height);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2Mode::kde_output_device_mode_v2_refresh(int32_t refresh)
|
|
|
|
{
|
|
|
|
m_refreshRate = refresh;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2Mode::kde_output_device_mode_v2_preferred()
|
|
|
|
{
|
|
|
|
m_preferred = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2Mode::kde_output_device_mode_v2_removed()
|
|
|
|
{
|
|
|
|
Q_EMIT removed();
|
|
|
|
}
|
|
|
|
|
|
|
|
int WaylandOutputDeviceV2Mode::refreshRate() const
|
|
|
|
{
|
|
|
|
return m_refreshRate;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize WaylandOutputDeviceV2Mode::size() const
|
|
|
|
{
|
|
|
|
return m_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaylandOutputDeviceV2Mode::preferred() const
|
|
|
|
{
|
|
|
|
return m_preferred;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaylandOutputDeviceV2Mode::operator==(const WaylandOutputDeviceV2Mode &other)
|
|
|
|
{
|
|
|
|
return m_size == other.m_size && m_refreshRate == other.m_refreshRate && m_preferred == other.m_preferred;
|
|
|
|
}
|
|
|
|
|
|
|
|
WaylandOutputDeviceV2Mode *WaylandOutputDeviceV2Mode::get(struct ::kde_output_device_mode_v2 *object)
|
|
|
|
{
|
|
|
|
auto mode = QtWayland::kde_output_device_mode_v2::fromObject(object);
|
|
|
|
return static_cast<WaylandOutputDeviceV2Mode *>(mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
WaylandOutputDeviceV2::WaylandOutputDeviceV2(int id)
|
|
|
|
: QObject()
|
|
|
|
, kde_output_device_v2()
|
|
|
|
, m_id(id)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
WaylandOutputDeviceV2::~WaylandOutputDeviceV2()
|
|
|
|
{
|
|
|
|
qDeleteAll(m_modes);
|
|
|
|
|
|
|
|
kde_output_device_v2_destroy(object());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_geometry(int32_t x,
|
|
|
|
int32_t y,
|
|
|
|
int32_t physical_width,
|
|
|
|
int32_t physical_height,
|
|
|
|
int32_t subpixel,
|
|
|
|
const QString &make,
|
|
|
|
const QString &model,
|
|
|
|
int32_t transform)
|
|
|
|
{
|
|
|
|
m_pos = QPoint(x, y);
|
|
|
|
m_physicalSize = QSize(physical_width, physical_height);
|
|
|
|
m_subpixel = subpixel;
|
|
|
|
m_manufacturer = make;
|
|
|
|
m_model = model;
|
|
|
|
m_transform = transform;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_current_mode(struct ::kde_output_device_mode_v2 *mode)
|
|
|
|
{
|
|
|
|
auto m = WaylandOutputDeviceV2Mode::get(mode);
|
|
|
|
|
|
|
|
if (*m == *m_mode) {
|
|
|
|
// unchanged
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_mode = m;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_mode(struct ::kde_output_device_mode_v2 *mode)
|
|
|
|
{
|
|
|
|
WaylandOutputDeviceV2Mode *m = new WaylandOutputDeviceV2Mode(mode);
|
|
|
|
// last mode sent is the current one
|
|
|
|
m_mode = m;
|
|
|
|
m_modes.append(m);
|
|
|
|
|
|
|
|
connect(m, &WaylandOutputDeviceV2Mode::removed, this, [this, m]() {
|
|
|
|
m_modes.removeOne(m);
|
|
|
|
if (m_mode == m) {
|
|
|
|
if (!m_modes.isEmpty()) {
|
|
|
|
m_mode = m_modes.first();
|
|
|
|
} else {
|
|
|
|
// was last mode
|
|
|
|
qFatal("KWaylandBackend: no output modes available anymore, this seems like a compositor bug");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete m;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
QString WaylandOutputDeviceV2::modeId() const
|
|
|
|
{
|
|
|
|
return QString::number(m_modes.indexOf(m_mode));
|
|
|
|
}
|
|
|
|
|
|
|
|
WaylandOutputDeviceV2Mode *WaylandOutputDeviceV2::deviceModeFromId(const int modeId) const
|
|
|
|
{
|
|
|
|
return m_modes.at(modeId);
|
|
|
|
}
|
|
|
|
|
|
|
|
QString WaylandOutputDeviceV2::modeName(const WaylandOutputDeviceV2Mode *m) const
|
|
|
|
{
|
|
|
|
return QString::number(m->size().width()) + QLatin1Char('x') + QString::number(m->size().height()) + QLatin1Char('@')
|
|
|
|
+ QString::number(qRound(m->refreshRate() / 1000.0));
|
|
|
|
}
|
|
|
|
|
|
|
|
QString WaylandOutputDeviceV2::name() const
|
|
|
|
{
|
|
|
|
return QStringLiteral("%1 %2").arg(m_manufacturer, m_model);
|
|
|
|
}
|
|
|
|
|
|
|
|
QDebug operator<<(QDebug dbg, const WaylandOutputDeviceV2 *output)
|
|
|
|
{
|
|
|
|
dbg << "WaylandOutput(Id:" << output->id() << ", Name:" << QString(output->manufacturer() + QLatin1Char(' ') + output->model()) << ")";
|
|
|
|
return dbg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_done()
|
|
|
|
{
|
|
|
|
Q_EMIT done();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_scale(wl_fixed_t factor)
|
|
|
|
{
|
|
|
|
m_factor = wl_fixed_to_double(factor);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_edid(const QString &edid)
|
|
|
|
{
|
|
|
|
m_edid = QByteArray::fromBase64(edid.toUtf8());
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_enabled(int32_t enabled)
|
|
|
|
{
|
|
|
|
if (m_enabled != enabled) {
|
|
|
|
m_enabled = enabled;
|
|
|
|
Q_EMIT enabledChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_uuid(const QString &uuid)
|
|
|
|
{
|
|
|
|
m_uuid = uuid;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_serial_number(const QString &serialNumber)
|
|
|
|
{
|
|
|
|
m_serialNumber = serialNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_eisa_id(const QString &eisaId)
|
|
|
|
{
|
|
|
|
m_eisaId = eisaId;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_capabilities(uint32_t flags)
|
|
|
|
{
|
|
|
|
m_flags = flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_overscan(uint32_t overscan)
|
|
|
|
{
|
|
|
|
m_overscan = overscan;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_vrr_policy(uint32_t vrr_policy)
|
|
|
|
{
|
|
|
|
m_vrr_policy = vrr_policy;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaylandOutputDeviceV2::kde_output_device_v2_rgb_range(uint32_t rgb_range)
|
|
|
|
{
|
|
|
|
m_rgbRange = rgb_range;
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray WaylandOutputDeviceV2::edid() const
|
|
|
|
{
|
|
|
|
return m_edid;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WaylandOutputDeviceV2::enabled() const
|
|
|
|
{
|
|
|
|
return m_enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
int WaylandOutputDeviceV2::id() const
|
|
|
|
{
|
|
|
|
return m_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal WaylandOutputDeviceV2::scale() const
|
|
|
|
{
|
|
|
|
return m_factor;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString WaylandOutputDeviceV2::manufacturer() const
|
|
|
|
{
|
|
|
|
return m_manufacturer;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString WaylandOutputDeviceV2::model() const
|
|
|
|
{
|
|
|
|
return m_model;
|
|
|
|
}
|
|
|
|
|
|
|
|
QPoint WaylandOutputDeviceV2::globalPosition() const
|
|
|
|
{
|
|
|
|
return m_pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize WaylandOutputDeviceV2::pixelSize() const
|
|
|
|
{
|
|
|
|
return m_mode->size();
|
|
|
|
}
|
|
|
|
|
|
|
|
int WaylandOutputDeviceV2::refreshRate() const
|
|
|
|
{
|
|
|
|
return m_mode->refreshRate();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t WaylandOutputDeviceV2::vrrPolicy() const
|
|
|
|
{
|
|
|
|
return m_vrr_policy;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t WaylandOutputDeviceV2::overscan() const
|
|
|
|
{
|
|
|
|
return m_overscan;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t WaylandOutputDeviceV2::capabilities() const
|
|
|
|
{
|
|
|
|
return m_flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t WaylandOutputDeviceV2::rgbRange() const
|
|
|
|
{
|
|
|
|
return m_rgbRange;
|
|
|
|
}
|
|
|
|
|
2016-06-30 11:32:54 +00:00
|
|
|
}
|
|
|
|
}
|