#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "videodecoder.hpp" #include "wallpapercache.hpp" #include "videotranscoder.hpp" #include #include #include "wallpapersurface.hpp" #include "config/enums.hpp" struct mpv_handle; struct mpv_render_context; class QProcess; namespace ZShell::wallpaper { class WallpaperSurface; class WallpaperService : public QObject { Q_OBJECT QML_ELEMENT Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED) Q_PROPERTY(QVariantMap crops READ crops WRITE setCrops NOTIFY cropsChanged) Q_PROPERTY(QString dbPath READ dbPath WRITE setDbPath NOTIFY dbPathChanged) Q_PROPERTY( QSize largestBufferSize READ largestBufferSize NOTIFY largestBufferSizeChanged) public: explicit WallpaperService(QObject* parent = nullptr); ~WallpaperService() override; [[nodiscard]] QString path() const; void setPath(const QString& path); [[nodiscard]] QSize largestBufferSize() const; [[nodiscard]] QString dbPath() const; void setDbPath(const QString& path); void addScreen(const QString& screen); void removeScreen(const QString& screen); [[nodiscard]] const QVariantMap& crops() const; void setCrops(const QVariantMap& crops); Q_INVOKABLE void redraw(); Q_INVOKABLE void setCrop(const QString& screen, const QVariantMap& crop); Q_INVOKABLE void invalidateCache(); signals: void pathChanged(); void cropsChanged(); void dbPathChanged(); void largestBufferSizeChanged(); private: friend class WallpaperSurface; static bool isVideoPath(const QString& path); void applyPath(const QString& path); void startVideo(const QString& path); void teardownMpv(); void initMpvRender(); void renderMpvToSurface(const QSize& size); void pumpMpvEvents(); static void* getMpvProcAddress(void* ctx, const char* name); static void mpvUpdate(void* ctx); #ifdef ZHAVE_FFMPEG bool startDecoderVideo(const QString& path); void stopDecoderVideo(); [[nodiscard]] uint32_t createVideoProgram(); void drawVideoTexture( uint32_t lumaTex, uint32_t chromaTex, int matrix, int range, const QRectF& rect, float sizeW, float sizeH, float srcW, float srcH, float alpha); #endif void bindGlobals(); static void registryGlobal( void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version); void storeGlobal(const char* interface, void* proxy); struct TransProgram { uint32_t id = 0; int size = -1; int progress = -1; int from = -1; int to = -1; int fromCrop = -1; int toCrop = -1; int fromFlip = -1; int toFlip = -1; }; [[nodiscard]] bool setupGl(); [[nodiscard]] uint32_t createProgram(); [[nodiscard]] TransProgram createTransitionProgram(const QString& file); [[nodiscard]] TransProgram programFor(const QString& file); bool ensureTransitionProgram(); QString transitionFileFor(ZShell::config::WallpaperTransition::Enum value); [[nodiscard]] QString randomTransitionFile(); void ensureCapture(const QSize& size); uint32_t renderVideoIntoTexture(const QSize& size); void drawSurfaceTransition(WallpaperSurface* surface); struct TransitionLayer { uint32_t tex = 0; QImage image; QVariantMap crops; QString transitionFile; bool isVideo = false; bool ready = false; bool started = false; QElapsedTimer timer; qreal progress = 0.0; }; void ensureBaseFbo(); void ensureAccum(const QSize& size); void bindRtt(uint32_t fbo, uint32_t tex, const QSize& size); void drawTransitionPair( const TransProgram& program, uint32_t fromTex, float fromFlip, const QRectF& fromCrop, uint32_t toTex, float toFlip, const QRectF& toCrop, qreal progress, const QSize& size); uint32_t targetTexFor(const TransitionLayer& layer, int index) const; QRectF cropForLayer( const TransitionLayer& layer, const QString& screen, const QSize& size) const; void captureBase( const QSize& size, const QString& screen, uint32_t oldTex, float oldFlip, const QRectF& oldCrop); void renderComposite( const QSize& size, const QString& screen, uint32_t outFbo, uint32_t outTex, int first, int last, uint32_t videoTex, uint32_t posterTex); void uploadTexture(uint32_t& texture, const QImage& image); void uploadImage(); void createBlackTexture(uint32_t& texture); void drawTexture( uint32_t texture, const QRectF& rect, float sizeW, float sizeH, float alpha); void ensureImage(); void ensurePoster(); void notifyBufferSizes(); void queue(WallpaperSurface* surface); void queueAll(); void loop(); void present(); void drawSurface(WallpaperSurface* surface); void wake(); [[nodiscard]] wl_event_queue* eventQueue() const { return m_queue; } [[nodiscard]] QRectF sourceRect( const QVariantMap& crops, const QString& screen, const QSize& size, const QSize& image) const; void start(); void addSurfaces(); void addSurface(const QString& screen); QString m_path; QString m_dbPath; QStringList m_screens; QVariantMap m_crops; WallpaperCache m_cache; QImage m_image; bool m_isVideo = false; bool m_videoReload = false; mpv_handle* m_mpv = nullptr; mpv_render_context* m_mpvRender = nullptr; uint32_t m_blackTex = 0; std::atomic m_mpvFrameReady{false}; std::atomic m_videoReady{false}; std::atomic m_videoLoaded{false}; VideoTranscoder* m_transcoder = nullptr; QSize m_decodeSize; QImage m_stagedImage; QString m_stagedPath; bool m_stagedReady = false; bool m_decoding = false; QString m_decodePath; bool m_replacementReady = false; wl_display* m_display = nullptr; wl_registry* m_registry = nullptr; wl_compositor* m_compositor = nullptr; QtWayland::zwlr_layer_shell_v1* m_layerShell = nullptr; QtWayland::wp_viewporter* m_viewporter = nullptr; QtWayland::wp_fractional_scale_manager_v1* m_scaleManager = nullptr; EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; EGLConfig m_config = nullptr; EGLContext m_context = EGL_NO_CONTEXT; uint32_t m_texture = 0; uint32_t m_program = 0; int m_sizeUniform = -1; int m_cropUniform = -1; int m_alphaUniform = -1; int m_sourceUniform = -1; bool m_randomTransition = false; QString m_transitionFile; bool m_transitionDirty = false; QHash m_transPrograms; TransProgram m_neutralProgram; QVector m_transitionUses; QString m_lastTransition; uint32_t m_captureFbo = 0; uint32_t m_captureTex = 0; QSize m_captureSize; uint32_t m_baseFbo = 0; uint32_t m_baseTex = 0; QSize m_baseSize; bool m_baseReady = false; uint32_t m_accumFbo = 0; uint32_t m_accumA = 0; uint32_t m_accumB = 0; QSize m_accumSize; std::vector m_transitions; bool m_needCaptureBase = false; QImage m_baseImage; QVariantMap m_baseCrops; bool m_baseIsVideo = false; bool m_baseIsBlack = false; bool m_collapseVideo = false; bool m_promoteOldest = false; bool m_newestDone = false; static constexpr int kMaxTransitions = 4; uint32_t m_videoProgram = 0; uint32_t m_vao = 0; int m_videoSizeUniform = -1; int m_videoSrcSizeUniform = -1; int m_videoCropUniform = -1; int m_videoAlphaUniform = -1; int m_videoLumaUniform = -1; int m_videoChromaUniform = -1; int m_videoMatrixUniform = -1; int m_videoRangeUniform = -1; int m_videoChromaOffUniform = -1; #ifdef ZHAVE_FFMPEG std::unique_ptr m_decoder; bool m_useDecoder = true; #endif std::vector m_orphanTextures; bool m_programsCompiled = false; QList m_surfaces; std::thread m_thread; mutable std::recursive_mutex m_mutex; wl_event_queue* m_queue = nullptr; int m_wake[2] = {-1, -1}; bool m_stop = false; bool m_drawing = false; bool started = false; bool m_forceDecode = false; bool m_imageChanged = false; QImage m_posterImage; uint32_t m_posterTex = 0; QString m_posterPath; bool m_posterReady = false; bool m_posterLoading = false; bool m_posterChanged = false; }; } // namespace ZShell::wallpaper