fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed

Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
This commit is contained in:
2026-05-05 20:20:37 +01:00
parent a5f97b6632
commit f31522130f
81834 changed files with 11051982 additions and 108 deletions
@@ -0,0 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(custom-extension)
add_subdirectory(qml-client)
add_subdirectory(compositor)
add_subdirectory(cpp-client)
@@ -0,0 +1,137 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "customextension.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QWindow>
#include <QtGui/QPlatformSurfaceEvent>
#include <QtGui/qpa/qplatformnativeinterface.h>
#include <QDebug>
QT_BEGIN_NAMESPACE
CustomExtension::CustomExtension()
: QWaylandClientExtensionTemplate(/* Supported protocol version */ 1 )
{
connect(this, &CustomExtension::activeChanged, this, &CustomExtension::handleExtensionActive);
}
static inline struct ::wl_surface *getWlSurface(QWindow *window)
{
void *surf = QGuiApplication::platformNativeInterface()->nativeResourceForWindow("surface", window);
return static_cast<struct ::wl_surface *>(surf);
}
QWindow *CustomExtension::windowForSurface(struct ::wl_surface *surface)
{
for (QWindow *w : std::as_const(m_windows)) {
if (getWlSurface(w) == surface)
return w;
}
return nullptr;
}
bool CustomExtension::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::Expose) {
QWindow *window = qobject_cast<QWindow*>(object);
Q_ASSERT(window);
if (window->isExposed()) {
window->removeEventFilter(this);
QtWayland::qt_example_extension::register_surface(getWlSurface(window));
}
}
return false;
}
void CustomExtension::sendWindowRegistration(QWindow *window)
{
if (window->handle())
QtWayland::qt_example_extension::register_surface(getWlSurface(window));
else
window->installEventFilter(this); // register when created
}
void CustomExtension::registerWindow(QWindow *window)
{
m_windows << window;
if (isActive()) {
m_activated = true;
sendWindowRegistration(window);
}
}
CustomExtensionObject *CustomExtension::createCustomObject(const QString &color, const QString &text)
{
auto *obj = create_local_object(color, text);
return new CustomExtensionObject(obj, text);
}
//! [sendBounce]
void CustomExtension::sendBounce(QWindow *window, uint ms)
{
QtWayland::qt_example_extension::bounce(getWlSurface(window), ms);
}
//! [sendBounce]
void CustomExtension::sendSpin(QWindow *window, uint ms)
{
QtWayland::qt_example_extension::spin(getWlSurface(window), ms);
}
void CustomExtension::handleExtensionActive()
{
if (isActive() && !m_activated) {
m_activated = true;
for (QWindow *w : std::as_const(m_windows))
sendWindowRegistration(w);
}
}
void CustomExtension::example_extension_close(wl_surface *surface)
{
QWindow *w = windowForSurface(surface);
if (w)
w->close();
}
void CustomExtension::example_extension_set_font_size(wl_surface *surface, uint32_t pixel_size)
{
emit fontSize(windowForSurface(surface), pixel_size);
}
void CustomExtension::example_extension_set_window_decoration(uint32_t state)
{
bool shown = state;
for (QWindow *w : std::as_const(m_windows)) {
Qt::WindowFlags f = w->flags();
if (shown)
f &= ~Qt::FramelessWindowHint;
else
f |= Qt::FramelessWindowHint;
w->setFlags(f);
}
}
CustomExtensionObject::CustomExtensionObject(struct ::qt_example_local_object *wl_object, const QString &text)
: QWaylandClientExtensionTemplate<CustomExtensionObject>(1)
, QtWayland::qt_example_local_object(wl_object)
, m_text(text)
{
}
void CustomExtensionObject::example_local_object_clicked()
{
qDebug() << "Object clicked:" << m_text;
emit clicked();
}
void CustomExtensionObject::setText(const QString &text)
{
m_text = text;
set_text(text);
}
QT_END_NAMESPACE
@@ -0,0 +1,87 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CUSTOMEXTENSION_H
#define CUSTOMEXTENSION_H
#include <QtWaylandClient/QWaylandClientExtension>
#include <QtGui/QWindow>
#include <QtQmlIntegration/qqmlintegration.h>
#include "qwayland-custom.h"
QT_BEGIN_NAMESPACE
class CustomExtensionObject;
//! [CustomExtension]
class CustomExtension : public QWaylandClientExtensionTemplate<CustomExtension>
, public QtWayland::qt_example_extension
//! [CustomExtension]
{
Q_OBJECT
QML_ELEMENT
public:
CustomExtension();
Q_INVOKABLE void registerWindow(QWindow *window);
CustomExtensionObject *createCustomObject(const QString &color, const QString &text);
public slots:
void sendBounce(QWindow *window, uint ms);
void sendSpin(QWindow *window, uint ms);
signals:
void eventReceived(const QString &text, uint value);
void fontSize(QWindow *window, uint pixelSize);
void showDecorations(bool);
private slots:
void handleExtensionActive();
private:
void example_extension_close(wl_surface *surface) override;
void example_extension_set_font_size(wl_surface *surface, uint32_t pixel_size) override;
void example_extension_set_window_decoration(uint32_t state) override;
bool eventFilter(QObject *object, QEvent *event) override;
QWindow *windowForSurface(struct ::wl_surface *);
void sendWindowRegistration(QWindow *);
QList<QWindow *> m_windows;
bool m_activated = false;
};
class CustomExtensionObject : public QWaylandClientExtensionTemplate<CustomExtensionObject>
, public QtWayland::qt_example_local_object
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
CustomExtensionObject(struct ::qt_example_local_object *wl_object, const QString &text);
QString text() const
{
return m_text;
}
protected:
void example_local_object_clicked() override;
public slots:
void setText(const QString &text);
signals:
void textChanged(const QString &text);
void clicked();
private:
QString m_text;
};
QT_END_NAMESPACE
#endif // CUSTOMEXTENSION_H
@@ -0,0 +1,63 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(custom-extension-compositor)
set(CMAKE_AUTOMOC ON)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/wayland/custom-extension/compositor")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml WaylandCompositor)
qt6_policy(SET QTP0001 NEW)
qt_add_executable(custom-extension-compositor
customextension.cpp customextension.h
main.cpp
)
qt6_generate_wayland_protocol_server_sources(custom-extension-compositor
FILES
${CMAKE_CURRENT_SOURCE_DIR}/../protocol/custom.xml
)
set_target_properties(custom-extension-compositor PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(custom-extension-compositor PUBLIC
Qt::Core
Qt::Gui
Qt::Qml
Qt::WaylandCompositor
)
# Resources:
set(compositor_resource_files
"images/background.png"
"qml/CompositorScreen.qml"
"qml/main.qml"
)
qt6_add_resources(custom-extension-compositor "compositor"
PREFIX
"/"
FILES
${compositor_resource_files}
)
qt6_add_qml_module(custom-extension-compositor
URI io.qt.examples.customextension
VERSION 1.0
)
install(TARGETS custom-extension-compositor
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)
@@ -0,0 +1,31 @@
QT += core gui qml
QT += waylandcompositor
CONFIG += qmltypes
QML_IMPORT_NAME = io.qt.examples.customextension
QML_IMPORT_MAJOR_VERSION = 1
CONFIG += wayland-scanner
CONFIG += c++11
SOURCES += \
main.cpp \
customextension.cpp
OTHER_FILES = \
qml/main.qml \
qml/CompositorScreen.qml \
images/background.jpg
WAYLANDSERVERSOURCES += \
../protocol/custom.xml
RESOURCES += compositor.qrc
TARGET = custom-extension-compositor
HEADERS += \
customextension.h
target.path = $$[QT_INSTALL_EXAMPLES]/wayland/custom-extension/compositor
INSTALLS += target
@@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/">
<file>images/background.png</file>
<file>qml/main.qml</file>
<file>qml/CompositorScreen.qml</file>
</qresource>
</RCC>
@@ -0,0 +1,116 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "customextension.h"
#include <QWaylandSurface>
#include <QDebug>
CustomExtension::CustomExtension(QWaylandCompositor *compositor)
: QWaylandCompositorExtensionTemplate(compositor)
{
}
void CustomExtension::initialize()
{
QWaylandCompositorExtensionTemplate::initialize();
QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer());
init(compositor->display(), 1);
}
//! [setFontSize]
void CustomExtension::setFontSize(QWaylandSurface *surface, uint pixelSize)
{
if (surface) {
Resource *target = resourceMap().value(surface->waylandClient());
if (target) {
qDebug() << "Server-side extension sending setFontSize:" << pixelSize;
send_set_font_size(target->handle, surface->resource(), pixelSize);
}
}
}
//! [setFontSize]
void CustomExtension::showDecorations(QWaylandClient *client, bool shown)
{
if (client) {
Resource *target = resourceMap().value(client->client());
if (target) {
qDebug() << "Server-side extension sending showDecorations:" << shown;
send_set_window_decoration(target->handle, shown);
}
}
}
void CustomExtension::close(QWaylandSurface *surface)
{
if (surface) {
Resource *target = resourceMap().value(surface->waylandClient());
if (target) {
qDebug() << "Server-side extension sending close for" << surface;
send_close(target->handle, surface->resource());
}
}
}
//! [example_extension_bounce]
void CustomExtension::example_extension_bounce(QtWaylandServer::qt_example_extension::Resource *resource, wl_resource *wl_surface, uint32_t duration)
{
Q_UNUSED(resource);
auto surface = QWaylandSurface::fromResource(wl_surface);
qDebug() << "server received bounce" << surface << duration;
emit bounce(surface, duration);
}
//! [example_extension_bounce]
void CustomExtension::example_extension_spin(QtWaylandServer::qt_example_extension::Resource *resource, wl_resource *wl_surface, uint32_t duration)
{
Q_UNUSED(resource);
auto surface = QWaylandSurface::fromResource(wl_surface);
qDebug() << "server received spin" << surface << duration;
emit spin(surface, duration);
}
void CustomExtension::example_extension_register_surface(QtWaylandServer::qt_example_extension::Resource *resource, wl_resource *wl_surface)
{
Q_UNUSED(resource);
auto surface = QWaylandSurface::fromResource(wl_surface);
qDebug() << "server received new surface" << surface;
emit surfaceAdded(surface);
}
void CustomExtension::example_extension_create_local_object(Resource *resource, uint32_t id, const QString &color, const QString &text)
{
auto *obj = new CustomExtensionObject(color, text, resource->client(), id, 1);
qDebug() << "Object created" << text << color;
emit customObjectCreated(obj);
}
CustomExtensionObject::CustomExtensionObject(const QString &color, const QString &text, wl_client *client, int id, int version)
: QtWaylandServer::qt_example_local_object(client, id, version)
, m_color(color)
, m_text(text)
{
}
void CustomExtensionObject::sendClicked()
{
send_clicked();
}
void CustomExtensionObject::example_local_object_destroy_resource(QtWaylandServer::qt_example_local_object::Resource *resource)
{
Q_UNUSED(resource);
qDebug() << "Object destroyed" << m_text << m_color;
emit resourceDestroyed();
}
void CustomExtensionObject::example_local_object_set_text(QtWaylandServer::qt_example_local_object::Resource *resource, const QString &text)
{
Q_UNUSED(resource);
qDebug() << "Client changed text from" << m_text << "to" << text;
setText(text);
}
@@ -0,0 +1,105 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef CUSTOMEXTENSION_H
#define CUSTOMEXTENSION_H
#include "wayland-util.h"
#include <QtWaylandCompositor/QWaylandCompositorExtensionTemplate>
#include <QtWaylandCompositor/QWaylandQuickExtension>
#include <QtWaylandCompositor/QWaylandCompositor>
#include <QtWaylandCompositor/QWaylandSurface>
#include "qwayland-server-custom.h"
class CustomExtensionObject;
//! [CustomExtension]
class CustomExtension : public QWaylandCompositorExtensionTemplate<CustomExtension>
, public QtWaylandServer::qt_example_extension
{
Q_OBJECT
QML_ELEMENT
//! [CustomExtension]
public:
CustomExtension(QWaylandCompositor *compositor = nullptr);
void initialize() override;
signals:
void surfaceAdded(QWaylandSurface *surface);
void bounce(QWaylandSurface *surface, uint ms);
void spin(QWaylandSurface *surface, uint ms);
void customObjectCreated(CustomExtensionObject *obj);
public slots:
void setFontSize(QWaylandSurface *surface, uint pixelSize);
void showDecorations(QWaylandClient *client, bool);
void close(QWaylandSurface *surface);
//! [example_extension_bounce]
protected:
void example_extension_bounce(Resource *resource, wl_resource *surface, uint32_t duration) override;
//! [example_extension_bounce]
void example_extension_spin(Resource *resource, wl_resource *surface, uint32_t duration) override;
void example_extension_register_surface(Resource *resource, wl_resource *surface) override;
void example_extension_create_local_object(Resource *resource, uint32_t id, const QString &color, const QString &text) override;
};
class CustomExtensionObject : public QWaylandCompositorExtensionTemplate<CustomExtensionObject>
, public QtWaylandServer::qt_example_local_object
{
Q_OBJECT
Q_PROPERTY(QString color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
public:
CustomExtensionObject(const QString &color, const QString &text, struct ::wl_client *client, int id, int version);
QString color() const
{
return m_color;
}
QString text() const
{
return m_text;
}
public slots:
void setColor(const QString &color)
{
if (m_color == color)
return;
m_color = color;
emit colorChanged(m_color);
}
void setText(QString text)
{
if (m_text == text)
return;
m_text = text;
emit textChanged(m_text);
}
void sendClicked();
signals:
void colorChanged(const QString &color);
void resourceDestroyed();
void textChanged(QString text);
protected:
void example_local_object_destroy_resource(Resource *resource) override;
void example_local_object_set_text(Resource *resource, const QString &text) override;
private:
QString m_color;
QString m_text;
};
Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(CustomExtension)
#endif // CUSTOMEXTENSION_H
Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

@@ -0,0 +1,23 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtCore/QUrl>
#include <QtCore/QDebug>
#include <QtGui/QGuiApplication>
#include <QtQml/QQmlApplicationEngine>
#include <QtQml/qqml.h>
#include <QtQml/QQmlEngine>
#include "customextension.h"
int main(int argc, char *argv[])
{
// ShareOpenGLContexts is needed for using the threaded renderer
// on Nvidia EGLStreams
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
QGuiApplication app(argc, argv);
QQmlApplicationEngine appEngine(QUrl("qrc:///qml/main.qml"));
return app.exec();
}
@@ -0,0 +1,118 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Window
import QtWayland.Compositor
WaylandOutput {
id: output
property alias surfaceArea: background
sizeFollowsWindow: true
window: Window {
id: screen
property QtObject output
width: 1600
height: 900
visible: true
Rectangle {
id: sidebar
width: 250
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
color: "lightgray"
Column {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 5
Repeater {
model: comp.itemList
Rectangle {
height: 54
width: sidebar.width - 5
color: "white"
radius: 5
Text {
text: "window: " + modelData.shellSurface.toplevel.title + "\n["
+ modelData.shellSurface.toplevel.appId
+ (modelData.isCustom ? "]\nfont size: " + modelData.fontSize : "]\nNo extension")
color: modelData.isCustom ? "black" : "darkgray"
}
MouseArea {
enabled: modelData.isCustom
anchors.fill: parent
onWheel: (wheel) => {
if (wheel.angleDelta.y > 0)
modelData.fontSize++
else if (wheel.angleDelta.y < 0 && modelData.fontSize > 3)
modelData.fontSize--
}
onDoubleClicked: {
output.compositor.customExtension.close(modelData.surface)
}
}
}
}
Text {
visible: comp.itemList.length > 0
width: sidebar.width - 5
text: "Mouse wheel to change font size. Double click to close"
wrapMode: Text.Wrap
}
}
}
WaylandMouseTracker {
id: mouseTracker
anchors.left: sidebar.right
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
windowSystemCursorEnabled: !clientCursor.visible
Image {
id: background
anchors.fill: parent
fillMode: Image.Tile
source: "qrc:/images/background.png"
smooth: false
}
WaylandCursorItem {
id: clientCursor
x: mouseTracker.mouseX
y: mouseTracker.mouseY
seat: output.compositor.defaultSeat
}
Rectangle {
anchors.top: parent.top
anchors.right: parent.right
width: 100
height: 100
property bool on : true
color: on ? "#DEC0DE" : "#FACADE"
Text {
anchors.fill: parent
text: "Toggle window decorations"
wrapMode: Text.WordWrap
}
MouseArea {
anchors.fill: parent
onClicked: {
parent.on = !parent.on
comp.setDecorations(parent.on);
}
}
}
}
}
}
@@ -0,0 +1,183 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtWayland.Compositor
import QtWayland.Compositor.XdgShell
import io.qt.examples.customextension 1.0
WaylandCompositor {
id: comp
property alias customExtension: custom
property var itemList: []
function itemForSurface(surface) {
var n = itemList.length
for (var i = 0; i < n; i++) {
if (itemList[i].surface === surface)
return itemList[i]
}
}
CompositorScreen {
compositor: comp
}
Component {
id: chromeComponent
ShellSurfaceItem {
id: chrome
property bool isCustom
property int fontSize: 12
onSurfaceDestroyed: {
var index = itemList.indexOf(chrome)
if (index > -1) {
var listCopy = itemList
listCopy.splice(index, 1)
itemList = listCopy
}
chrome.destroy()
}
transform: [
Rotation {
id: xRot
origin.x: chrome.width / 2; origin.y: chrome.height / 2
angle: 0
axis { x: 1; y: 0; z: 0 }
},
Rotation {
id: yRot
origin.x: chrome.width / 2; origin.y: chrome.height / 2
angle: 0
axis { x: 0; y: 1; z: 0 }
}
]
NumberAnimation {
id: spinAnimation
running: false
loops: 2
target: yRot
property: "angle"
from: 0; to: 360
duration: 400
}
function doSpin(ms) {
console.log("spin " + ms)
// using the 'ms' argument is left as an exercise for the reader...
spinAnimation.start()
}
NumberAnimation {
id: bounceAnimation
running: false
target: chrome
property: "y"
from: 0
to: output.window.height - chrome.height
easing.type: Easing.OutBounce
duration: 1000
}
function doBounce(ms) {
console.log("bounce " + ms)
// using the 'ms' argument is left as an exercise for the reader...
bounceAnimation.start()
}
//! [setFontSize]
onFontSizeChanged: {
custom.setFontSize(surface, fontSize)
}
//! [setFontSize]
}
}
Component {
id: customObjectComponent
Rectangle {
id: customItem
property QtObject obj
property alias text: label.text
width: 100
height: 100
radius: width / 2
x: Math.random() * (defaultOutput.surfaceArea.width - 100)
y: Math.random() * (defaultOutput.surfaceArea.height - 100)
Text {
id: label
anchors.centerIn: parent
text: "?"
}
MouseArea {
anchors.fill: parent
onClicked: obj.sendClicked()
}
Connections {
target: obj
function onResourceDestroyed() {
customItem.destroy()
}
}
}
}
XdgShell {
onToplevelCreated: (toplevel, xdgSurface) => {
var item = chromeComponent.createObject(defaultOutput.surfaceArea, { "shellSurface": xdgSurface } )
var w = defaultOutput.surfaceArea.width / 2
var h = defaultOutput.surfaceArea.height / 2
item.x = Math.random() * w
item.y = Math.random() * h
var listCopy = itemList // List properties cannot be modified through Javascript operations
listCopy.push(item)
itemList = listCopy
}
}
//! [CustomExtension]
CustomExtension {
id: custom
onSurfaceAdded: (surface) => {
var item = itemForSurface(surface)
item.isCustom = true
}
onBounce: (surface, ms) => {
var item = itemForSurface(surface)
item.doBounce(ms)
}
onSpin: (surface, ms) => {
var item = itemForSurface(surface)
item.doSpin(ms)
}
onCustomObjectCreated: (obj) => {
var item = customObjectComponent.createObject(defaultOutput.surfaceArea,
{ "color": obj.color,
"text": obj.text,
"obj": obj } )
}
}
function setDecorations(shown) {
var n = itemList.length
for (var i = 0; i < n; i++) {
if (itemList[i].isCustom)
custom.showDecorations(itemList[i].surface.client, shown)
}
}
//! [CustomExtension]
}
@@ -0,0 +1,44 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(custom-extension-cpp-client)
set(CMAKE_AUTOMOC ON)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/wayland/custom-extension/cpp-client")
find_package(Qt6 REQUIRED COMPONENTS Core GuiPrivate WaylandClient)
qt_add_executable(custom-extension-cpp-client
../client-common/customextension.cpp ../client-common/customextension.h
main.cpp
)
qt6_generate_wayland_protocol_client_sources(custom-extension-cpp-client
PRIVATE_CODE
FILES
${CMAKE_CURRENT_SOURCE_DIR}/../protocol/custom.xml
)
set_target_properties(custom-extension-cpp-client PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(custom-extension-cpp-client PUBLIC
Qt::Core
Qt::Gui
Qt::GuiPrivate
Qt::WaylandClient
)
install(TARGETS custom-extension-cpp-client
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)
@@ -0,0 +1,15 @@
QT += waylandclient gui-private
CONFIG += wayland-scanner
WAYLANDCLIENTSOURCES += ../protocol/custom.xml
SOURCES += main.cpp \
../client-common/customextension.cpp
HEADERS += \
../client-common/customextension.h
TARGET = custom-extension-cpp-client
target.path = $$[QT_INSTALL_EXAMPLES]/wayland/custom-extension/cpp-client
INSTALLS += target
@@ -0,0 +1,133 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
#include <QRasterWindow>
#include <QPainter>
#include <QMouseEvent>
#include <QPlatformSurfaceEvent>
#include <QDebug>
#include <QTimer>
#include "../client-common/customextension.h"
class TestWindow : public QRasterWindow
{
Q_OBJECT
public:
TestWindow(CustomExtension *customExtension)
: m_extension(customExtension)
, rect1(50, 50, 100, 100)
, rect2(50, 200, 100, 100)
, rect3(50, 350, 100, 100)
, rect4(200,350, 100, 100)
{
setTitle(tr("C++ Client"));
m_extension->registerWindow(this);
//! [connection]
connect(m_extension, &CustomExtension::fontSize, this, &TestWindow::handleSetFontSize);
//! [connection]
}
public slots:
void doSpin()
{
if (!m_extension->isActive()) {
qWarning() << "Extension is not active";
return;
}
qDebug() << "sending spin...";
m_extension->sendSpin(this, 500);
}
void doBounce()
{
if (!m_extension->isActive()) {
qWarning() << "Extension is not active";
return;
}
qDebug() << "sending bounce...";
m_extension->sendBounce(this, 500);
}
void newWindow()
{
auto w = new TestWindow(m_extension);
w->show();
}
CustomExtensionObject *newObject()
{
m_objectCount++;
QColor col = QColor::fromHsv(0, 511 / (m_objectCount + 1), 255);
return m_extension->createCustomObject(col.name(), QString::number(m_objectCount));
}
void handleSetFontSize(QWindow *w, uint pixelSize)
{
if (w == this) {
m_font.setPixelSize(pixelSize);
update();
}
}
protected:
void paintEvent(QPaintEvent *) override
{
QPainter p(this);
p.setFont(m_font);
p.fillRect(QRect(0, 0, width(), height()), Qt::gray);
p.fillRect(rect1, QColor::fromString("#C0FFEE"));
p.drawText(rect1, Qt::TextWordWrap, "Press here to send spin request.");
p.fillRect(rect2, QColor::fromString("#decaff"));
p.drawText(rect2, Qt::TextWordWrap, "Press here to send bounce request.");
p.fillRect(rect3, QColor::fromString("#7EA"));
p.drawText(rect3, Qt::TextWordWrap, "Create new window.");
p.fillRect(rect4, QColor::fromString("#7EABA6"));
p.drawText(rect4, Qt::TextWordWrap, "Create custom object.");
}
//! [mousePressEvent]
void mousePressEvent(QMouseEvent *ev) override
{
if (rect1.contains(ev->position()))
doSpin();
else if (rect2.contains(ev->position()))
doBounce();
else if (rect3.contains(ev->position()))
newWindow();
else if (rect4.contains(ev->position()))
newObject();
}
//! [mousePressEvent]
private:
CustomExtension *m_extension = nullptr;
QRectF rect1;
QRectF rect2;
QRectF rect3;
QRectF rect4;
QFont m_font;
static int m_objectCount;
static int m_hue;
};
int TestWindow::m_objectCount = 0;
int TestWindow::m_hue;
int main (int argc, char **argv)
{
QGuiApplication app(argc, argv);
CustomExtension customExtension;
TestWindow window(&customExtension);
window.show();
return app.exec();
}
#include "main.moc"
@@ -0,0 +1,7 @@
TEMPLATE=subdirs
SUBDIRS += qml-client
SUBDIRS += compositor
SUBDIRS += cpp-client
OTHER_FILES += protocol/custom.xml
Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

@@ -0,0 +1,148 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
* \title Custom Extension
* \example custom-extension
* \examplecategory {Embedded}
* \brief Custom Extension shows how to implement a custom Wayland extension.
* \ingroup qtwaylandcompositor-examples
*
* It's easy to write new extensions for Wayland. They are defined using a XML-based format and
* the \c wayland-scanner tool converts this to glue code in C. Qt expands on this with the
* \c qtwaylandscanner, which generates additional glue code in Qt and C++.
*
* \image custom-extension.png
*
* The Custom Extension example shows how to use these tools to extend the Wayland protocol and
* send custom requests and events between a Wayland client and a server.
*
* The example consists of four items:
* \list
* \li The definition of the protocol itself.
* \li A compositor that supports the extension.
* \li A C++-based client that supports the extension.
* \li A QML-based client that supports the extension.
* \endlist
*
* \section1 The Protocol Definition
*
* The XML file \c custom.xml defines the protocol. It contains an interface called
* "qt_example_extension". This is the name which will be broadcast from the server and which the
* client will attach to in order to send requests and receive events. This name should be unique,
* so it is good to use a prefix that sets it apart from official interfaces.
*
* An interface typically consists of two types of remote procedure calls:
* \e requests and \e events. "Requests" are calls the client makes on the server-side, and
* "events" are calls the server makes on the client-side.
*
* The example extension contains a set of \e requests which instructs the server to apply certain
* transforms to the client window. For instance, if the client sends a "bounce" request, then the
* server should respond to this by making the window bounce on the screen.
*
* Similarly, it has a set of \e events which the server can use to provide instructions for the
* client. For instance, the "set_font_size" event is an instruction for the client to set its
* default font size to a specific size.
*
* The protocol defines the existence of requests and events, as well as the arguments they
* take. When \c qtwaylandscanner is run on it, it will generate the code needed to marshall the
* procedure call and its arguments and to transmit this over the connection. On the other end, this
* becomes a call to a virtual function which can be implemented to provide the actual response.
*
* In order to have \c qtwaylandscanner run automatically as part of the build, we use the
* CMake functions \l{qt_generate_wayland_protocol_server_sources}{qt_generate_wayland_protocol_server_sources()} and
* \l{qt_generate_wayland_protocol_client_sources}{qt_generate_wayland_protocol_client_sources()} for generating the server-side and
* client-side glue code, respectively. (When using \c qmake, the \c WAYLANDSERVERSOURCES and
* \c WAYLANDCLIENTSOURCES variables achieve the same.)
*
* \section1 The Compositor Implementation
*
* The Compositor application itself is implemented using QML and Qt Quick, but the extension is
* implemented in C++.
*
* The first step is to create a subclass of the glue code generated by \c qtwaylandscanner so
* that we can access its functionality. We add the \l QML_ELEMENT macro to the class in order to
* make it accessible from QML.
*
* \snippet custom-extension/compositor/customextension.h CustomExtension
*
* In addition to inheriting from the generated class, we also inherit the class
* \l QWaylandCompositorExtensionTemplate which provides some additional convenience when dealing
* with extensions, using the
* \l{https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern}{Curiously Recurring Template Pattern}.
*
* Note that the \l QWaylandCompositorExtensionTemplate must be first in the inheritance list, since
* it is a \l{QObject}-based class.
*
* The subclass has re-implementations of virtual functions in the generated base class, where
* we can handle requests issued by a client.
*
* \snippet custom-extension/compositor/customextension.h example_extension_bounce
*
* In these re-implementations, we simply translate the request to a \e signal emission, so that we
* can handle it in the actual QML code of the compositor.
*
* \snippet custom-extension/compositor/customextension.cpp example_extension_bounce
*
* In addition, the subclass defines \e slots for each of the events, so that these can be either
* called from QML or be connected to signals. The slots simply call the generated functions which
* send the events to the client.
*
* \snippet custom-extension/compositor/customextension.cpp setFontSize
*
* Since we added the \l QML_ELEMENT macro to the class definition (and added the corresponding
* build steps to the build system files), it can be instantiated in QML.
*
* We make it a direct child of the \l WaylandCompositor object in order for the compositor to
* register it as an extension.
*
* \snippet custom-extension/compositor/qml/main.qml CustomExtension
*
* The object has signal handlers for the requests it may get from the client and reacts to them
* accordingly. In addition, we can call its slots to send events.
*
* \snippet custom-extension/compositor/qml/main.qml setFontSize
*
* \section1 The C++ Client Implementation
*
* Both clients share the C++ implementation of the interface. Like in the compositor, we make
* a subclass of the generated code which also inherits from a template class. In this case,
* we inherit QWaylandClientExtensionTemplate.
*
* \snippet custom-extension/client-common/customextension.h CustomExtension
*
* The approach is very similar to that of the compositor, except inverted: Requests are implemented
* as slots which call the generated functions, and events virtual functions which we re-implement
* to emit signals.
*
* \snippet custom-extension/client-common/customextension.cpp sendBounce
*
* The client code itself is very simple and only intended to show how to trigger the behavior. In
* a custom paint event, it draws a set of rectangles and labels. When any of these are clicked, it
* issues requests to the server.
*
* \snippet custom-extension/cpp-client/main.cpp mousePressEvent
*
* To update the font size when the \c set_font_size event is received, the signal in our extension
* class is connected to a slot.
*
* \snippet custom-extension/cpp-client/main.cpp connection
*
* The slot will update the font size and repaint the window.
*
* \section1 The QML Client Implementation
*
* The QML client is similar to the C++ client. It relies on the same implementation of the custom
* extension as the C++ client, and instantiates this in QML to enable it.
*
* \snippet custom-extension/qml-client/main.qml CustomExtension
*
* The UI consists of some clickable rectangles, and uses \l TapHandler to send the corresponding
* requests when a rectangle is clicked.
*
* \snippet custom-extension/qml-client/main.qml sendBounce
*
* For simplicity, the example has been limited to only demonstrate the \c bounce and \c spin
* requests as well as the \c set_font_size event. Adding support for the additional features is
* left as an exercise for the reader.
*/
@@ -0,0 +1,103 @@
<protocol name="custom">
<copyright>
Copyright (C) 2015 The Qt Company Ltd.
SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
</copyright>
<interface name="qt_example_extension" version="1">
<description summary="Example Wayland extension">
This example shows how to add extra functionality to Wayland
through an extension. This is the global object of the extension.
</description>
<request name="register_surface">
<description summary="Register a surface with the example extension">
Inform the compositor that the client has a new surface that is
covered by the extension.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="bounce">
<description summary="Move the surface around for a little while">
The compositor should perform a move animation on the surface.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="duration" type="uint" summary="duration in ms"/>
</request>
<request name="spin">
<description summary="Rotate the surface for a little while">
The compositor should perform a rotating animation on the surface.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="duration" type="uint" summary="duration in ms"/>
</request>
<event name="close">
<description summary="close window">
Ask the client to close the window for the surface.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
</event>
<event name="set_font_size">
<description summary="change the font size">
Ask the client to change the font size on the surface.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="pixel_size" type="uint"/>
</event>
<enum name="decoration_state">
<description summary="window decoration state">
Describes whether window decorations should be shown.
</description>
<entry name="hidden" value="0" summary="Decorations are not shown"/>
<entry name="shown" value="1" summary="Decorations are shown"/>
</enum>
<event name="set_window_decoration">
<description summary="turn window decoration on/off">
Ask the client to turn window decoration on/off on all surfaces.
</description>
<arg name="state" type="uint"/>
</event>
<request name="create_local_object">
<description summary="Create a sily object">
Create a new object that should be visualized by the compositor
</description>
<arg name="id" type="new_id" interface="qt_example_local_object"/>
<arg name="color" type = "string"/>
<arg name="text" type = "string"/>
</request>
</interface>
<interface name="qt_example_local_object" version="1">
<description summary="An object created on the client side">
This object should have a visual representation in the compositor.
</description>
<request name="set_text">
<description summary="Change the text">
Tell the compositor that the object's text is changed
</description>
<arg name="text" type="string"/>
</request>
<event name="clicked">
<description summary="The object was clicked">
Notification to the client that the user clicked the representation of
the object in the compositor.
</description>
</event>
</interface>
</protocol>
@@ -0,0 +1,65 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(custom-extension-qml-client)
set(CMAKE_AUTOMOC ON)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/wayland/custom-extension/qml-client")
find_package(Qt6 REQUIRED COMPONENTS Core GuiPrivate Qml Quick WaylandClient)
qt6_policy(SET QTP0001 NEW)
qt_add_executable(custom-extension-qml-client
../client-common/customextension.cpp ../client-common/customextension.h
main.cpp
)
qt6_generate_wayland_protocol_client_sources(custom-extension-qml-client
FILES
${CMAKE_CURRENT_SOURCE_DIR}/../protocol/custom.xml
)
set_target_properties(custom-extension-qml-client PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(custom-extension-qml-client PUBLIC
Qt::Core
Qt::Gui
Qt::GuiPrivate
Qt::Qml
Qt::Quick
Qt::WaylandClient
)
# Resources:
set(qml_resource_files
"main.qml"
)
qt6_add_resources(custom-extension-qml-client "qml"
PREFIX
"/"
FILES
${qml_resource_files}
)
target_include_directories(custom-extension-qml-client PUBLIC ../client-common/)
qt6_add_qml_module(custom-extension-qml-client
URI io.qt.examples.customextension
VERSION 1.0
)
install(TARGETS custom-extension-qml-client
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)
@@ -0,0 +1,19 @@
// Copyright (C) 2017 Erik Larsson.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtGui/QGuiApplication>
#include <QtQml/QQmlApplicationEngine>
#include <QtQml/QQmlEngine>
#include "../client-common/customextension.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
@@ -0,0 +1,91 @@
// Copyright (C) 2017 Erik Larsson.
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
import QtQuick
import QtQuick.Window
import io.qt.examples.customextension
Window {
id: topLevelWindow
title: "QML Client"
visible: true
width: 800
height: 600
property real fontSize: height / 50
Column {
anchors.centerIn: parent
width: topLevelWindow.width / 4
height: 2 * (width + topLevelWindow.height / 12)
spacing: topLevelWindow.height / 12
Rectangle {
id: rect1
color: "#C0FFEE"
width: parent.width
height: width
clip: true
Text {
anchors.centerIn: parent
text: "Press here to send spin request."
font.pixelSize: fontSize
width: parent.width
height: parent.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
TapHandler {
onTapped: {
if (customExtension.active)
customExtension.sendSpin(topLevelWindow, 500)
}
}
}
Rectangle {
id: rect2
color: "#decaff"
width: parent.width
height: parent.height / 2
clip: true
Text {
anchors.centerIn: parent
text: "Press here to send bounce request."
font.pixelSize: fontSize
width: parent.width
height: parent.height
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
}
//! [sendBounce]
TapHandler {
onTapped: {
if (customExtension.active)
customExtension.sendBounce(topLevelWindow, 1000)
}
}
//! [sendBounce]
}
}
//! [CustomExtension]
CustomExtension {
id: customExtension
onActiveChanged: {
registerWindow(topLevelWindow)
}
onFontSize: (window, pixelSize) => {
topLevelWindow.fontSize = pixelSize
}
}
//! [CustomExtension]
}
@@ -0,0 +1,26 @@
TEMPLATE = app
QT += qml quick waylandclient gui-private
CONFIG += wayland-scanner
CONFIG += qmltypes
QML_IMPORT_NAME = io.qt.examples.customextension
QML_IMPORT_MAJOR_VERSION = 1
INCLUDEPATH += $$PWD/../client-common
WAYLANDCLIENTSOURCES += ../protocol/custom.xml
SOURCES += main.cpp \
../client-common/customextension.cpp
HEADERS += \
../client-common/customextension.h
TARGET = custom-extension-qml-client
RESOURCES += qml.qrc
target.path = $$[QT_INSTALL_EXAMPLES]/wayland/custom-extension/qml-client
INSTALLS += target
@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>