KDEAmbi/lessons_learned.md

17 KiB
Raw Blame History

Lessons Learned from Hyperion Grabber Wayland Adaptation

This document summarizes the key challenges, debugging steps, and solutions encountered during the process of adapting the Hyperion Grabber for Wayland, specifically focusing on the wayland_poc executable.

1. Initial Problem & Diagnosis

  • Problem: The user's existing Hyperion_Grabber_X11_QT application was not logging "picture recorded and sent" messages when run on a Wayland session, indicating a failure in screen capture and transmission.
  • Initial Analysis: The grabber is designed for X11, relying on XDamage and other X11-specific features. Wayland's security model prevents direct screen access, requiring applications to use xdg-desktop-portal and PipeWire for screen sharing with user consent.
  • Conclusion: The X11 grabber would not work natively on Wayland. A Wayland-compatible screen grabbing solution was needed.

2. Wayland Grabber Adaptation Strategy

  • Approach: Replace the X11 screen grabbing logic with a Wayland-compatible one, primarily using PipeWire for video streaming and xdg-desktop-portal for user consent.
  • Proof of Concept (POC): Developed wayland_poc.cpp to test the xdg-desktop-portal interaction and PipeWire stream connection. Also used tutorial5.c from PipeWire documentation to verify core PipeWire API usage.

3. Build Environment Challenges & Docker Adoption

  • Initial Host Compilation Issues: Attempting to compile wayland_poc.cpp directly on the user's Arch Linux host resulted in a persistent fatal error: pipewire/extensions/session-manager/session-manager.h: No such file or directory.
    • Troubleshooting: Confirmed file existence and permissions, verified pkg-config output, modified CMakeLists.txt for include paths and C++ standards, tried different compilers (GCC). None resolved the issue.
    • Conclusion: The error was highly unusual and suggested a deep, system-specific problem with how PipeWire headers were exposed or how the compiler resolved includes on the host.
  • Adoption of Docker: Decided to use Docker to create a reproducible and controlled build environment, aiming to bypass host-specific compilation issues.

4. Docker Build Troubleshooting (Arch Linux Base)

  • Dockerfile (Arch): Initial Dockerfile used archlinux:latest as base.
  • Error 1: sudo: command not found: Occurred because pacman commands were run as builder user without sudo installed or configured.
    • Fix: Moved pacman commands to run as root before switching to builder user.
  • Error 2: mkdir: cannot create directory build: File exists (Permission Denied): Occurred because build directory was copied from host with root ownership, and builder user lacked permissions to modify it.
    • Fix: Added chown -R builder:builder after COPY to transfer ownership to builder user.
  • Error 3: CMakeCache.txt mismatch: Occurred because CMakeCache.txt from host's project root was copied into container, confusing CMake.
    • Fix: Added CMakeCache.txt and CMakeFiles/ to .dockerignore to prevent them from being copied.
  • Error 4: X11/extensions/Xdamage.h: No such file or directory (and scrnsaver.h, Xrender.h): Occurred because necessary X11 development headers were missing in the Docker image.
    • Fix: Added libx11, libxext, libxdamage, libxss, libxrender to pacman install list.
  • Persistent session-manager.h error: Despite all fixes, the fatal error: pipewire/extensions/session-manager/session-manager.h: No such file or directory persisted even in the Arch Docker environment. This indicated the problem was not specific to the host Arch setup, but a deeper issue with PipeWire headers or their interaction with the compiler.

5. Docker Build Troubleshooting (Ubuntu Base & Clang)

  • Switch to Ubuntu: Decided to switch the Docker base image to ubuntu:latest to rule out Arch-specific packaging issues. Also switched to clang as the compiler.
  • Error: fatal error: 'pipewire/extensions/session-manager/impl-session-manager.h' file not found: The session-manager.h issue persisted, even with Ubuntu and Clang. This confirmed the problem was not distribution-specific.
  • Attempt to bypass umbrella header: Modified wayland_poc.cpp to include individual headers (introspect.h, interfaces.h, etc.) instead of session-manager.h. This also failed with similar "file not found" errors for the individual headers.
  • Conclusion: The session-manager.h (and related impl-session-manager.h) compilation issue is extremely persistent and baffling, suggesting a very subtle, low-level problem that cannot be resolved by standard means or remote debugging.

6. Refocusing on xdg-desktop-portal with QDBus

  • Realization: The tutorial5.c (core PipeWire API) compiled successfully. The session-manager.h problem was specific to the xdg-desktop-portal interaction using PipeWire's session manager headers.
  • New Strategy: Isolate the xdg-desktop-portal interaction using Qt's QDBus module, completely bypassing direct PipeWire session manager header includes.
  • wayland_poc.cpp Refactoring: Simplified wayland_poc.cpp to only handle the QDBus interaction to get the pipewire_node_id. Removed all PipeWire stream creation/processing logic.
  • QDBus API Challenges:
    • QDBusMessage::createMethodCall vs createMethod: Discovered createMethodCall is the correct static method for creating method call messages in Qt 5.15.
    • QDBusMessage constructor: Found that the direct constructor QDBusMessage("service", "path", "interface", "method"); is the correct way to initialize QDBusMessage for method calls in the specific Qt5 version in Ubuntu.
    • QDBusPendingCall conversion: Ensured sessionBus.asyncCall(message) is used, which returns QDBusPendingCall.
  • Successful Compilation: The simplified wayland_poc.cpp (QDBus-only) compiled successfully in the Ubuntu Docker container with Clang. This is a major breakthrough, confirming that the QDBus approach for xdg-desktop-portal interaction is viable.

7. Runtime Challenges (D-Bus Connection)

  • Problem: Running the compiled wayland_poc executable (from Docker) on the host failed with Failed to connect to D-Bus session bus.
  • Reason: Docker containers are isolated and do not have direct access to the host's D-Bus session bus by default. Environment variables (DBUS_SESSION_BUS_ADDRESS, XDG_RUNTIME_DIR, DISPLAY, WAYLAND_DISPLAY) and volume mounts (/tmp/.X11-unix, $XDG_RUNTIME_DIR) are needed.
  • Further Problem: Even with correct docker run parameters, the wayland_poc on the host failed with D-Bus call to xdg-desktop-portal failed: "Invalid session".
    • Diagnosis: This error indicates xdg-desktop-portal is not recognizing the session context. It's often related to XDG_SESSION_ID, XDG_SESSION_TYPE, or XDG_CURRENT_DESKTOP not being correctly propagated or interpreted.
  • Method Name Mismatch: The wayland_poc was initially calling PickSource on org.freedesktop.portal.ScreenCast, but the correct method name is SelectSources.
    • Fix: Changed method call from PickSource to SelectSources in wayland_poc.cpp.
  • Argument Type Mismatch: The SelectSources method expects an object path (o) for the parent_window argument, but wayland_poc was sending an empty string (s).
    • Fix: Changed the first argument to QDBusObjectPath("/") in wayland_poc.cpp.
  • Current Status: The wayland_poc executable now compiles successfully. The D-Bus connection issue (Failed to connect to D-Bus session bus. or Invalid session) is still occurring when run on the host, indicating a persistent runtime environment issue with D-Bus access from the application.

8. Next Steps

  • Verify wayland_poc execution on host: The user needs to run the wayland_poc executable on their host with the provided docker run command (or directly if copied out) and confirm if the xdg-desktop-portal dialog appears and a PipeWire node ID is printed. The current D-Bus connection failure needs to be resolved.
  • Integrate: If successful, the next phase will involve integrating the QDBus based xdg-desktop-portal interaction with the core PipeWire stream capture logic (from tutorial5.c) into the main Hyperion grabber.
  • Persistent session-manager.h: The original session-manager.h compilation issue remains unresolved for direct PipeWire session manager API usage. The QDBus approach is a workaround. If direct PipeWire session manager API interaction is ever needed, this issue would need further, likely local, investigation.

9. Recent Debugging and Solutions

  • SelectSources Request Denied:

    • Problem: After initial compilation, wayland_poc's SelectSources call was denied by xdg-desktop-portal.
    • Diagnosis: The D-Bus message for SelectSources was missing required options (e.g., types, cursor_mode, persist_mode, handle_token) that are present in successful calls (e.g., from Firefox).
    • Fix: Modified wayland_poc.cpp to include these missing options in the SelectSources D-Bus message.
  • Persistent QDateTime Compilation Error:

    • Problem: Encountered a persistent "incomplete type 'QDateTime'" error during compilation, even though <QDateTime> was included and its position was adjusted.
    • Diagnosis: This was a highly unusual and difficult-to-diagnose error, possibly related to subtle interactions within the Qt build system or compiler.
    • Workaround: Replaced all uses of QDateTime::currentMSecsSinceEpoch() with QUuid::createUuid().toString() for generating unique tokens, and added #include <QUuid>. This successfully bypassed the compilation error.
  • Docker Filesystem Isolation and replace Tool Misunderstanding:

    • Problem: Changes made to wayland_poc.cpp using the replace tool were not reflected in the Docker container's build process.
    • Diagnosis: The replace tool modifies the host's filesystem, while the Docker container operates on its own isolated filesystem. The container was building from an outdated copy of the source file.
    • Fix: After modifying wayland_poc.cpp on the host, the updated file was explicitly copied into the running Docker container using docker cp.
  • Executable Retrieval from Docker Container:

    • Problem: Difficulty in copying the built wayland_poc executable from the Docker container to the host. The build container would exit immediately, and docker cp failed to find the file.
    • Diagnosis: The original build container was not designed to stay running. docker cp requires a running container or a committed image. The initial docker commit did not seem to capture the build artifacts correctly.
    • Fix: Committed the original build container to a new image (hyperion-grabber-build). Then, a new container was launched from this image with a command (tail -f /dev/null) to keep it running. The executable was then successfully copied from this running container using docker cp.
  • D-Bus SelectSources Signature Mismatch:

    • Problem: wayland_poc was failing with "Type of message, “(oosa{sv})”, does not match expected type “(a{sv})”" when calling SelectSources. This occurred because the handleSelectSourcesResponse function, designed for SelectSources replies, was incorrectly connected to the CreateSession D-Bus call's finished signal. The CreateSession reply's signature (starting with an object path) was being misinterpreted, leading to an incorrect SelectSources call.
    • Diagnosis: The QObject::connect in main was incorrectly routing the CreateSession reply to the handleSelectSourcesResponse function.
    • Fix:
      1. Created a new handler function, handleCreateSessionFinished, to specifically process the CreateSession reply. This function extracts the session handle and then correctly initiates the SelectSources D-Bus call with its own QDBusPendingCallWatcher connected to handleSelectSourcesResponse.
      2. Modified the main function to connect the CreateSession's QDBusPendingCallWatcher to handleCreateSessionFinished.
  • QDBusMessage::clearArguments() Method Not Found:

    • Problem: Compilation failed with an error indicating QDBusMessage had no member named clearArguments().
    • Diagnosis: The clearArguments() method was removed in Qt 5.15, which is the version likely used in the Docker build environment. The call was also redundant as arguments were being cleared and then immediately re-added.
    • Fix: Removed the line message.clearArguments(); from wayland_poc.cpp.
  • D-Bus CreateSession Signature Mismatch:

    • Problem: After fixing the SelectSources signature, the CreateSession call itself started failing with "Type of message, “(oosa{sv})”, does not match expected type “(a{sv})”". This indicated that CreateSession also expects only a single dictionary of options (a{sv}).
    • Diagnosis: The xdg-desktop-portal API for CreateSession had changed, and the handlePath, sessionPath, and applicationName arguments were no longer expected.
    • Fix: Modified the CreateSession call in wayland_poc.cpp to only send an empty QVariantMap as its argument.
  • D-Bus CreateSession "Missing token" Error:

    • Problem: After fixing the CreateSession signature, the call failed with a "Missing token" error.
    • Diagnosis: The CreateSession method, while expecting a single dictionary of options, also requires a handle_token within that dictionary.
    • Fix: Added a handle_token to the QVariantMap passed to the CreateSession call in wayland_poc.cpp.
  • D-Bus CreateSession Signature Mismatch (Revisited):

    • Problem: The CreateSession call was failing with "Type of message, “(oa{sv})”, does not match expected type “(a{sv})”". This indicated that CreateSession expects only a single dictionary of options (a{sv}), and the previously added parent_window object path argument was unexpected.
    • Diagnosis: The xdg-desktop-portal API for CreateSession does not expect a parent_window argument.
    • Fix: Removed the QDBusObjectPath("/") argument from the CreateSession call in wayland_poc.cpp.
  • D-Bus CreateSession Signature Mismatch (Revisited):

    • Problem: The CreateSession call was failing with "Type of message, “(oa{sv})”, does not match expected type “(a{sv})”". This indicated that CreateSession expects only a single dictionary of options (a{sv}), and the previously added parent_window object path argument was unexpected.
    • Diagnosis: The xdg-desktop-portal API for CreateSession does not expect a parent_window argument.
    • Fix: Removed the QDBusObjectPath("/") argument from the CreateSession call in wayland_poc.cpp.
  • D-Bus CreateSession Signature Mismatch (Final Fix):

    • Problem: The CreateSession call was failing with "Type of message, “(oa{sv})”, does not match expected type “(a{sv})”". This indicated that CreateSession expects only a single dictionary of options (a{sv}), and the previously added parent_window object path argument was unexpected.
    • Diagnosis: The xdg-desktop-portal API for CreateSession does not expect a parent_window argument. The previous lessons_learned.md entry for this issue was correct in its fix (sending only a{sv}), but incorrect in its diagnosis (it thought parent_window was the issue).
    • Fix: Removed the QDBusObjectPath("/") argument from the CreateSession call in wayland_poc.cpp.
  • D-Bus CreateSession Signature Mismatch (Final Fix):

    • Problem: The CreateSession call was failing with "Type of message, “(oa{sv})”, does not match expected type “(a{sv})”". This indicated that CreateSession expects only a single dictionary of options (a{sv}), and the previously added parent_window object path argument was unexpected.
    • Diagnosis: The xdg-desktop-portal API for CreateSession does not expect a parent_window argument. The previous lessons_learned.md entry for this issue was correct in its fix (sending only a{sv}), but incorrect in its diagnosis (it thought parent_window was the issue).
    • Fix: Removed the QDBusObjectPath("/") argument from the CreateSession call in wayland_poc.cpp.
  • New Runtime Error: "Remote peer disconnected"

    • Problem: After successfully building and copying wayland_poc to the host, running it resulted in "D-Bus call to SelectSources failed: "Remote peer disconnected"".
    • Diagnosis: This indicates a problem with the D-Bus connection itself, rather than a rejection from the portal. Possible causes include incorrect D-Bus environment variables on the host, xdg-desktop-portal not running, or permission issues.
    • Next Steps: Investigate D-Bus environment variables (DBUS_SESSION_BUS_ADDRESS) and xdg-desktop-portal service status on the host.