Back to Home

C++20 wallpaper manager: D-Bus and RAII

The article breaks down the architecture of the CLI utility Rwal for managing wallpapers in different DEs. Uses Adapter pattern, D-Bus scripting for KDE, GSettings for GNOME, RAII wrapper for libcurl and std::jthread C++20. Code examples and CMake are provided.

Rwal: C++20 wallpaper utility for KDE and GNOME
Advertisement 728x90

Cross-Platform Wallpaper Manager in C++20: Architecture and System Integration

The CLI utility Rwal manages wallpapers in KDE, GNOME, and is planned for Windows. A key element is the IWallpaperSetter interface, implementing the Adapter pattern. This isolates business logic from DE specifics, minimizing #ifdef.

// src/wallpaper/IWallpaperSetter.hpp
class IWallpaperSetter {
public:    
  virtual ~IWallpaperSetter() = default;    
  virtual bool setWallpaper(const std::string& path) = 0;
};

A runtime factory detects the environment and returns the appropriate implementation. Adding support for a new DE only requires a new derived class.

Integration with KDE Plasma via D-Bus

KDE requires interaction with org.kde.plasmashell. The KdeSetter implementation sends a JavaScript script to apply wallpapers to all desktops.

Google AdInline article slot
// Simplified snippet from src/wallpaper/KdeSetter.cpp
QDBusInterface remoteApp("org.kde.plasmashell", "/PlasmaShell", "org.kde.PlasmaShell");
QString script = QString(
    "var allDesktops = desktops();"
    "for (var i = 0; i < allDesktops.length; i++) {"
    "    var d = allDesktops[i];"
    "    d.wallpaperPlugin = 'org.kde.image';"
    "    d.currentConfigGroup = Array('Wallpapers', 'org.kde.image', 'General');"
    "    d.writeConfig('Image', 'file://%1');"
    "}"
).arg(QString::fromStdString(path));

remoteApp.call("evaluateScript", script);

The script dynamically configures wallpaperPlugin and Image for each desktop's settings. D-Bus error handling is implemented via QDBusError.

GNOME: GSettings and Theme Modes

For GNOME, gsettings is used via QProcess. It accounts for picture-uri-dark and picture-uri for dark/light themes.

// From src/wallpaper/GnomeSetter.cpp
QProcess::execute("gsettings", {    
  "set", "org.gnome.desktop.background", "picture-uri-dark",     
  QString("file://%1").arg(QString::fromStdString(path))
});

The code checks for gsettings availability and falls back to direct copying to ~/.config. This ensures stability in containers.

Google AdInline article slot

Network Layer: RAII Wrapper for libcurl

Image downloading is implemented via CurlWrapper with RAII. std::unique_ptr with a custom deleter guarantees cleanup.

// src/net/CurlWrapper.hpp
using CurlPtr = std::unique_ptr<CURL, void(*)(CURL*)>;
// Implementation
CurlWrapper::CurlWrapper() : curl_(curl_easy_init(), curl_easy_cleanup) {    
  if (!curl_) throw std::runtime_error("Failed to initialize CURL");
}

The class encapsulates CURLOPT_USERAGENT, CURLOPT_TIMEOUT, and progress callbacks. Downloading to memory uses CURLOPT_WRITEFUNCTION.

CMake: Modular C++20 Build

The project requires C++20 for std::jthread and planned std::format. CMake finds Qt5 DBus, CURL, nlohmann_json.

Google AdInline article slot
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt5 REQUIRED COMPONENTS Core DBus Widgets)
find_package(CURL REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
add_subdirectory(src/wallpaper)
target_link_libraries(${PROJECT_NAME} PRIVATE wallpaper_lib Qt5::DBus CURL::libcurl nlohmann_json::nlohmann_json)

Avoiding Docker simplified access to the host's D-Bus. Static linking of CURL minimizes dependencies.

Asynchrony with std::jthread

Downloading 4K images blocked the UI. The solution is std::jthread from C++20:

  • Auto-join when exiting scope.
  • stop_token for operation cancellation.
  • co_await compatibility with coroutines.
std::jthread loader(& {
    // Loading with stoken.stop_requested() check
});

Cross-compiler compatibility: GCC 11+ and Clang 14+ with -fcoroutines.

Key Takeaways

  • Adapter pattern allows adding DE support in 15 minutes without core refactoring.
  • RAII for libcurl prevents leaks in exception paths.
  • std::jthread solves UI freezes during network operations.
  • Modular CMake ensures portable builds.
  • D-Bus scripting is the only reliable method for Plasma.

Architectural Decisions and Analysis

Static analysis with clang-tidy revealed Qt signals with potential use-after-free. ADRs document the choice of D-Bus over direct config editing.

Development Plans:

  • Windows WinAPI SystemParametersInfoW.
  • QtNetwork or boost::asio for async.
  • JSON parsing optimization for 100MB+ responses.

The project demonstrates system programming in C++20 for mid-level developers: from interfaces to coroutines.

— Editorial Team

Advertisement 728x90

Read Next