Skip to content

Commit

Permalink
Add eglfs_x11
Browse files Browse the repository at this point in the history
Closes: #33
  • Loading branch information
plfiorini committed Jul 30, 2023
1 parent f630ebb commit fd952aa
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ if(FEATURE_aurora_qpa)
add_subdirectory(src/plugins/platforms/eglfs/deviceintegration/eglfs_kms)
#add_subdirectory(src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_egldevice)
add_subdirectory(src/plugins/platforms/eglfs/deviceintegration/eglfs_kms_support)
if(FEATURE_aurora_qpa_x11)
add_subdirectory(src/plugins/platforms/eglfs/deviceintegration/eglfs_x11)
endif()
endif()
if(BUILD_TESTING)
if(TARGET AuroraCompositor)
Expand Down
22 changes: 22 additions & 0 deletions features.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,28 @@ endif()
add_feature_info("Aurora::QPA" FEATURE_aurora_qpa "Build Qt platform plugin for Wayland compositors")
set(LIRI_FEATURE_aurora_qpa "$<IF:${FEATURE_aurora_qpa},1,0>")

# x11
if(FEATURE_aurora_qpa)
option(FEATURE_aurora_qpa_x11 "Qt platform plugin for Wayland compositors: X11 support" ON)
if(FEATURE_aurora_qpa_x11)
find_package(X11)
if(NOT X11_FOUND)
message(WARNING "You need X11 for Aurora::QPA::X11")
set(FEATURE_aurora_qpa_x11 OFF)
endif()

find_package(XCB COMPONENTS XCB)
if(NOT XCB_FOUND)
message(WARNING "You need XCB for Aurora::QPA::X11")
set(FEATURE_aurora_qpa_x11 OFF)
endif()
endif()
else()
set(FEATURE_aurora_qpa_x11 OFF)
endif()
add_feature_info("Aurora::QPA::X11" FEATURE_aurora_qpa "Build X11 support for the Qt platform plugin for Wayland compositors")
set(LIRI_FEATURE_aurora_qpa_x11 "$<IF:${FEATURE_aurora_qpa_x11},1,0>")

# shm-emulation-server
option(FEATURE_aurora_shm_emulation_server "Shm emulation server" ON)
if(FEATURE_aurora_shm_emulation_server)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini <[email protected]>
#
# SPDX-License-Identifier: BSD-3-Clause

liri_add_plugin(eglfs-x11-integration
TYPE
liri/egldeviceintegrations
SOURCES
qeglfsx11integration.cpp qeglfsx11integration.h
qeglfsx11main.cpp
DEFINES
QT_EGL_NO_X11
INCLUDE_DIRECTORIES
../../api
LIBRARIES
Qt::Core
Qt::CorePrivate
Qt::Gui
Qt::GuiPrivate
Liri::EglFSDeviceIntegration
Liri::EglFSDeviceIntegrationPrivate
X11::X11
X11::xcb
X11::X11_xcb
XCB::XCB
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Keys": [ "eglfs_x11" ]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "qeglfsx11integration.h"
#include <QThread>

#include <X11/Xlib.h>
#include <X11/Xlib-xcb.h>

/* Make no mistake: This is not a replacement for the xcb platform plugin.
This here is barely an extremely useful tool for developing eglfs itself because
it allows to do so without any requirements for devices or drivers. */

QT_BEGIN_NAMESPACE

class EventReader : public QThread
{
public:
EventReader(QEglFSX11Integration *integration)
: m_integration(integration) { }

void run() override;

private:
QEglFSX11Integration *m_integration;
};

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
Q_CONSTINIT static QBasicAtomicInt running = Q_BASIC_ATOMIC_INITIALIZER(0);
#else
static QBasicAtomicInt running;
#endif

void EventReader::run()
{
xcb_generic_event_t *event = nullptr;
while (running.loadRelaxed() && (event = xcb_wait_for_event(m_integration->connection()))) {
uint response_type = event->response_type & ~0x80;
switch (response_type) {
case XCB_CLIENT_MESSAGE: {
xcb_client_message_event_t *client = (xcb_client_message_event_t *) event;
const xcb_atom_t *atoms = m_integration->atoms();
if (client->format == 32
&& client->type == atoms[Atoms::WM_PROTOCOLS]
&& client->data.data32[0] == atoms[Atoms::WM_DELETE_WINDOW]) {
QWindow *window = m_integration->platformWindow() ? m_integration->platformWindow()->window() : nullptr;
if (window)
QWindowSystemInterface::handleCloseEvent(window);
}
break;
}
default:
break;
}
}
}

void QEglFSX11Integration::sendConnectionEvent(xcb_atom_t a)
{
xcb_client_message_event_t event;
memset(&event, 0, sizeof(event));

event.response_type = XCB_CLIENT_MESSAGE;
event.format = 32;
event.sequence = 0;
event.window = m_connectionEventListener;
event.type = a;

xcb_send_event(m_connection, false, m_connectionEventListener, XCB_EVENT_MASK_NO_EVENT, (const char *)&event);
xcb_flush(m_connection);
}

#define DISPLAY ((Display *) m_display)

void QEglFSX11Integration::platformInit()
{
m_display = XOpenDisplay(nullptr);
if (Q_UNLIKELY(!m_display))
qFatal("Could not open display");

XSetEventQueueOwner(DISPLAY, XCBOwnsEventQueue);
m_connection = XGetXCBConnection(DISPLAY);

running.ref();

xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection));

m_connectionEventListener = xcb_generate_id(m_connection);
xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
m_connectionEventListener, it.data->root,
0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
it.data->root_visual, 0, nullptr);

m_eventReader = new EventReader(this);
m_eventReader->start();
}

void QEglFSX11Integration::platformDestroy()
{
running.deref();

sendConnectionEvent(XCB_ATOM_NONE);

m_eventReader->wait();
delete m_eventReader;
m_eventReader = nullptr;

XCloseDisplay(DISPLAY);
m_display = nullptr;
m_connection = nullptr;
}

EGLNativeDisplayType QEglFSX11Integration::platformDisplay() const
{
return DISPLAY;
}

QSize QEglFSX11Integration::screenSize() const
{
if (m_screenSize.isEmpty()) {
QList<QByteArray> env = qgetenv("EGLFS_X11_SIZE").split('x');
if (env.size() == 2) {
m_screenSize = QSize(env.at(0).toInt(), env.at(1).toInt());
} else {
XWindowAttributes a;
if (XGetWindowAttributes(DISPLAY, DefaultRootWindow(DISPLAY), &a))
m_screenSize = QSize(a.width, a.height);
}
}
return m_screenSize;
}

EGLNativeWindowType QEglFSX11Integration::createNativeWindow(QPlatformWindow *platformWindow,
const QSize &size,
const QSurfaceFormat &format)
{
Q_UNUSED(format);

m_platformWindow = platformWindow;

xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(m_connection));
m_window = xcb_generate_id(m_connection);
xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, m_window, it.data->root,
0, 0, size.width(), size.height(), 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, it.data->root_visual,
0, nullptr);

xcb_intern_atom_cookie_t cookies[Atoms::N_ATOMS];
static const char *atomNames[Atoms::N_ATOMS] = {
"_NET_WM_NAME",
"UTF8_STRING",
"WM_PROTOCOLS",
"WM_DELETE_WINDOW",
"_NET_WM_STATE",
"_NET_WM_STATE_FULLSCREEN"
};

for (int i = 0; i < Atoms::N_ATOMS; ++i) {
cookies[i] = xcb_intern_atom(m_connection, false, strlen(atomNames[i]), atomNames[i]);
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(m_connection, cookies[i], nullptr);
m_atoms[i] = reply->atom;
free(reply);
}

// Set window title
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window,
m_atoms[Atoms::_NET_WM_NAME], m_atoms[Atoms::UTF8_STRING], 8, 5, "EGLFS");

// Enable WM_DELETE_WINDOW
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window,
m_atoms[Atoms::WM_PROTOCOLS], XCB_ATOM_ATOM, 32, 1, &m_atoms[Atoms::WM_DELETE_WINDOW]);

// Go fullscreen.
xcb_change_property(m_connection, XCB_PROP_MODE_REPLACE, m_window,
m_atoms[Atoms::_NET_WM_STATE], XCB_ATOM_ATOM, 32, 1, &m_atoms[Atoms::_NET_WM_STATE_FULLSCREEN]);

xcb_map_window(m_connection, m_window);

xcb_flush(m_connection);

return qt_egl_cast<EGLNativeWindowType>(m_window);
}

void QEglFSX11Integration::destroyNativeWindow(EGLNativeWindowType window)
{
xcb_destroy_window(m_connection, qt_egl_cast<xcb_window_t>(window));
}

bool QEglFSX11Integration::hasCapability(QPlatformIntegration::Capability cap) const
{
Q_UNUSED(cap);
return false;
}

QT_END_NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QEGLFSX11INTEGRATION_H
#define QEGLFSX11INTEGRATION_H

#include "private/qeglfsdeviceintegration_p.h"

#include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatformwindow.h>

#include <xcb/xcb.h>

QT_BEGIN_NAMESPACE

namespace Atoms {
enum {
_NET_WM_NAME = 0,
UTF8_STRING,
WM_PROTOCOLS,
WM_DELETE_WINDOW,
_NET_WM_STATE,
_NET_WM_STATE_FULLSCREEN,

N_ATOMS
};
}

class EventReader;

class QEglFSX11Integration : public QEglFSDeviceIntegration
{
public:
QEglFSX11Integration() : m_connection(nullptr), m_window(0), m_eventReader(nullptr) {}

void platformInit() override;
void platformDestroy() override;
EGLNativeDisplayType platformDisplay() const override;
QSize screenSize() const override;
EGLNativeWindowType createNativeWindow(QPlatformWindow *window,
const QSize &size,
const QSurfaceFormat &format) override;
void destroyNativeWindow(EGLNativeWindowType window) override;
bool hasCapability(QPlatformIntegration::Capability cap) const override;

xcb_connection_t *connection() { return m_connection; }
const xcb_atom_t *atoms() const { return m_atoms; }
QPlatformWindow *platformWindow() { return m_platformWindow; }

private:
void sendConnectionEvent(xcb_atom_t a);

void *m_display;
xcb_connection_t *m_connection;
xcb_atom_t m_atoms[Atoms::N_ATOMS];
xcb_window_t m_window;
EventReader *m_eventReader;
xcb_window_t m_connectionEventListener;
QPlatformWindow *m_platformWindow;
mutable QSize m_screenSize;
};

QT_END_NAMESPACE

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#include "private/qeglfsdeviceintegration_p.h"
#include "qeglfsx11integration.h"

QT_BEGIN_NAMESPACE

class QEglFSX11IntegrationPlugin : public QEglFSDeviceIntegrationPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QEglFSDeviceIntegrationFactoryInterface_iid FILE "eglfs_x11.json")

public:
QEglFSDeviceIntegration *create() override { return new QEglFSX11Integration; }
};

QT_END_NAMESPACE

#include "qeglfsx11main.moc"

0 comments on commit fd952aa

Please sign in to comment.