[wayland] Add an X11 output backend
This new backend allows to start a kwin_wayland server nested on an X-Server by using a normal X11 window as output. This allows testing kwin_wayland without needing to start another Wayland server first. The behavior is triggered by using new command line arguments: --windowed --x11-display=<:0> With optional --width and --height arguments. In this mode the WaylandBackend is not created at all. So far the backend is not fully integrated yet and only the QPainter backend supports this mode.
This commit is contained in:
parent
c1ab92d8b4
commit
6bf44b7db4
11 changed files with 676 additions and 20 deletions
|
@ -415,8 +415,10 @@ if(HAVE_WAYLAND)
|
|||
set(kwin_KDEINIT_SRCS
|
||||
${kwin_KDEINIT_SRCS}
|
||||
screens_wayland.cpp
|
||||
screens_x11windowed.cpp
|
||||
wayland_backend.cpp
|
||||
wayland_server.cpp
|
||||
x11windowed_backend.cpp
|
||||
)
|
||||
if(KWIN_HAVE_EGL AND Wayland_Egl_FOUND)
|
||||
set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} egl_wayland_backend.cpp)
|
||||
|
|
|
@ -118,8 +118,12 @@ Compositor::Compositor(QObject* workspace)
|
|||
connect(&m_unusedSupportPropertyTimer, SIGNAL(timeout()), SLOT(deleteUnusedSupportProperties()));
|
||||
#if HAVE_WAYLAND
|
||||
if (kwinApp()->operationMode() != Application::OperationModeX11) {
|
||||
connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::systemCompositorDied, this, &Compositor::finish);
|
||||
connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::backendReady, this, &Compositor::setup);
|
||||
if (Wayland::WaylandBackend::self()) {
|
||||
connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::systemCompositorDied, this, &Compositor::finish);
|
||||
connect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::backendReady, this, &Compositor::setup);
|
||||
} else {
|
||||
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
|
||||
|
|
31
input.cpp
31
input.cpp
|
@ -35,6 +35,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#if HAVE_WAYLAND
|
||||
#include "wayland_backend.h"
|
||||
#include "wayland_server.h"
|
||||
#include "x11windowed_backend.h"
|
||||
#include <KWayland/Server/seat_interface.h>
|
||||
#endif
|
||||
// Qt
|
||||
|
@ -255,6 +256,25 @@ static KWayland::Server::SeatInterface *findSeat()
|
|||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
static
|
||||
void disconnectSeat(KWayland::Server::SeatInterface *seat)
|
||||
{
|
||||
if (T *w = T::self()) {
|
||||
QObject::disconnect(seat->focusedPointer()->cursor(), &KWayland::Server::Cursor::changed, w, &T::installCursorFromServer);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static
|
||||
void connectSeat(KWayland::Server::SeatInterface *seat)
|
||||
{
|
||||
if (T *w = T::self()) {
|
||||
w->installCursorFromServer();
|
||||
QObject::connect(seat->focusedPointer()->cursor(), &KWayland::Server::Cursor::changed, w, &T::installCursorFromServer);
|
||||
}
|
||||
}
|
||||
|
||||
void InputRedirection::updatePointerWindow()
|
||||
{
|
||||
// TODO: handle pointer grab aka popups
|
||||
|
@ -268,17 +288,14 @@ void InputRedirection::updatePointerWindow()
|
|||
// disconnect old surface
|
||||
if (oldWindow) {
|
||||
disconnect(oldWindow.data(), &Toplevel::geometryChanged, this, &InputRedirection::updateFocusedPointerPosition);
|
||||
if (Wayland::WaylandBackend *w = Wayland::WaylandBackend::self()) {
|
||||
disconnect(seat->focusedPointer()->cursor(), &KWayland::Server::Cursor::changed, w, &Wayland::WaylandBackend::installCursorFromServer);
|
||||
}
|
||||
disconnectSeat<Wayland::WaylandBackend>(seat);
|
||||
disconnectSeat<X11WindowedBackend>(seat);
|
||||
}
|
||||
if (t && t->surface()) {
|
||||
seat->setFocusedPointerSurface(t->surface(), t->pos());
|
||||
connect(t, &Toplevel::geometryChanged, this, &InputRedirection::updateFocusedPointerPosition);
|
||||
if (Wayland::WaylandBackend *w = Wayland::WaylandBackend::self()) {
|
||||
w->installCursorFromServer();
|
||||
connect(seat->focusedPointer()->cursor(), &KWayland::Server::Cursor::changed, w, &Wayland::WaylandBackend::installCursorFromServer);
|
||||
}
|
||||
connectSeat<Wayland::WaylandBackend>(seat);
|
||||
connectSeat<X11WindowedBackend>(seat);
|
||||
} else {
|
||||
seat->setFocusedPointerSurface(nullptr);
|
||||
t = nullptr;
|
||||
|
|
|
@ -24,9 +24,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "wayland_backend.h"
|
||||
#include "wayland_server.h"
|
||||
#include "xcbutils.h"
|
||||
#include "x11windowed_backend.h"
|
||||
|
||||
// KWayland
|
||||
#include <KWayland/Server/display.h>
|
||||
#include <KWayland/Server/seat_interface.h>
|
||||
// KDE
|
||||
#include <KLocalizedString>
|
||||
// Qt
|
||||
|
@ -86,19 +88,26 @@ void ApplicationWayland::performStartup()
|
|||
|
||||
// try creating the Wayland Backend
|
||||
createInput();
|
||||
Wayland::WaylandBackend *backend = Wayland::WaylandBackend::create();
|
||||
connect(backend, &Wayland::WaylandBackend::connectionFailed, this,
|
||||
[] () {
|
||||
fputs(i18n("kwin_wayland: could not connect to Wayland Server, ensure WAYLAND_DISPLAY is set.\n").toLocal8Bit().constData(), stderr);
|
||||
::exit(1);
|
||||
}
|
||||
);
|
||||
connect(backend, &Wayland::WaylandBackend::outputsChanged, this, &ApplicationWayland::continueStartupWithScreens);
|
||||
if (!X11WindowedBackend::self()) {
|
||||
// only create WaylandBackend if we do not use X11WindowedBackend
|
||||
Wayland::WaylandBackend *backend = Wayland::WaylandBackend::create();
|
||||
connect(backend, &Wayland::WaylandBackend::connectionFailed, this,
|
||||
[] () {
|
||||
fputs(i18n("kwin_wayland: could not connect to Wayland Server, ensure WAYLAND_DISPLAY is set.\n").toLocal8Bit().constData(), stderr);
|
||||
::exit(1);
|
||||
}
|
||||
);
|
||||
connect(backend, &Wayland::WaylandBackend::outputsChanged, this, &ApplicationWayland::continueStartupWithScreens);
|
||||
} else {
|
||||
continueStartupWithScreens();
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationWayland::continueStartupWithScreens()
|
||||
{
|
||||
disconnect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::outputsChanged, this, &ApplicationWayland::continueStartupWithScreens);
|
||||
if (Wayland::WaylandBackend::self()) {
|
||||
disconnect(Wayland::WaylandBackend::self(), &Wayland::WaylandBackend::outputsChanged, this, &ApplicationWayland::continueStartupWithScreens);
|
||||
}
|
||||
createScreens();
|
||||
waylandServer()->initOutputs();
|
||||
|
||||
|
@ -338,11 +347,28 @@ KWIN_EXPORT int kdemain(int argc, char * argv[])
|
|||
QCommandLineOption waylandSocketOption(QStringList{QStringLiteral("s"), QStringLiteral("socket")},
|
||||
i18n("Name of the Wayland socket to listen on. If not set \"wayland-0\" is used."),
|
||||
QStringLiteral("socket"));
|
||||
QCommandLineOption windowedOption(QStringLiteral("windowed"),
|
||||
i18n("Use a nested compositor in windowed mode."));
|
||||
QCommandLineOption x11DisplayOption(QStringLiteral("x11-display"),
|
||||
i18n("The X11 Display to use in windowed mode on platform X11."),
|
||||
QStringLiteral("display"));
|
||||
QCommandLineOption widthOption(QStringLiteral("width"),
|
||||
i18n("The width for windowed mode. Default width is 1024."),
|
||||
QStringLiteral("width"));
|
||||
widthOption.setDefaultValue(QString::number(1024));
|
||||
QCommandLineOption heightOption(QStringLiteral("height"),
|
||||
i18n("The height for windowed mode. Default height is 768."),
|
||||
QStringLiteral("height"));
|
||||
heightOption.setDefaultValue(QString::number(768));
|
||||
|
||||
QCommandLineParser parser;
|
||||
a.setupCommandLine(&parser);
|
||||
parser.addOption(xwaylandOption);
|
||||
parser.addOption(waylandSocketOption);
|
||||
parser.addOption(windowedOption);
|
||||
parser.addOption(x11DisplayOption);
|
||||
parser.addOption(widthOption);
|
||||
parser.addOption(heightOption);
|
||||
#if HAVE_INPUT
|
||||
QCommandLineOption libinputOption(QStringLiteral("libinput"),
|
||||
i18n("Enable libinput support for input events processing. Note: never use in a nested session."));
|
||||
|
@ -356,6 +382,27 @@ KWIN_EXPORT int kdemain(int argc, char * argv[])
|
|||
KWin::Application::setUseLibinput(parser.isSet(libinputOption));
|
||||
#endif
|
||||
|
||||
if (parser.isSet(windowedOption)) {
|
||||
bool ok = false;
|
||||
const int width = parser.value(widthOption).toInt(&ok);
|
||||
if (!ok) {
|
||||
std::cerr << "FATAL ERROR incorrect value for width" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
const int height = parser.value(heightOption).toInt(&ok);
|
||||
if (!ok) {
|
||||
std::cerr << "FATAL ERROR incorrect value for height" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
KWin::X11WindowedBackend *x11Backend = KWin::X11WindowedBackend::create(parser.value(x11DisplayOption), QSize(width, height), &a);
|
||||
if (!x11Backend->isValid()) {
|
||||
std::cerr << "FATAL ERROR failed to connet to X Server" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
server->seat()->setHasPointer(true);
|
||||
server->seat()->setHasKeyboard(true);
|
||||
}
|
||||
|
||||
a.setStartXwayland(parser.isSet(xwaylandOption));
|
||||
a.start();
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "toplevel.h"
|
||||
#if HAVE_WAYLAND
|
||||
#include "wayland_backend.h"
|
||||
#include "x11windowed_backend.h"
|
||||
#include <KWayland/Client/buffer.h>
|
||||
#include <KWayland/Client/shm_pool.h>
|
||||
#include <KWayland/Client/surface.h>
|
||||
|
@ -182,6 +183,66 @@ bool WaylandQPainterBackend::needsFullRepaint() const
|
|||
return m_needsFullRepaint;
|
||||
}
|
||||
|
||||
//****************************************
|
||||
// X11WindowedBackend
|
||||
//****************************************
|
||||
X11WindowedQPainterBackend::X11WindowedQPainterBackend()
|
||||
: QPainterBackend()
|
||||
, m_backBuffer(X11WindowedBackend::self()->size(), QImage::Format_RGB32)
|
||||
{
|
||||
}
|
||||
|
||||
X11WindowedQPainterBackend::~X11WindowedQPainterBackend()
|
||||
{
|
||||
if (m_gc) {
|
||||
xcb_free_gc(X11WindowedBackend::self()->connection(), m_gc);
|
||||
}
|
||||
}
|
||||
|
||||
QImage *X11WindowedQPainterBackend::buffer()
|
||||
{
|
||||
return &m_backBuffer;
|
||||
}
|
||||
|
||||
bool X11WindowedQPainterBackend::needsFullRepaint() const
|
||||
{
|
||||
return m_needsFullRepaint;
|
||||
}
|
||||
|
||||
void X11WindowedQPainterBackend::prepareRenderingFrame()
|
||||
{
|
||||
}
|
||||
|
||||
void X11WindowedQPainterBackend::screenGeometryChanged(const QSize &size)
|
||||
{
|
||||
if (m_backBuffer.size() != size) {
|
||||
m_backBuffer = QImage(size, QImage::Format_RGB32);
|
||||
m_backBuffer.fill(Qt::black);
|
||||
m_needsFullRepaint = true;
|
||||
}
|
||||
}
|
||||
|
||||
void X11WindowedQPainterBackend::present(int mask, const QRegion &damage)
|
||||
{
|
||||
Q_UNUSED(mask)
|
||||
Q_UNUSED(damage)
|
||||
xcb_connection_t *c = X11WindowedBackend::self()->connection();
|
||||
const xcb_window_t window = X11WindowedBackend::self()->window();
|
||||
if (m_gc == XCB_NONE) {
|
||||
m_gc = xcb_generate_id(c);
|
||||
xcb_create_gc(c, m_gc, window, 0, nullptr);
|
||||
}
|
||||
// TODO: only update changes?
|
||||
xcb_put_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, window, m_gc,
|
||||
m_backBuffer.width(), m_backBuffer.height(), 0, 0, 0, 24,
|
||||
m_backBuffer.byteCount(), m_backBuffer.constBits());
|
||||
}
|
||||
|
||||
bool X11WindowedQPainterBackend::usesOverlayWindow() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//****************************************
|
||||
|
@ -192,7 +253,11 @@ SceneQPainter *SceneQPainter::createScene(QObject *parent)
|
|||
QScopedPointer<QPainterBackend> backend;
|
||||
#if HAVE_WAYLAND
|
||||
if (kwinApp()->shouldUseWaylandForCompositing()) {
|
||||
backend.reset(new WaylandQPainterBackend);
|
||||
if (X11WindowedBackend::self()) {
|
||||
backend.reset(new X11WindowedQPainterBackend);
|
||||
} else {
|
||||
backend.reset(new WaylandQPainterBackend);
|
||||
}
|
||||
if (backend->isFailed()) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -280,6 +345,12 @@ Shadow *SceneQPainter::createShadow(Toplevel *toplevel)
|
|||
return new SceneQPainterShadow(toplevel);
|
||||
}
|
||||
|
||||
void SceneQPainter::screenGeometryChanged(const QSize &size)
|
||||
{
|
||||
Scene::screenGeometryChanged(size);
|
||||
m_backend->screenGeometryChanged(size);
|
||||
}
|
||||
|
||||
//****************************************
|
||||
// SceneQPainter::Window
|
||||
//****************************************
|
||||
|
|
|
@ -124,6 +124,26 @@ private:
|
|||
QImage m_backBuffer;
|
||||
QWeakPointer<KWayland::Client::Buffer> m_buffer;
|
||||
};
|
||||
|
||||
class X11WindowedQPainterBackend : public QObject, public QPainterBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
X11WindowedQPainterBackend();
|
||||
virtual ~X11WindowedQPainterBackend();
|
||||
|
||||
QImage *buffer() override;
|
||||
bool needsFullRepaint() const override;
|
||||
bool usesOverlayWindow() const override;
|
||||
void prepareRenderingFrame() override;
|
||||
void present(int mask, const QRegion &damage) override;
|
||||
void screenGeometryChanged(const QSize &size);
|
||||
|
||||
private:
|
||||
bool m_needsFullRepaint = true;
|
||||
xcb_gcontext_t m_gc = XCB_NONE;
|
||||
QImage m_backBuffer;
|
||||
};
|
||||
#endif
|
||||
|
||||
class SceneQPainter : public Scene
|
||||
|
@ -141,6 +161,7 @@ public:
|
|||
virtual EffectFrame *createEffectFrame(EffectFrameImpl *frame) override;
|
||||
virtual Shadow *createShadow(Toplevel *toplevel) override;
|
||||
Decoration::Renderer *createDecorationRenderer(Decoration::DecoratedClientImpl *impl) override;
|
||||
void screenGeometryChanged(const QSize &size) override;
|
||||
|
||||
QPainter *painter();
|
||||
|
||||
|
|
10
screens.cpp
10
screens.cpp
|
@ -26,6 +26,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "screens_xrandr.h"
|
||||
#if HAVE_WAYLAND
|
||||
#include "screens_wayland.h"
|
||||
#include "wayland_backend.h"
|
||||
#include "x11windowed_backend.h"
|
||||
#include "screens_x11windowed.h"
|
||||
#endif
|
||||
#ifdef KWIN_UNIT_TEST
|
||||
#include <mock_screens.h>
|
||||
|
@ -43,13 +46,18 @@ Screens *Screens::create(QObject *parent)
|
|||
#else
|
||||
#if HAVE_WAYLAND
|
||||
if (kwinApp()->shouldUseWaylandForCompositing()) {
|
||||
s_self = new WaylandScreens(parent);
|
||||
if (X11WindowedBackend::self()) {
|
||||
s_self = new X11WindowedScreens(parent);
|
||||
} else if (Wayland::WaylandBackend::self()) {
|
||||
s_self = new WaylandScreens(parent);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (kwinApp()->operationMode() == Application::OperationModeX11) {
|
||||
s_self = new XRandRScreens(parent);
|
||||
}
|
||||
#endif
|
||||
Q_ASSERT(s_self);
|
||||
s_self->init();
|
||||
return s_self;
|
||||
}
|
||||
|
|
69
screens_x11windowed.cpp
Normal file
69
screens_x11windowed.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "screens_x11windowed.h"
|
||||
#include "x11windowed_backend.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
X11WindowedScreens::X11WindowedScreens(QObject *parent)
|
||||
: Screens(parent)
|
||||
{
|
||||
}
|
||||
|
||||
X11WindowedScreens::~X11WindowedScreens() = default;
|
||||
|
||||
void X11WindowedScreens::init()
|
||||
{
|
||||
KWin::Screens::init();
|
||||
connect(X11WindowedBackend::self(), &X11WindowedBackend::sizeChanged,
|
||||
this, &X11WindowedScreens::startChangedTimer);
|
||||
updateCount();
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QRect X11WindowedScreens::geometry(int screen) const
|
||||
{
|
||||
if (screen == 0) {
|
||||
return QRect(QPoint(0, 0), size(screen));
|
||||
}
|
||||
return QRect();
|
||||
}
|
||||
|
||||
QSize X11WindowedScreens::size(int screen) const
|
||||
{
|
||||
if (screen == 0) {
|
||||
return X11WindowedBackend::self()->size();
|
||||
}
|
||||
return QSize();
|
||||
}
|
||||
|
||||
void X11WindowedScreens::updateCount()
|
||||
{
|
||||
setCount(1);
|
||||
}
|
||||
|
||||
int X11WindowedScreens::number(const QPoint &pos) const
|
||||
{
|
||||
Q_UNUSED(pos)
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
42
screens_x11windowed.h
Normal file
42
screens_x11windowed.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_SCREENS_X11WINDOWED_H
|
||||
#define KWIN_SCREENS_X11WINDOWED_H
|
||||
#include "screens.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class X11WindowedScreens : public Screens
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
X11WindowedScreens(QObject *parent = nullptr);
|
||||
virtual ~X11WindowedScreens();
|
||||
void init() override;
|
||||
QRect geometry(int screen) const override;
|
||||
int number(const QPoint &pos) const override;
|
||||
QSize size(int screen) const override;
|
||||
void updateCount() override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
281
x11windowed_backend.cpp
Normal file
281
x11windowed_backend.cpp
Normal file
|
@ -0,0 +1,281 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "x11windowed_backend.h"
|
||||
#include "composite.h"
|
||||
#include "input.h"
|
||||
#include "utils.h"
|
||||
#include "wayland_server.h"
|
||||
#include "xcbutils.h"
|
||||
#include <kwinxrenderutils.h>
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QSocketNotifier>
|
||||
// kwayland
|
||||
#include <KWayland/Server/buffer_interface.h>
|
||||
#include <KWayland/Server/seat_interface.h>
|
||||
#include <KWayland/Server/surface_interface.h>
|
||||
// system
|
||||
#include <linux/input.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
X11WindowedBackend *X11WindowedBackend::s_self = nullptr;
|
||||
|
||||
X11WindowedBackend *X11WindowedBackend::create(const QString &display, const QSize &size, QObject *parent)
|
||||
{
|
||||
Q_ASSERT(!s_self);
|
||||
s_self = new X11WindowedBackend(display, size, parent);
|
||||
return s_self;
|
||||
}
|
||||
|
||||
X11WindowedBackend::X11WindowedBackend(const QString &display, const QSize &size, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_size(size)
|
||||
{
|
||||
int screen = 0;
|
||||
auto c = xcb_connect(display.toUtf8().constData(), &screen);
|
||||
if (!xcb_connection_has_error(c)) {
|
||||
m_connection = c;
|
||||
m_screenNumber = screen;
|
||||
for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection));
|
||||
it.rem;
|
||||
--screen, xcb_screen_next(&it)) {
|
||||
if (screen == m_screenNumber) {
|
||||
m_screen = it.data;
|
||||
}
|
||||
}
|
||||
XRenderUtils::init(m_connection, m_screen->root);
|
||||
createWindow();
|
||||
startEventReading();
|
||||
}
|
||||
}
|
||||
|
||||
X11WindowedBackend::~X11WindowedBackend()
|
||||
{
|
||||
if (m_connection) {
|
||||
if (m_window) {
|
||||
xcb_unmap_window(m_connection, m_window);
|
||||
xcb_destroy_window(m_connection, m_window);
|
||||
}
|
||||
if (m_cursor) {
|
||||
xcb_free_cursor(m_connection, m_cursor);
|
||||
}
|
||||
xcb_disconnect(m_connection);
|
||||
}
|
||||
}
|
||||
|
||||
void X11WindowedBackend::createWindow()
|
||||
{
|
||||
Q_ASSERT(m_window == XCB_WINDOW_NONE);
|
||||
Xcb::Atom protocolsAtom(QByteArrayLiteral("WM_PROTOCOLS"), false, m_connection);
|
||||
Xcb::Atom deleteWindowAtom(QByteArrayLiteral("WM_DELETE_WINDOW"), false, m_connection);
|
||||
m_window = xcb_generate_id(m_connection);
|
||||
uint32_t mask = XCB_CW_EVENT_MASK;
|
||||
const uint32_t values[] = {
|
||||
XCB_EVENT_MASK_KEY_PRESS |
|
||||
XCB_EVENT_MASK_KEY_RELEASE |
|
||||
XCB_EVENT_MASK_BUTTON_PRESS |
|
||||
XCB_EVENT_MASK_BUTTON_RELEASE |
|
||||
XCB_EVENT_MASK_POINTER_MOTION |
|
||||
XCB_EVENT_MASK_ENTER_WINDOW |
|
||||
XCB_EVENT_MASK_LEAVE_WINDOW |
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY |
|
||||
XCB_EVENT_MASK_EXPOSURE
|
||||
};
|
||||
xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_window, m_screen->root,
|
||||
0, 0, m_size.width(), m_size.height(),
|
||||
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, mask, values);
|
||||
xcb_map_window(m_connection, m_window);
|
||||
|
||||
m_protocols = protocolsAtom;
|
||||
m_deleteWindowProtocol = deleteWindowAtom;
|
||||
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window, m_protocols, XCB_ATOM_ATOM, 32, 1, &m_deleteWindowProtocol);
|
||||
|
||||
xcb_flush(m_connection);
|
||||
}
|
||||
|
||||
void X11WindowedBackend::startEventReading()
|
||||
{
|
||||
QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this);
|
||||
auto processXcbEvents = [this] {
|
||||
while (auto event = xcb_poll_for_event(m_connection)) {
|
||||
handleEvent(event);
|
||||
free(event);
|
||||
}
|
||||
xcb_flush(m_connection);
|
||||
};
|
||||
connect(notifier, &QSocketNotifier::activated, this, processXcbEvents);
|
||||
connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, processXcbEvents);
|
||||
connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, processXcbEvents);
|
||||
}
|
||||
|
||||
void X11WindowedBackend::handleEvent(xcb_generic_event_t *e)
|
||||
{
|
||||
const uint8_t eventType = e->response_type & ~0x80;
|
||||
switch (eventType) {
|
||||
case XCB_BUTTON_PRESS:
|
||||
case XCB_BUTTON_RELEASE:
|
||||
handleButtonPress(reinterpret_cast<xcb_button_press_event_t*>(e));
|
||||
break;
|
||||
case XCB_MOTION_NOTIFY:
|
||||
if (input()) {
|
||||
auto event = reinterpret_cast<xcb_motion_notify_event_t*>(e);
|
||||
input()->processPointerMotion(QPointF(event->event_x, event->event_y), event->time);
|
||||
}
|
||||
break;
|
||||
case XCB_KEY_PRESS:
|
||||
case XCB_KEY_RELEASE:
|
||||
if (input()) {
|
||||
auto event = reinterpret_cast<xcb_key_press_event_t*>(e);
|
||||
input()->processKeyboardKey(event->detail - 8, eventType == XCB_KEY_PRESS ? InputRedirection::KeyboardKeyPressed : InputRedirection::KeyboardKeyReleased, event->time);
|
||||
}
|
||||
break;
|
||||
case XCB_CONFIGURE_NOTIFY:
|
||||
updateSize(reinterpret_cast<xcb_configure_notify_event_t*>(e));
|
||||
break;
|
||||
case XCB_ENTER_NOTIFY:
|
||||
if (input()) {
|
||||
auto event = reinterpret_cast<xcb_enter_notify_event_t*>(e);
|
||||
input()->processPointerMotion(QPointF(event->event_x, event->event_y), event->time);
|
||||
}
|
||||
break;
|
||||
case XCB_CLIENT_MESSAGE:
|
||||
handleClientMessage(reinterpret_cast<xcb_client_message_event_t*>(e));
|
||||
break;
|
||||
case XCB_EXPOSE:
|
||||
handleExpose(reinterpret_cast<xcb_expose_event_t*>(e));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void X11WindowedBackend::handleClientMessage(xcb_client_message_event_t *event)
|
||||
{
|
||||
if (event->window != m_window) {
|
||||
return;
|
||||
}
|
||||
if (event->type == m_protocols && m_protocols != XCB_ATOM_NONE) {
|
||||
if (event->data.data32[0] == m_deleteWindowProtocol && m_deleteWindowProtocol != XCB_ATOM_NONE) {
|
||||
qCDebug(KWIN_CORE) << "Backend window is going to be closed, shutting down.";
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void X11WindowedBackend::handleButtonPress(xcb_button_press_event_t *event)
|
||||
{
|
||||
if (!input()) {
|
||||
return;
|
||||
}
|
||||
bool const pressed = (event->response_type & ~0x80) == XCB_BUTTON_PRESS;
|
||||
if (event->detail >= XCB_BUTTON_INDEX_4 && event->detail <= 7) {
|
||||
// wheel
|
||||
if (!pressed) {
|
||||
return;
|
||||
}
|
||||
const int delta = (event->detail == XCB_BUTTON_INDEX_4 || event->detail == 6) ? -1 : 1;
|
||||
InputRedirection::PointerAxis axis = (event->detail > 5) ? InputRedirection::PointerAxisHorizontal : InputRedirection::PointerAxisVertical;
|
||||
static const qreal s_defaultAxisStepDistance = 10.0;
|
||||
input()->processPointerAxis(axis, delta * s_defaultAxisStepDistance, event->time);
|
||||
return;
|
||||
}
|
||||
uint32_t button = 0;
|
||||
switch (event->detail) {
|
||||
case XCB_BUTTON_INDEX_1:
|
||||
button = BTN_LEFT;
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_2:
|
||||
button = BTN_MIDDLE;
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_3:
|
||||
button = BTN_RIGHT;
|
||||
break;
|
||||
default:
|
||||
button = event->detail + BTN_LEFT - 1;
|
||||
return;
|
||||
}
|
||||
input()->processPointerMotion(QPointF(event->event_x, event->event_y), event->time);
|
||||
input()->processPointerButton(button, pressed ? InputRedirection::PointerButtonPressed : InputRedirection::PointerButtonReleased, event->time);
|
||||
}
|
||||
|
||||
void X11WindowedBackend::handleExpose(xcb_expose_event_t *event)
|
||||
{
|
||||
if (!Compositor::self()) {
|
||||
return;
|
||||
}
|
||||
Compositor::self()->addRepaint(QRect(event->x, event->y, event->width, event->height));
|
||||
}
|
||||
|
||||
void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event)
|
||||
{
|
||||
if (event->window != m_window) {
|
||||
return;
|
||||
}
|
||||
QSize s = QSize(event->width, event->height);
|
||||
if (s != m_size) {
|
||||
m_size = s;
|
||||
emit sizeChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void X11WindowedBackend::installCursorFromServer()
|
||||
{
|
||||
if (!waylandServer() || !waylandServer()->seat()->focusedPointer()) {
|
||||
return;
|
||||
}
|
||||
auto c = waylandServer()->seat()->focusedPointer()->cursor();
|
||||
if (c) {
|
||||
auto cursorSurface = c->surface();
|
||||
if (!cursorSurface.isNull()) {
|
||||
auto buffer = cursorSurface.data()->buffer();
|
||||
if (buffer) {
|
||||
// TODO: cache generated cursors?
|
||||
const xcb_pixmap_t pix = xcb_generate_id(m_connection);
|
||||
const xcb_gcontext_t gc = xcb_generate_id(m_connection);
|
||||
const xcb_cursor_t cid = xcb_generate_id(m_connection);
|
||||
|
||||
xcb_create_pixmap(m_connection, 32, pix, m_screen->root, buffer->size().width(), buffer->size().height());
|
||||
xcb_create_gc(m_connection, gc, pix, 0, nullptr);
|
||||
|
||||
const QImage img = buffer->data();
|
||||
xcb_put_image(m_connection, XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, img.width(), img.height(), 0, 0, 0, 32, img.byteCount(), img.constBits());
|
||||
|
||||
XRenderPicture pic(pix, 32);
|
||||
xcb_render_create_cursor(m_connection, cid, pic, c->hotspot().x(), c->hotspot().y());
|
||||
xcb_change_window_attributes(m_connection, m_window, XCB_CW_CURSOR, &cid);
|
||||
|
||||
xcb_free_pixmap(m_connection, pix);
|
||||
xcb_free_gc(m_connection, gc);
|
||||
if (m_cursor) {
|
||||
xcb_free_cursor(m_connection, m_cursor);
|
||||
}
|
||||
m_cursor = cid;
|
||||
xcb_flush(m_connection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: unset cursor
|
||||
}
|
||||
|
||||
}
|
94
x11windowed_backend.h
Normal file
94
x11windowed_backend.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_X11WINDOWED_BACKEND_H
|
||||
#define KWIN_X11WINDOWED_BACKEND_H
|
||||
|
||||
#include <kwin_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QSize>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class KWIN_EXPORT X11WindowedBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QSize size READ size NOTIFY sizeChanged)
|
||||
public:
|
||||
virtual ~X11WindowedBackend();
|
||||
|
||||
xcb_connection_t *connection() const {
|
||||
return m_connection;
|
||||
}
|
||||
int screenNumer() const {
|
||||
return m_screenNumber;
|
||||
}
|
||||
xcb_window_t window() const {
|
||||
return m_window;
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
return m_connection != nullptr && m_window != XCB_WINDOW_NONE;
|
||||
}
|
||||
|
||||
QSize size() const {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void installCursorFromServer();
|
||||
|
||||
static X11WindowedBackend *self();
|
||||
static X11WindowedBackend *create(const QString &display, const QSize &size, QObject *parent);
|
||||
|
||||
Q_SIGNALS:
|
||||
void sizeChanged();
|
||||
|
||||
private:
|
||||
X11WindowedBackend(const QString &display, const QSize &size, QObject *parent);
|
||||
void createWindow();
|
||||
void startEventReading();
|
||||
void handleEvent(xcb_generic_event_t *event);
|
||||
void handleClientMessage(xcb_client_message_event_t *event);
|
||||
void handleButtonPress(xcb_button_press_event_t *event);
|
||||
void handleExpose(xcb_expose_event_t *event);
|
||||
void updateSize(xcb_configure_notify_event_t *event);
|
||||
|
||||
xcb_connection_t *m_connection = nullptr;
|
||||
xcb_screen_t *m_screen = nullptr;
|
||||
int m_screenNumber = 0;
|
||||
xcb_window_t m_window = XCB_WINDOW_NONE;
|
||||
QSize m_size;
|
||||
xcb_atom_t m_protocols = XCB_ATOM_NONE;
|
||||
xcb_atom_t m_deleteWindowProtocol = XCB_ATOM_NONE;
|
||||
xcb_cursor_t m_cursor = XCB_CURSOR_NONE;
|
||||
static X11WindowedBackend *s_self;
|
||||
};
|
||||
|
||||
inline X11WindowedBackend *X11WindowedBackend::self()
|
||||
{
|
||||
return s_self;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue