Refactor: Initial setup for Wayland POC with KDE-specific protocol and Docker build environment.
This commit is contained in:
parent
795e6202ac
commit
ab4bb5a7ec
@ -9,39 +9,40 @@ set(CMAKE_BUILD_TYPE Release)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
find_package(Qt5DBus REQUIRED) # Add Qt5DBus
|
||||
|
||||
# Find PipeWire
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(PIPEWIRE REQUIRED libpipewire-0.3)
|
||||
# Find GIO/GLib for GDBus using pkg-config
|
||||
pkg_check_modules(GIO REQUIRED gio-2.0) # Use gio-2.0 for pkg-config
|
||||
|
||||
set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -lXrender -lXdamage -lXss")
|
||||
include_directories(${X11_INCLUDE_DIR} ${PIPEWIRE_INCLUDE_DIRS})
|
||||
|
||||
# add_executable(${PROJECT_NAME} "main.cpp" "hgx11.h" "hgx11.cpp" "hgx11net.h" "hgx11net.cpp" "hgx11damage.h" "hgx11damage.cpp" "hgx11grab.h" "hgx11grab.cpp" "hgx11screensaver.h" "hgx11screensaver.cpp")
|
||||
# target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Network Qt5::Widgets ${X11_LIBRARIES})
|
||||
find_package(Qt5 COMPONENTS Core Gui WaylandClient REQUIRED)
|
||||
|
||||
# Add the Wayland POC executable
|
||||
add_executable(wayland_poc "wayland_poc.cpp")
|
||||
|
||||
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner++)
|
||||
|
||||
set(WAYLAND_PROTOCOLS_DIR "${CMAKE_CURRENT_BINARY_DIR}/wayland_protocols")
|
||||
file(MAKE_DIRECTORY ${WAYLAND_PROTOCOLS_DIR})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WAYLAND_PROTOCOLS_DIR}/qwayland-zkde-screencast-unstable-v1.h
|
||||
COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header "${CMAKE_CURRENT_SOURCE_DIR}/cloned_repos/plasma-wayland-protocols/src/protocols/zkde-screencast-unstable-v1.xml" ${WAYLAND_PROTOCOLS_DIR}/qwayland-zkde-screencast-unstable-v1.h
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/cloned_repos/plasma-wayland-protocols/src/protocols/zkde-screencast-unstable-v1.xml"
|
||||
COMMENT "Generating Wayland client header for zkde-screencast-unstable-v1"
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WAYLAND_PROTOCOLS_DIR}/zkde-screencast-unstable-v1-client-protocol.h
|
||||
COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code "${CMAKE_CURRENT_SOURCE_DIR}/cloned_repos/plasma-wayland-protocols/src/protocols/zkde-screencast-unstable-v1.xml" ${WAYLAND_PROTOCOLS_DIR}/zkde-screencast-unstable-v1-client-protocol.h
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/cloned_repos/plasma-wayland-protocols/src/protocols/zkde-screencast-unstable-v1.xml"
|
||||
COMMENT "Generating Wayland client protocol code for zkde-screencast-unstable-v1"
|
||||
)
|
||||
|
||||
add_custom_target(generate_wayland_protocols ALL
|
||||
DEPENDS ${WAYLAND_PROTOCOLS_DIR}/qwayland-zkde-screencast-unstable-v1.h
|
||||
${WAYLAND_PROTOCOLS_DIR}/zkde-screencast-unstable-v1-client-protocol.h
|
||||
)
|
||||
|
||||
target_include_directories(wayland_poc PRIVATE ${WAYLAND_PROTOCOLS_DIR})
|
||||
add_dependencies(wayland_poc generate_wayland_protocols)
|
||||
|
||||
target_link_libraries(wayland_poc
|
||||
Qt5::Core
|
||||
Qt5::Gui
|
||||
Qt5::DBus # Add Qt5DBus
|
||||
${PIPEWIRE_LIBRARIES}
|
||||
${GIO_LIBRARIES}
|
||||
)
|
||||
|
||||
# target_include_directories(wayland_poc PRIVATE ${PIPEWIRE_INCLUDE_DIRS})
|
||||
|
||||
# Add the Tutorial 5 executable
|
||||
add_executable(tutorial5 tutorial/tutorial5.c)
|
||||
set_property(SOURCE tutorial/tutorial5.c PROPERTY LANGUAGE C)
|
||||
target_link_libraries(tutorial5
|
||||
${PIPEWIRE_LIBRARIES}
|
||||
Qt5::WaylandClient
|
||||
)
|
||||
@ -1,8 +1,8 @@
|
||||
# Use a base image with a recent Linux distribution
|
||||
FROM ubuntu:latest
|
||||
FROM ubuntu:22.04
|
||||
|
||||
# Install necessary dependencies as root
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends build-essential cmake qtbase5-dev libpipewire-0.3-dev libgirepository1.0-dev pkg-config git libx11-dev libxext-dev libxdamage-dev libxss-dev libxrender-dev clang && rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends build-essential cmake qtbase5-dev libqt5waylandclient5-dev libpipewire-0.3-dev libgirepository1.0-dev pkg-config git libx11-dev libxext-dev libxdamage-dev libxss-dev libxrender-dev clang wayland-scanner++ && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
# Set up a non-root user
|
||||
@ -15,12 +15,10 @@ RUN chown -R builder:builder /home/builder/Hyperion_Grabber_X11_QT
|
||||
USER builder
|
||||
WORKDIR /home/builder/Hyperion_Grabber_X11_QT
|
||||
|
||||
# Create a build directory and build the project
|
||||
# Create a build directory and configure cmake
|
||||
RUN rm -rf build && mkdir build
|
||||
WORKDIR build
|
||||
RUN cmake .. -DCMAKE_CXX_COMPILER=clang++
|
||||
RUN qmake -v
|
||||
RUN make
|
||||
|
||||
# You can add commands here to run tests or package the application
|
||||
# For now, we'll just ensure it builds successfully
|
||||
|
||||
180
wayland_poc.cpp
180
wayland_poc.cpp
@ -1,147 +1,99 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QUuid>
|
||||
#include <QDebug>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusPendingCall>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QDBusObjectPath>
|
||||
#include <QVariantMap>
|
||||
#include <QThread>
|
||||
#include <QScreen>
|
||||
#include <QGuiApplication>
|
||||
#include <QtWaylandClient/QWaylandClientExtensionTemplate>
|
||||
#include <QNativeInterface>
|
||||
|
||||
// Global variable to store the session handle
|
||||
static QDBusObjectPath sessionHandle;
|
||||
#include "qwayland-zkde-screencast-unstable-v1.h"
|
||||
|
||||
// QDBus callback for xdg-desktop-portal SelectSources response
|
||||
void handleSelectSourcesResponse(QDBusPendingCallWatcher *watcher) {
|
||||
QDBusPendingCall reply = *watcher;
|
||||
watcher->deleteLater();
|
||||
class ScreencastManager : public QObject, public QtWayland::zkde_screencast_unstable_v1
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ScreencastManager(QObject *parent = nullptr)
|
||||
: QObject(parent)
|
||||
, QtWayland::zkde_screencast_unstable_v1(5) // Version 5 is required for stream_output
|
||||
{
|
||||
if (!isInitialized()) {
|
||||
qCritical() << "Failed to initialize zkde_screencast_unstable_v1. Make sure KWin is running and supports this protocol.";
|
||||
QCoreApplication::quit();
|
||||
return;
|
||||
}
|
||||
qDebug() << "zkde_screencast_unstable_v1 initialized.";
|
||||
|
||||
if (reply.isError()) {
|
||||
qCritical() << "D-Bus call to SelectSources failed:" << reply.error().message(); fflush(stderr);
|
||||
// Request a screencast of the primary screen
|
||||
QScreen *screen = QGuiApplication::primaryScreen();
|
||||
if (!screen) {
|
||||
qCritical() << "No primary screen found.";
|
||||
QCoreApplication::quit();
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QVariant> arguments = reply.reply().arguments();
|
||||
if (arguments.isEmpty()) {
|
||||
qCritical() << "SelectSources response has no arguments."; fflush(stderr);
|
||||
if (auto *waylandScreen = qobject_cast<QNativeInterface::QWaylandScreen *>(screen->nativeInterface())) {
|
||||
wl_output *output = waylandScreen->output();
|
||||
if (!output) {
|
||||
qCritical() << "Failed to get native Wayland output from QWaylandScreen.";
|
||||
QCoreApplication::quit();
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = arguments.at(0).toBool();
|
||||
if (!success) {
|
||||
qCritical() << "SelectSources request denied or failed."; fflush(stderr);
|
||||
// Create a stream for the output
|
||||
// The 'mode' parameter (last argument) can be Hidden = 1, Embedded = 2, Metadata = 4
|
||||
// We'll use Metadata for now.
|
||||
stream_output(output, 4); // 4 for Metadata cursor mode
|
||||
} else {
|
||||
qCritical() << "Failed to get Wayland screen interface from QScreen.";
|
||||
QCoreApplication::quit();
|
||||
return;
|
||||
}
|
||||
|
||||
QVariantList sources = arguments.at(1).toList();
|
||||
if (sources.isEmpty()) {
|
||||
qCritical() << "No sources selected."; fflush(stderr);
|
||||
QCoreApplication::quit();
|
||||
return;
|
||||
}
|
||||
|
||||
// For POC, just take the first source
|
||||
QVariantMap sourceMap = sources.at(0).toMap();
|
||||
if (!sourceMap.contains("pipewire_node_id")) {
|
||||
qCritical() << "Selected source missing pipewire_node_id."; fflush(stderr);
|
||||
QCoreApplication::quit();
|
||||
return;
|
||||
protected:
|
||||
// Implement the generated Wayland protocol callbacks
|
||||
void zkde_screencast_unstable_v1_stream_created(zkde_screencast_stream_unstable_v1 *stream) override
|
||||
{
|
||||
qDebug() << "Screencast stream created!";
|
||||
// Connect to the stream's signals to get the PipeWire node ID
|
||||
connect(static_cast<QtWayland::zkde_screencast_stream_unstable_v1 *>(stream), &QtWayland::zkde_screencast_stream_unstable_v1::created, this, &ScreencastManager::onStreamCreated);
|
||||
connect(static_cast<QtWayland::zkde_screencast_stream_unstable_v1 *>(stream), &QtWayland::zkde_screencast_stream_unstable_v1::closed, this, &ScreencastManager::onStreamClosed);
|
||||
connect(static_cast<QtWayland::zkde_screencast_stream_unstable_v1 *>(stream), &QtWayland::zkde_screencast_stream_unstable_v1::failed, this, &ScreencastManager::onStreamFailed);
|
||||
}
|
||||
|
||||
quint32 node_id = sourceMap.value("pipewire_node_id").toUInt();
|
||||
qDebug() << "xdg-desktop-portal granted screen capture. PipeWire node ID:" << node_id;
|
||||
|
||||
QCoreApplication::quit(); // Quit after getting the node ID for POC
|
||||
}
|
||||
|
||||
// QDBus callback for xdg-desktop-portal CreateSession response
|
||||
// QDBus callback for xdg-desktop-portal CreateSession response
|
||||
void handleCreateSessionFinished(QDBusPendingCallWatcher *watcher) {
|
||||
QDBusPendingCall reply = *watcher;
|
||||
watcher->deleteLater();
|
||||
|
||||
if (reply.isError()) {
|
||||
qCritical() << "D-Bus call to CreateSession failed:" << reply.error().message(); fflush(stderr);
|
||||
QCoreApplication::quit();
|
||||
return;
|
||||
void zkde_screencast_unstable_v1_stream_destroyed(zkde_screencast_stream_unstable_v1 *stream) override
|
||||
{
|
||||
qDebug() << "Screencast stream destroyed.";
|
||||
}
|
||||
|
||||
QList<QVariant> arguments = reply.reply().arguments();
|
||||
if (arguments.isEmpty()) {
|
||||
qCritical() << "CreateSession response has no arguments."; fflush(stderr);
|
||||
QCoreApplication::quit();
|
||||
return;
|
||||
private slots:
|
||||
void onStreamCreated(quint32 node_id)
|
||||
{
|
||||
qDebug() << "PipeWire node ID:" << node_id;
|
||||
// Here you would typically use the node_id to connect to PipeWire and start processing frames.
|
||||
QCoreApplication::quit(); // For POC, quit after getting the node ID
|
||||
}
|
||||
|
||||
QVariant firstArg = arguments.at(0);
|
||||
if (!firstArg.canConvert<QDBusObjectPath>()) {
|
||||
qCritical() << "CreateSession response first argument is not an object path."; fflush(stderr);
|
||||
void onStreamClosed()
|
||||
{
|
||||
qDebug() << "Screencast stream closed.";
|
||||
QCoreApplication::quit();
|
||||
return;
|
||||
}
|
||||
|
||||
sessionHandle = qvariant_cast<QDBusObjectPath>(firstArg);
|
||||
qDebug() << "Session created with handle:" << sessionHandle.path();
|
||||
void onStreamFailed(const QString &error)
|
||||
{
|
||||
qCritical() << "Screencast stream failed:" << error;
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
};
|
||||
|
||||
// Now call SelectSources
|
||||
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
||||
QDBusMessage message = QDBusMessage::createMethodCall(
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.ScreenCast",
|
||||
"SelectSources"
|
||||
);
|
||||
|
||||
QVariantMap options;
|
||||
options.insert("multiple", false); // Request single source
|
||||
options.insert("types", (uint)3); // Monitor and Window
|
||||
options.insert("cursor_mode", (uint)4); // Metadata
|
||||
options.insert("persist_mode", (uint)2); // Persist for session (OBS value)
|
||||
options.insert("handle_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Use QUuid without braces
|
||||
options.insert("restore_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Add restore_token (OBS value)
|
||||
|
||||
|
||||
message << options;
|
||||
|
||||
QDBusPendingCall pendingCall = sessionBus.asyncCall(message);
|
||||
QDBusPendingCallWatcher *selectSourcesWatcher = new QDBusPendingCallWatcher(pendingCall); // Use a new watcher
|
||||
|
||||
QObject::connect(selectSourcesWatcher, &QDBusPendingCallWatcher::finished, handleSelectSourcesResponse);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
qDebug() << "Requesting screen capture via xdg-desktop-portal...";
|
||||
qDebug() << "Starting Wayland POC...";
|
||||
|
||||
QDBusConnection sessionBus = QDBusConnection::sessionBus();
|
||||
if (!sessionBus.isConnected()) {
|
||||
qCritical() << "Failed to connect to D-Bus session bus."; fflush(stderr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Call CreateSession first
|
||||
QDBusMessage message = QDBusMessage::createMethodCall(
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.ScreenCast",
|
||||
"CreateSession"
|
||||
);
|
||||
|
||||
QVariantMap options;
|
||||
options.insert("handle_token", QUuid::createUuid().toString(QUuid::WithoutBraces)); // Add handle_token
|
||||
message << options;
|
||||
|
||||
QDBusPendingCall pendingCall = sessionBus.asyncCall(message);
|
||||
QDBusPendingCallWatcher *mainWatcher = new QDBusPendingCallWatcher(pendingCall);
|
||||
|
||||
QObject::connect(mainWatcher, &QDBusPendingCallWatcher::finished, handleCreateSessionFinished);
|
||||
ScreencastManager manager;
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
#include "wayland_poc.moc"
|
||||
Loading…
x
Reference in New Issue
Block a user