From fd952aa746128650165a34fb05789e2d4b18be5b Mon Sep 17 00:00:00 2001 From: Pier Luigi Fiorini Date: Sun, 30 Jul 2023 10:31:42 +0200 Subject: [PATCH] Add eglfs_x11 Closes: #33 --- CMakeLists.txt | 3 + features.cmake | 22 ++ .../eglfs_x11/CMakeLists.txt | 26 +++ .../eglfs_x11/eglfs_x11.json | 3 + .../eglfs_x11/qeglfsx11integration.cpp | 195 ++++++++++++++++++ .../eglfs_x11/qeglfsx11integration.h | 65 ++++++ .../eglfs_x11/qeglfsx11main.cpp | 20 ++ 7 files changed, 334 insertions(+) create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/CMakeLists.txt create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.json create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.h create mode 100644 src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 498003c6..043ac7b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/features.cmake b/features.cmake index 6afe7382..456533b6 100644 --- a/features.cmake +++ b/features.cmake @@ -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 "$") +# 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 "$") + # shm-emulation-server option(FEATURE_aurora_shm_emulation_server "Shm emulation server" ON) if(FEATURE_aurora_shm_emulation_server) diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/CMakeLists.txt b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/CMakeLists.txt new file mode 100644 index 00000000..27d26644 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/CMakeLists.txt @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2023 Pier Luigi Fiorini +# +# 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 +) diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.json b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.json new file mode 100644 index 00000000..84625278 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/eglfs_x11.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "eglfs_x11" ] +} diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp new file mode 100644 index 00000000..e0b1caf2 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.cpp @@ -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 + +#include +#include + +/* 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 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(m_window); +} + +void QEglFSX11Integration::destroyNativeWindow(EGLNativeWindowType window) +{ + xcb_destroy_window(m_connection, qt_egl_cast(window)); +} + +bool QEglFSX11Integration::hasCapability(QPlatformIntegration::Capability cap) const +{ + Q_UNUSED(cap); + return false; +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.h new file mode 100644 index 00000000..e083c157 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11integration.h @@ -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 +#include + +#include + +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 diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11main.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11main.cpp new file mode 100644 index 00000000..b46e8884 --- /dev/null +++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_x11/qeglfsx11main.cpp @@ -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"