Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package aquamarine for openSUSE:Factory 
checked in at 2026-07-28 17:50:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/aquamarine (Old)
 and      /work/SRC/openSUSE:Factory/.aquamarine.new.2004 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "aquamarine"

Tue Jul 28 17:50:20 2026 rev:11 rq:1368167 version:0.14.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/aquamarine/aquamarine.changes    2026-07-17 
01:45:25.809192737 +0200
+++ /work/SRC/openSUSE:Factory/.aquamarine.new.2004/aquamarine.changes  
2026-07-28 17:51:38.079307455 +0200
@@ -1,0 +2,28 @@
+Tue Jul 28 06:59:50 UTC 2026 - Nassim Lhadj <[email protected]>
+
+- Update to aquamarine version 0.14.0:
+  - A New ABI breaking release:
+     * drm: do not stash failed commits
+     * drm: add support for COLOR_RANGE prop
+     * drm: add frameguard for idle frame
+     * drm: dont set HW_CLOCK on nvidia reporting seq 0
+
+-------------------------------------------------------------------
+Thu Jul 23 16:27:27 UTC 2026 - Nassim Lhadj <[email protected]>
+
+- Update to aquamarine version 0.13.0:
+  - A new major ABI breaking release for aquamarine:
+     * renderer: use version-compatible EGL debug context attributes
+     * drm: fix resource leaks
+     * atomic: fix undefined behavior in cursor plane position cast
+     * frame: ensure only one frame when pageflip occurs
+     * internal: chase hu fixes
+     * backend: fix implicit bool cast
+     * drm: deterministic page-flip scheduling + coalescing (#304,#306)
+     * drm: attach a right-sized swapchain buffer on modeset commits
+     * backend: enable libinput plugins if available
+     * drm: snapshot the commit for stashing
+     * backend/drm: fix dangling "this" in a callback
+     * drm: don't reset needFrame on stashing
+     
+-------------------------------------------------------------------
@@ -12 +40,2 @@
-    * drm: CRTC starvation recovery + clear stale page-flip state after suspend
+    * drm: CRTC starvation recovery + clear stale page-flip state
+      after suspend
@@ -18 +47,2 @@
-    * drm: fall back to first render node on single-renderD systems when 
parent-syspath match fails
+    * drm: fall back to first render node on single-renderD systems
+      when parent-syspath match fails
@@ -20 +50,2 @@
-    * session: Add AQ_NO_KMS_REQUIREMENT environment variable for bypassing 
KMS requirement
+    * session: Add AQ_NO_KMS_REQUIREMENT environment variable for
+      bypassing KMS requirement
@@ -50 +81,2 @@
-    * backend/drm: fix multi-GPU buffer import failure during restoreAfterVT
+    * backend/drm: fix multi-GPU buffer import failure during
+      restoreAfterVT
@@ -52,2 +84,4 @@
-    * backend: handle unhandled libinput switch enum with default case
-    * drm: guard logging in connector disconnect to avoid null dereference
+    * backend: handle unhandled libinput switch enum with
+      default case
+    * drm: guard logging in connector disconnect to avoid
+      null dereference
@@ -56 +90,2 @@
-    * refactor: avoid repeated vector access in backend and DRM loops
+    * refactor: avoid repeated vector access in backend
+      and DRM loops

Old:
----
  aquamarine-0.12.1.tar.xz

New:
----
  aquamarine-0.14.0.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ aquamarine.spec ++++++
--- /var/tmp/diff_new_pack.i5w4ly/_old  2026-07-28 17:51:39.751365922 +0200
+++ /var/tmp/diff_new_pack.i5w4ly/_new  2026-07-28 17:51:39.755366062 +0200
@@ -16,13 +16,13 @@
 #
 
 
-%define sover   11
+%define sover   13
 %define _description %{expand:
 Aquamarine is a very light linux rendering backend library. It
 provides basic abstractions for an application to render on a Wayland
 session (in a window) or a native DRM session.}
 Name:           aquamarine
-Version:        0.12.1
+Version:        0.14.0
 Release:        0
 Summary:        Rendering backend library
 License:        BSD-3-Clause

++++++ aquamarine-0.12.1.tar.xz -> aquamarine-0.14.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/.gitignore 
new/aquamarine-0.14.0/.gitignore
--- old/aquamarine-0.12.1/.gitignore    2026-06-11 18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/.gitignore    2026-07-27 18:34:54.000000000 +0200
@@ -36,4 +36,7 @@
 .cache/
 
 protocols/*.cpp
-protocols/*.hpp
\ No newline at end of file
+protocols/*.hpp
+
+.envrc
+.direnv/
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/CMakeLists.txt 
new/aquamarine-0.14.0/CMakeLists.txt
--- old/aquamarine-0.12.1/CMakeLists.txt        2026-06-11 18:17:06.000000000 
+0200
+++ new/aquamarine-0.14.0/CMakeLists.txt        2026-07-27 18:34:54.000000000 
+0200
@@ -61,12 +61,15 @@
 file(GLOB_RECURSE PUBLIC_HEADERS CONFIGURE_DEPENDS "include/*.hpp")
 
 add_library(aquamarine SHARED ${SRCFILES})
+if(deps_libinput_VERSION VERSION_GREATER_EQUAL "1.30")
+  target_compile_definitions(aquamarine PRIVATE 
AQUAMARINE_HAS_LIBINPUT_PLUGINS)
+endif()
 target_include_directories(
   aquamarine
   PUBLIC "./include"
   PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}")
 set_target_properties(aquamarine PROPERTIES VERSION ${AQUAMARINE_VERSION}
-                                            SOVERSION 11)
+                                            SOVERSION 13)
 target_link_libraries(aquamarine OpenGL::EGL OpenGL::OpenGL PkgConfig::deps)
 
 check_include_file("sys/timerfd.h" HAS_TIMERFD)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/VERSION 
new/aquamarine-0.14.0/VERSION
--- old/aquamarine-0.12.1/VERSION       2026-06-11 18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/VERSION       2026-07-27 18:34:54.000000000 +0200
@@ -1 +1 @@
-0.12.1
+0.14.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/docs/env.md 
new/aquamarine-0.14.0/docs/env.md
--- old/aquamarine-0.12.1/docs/env.md   2026-06-11 18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/docs/env.md   2026-07-27 18:34:54.000000000 +0200
@@ -9,6 +9,10 @@
 `AQ_MGPU_NO_EXPLICIT` -> Disables explicit syncing on mgpu buffers
 `AQ_NO_MODIFIERS` -> Disables modifiers for DRM buffers
 
+### Input
+
+`AQ_LIBINPUT_NO_PLUGINS` -> Disables libinput plugin loading
+
 ### Debugging
 
 `AQ_TRACE` -> Enables trace (very verbose) logging
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/aquamarine-0.12.1/include/aquamarine/backend/Backend.hpp 
new/aquamarine-0.14.0/include/aquamarine/backend/Backend.hpp
--- old/aquamarine-0.12.1/include/aquamarine/backend/Backend.hpp        
2026-06-11 18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/include/aquamarine/backend/Backend.hpp        
2026-07-27 18:34:54.000000000 +0200
@@ -30,6 +30,15 @@
         AQ_BACKEND_NULL,
     };
 
+    enum eBackendGPUDriver : uint32_t {
+        AQ_BACKEND_GPU_DRIVER_UNKNOWN = 0,
+        AQ_BACKEND_GPU_DRIVER_INTEL,   // i915, xe
+        AQ_BACKEND_GPU_DRIVER_AMD,     // amdgpu, radeon
+        AQ_BACKEND_GPU_DRIVER_NVIDIA,  // proprietary nvidia-drm
+        AQ_BACKEND_GPU_DRIVER_NOUVEAU, // open source nouveau
+        AQ_BACKEND_GPU_DRIVER_EVDI,    // displayLink virtual driver
+    };
+
     enum eBackendRequestMode : uint32_t {
         /*
             Require the provided backend, will error out if it's not available.
@@ -95,6 +104,7 @@
         virtual std::vector<Hyprutils::Memory::CSharedPointer<IAllocator>> 
getAllocators()   = 0;
         virtual Hyprutils::Memory::CWeakPointer<IBackendImplementation>    
getPrimary()      = 0;
         virtual int                                                        
drmRenderNodeFD() = 0;
+        virtual eBackendGPUDriver                                          
gpuDriver();
     };
 
     class CBackend {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/include/aquamarine/backend/DRM.hpp 
new/aquamarine-0.14.0/include/aquamarine/backend/DRM.hpp
--- old/aquamarine-0.12.1/include/aquamarine/backend/DRM.hpp    2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/include/aquamarine/backend/DRM.hpp    2026-07-27 
18:34:54.000000000 +0200
@@ -4,6 +4,7 @@
 #include "../allocator/Swapchain.hpp"
 #include "../output/Output.hpp"
 #include "../input/Input.hpp"
+#include "FrameScheduler.hpp"
 #include <hyprutils/memory/WeakPtr.hpp>
 #include <wayland-client.h>
 #include <xf86drmMode.h>
@@ -139,10 +140,21 @@
                 uint32_t hotspot_x;
                 uint32_t hotspot_y;
                 uint32_t in_fence_fd;
+                uint32_t color_range;
             } values;
-            uint32_t props[17] = {0};
+            uint32_t props[18] = {0};
         };
         UDRMPlaneProps props;
+
+        // only valid when color_range != 0
+        union UDRMPlaneColorRange {
+            struct {
+                uint32_t Full_YCbCr;
+                uint32_t Limited_YCbCr;
+            } values;
+            uint32_t props[2] = {0};
+        };
+        UDRMPlaneColorRange colorRange;
     };
 
     struct SDRMCRTC {
@@ -155,13 +167,13 @@
         } legacy;
 
         struct {
-            bool     ownModeID     = false;
-            uint32_t modeID        = 0;
-            uint32_t gammaLut      = 0;
-            uint32_t ctm           = 0;
-            uint32_t hdr           = 0;
+            bool     ownModeID = false;
+            uint32_t modeID    = 0;
+            uint32_t gammaLut  = 0;
+            uint32_t ctm       = 0;
+            uint32_t hdr       = 0;
             // true once a real commit has made the kernel CTM match our state.
-            bool     ctmStateKnown = false;
+            bool ctmStateKnown = false;
         } atomic;
 
         Hyprutils::Memory::CSharedPointer<SDRMPlane> primary;
@@ -205,6 +217,7 @@
         virtual size_t                                                    
getDeGammaSize();
         virtual std::vector<SDRMFormat>                                   
getRenderFormats();
         virtual bool                                                      
pendingPageFlip();
+        virtual bool                                                      
pendingIdleFrame();
         void                                                              
releaseMgpuResources();
 
         int                                                               
getConnectorID();
@@ -225,6 +238,7 @@
         Hyprutils::Memory::CWeakPointer<CDRMBackend>                 backend;
         Hyprutils::Memory::CSharedPointer<SDRMConnector>             connector;
         Hyprutils::Memory::CSharedPointer<std::function<void(void)>> frameIdle;
+        Hyprutils::Signal::CHyprSignalListener                       
frameReadyListener;
 
         struct {
             Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain;
@@ -243,10 +257,13 @@
 
     struct SDRMConnectorCommitData {
         Hyprutils::Memory::CSharedPointer<CDRMFB> mainFB, cursorFB;
-        bool                                      modeset  = false;
-        bool                                      blocking = false;
-        uint32_t                                  flags    = 0;
-        bool                                      test     = false;
+        bool                                      modeset   = false;
+        bool                                      blocking  = false;
+        uint32_t                                  flags     = 0;
+        bool                                      test      = false;
+        bool                                      enabled   = false;
+        uint32_t                                  committed = 0;
+        Hyprutils::Math::CRegion                  damage;
         drmModeModeInfo                           modeInfo;
         std::optional<Hyprutils::Math::Mat3x3>    ctm;
         std::optional<hdr_output_metadata>        hdrMetadata;
@@ -324,11 +341,8 @@
         Hyprutils::Math::Vector2D                      cursorPos, cursorSize, 
cursorHotspot;
         Hyprutils::Memory::CSharedPointer<CDRMFB>      pendingCursorFB;
 
-        bool                                           isPageFlipPending   = 
false;
-        uint64_t                                       pageFlipPendingAtMs = 
0; // CLOCK_BOOTTIME ms when isPageFlipPending was set
         SDRMPageFlip                                   pendingPageFlip;
-        bool                                           frameEventScheduled = 
false;
-        bool                                           isFrameRunning      = 
false;
+        CFrameScheduler                                sched;
 
         // the current state is invalid and won't commit, don't try to modeset.
         bool                                           commitTainted = false;
@@ -420,6 +434,7 @@
         std::vector<FIdleCallback>                                         
idleCallbacks;
         std::string                                                        
gpuName;
         virtual int                                                        
drmRenderNodeFD();
+        virtual eBackendGPUDriver                                          
gpuDriver();
 
       private:
         CDRMBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend);
@@ -453,6 +468,7 @@
         } rendererState;
 
         bool                                                          
rendererRequired = true;
+        eBackendGPUDriver                                             driver   
        = AQ_BACKEND_GPU_DRIVER_UNKNOWN;
 
         Hyprutils::Memory::CWeakPointer<CBackend>                     backend;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/aquamarine-0.12.1/include/aquamarine/backend/FrameScheduler.hpp 
new/aquamarine-0.14.0/include/aquamarine/backend/FrameScheduler.hpp
--- old/aquamarine-0.12.1/include/aquamarine/backend/FrameScheduler.hpp 
1970-01-01 01:00:00.000000000 +0100
+++ new/aquamarine-0.14.0/include/aquamarine/backend/FrameScheduler.hpp 
2026-07-27 18:34:54.000000000 +0200
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <hyprutils/signal/Signal.hpp>
+
+namespace Aquamarine {
+    // Per-output frame scheduling state, shared by the DRM and Wayland 
backends.
+    //
+    // Both backends produce an end-of-frame event — DRM from the kernel 
page-flip
+    // completion, Wayland from wl_callback.done on a wl_surface.frame. This 
class
+    // models that lifecycle uniformly and fires frameReady on completion.
+    class CFrameScheduler {
+      public:
+        // a frame was submitted to the host with a completion event requested.
+        // DRM: page-flip submitted with PAGE_FLIP_EVENT. Wayland: sendCommit 
followed
+        // by a wl_surface.frame whose done is wired to onFrameComplete.
+        void onFrameSubmitted();
+
+        // the host delivered the frame completion event.
+        // DRM: kernel page-flip handler. Wayland: wl_callback.done.
+        // State-only — clears m_pending. The backend fires frameReady 
explicitly
+        // at the right point in its handler (preserving the 
present-before-frame
+        // ordering on DRM).
+        void onFrameComplete();
+
+        // the in-flight frame (if any) is void and no completion event will 
arrive;
+        // reset the frame bookkeeping.
+        void invalidate();
+
+        // is a frame in flight awaiting completion?
+        bool frameInFlight() const;
+
+        // may a new frame be scheduled? (does not consider output enabled 
state)
+        bool canSchedule() const;
+
+        // State accessors. setFrameScheduled / frameScheduled() drive the 
idle-frame
+        // loop; setFrameRunning / frameRunning() guard event emission on 
completion.
+        bool frameScheduled() const;
+        void setFrameScheduled(bool v);
+        bool frameRunning() const;
+        void setFrameRunning(bool v);
+
+        // Fires from onFrameComplete. The output wires this to 
events.frame.emit.
+        Hyprutils::Signal::CSignalT<> frameReady;
+
+      private:
+        bool m_pending        = false;
+        bool m_frameScheduled = false;
+        bool m_frameRunning   = false;
+    };
+
+    // RAII pair for isFrameRunning: set on ctor, cleared on every exit path 
so a
+    // reentrant emit handler cannot strand it.
+    class CFrameRunningGuard {
+      public:
+        explicit CFrameRunningGuard(CFrameScheduler& s);
+        ~CFrameRunningGuard();
+        CFrameRunningGuard(const CFrameRunningGuard&)            = delete;
+        CFrameRunningGuard& operator=(const CFrameRunningGuard&) = delete;
+
+      private:
+        CFrameScheduler& m_s;
+    };
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/aquamarine-0.12.1/include/aquamarine/backend/Headless.hpp 
new/aquamarine-0.14.0/include/aquamarine/backend/Headless.hpp
--- old/aquamarine-0.12.1/include/aquamarine/backend/Headless.hpp       
2026-06-11 18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/include/aquamarine/backend/Headless.hpp       
2026-07-27 18:34:54.000000000 +0200
@@ -21,6 +21,7 @@
         virtual bool                                                      
destroy();
         virtual std::vector<SDRMFormat>                                   
getRenderFormats();
         virtual bool                                                      
pendingPageFlip();
+        virtual bool                                                      
pendingIdleFrame();
 
         Hyprutils::Memory::CWeakPointer<CHeadlessOutput>                  self;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/aquamarine-0.12.1/include/aquamarine/backend/Wayland.hpp 
new/aquamarine-0.14.0/include/aquamarine/backend/Wayland.hpp
--- old/aquamarine-0.12.1/include/aquamarine/backend/Wayland.hpp        
2026-06-11 18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/include/aquamarine/backend/Wayland.hpp        
2026-07-27 18:34:54.000000000 +0200
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "./Backend.hpp"
+#include "./FrameScheduler.hpp"
 #include "../allocator/Swapchain.hpp"
 #include "../output/Output.hpp"
 #include "../input/Input.hpp"
@@ -51,6 +52,7 @@
         virtual bool                                                      
destroy();
         virtual std::vector<SDRMFormat>                                   
getRenderFormats();
         virtual bool                                                      
pendingPageFlip();
+        virtual bool                                                      
pendingIdleFrame();
 
         Hyprutils::Memory::CWeakPointer<CWaylandOutput>                   self;
 
@@ -61,14 +63,13 @@
 
         Hyprutils::Memory::CSharedPointer<CWaylandBuffer> 
wlBufferFromBuffer(Hyprutils::Memory::CSharedPointer<IBuffer> buffer);
 
-        void                                              
sendFrameAndSetCallback();
         void                                              onFrameDone();
         void                                              
onEnter(Hyprutils::Memory::CSharedPointer<CCWlPointer> pointer, uint32_t 
serial);
 
-        // frame loop
-        bool frameScheduledWhileWaiting = false;
-        bool readyForFrameCallback      = false; // true after attaching a 
buffer
-        bool frameScheduled             = false;
+        // frame loop — unified scheduler shared with DRM. See CFrameScheduler.
+        CFrameScheduler                                   sched;
+        Hyprutils::Signal::CHyprSignalListener            frameReadyListener;
+        Hyprutils::Memory::CSharedPointer<std::function<void(void)>> frameIdle;
 
         struct {
             std::vector<std::pair<Hyprutils::Memory::CWeakPointer<IBuffer>, 
Hyprutils::Memory::CSharedPointer<CWaylandBuffer>>> buffers;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/aquamarine-0.12.1/include/aquamarine/output/Output.hpp 
new/aquamarine-0.14.0/include/aquamarine/output/Output.hpp
--- old/aquamarine-0.12.1/include/aquamarine/output/Output.hpp  2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/include/aquamarine/output/Output.hpp  2026-07-27 
18:34:54.000000000 +0200
@@ -37,6 +37,12 @@
         AQ_SUBPIXEL_VERTICAL_BGR,
     };
 
+    enum eOutputColorRange : uint32_t {
+        AQ_OUTPUT_COLOR_RANGE_AUTO = 0, // leave the driver default
+        AQ_OUTPUT_COLOR_RANGE_FULL,     // force full range
+        AQ_OUTPUT_COLOR_RANGE_LIMITED,  // force limited (16-235) range
+    };
+
     class IOutput;
 
     class COutputState {
@@ -75,10 +81,11 @@
             uint32_t                                       drmFormat = 
DRM_FORMAT_INVALID;
             Hyprutils::Memory::CSharedPointer<IBuffer>     buffer;
             int32_t                                        explicitInFence = 
-1, explicitOutFence = -1;
-            Hyprutils::Math::Mat3x3                        ctm = 
Hyprutils::Math::Mat3x3::identity();
+            Hyprutils::Math::Mat3x3                        ctm            = 
Hyprutils::Math::Mat3x3::identity();
             bool                                           wideColorGamut = 
false;
             hdr_output_metadata                            hdrMetadata;
             uint16_t                                       contentType = 
DRM_MODE_CONTENT_TYPE_GRAPHICS;
+            eOutputColorRange                              colorRange  = 
AQ_OUTPUT_COLOR_RANGE_AUTO;
         };
 
         const SInternalState& state();
@@ -101,6 +108,7 @@
         void                  setWideColorGamut(bool wcg);
         void                  setHDRMetadata(const hdr_output_metadata& 
metadata);
         void                  setContentType(const uint16_t drmContentType);
+        void                  setColorRange(eOutputColorRange range);
 
       private:
         SInternalState internalState;
@@ -171,7 +179,8 @@
         virtual size_t                                                    
getGammaSize();
         virtual size_t                                                    
getDeGammaSize();
         virtual bool                                                      
destroy(); // not all backends allow this!!!
-        virtual bool                                                      
pendingPageFlip() = 0;
+        virtual bool                                                      
pendingPageFlip()  = 0;
+        virtual bool                                                      
pendingIdleFrame() = 0;
 
         std::string                                                       
name, description, make, model, serial;
         SParsedEDID                                                       
parsedEDID;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/Backend.cpp 
new/aquamarine-0.14.0/src/backend/Backend.cpp
--- old/aquamarine-0.12.1/src/backend/Backend.cpp       2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/src/backend/Backend.cpp       2026-07-27 
18:34:54.000000000 +0200
@@ -110,6 +110,9 @@
 }
 
 Aquamarine::CBackend::~CBackend() {
+    if (idle.fd >= 0)
+        close(idle.fd);
+
     // Tear down implementations before the logger is destroyed,
     // as backends may log during teardown (e.g. SDRMConnector::disconnect).
     implementations.clear();
@@ -130,7 +133,7 @@
 
     for (size_t i = 0; i < implementations.size(); ++i) {
         const auto& impl = implementations.at(i);
-        const bool ok = impl->start();
+        const bool  ok   = impl->start();
 
         if (!ok) {
             log(AQ_LOG_ERROR, std::format("Requested backend ({}) could not 
start, enabling fallbacks", backendTypeToName(impl->type())));
@@ -237,7 +240,7 @@
 }
 
 bool Aquamarine::CBackend::hasSession() {
-    return session;
+    return !!session;
 }
 
 std::vector<SDRMFormat> Aquamarine::CBackend::getPrimaryRenderFormats() {
@@ -386,3 +389,7 @@
 std::vector<SDRMFormat> 
Aquamarine::IBackendImplementation::getRenderableFormats() {
     return {};
 }
+
+eBackendGPUDriver Aquamarine::IBackendImplementation::gpuDriver() {
+    return AQ_BACKEND_GPU_DRIVER_UNKNOWN;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/FrameScheduler.cpp 
new/aquamarine-0.14.0/src/backend/FrameScheduler.cpp
--- old/aquamarine-0.12.1/src/backend/FrameScheduler.cpp        1970-01-01 
01:00:00.000000000 +0100
+++ new/aquamarine-0.14.0/src/backend/FrameScheduler.cpp        2026-07-27 
18:34:54.000000000 +0200
@@ -0,0 +1,49 @@
+#include <aquamarine/backend/FrameScheduler.hpp>
+
+using namespace Aquamarine;
+
+void CFrameScheduler::onFrameSubmitted() {
+    m_pending = true;
+}
+
+void CFrameScheduler::onFrameComplete() {
+    m_pending = false;
+}
+
+void CFrameScheduler::invalidate() {
+    m_pending        = false;
+    m_frameRunning   = false;
+    m_frameScheduled = false;
+}
+
+bool CFrameScheduler::frameInFlight() const {
+    return m_pending;
+}
+
+bool CFrameScheduler::canSchedule() const {
+    return !m_pending && !m_frameRunning && !m_frameScheduled;
+}
+
+bool CFrameScheduler::frameScheduled() const {
+    return m_frameScheduled;
+}
+
+void CFrameScheduler::setFrameScheduled(bool v) {
+    m_frameScheduled = v;
+}
+
+bool CFrameScheduler::frameRunning() const {
+    return m_frameRunning;
+}
+
+void CFrameScheduler::setFrameRunning(bool v) {
+    m_frameRunning = v;
+}
+
+CFrameRunningGuard::CFrameRunningGuard(CFrameScheduler& s) : m_s(s) {
+    m_s.setFrameRunning(true);
+}
+
+CFrameRunningGuard::~CFrameRunningGuard() {
+    m_s.setFrameRunning(false);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/Headless.cpp 
new/aquamarine-0.14.0/src/backend/Headless.cpp
--- old/aquamarine-0.12.1/src/backend/Headless.cpp      2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/src/backend/Headless.cpp      2026-07-27 
18:34:54.000000000 +0200
@@ -44,6 +44,10 @@
     return false;
 }
 
+bool Aquamarine::CHeadlessOutput::pendingIdleFrame() {
+    return frameScheduled;
+}
+
 Hyprutils::Memory::CSharedPointer<IBackendImplementation> 
Aquamarine::CHeadlessOutput::getBackend() {
     return backend.lock();
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/Session.cpp 
new/aquamarine-0.14.0/src/backend/Session.cpp
--- old/aquamarine-0.12.1/src/backend/Session.cpp       2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/src/backend/Session.cpp       2026-07-27 
18:34:54.000000000 +0200
@@ -1,4 +1,5 @@
 #include <aquamarine/backend/Backend.hpp>
+#include <cerrno>
 #include <fcntl.h>
 #include "Shared.hpp"
 
@@ -344,14 +345,29 @@
         return nullptr;
     }
 
+    libinput_log_set_handler(session->libinputHandle, ::libinputLog);
+    libinput_log_set_priority(session->libinputHandle, 
LIBINPUT_LOG_PRIORITY_DEBUG);
+
+#ifdef AQUAMARINE_HAS_LIBINPUT_PLUGINS
+    if (envEnabled("AQ_LIBINPUT_NO_PLUGINS"))
+        session->backend->log(AQ_LOG_DEBUG, "libinput: plugin loading disabled 
by AQ_LIBINPUT_NO_PLUGINS");
+    else {
+        libinput_plugin_system_append_default_paths(session->libinputHandle);
+
+        if (int ret = 
libinput_plugin_system_load_plugins(session->libinputHandle, 
LIBINPUT_PLUGIN_SYSTEM_FLAG_NONE); ret < 0) {
+            if (ret == -ENOSYS)
+                session->backend->log(AQ_LOG_WARNING, "libinput: plugin 
support is not available in this libinput build");
+            else
+                session->backend->log(AQ_LOG_WARNING, std::format("libinput: 
failed to load plugins: {}", strerror(-ret)));
+        }
+    }
+#endif
+
     if (libinput_udev_assign_seat(session->libinputHandle, 
session->seatName.c_str())) {
         session->backend->log(AQ_LOG_ERROR, "libinput: failed to assign a 
seat");
         return nullptr;
     }
 
-    libinput_log_set_handler(session->libinputHandle, ::libinputLog);
-    libinput_log_set_priority(session->libinputHandle, 
LIBINPUT_LOG_PRIORITY_DEBUG);
-
     return session;
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/Wayland.cpp 
new/aquamarine-0.14.0/src/backend/Wayland.cpp
--- old/aquamarine-0.12.1/src/backend/Wayland.cpp       2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/src/backend/Wayland.cpp       2026-07-27 
18:34:54.000000000 +0200
@@ -61,8 +61,28 @@
 }
 
 Aquamarine::CWaylandBackend::~CWaylandBackend() {
-    if (drmState.fd >= 0)
+    outputs.clear();
+    keyboards.clear();
+    pointers.clear();
+    idleCallbacks.clear();
+
+    waylandState.dmabufFeedback.reset();
+    waylandState.dmabuf.reset();
+    waylandState.shm.reset();
+    waylandState.compositor.reset();
+    waylandState.xdg.reset();
+    waylandState.seat.reset();
+    waylandState.registry.reset();
+
+    if (waylandState.display) {
+        wl_display_disconnect(waylandState.display);
+        waylandState.display = nullptr;
+    }
+
+    if (drmState.fd >= 0) {
         close(drmState.fd);
+        drmState.fd = -1;
+    }
 }
 
 eBackendType Aquamarine::CWaylandBackend::type() {
@@ -472,6 +492,21 @@
 Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, 
Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : backend(backend_) {
     name = name_;
 
+    // The scheduler's frameReady signal drives the public events.frame on 
this output.
+    frameReadyListener = sched.frameReady.listen([this]() { 
events.frame.emit(); });
+
+    // Idle that emits the scheduled frame, fired via the core backend's idle 
queue
+    // (addIdleEvent), which the consumer's event loop pumps every iteration. 
Deliberately
+    // not the backend-local idleCallbacks vector: that only drains inside 
dispatchEvents,
+    // i.e. when the wayland fd is readable, so a frame scheduled with no host 
traffic
+    // pending (e.g. right after a focus change) would never fire.
+    frameIdle = makeShared<std::function<void(void)>>([this]() {
+        sched.setFrameScheduled(false);
+        if (sched.frameInFlight() || sched.frameRunning())
+            return;
+        sched.frameReady.emit();
+    });
+
     waylandState.surface = 
makeShared<CCWlSurface>(backend->waylandState.compositor->sendCreateSurface());
 
     if (!waylandState.surface->resource()) {
@@ -509,7 +544,11 @@
             h = 720;
         }
         events.state.emit(SStateEvent{.size = {w, h}});
-        sendFrameAndSetCallback();
+        // Kick off the first frame synchronously: the consumer expects 
events.frame in
+        // the same dispatch cycle as the toplevel configure. Deferring via 
scheduleFrame's
+        // idle races the first commit and can leave the output blank until 
the next event.
+        needsFrame = true;
+        sched.frameReady.emit();
     });
 
     waylandState.xdgToplevel->setClose([this](CCXdgToplevel* r) { destroy(); 
});
@@ -531,6 +570,8 @@
 
 Aquamarine::CWaylandOutput::~CWaylandOutput() {
     events.destroy.emit();
+    // frameIdle captures a raw this and may still be queued; pull it before 
we die.
+    backend->backend->removeIdleEvent(frameIdle);
     if (waylandState.xdgToplevel)
         waylandState.xdgToplevel->sendDestroy();
     if (waylandState.xdgSurface)
@@ -547,7 +588,11 @@
 }
 
 bool Aquamarine::CWaylandOutput::pendingPageFlip() {
-    return false;
+    return sched.frameInFlight();
+}
+
+bool Aquamarine::CWaylandOutput::pendingIdleFrame() {
+    return sched.frameScheduled();
 }
 
 bool Aquamarine::CWaylandOutput::destroy() {
@@ -555,6 +600,7 @@
     waylandState.surface->sendAttach(nullptr, 0, 0);
     waylandState.surface->sendCommit();
     waylandState.frameCallback.reset();
+    sched.invalidate();
     std::erase(backend->outputs, self.lock());
     return true;
 }
@@ -618,9 +664,29 @@
 
     waylandState.surface->sendAttach(wlBuffer->waylandState.buffer.get(), 0, 
0);
     waylandState.surface->sendDamageBuffer(0, 0, INT32_MAX, INT32_MAX);
+
+    // Register the next wl_surface.frame callback as part of this commit's 
pending state,
+    // but only if one isn't already pending. A consumer that commits twice in 
quick
+    // succession (e.g. cursor + buffer) shares the in-flight callback — when 
it fires,
+    // onFrameDone runs and the next render cycle registers a fresh one. 
Mirrors DRM's
+    // "one flip in flight per CRTC" invariant, expressed via the scheduler.
+    //
+    // PROTOCOL: wl_surface.frame becomes part of pending state and takes 
effect on the
+    // NEXT wl_surface.commit. So the frame request must precede sendCommit() 
— sending
+    // it after commit would queue the callback for a future commit that never 
happens.
+    if (!sched.frameInFlight()) {
+        waylandState.frameCallback = 
makeShared<CCWlCallback>(waylandState.surface->sendFrame());
+        waylandState.frameCallback->setDone([this](CCWlCallback* r, uint32_t 
ms) { onFrameDone(); });
+        sched.onFrameSubmitted();
+    }
+
     waylandState.surface->sendCommit();
 
-    readyForFrameCallback = true;
+    // Flush immediately: commit() runs from the consumer's render path, 
outside
+    // dispatchEvents() (which flushes only at its top). Without this the 
buffer commit and
+    // frame request sit unflushed, the host never sends wl_callback.done, the 
fd never
+    // becomes readable, and the frame loop deadlocks.
+    wl_display_flush(backend->waylandState.display);
 
     events.commit.emit();
     state->onCommit();
@@ -654,33 +720,19 @@
     return wlBuffer;
 }
 
-void Aquamarine::CWaylandOutput::sendFrameAndSetCallback() {
-    events.frame.emit();
-    frameScheduled = false;
-    if (waylandState.frameCallback || !readyForFrameCallback)
-        return;
-
-    waylandState.frameCallback = 
makeShared<CCWlCallback>(waylandState.surface->sendFrame());
-    waylandState.frameCallback->setDone([this](CCWlCallback* r, uint32_t ms) { 
onFrameDone(); });
-
-    waylandState.surface->sendCommit();
-}
-
 void Aquamarine::CWaylandOutput::onFrameDone() {
+    // Mirrors DRM's handlePF: settle scheduler state, emit present, then emit 
frame
+    // via the scheduler signal. The loop self-limits — if the consumer doesn't
+    // commit (its needsFrame bit clears in renderMonitor), no new 
wl_surface.frame
+    // is registered (registration lives in commit()), so no further done 
arrives.
     waylandState.frameCallback.reset();
-    readyForFrameCallback = false;
-    events.present.emit(IOutput::SPresentEvent{.presented = true});
+    sched.onFrameComplete();
 
-    // FIXME: this is wrong, but otherwise we get bugs.
-    // thanks @phonetic112
-    scheduleFrame(AQ_SCHEDULE_NEEDS_FRAME);
-
-    if (frameScheduledWhileWaiting)
-        sendFrameAndSetCallback();
-    else
-        events.frame.emit();
+    CFrameRunningGuard frameRunning(sched);
 
-    frameScheduledWhileWaiting = false;
+    events.present.emit(IOutput::SPresentEvent{.presented = true});
+
+    sched.frameReady.emit();
 }
 
 bool 
Aquamarine::CWaylandOutput::setCursor(Hyprutils::Memory::CSharedPointer<IBuffer>
 buffer, const Hyprutils::Math::Vector2D& hotspot) {
@@ -785,22 +837,14 @@
 
 void Aquamarine::CWaylandOutput::scheduleFrame(const scheduleFrameReason 
reason) {
     TRACE(backend->backend->log(AQ_LOG_TRACE,
-                                std::format("CWaylandOutput::scheduleFrame: 
reason {}, needsFrame {}, frameScheduled {}", (uint32_t)reason, needsFrame, 
frameScheduled)));
+                                std::format("CWaylandOutput::scheduleFrame: 
reason {}, needsFrame {}, canSchedule {}", (uint32_t)reason, needsFrame, 
sched.canSchedule())));
     needsFrame = true;
 
-    if (frameScheduled)
+    if (!sched.canSchedule())
         return;
 
-    frameScheduled = true;
-
-    if (waylandState.frameCallback)
-        frameScheduledWhileWaiting = true;
-    else {
-        backend->idleCallbacks.emplace_back([w = self]() {
-            if (auto o = w.lock())
-                o->sendFrameAndSetCallback();
-        });
-    }
+    sched.setFrameScheduled(true);
+    backend->backend->addIdleEvent(frameIdle);
 }
 
 Aquamarine::CWaylandBuffer::CWaylandBuffer(SP<IBuffer> buffer_, 
Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : buffer(buffer_), 
backend(backend_) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/drm/DRM.cpp 
new/aquamarine-0.14.0/src/backend/drm/DRM.cpp
--- old/aquamarine-0.12.1/src/backend/drm/DRM.cpp       2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/src/backend/drm/DRM.cpp       2026-07-27 
18:34:54.000000000 +0200
@@ -19,7 +19,7 @@
 #include <unordered_set>
 #include <sys/mman.h>
 #include <fcntl.h>
-
+#include <unistd.h>
 extern "C" {
 #include <libseat.h>
 #include <libudev.h>
@@ -37,6 +37,9 @@
 #include "hwdata.hpp"
 #include "Renderer.hpp"
 
+#include <hyprutils/utils/ScopeGuard.hpp>
+using Hyprutils::Utils::CScopeGuard;
+
 using namespace Aquamarine;
 using namespace Hyprutils::Memory;
 using namespace Hyprutils::Math;
@@ -92,10 +95,11 @@
     int num = 0;
     for (int i = 0; i < resources->count_connectors; ++i) {
         auto drmConn = drmModeGetConnector(gpu->fd, resources->connectors[i]);
-        if (!drmConn || drmConn->connection != DRM_MODE_CONNECTED)
+        if (!drmConn)
             continue;
 
-        if (drmConn->connector_type == DRM_MODE_CONNECTOR_LVDS || 
drmConn->connector_type == DRM_MODE_CONNECTOR_eDP || drmConn->connector_type == 
DRM_MODE_CONNECTOR_DSI)
+        if (drmConn->connection == DRM_MODE_CONNECTED &&
+            (drmConn->connector_type == DRM_MODE_CONNECTOR_LVDS || 
drmConn->connector_type == DRM_MODE_CONNECTOR_eDP || drmConn->connector_type == 
DRM_MODE_CONNECTOR_DSI))
             num++;
 
         drmModeFreeConnector(drmConn);
@@ -115,8 +119,8 @@
     }
 
     if (!udev_enumerate_get_list_entry(enumerate)) {
-        // TODO: wait for them.
         backend->log(AQ_LOG_ERROR, "drm: No gpus in scanGPUs.");
+        udev_enumerate_unref(enumerate);
         return {};
     }
 
@@ -404,11 +408,9 @@
     // restore, but isPageFlipPending is already false so the = false
     // assignment is a harmless no-op.
     for (auto const& c : connectors) {
-        if (c->isPageFlipPending || c->isFrameRunning) {
+        if (c->sched.frameInFlight() || c->sched.frameRunning()) {
             backend->log(AQ_LOG_DEBUG, std::format("drm: Clearing stale 
page-flip state for {}", c->szName));
-            c->isPageFlipPending   = false;
-            c->isFrameRunning      = false;
-            c->frameEventScheduled = false;
+            c->sched.invalidate();
         }
     }
 
@@ -430,6 +432,8 @@
             .blocking    = true,
             .flags       = 0,
             .test        = false,
+            .enabled     = STATE.enabled,
+            .committed   = STATE.committed,
             .hdrMetadata = STATE.hdrMetadata,
         };
 
@@ -464,8 +468,8 @@
             }
 
             if (!drmFB) {
-                backend->log(AQ_LOG_DEBUG, std::format("drm: Buffer 
unavailable for crtc {} restore ({}), requesting re-render",
-                                                       c->crtc->id, 
shouldBlit() ? "multi-gpu" : "import failed"));
+                backend->log(AQ_LOG_DEBUG,
+                             std::format("drm: Buffer unavailable for crtc {} 
restore ({}), requesting re-render", c->crtc->id, shouldBlit() ? "multi-gpu" : 
"import failed"));
                 noMode.emplace_back(c);
                 continue;
             }
@@ -560,10 +564,21 @@
 bool Aquamarine::CDRMBackend::initResources() {
     auto resources = drmModeGetResources(gpu->fd);
     if (!resources) {
-        backend->log(AQ_LOG_ERROR, std::format("drm: drmModeGetResources 
failed"));
+        backend->log(AQ_LOG_ERROR, "drm: drmModeGetResources failed");
         return false;
     }
 
+    CScopeGuard resourcesGuard([resources] { drmModeFreeResources(resources); 
});
+
+    bool        success = false;
+
+    CScopeGuard rollbackGuard([&] {
+        if (!success) {
+            crtcs.clear();
+            planes.clear();
+        }
+    });
+
     backend->log(AQ_LOG_DEBUG, std::format("drm: found {} CRTCs", 
resources->count_crtcs));
 
     for (int i = 0; i < resources->count_crtcs; ++i) {
@@ -574,18 +589,15 @@
         auto drmCRTC = drmModeGetCrtc(gpu->fd, CRTC->id);
         if (!drmCRTC) {
             backend->log(AQ_LOG_ERROR, std::format("drm: drmModeGetCrtc for 
crtc {} failed", CRTC->id));
-            drmModeFreeResources(resources);
-            crtcs.clear();
             return false;
         }
 
+        CScopeGuard crtcGuard([drmCRTC] { drmModeFreeCrtc(drmCRTC); });
+
         CRTC->legacy.gammaSize = drmCRTC->gamma_size;
-        drmModeFreeCrtc(drmCRTC);
 
         if (!getDRMCRTCProps(gpu->fd, CRTC->id, &CRTC->props)) {
             backend->log(AQ_LOG_ERROR, std::format("drm: getDRMCRTCProps for 
crtc {} failed", CRTC->id));
-            drmModeFreeResources(resources);
-            crtcs.clear();
             return false;
         }
 
@@ -597,50 +609,43 @@
         return false;
     }
 
-    // initialize planes
     auto planeResources = drmModeGetPlaneResources(gpu->fd);
     if (!planeResources) {
-        backend->log(AQ_LOG_ERROR, std::format("drm: drmModeGetPlaneResources 
failed"));
+        backend->log(AQ_LOG_ERROR, "drm: drmModeGetPlaneResources failed");
         return false;
     }
 
-    backend->log(AQ_LOG_DEBUG, std::format("drm: found {} planes", 
planeResources->count_planes));
+    CScopeGuard planeResourcesGuard([planeResources] { 
drmModeFreePlaneResources(planeResources); });
 
     for (uint32_t i = 0; i < planeResources->count_planes; ++i) {
-        auto id    = planeResources->planes[i];
-        auto plane = drmModeGetPlane(gpu->fd, id);
+        const auto id = planeResources->planes[i];
+
+        auto       plane = drmModeGetPlane(gpu->fd, id);
         if (!plane) {
             backend->log(AQ_LOG_ERROR, std::format("drm: drmModeGetPlane for 
plane {} failed", id));
-            drmModeFreeResources(resources);
-            crtcs.clear();
-            planes.clear();
             return false;
         }
 
-        auto aqPlane     = makeShared<SDRMPlane>();
-        aqPlane->backend = self;
-        aqPlane->self    = aqPlane;
-        if (!aqPlane->init((drmModePlane*)plane)) {
+        CScopeGuard planeGuard([plane] { drmModeFreePlane(plane); });
+
+        auto        aqPlane = makeShared<SDRMPlane>();
+        aqPlane->backend    = self;
+        aqPlane->self       = aqPlane;
+
+        if (!aqPlane->init(plane)) {
             backend->log(AQ_LOG_ERROR, std::format("drm: aqPlane->init for 
plane {} failed", id));
-            drmModeFreeResources(resources);
-            crtcs.clear();
-            planes.clear();
             return false;
         }
 
         planes.emplace_back(aqPlane);
-
-        drmModeFreePlane(plane);
     }
 
-    drmModeFreePlaneResources(planeResources);
-    drmModeFreeResources(resources);
-
+    success = true;
     return true;
 }
 
 bool Aquamarine::CDRMBackend::shouldBlit() {
-    return primary;
+    return !!primary;
 }
 
 bool Aquamarine::CDRMBackend::initMgpu() {
@@ -768,8 +773,8 @@
     }
 
     for (size_t i = 0; i < crtcs.size(); ++i) {
-        const auto& crtc = crtcs.at(i);
-        bool taken = false;
+        const auto& crtc  = crtcs.at(i);
+        bool        taken = false;
         for (auto const& c : connectors) {
             if (c->crtc != crtc)
                 continue;
@@ -868,15 +873,31 @@
     gpu     = gpu_;
     primary = primary_;
 
+    auto driverFromName = [](const std::string_view name) {
+        if (name == "i915" || name == "xe")
+            return AQ_BACKEND_GPU_DRIVER_INTEL;
+        if (name == "amdgpu" || name == "radeon")
+            return AQ_BACKEND_GPU_DRIVER_AMD;
+        if (name == "nvidia-drm")
+            return AQ_BACKEND_GPU_DRIVER_NVIDIA;
+        if (name == "nouveau")
+            return AQ_BACKEND_GPU_DRIVER_NOUVEAU;
+        if (name == "evdi")
+            return AQ_BACKEND_GPU_DRIVER_EVDI;
+
+        return AQ_BACKEND_GPU_DRIVER_UNKNOWN;
+    };
+
     auto drmName = drmGetDeviceNameFromFd2(gpu->fd);
     auto drmVer  = drmGetVersion(gpu->fd);
 
-    gpuName = drmName;
+    gpuName = drmName ? drmName : "unknown";
 
-    auto drmVerName = drmVer->name ? drmVer->name : "unknown";
-    if (std::string_view(drmVerName) == "evdi") {
+    auto drmVerName = drmVer && drmVer->name ? drmVer->name : "unknown";
+    driver          = driverFromName(drmVerName);
+    if (driver == AQ_BACKEND_GPU_DRIVER_EVDI) {
         // DisplayLink/evdi exposes KMS without a usable EGL renderer.
-        primary = {};
+        primary          = {};
         rendererRequired = false;
     }
 
@@ -884,8 +905,10 @@
                  std::format("drm: Starting backend for {}, with driver {}{}", 
drmName ? drmName : "unknown", drmVerName,
                              (primary ? std::format(" with primary {}", 
primary->gpu->path) : "")));
 
-    drmFreeVersion(drmVer);
+    free(drmName);
 
+    if (drmVer)
+        drmFreeVersion(drmVer);
     listeners.gpuChange = gpu->events.change.listen([this](const 
CSessionDevice::SChangeEvent& E) {
         if (E.type == CSessionDevice::AQ_SESSION_EVENT_CHANGE_HOTPLUG) {
             backend->log(AQ_LOG_DEBUG, std::format("drm: Got a hotplug event 
for {}", gpuName));
@@ -1034,6 +1057,7 @@
             if (!conn->init(drmConn)) {
                 backend->log(AQ_LOG_ERROR, std::format("drm: Connector id {} 
failed initializing", connectorID));
                 connectors.pop_back();
+                drmModeFreeConnector(drmConn);
                 continue;
             }
         } else {
@@ -1128,13 +1152,17 @@
     return gpu->renderNodeFd;
 }
 
+eBackendGPUDriver Aquamarine::CDRMBackend::gpuDriver() {
+    return driver;
+}
+
 static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, 
unsigned crtc_id, void* data) {
     auto pageFlip = (SDRMPageFlip*)data;
 
     if (!pageFlip || !pageFlip->connector)
         return;
 
-    pageFlip->connector->isPageFlipPending = false;
+    pageFlip->connector->sched.onFrameComplete();
 
     const auto& BACKEND = pageFlip->connector->backend;
 
@@ -1145,8 +1173,9 @@
         return;
     }
 
-    if (BACKEND->sessionActive() && pageFlip->connector->output->enabledState)
-        pageFlip->connector->isFrameRunning = true;
+    // hold isFrameRunning around the emit (RAII pair, so reentrant 
enable/disable
+    // can't strand it).
+    CFrameRunningGuard frameRunning(pageFlip->connector->sched);
 
     pageFlip->connector->onPresent();
 
@@ -1154,6 +1183,12 @@
 
     timespec presented = {.tv_sec = (time_t)tv_sec, .tv_nsec = (long)(tv_usec 
* 1000)};
 
+    // nvidia-drm registers no vblank counter, unless module options 
'nvidia_drm vblank=1' is set.
+    // kernel checks for vblank support and fallbacks to setting seq 0 and the 
timestamp is a plain ktime_get()
+    // this is not a HW clock, its just a plain software clock fetched from 
whenever the event was called.
+    if (BACKEND->gpuDriver() == AQ_BACKEND_GPU_DRIVER_NVIDIA && seq == 0)
+        flags &= ~IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK;
+
     pageFlip->connector->output->events.present.emit(IOutput::SPresentEvent{
         .presented = BACKEND->sessionActive(),
         .when      = &presented,
@@ -1162,10 +1197,9 @@
         .flags     = flags,
     });
 
-    if (BACKEND->sessionActive() && pageFlip->connector->output->enabledState) 
{
-        pageFlip->connector->output->events.frame.emit();
-        pageFlip->connector->isFrameRunning = false;
-    }
+    // Skip if an idle frame is already queued: it emits events.frame itself, 
and #325 forbids double-firing.
+    if (BACKEND->sessionActive() && pageFlip->connector->output->enabledState 
&& !pageFlip->connector->sched.frameScheduled())
+        pageFlip->connector->sched.frameReady.emit();
 }
 
 bool Aquamarine::CDRMBackend::dispatchEvents() {
@@ -1287,6 +1321,7 @@
 
     if (drmIsMaster(fd) && drmDropMaster(fd) < 0) {
         backend->log(AQ_LOG_ERROR, "drm: couldn't drop master from duped fd");
+        close(fd);
         return -1;
     }
 
@@ -1311,6 +1346,9 @@
     if (!getDRMPlaneProps(backend->gpu->fd, id, &props))
         return false;
 
+    if (props.values.color_range)
+        getDRMPlaneColorRange(backend->gpu->fd, props.values.color_range, 
&colorRange);
+
     if (!getDRMProp(backend->gpu->fd, id, props.values.type, &type))
         return false;
 
@@ -1581,6 +1619,9 @@
     model  = mod ? mod : "";
     serial = ser ? ser : "";
 
+    free(mod);
+    free(ser);
+
     parsed.make   = make;
     parsed.model  = model;
     parsed.serial = serial;
@@ -1789,8 +1830,7 @@
 void Aquamarine::SDRMConnector::disconnect() {
     if (!output) {
         if (backend && backend->backend)
-            backend->backend->log(AQ_LOG_DEBUG,
-                std::format("drm: Not disconnecting connector {} because it's 
already disconnected", szName));
+            backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Not 
disconnecting connector {} because it's already disconnected", szName));
         return;
     }
 
@@ -1824,14 +1864,16 @@
 
     pendingCursorFB.reset();
 
-    if (output->state->state().committed & COutputState::AQ_OUTPUT_STATE_MODE)
+    if (data.committed & COutputState::AQ_OUTPUT_STATE_MODE)
         refresh = calculateRefresh(data.modeInfo);
 
     const bool wasEnabled = output->enabledState;
-    output->enabledState  = output->state->state().enabled;
+    output->enabledState  = data.enabled;
 
-    if (!output->enabledState)
+    if (!output->enabledState) {
         releaseFBReferences();
+        sched.invalidate();
+    }
 
     if (!backend->updateSecondaryRendererState())
         backend->backend->log(AQ_LOG_ERROR, std::format("drm: Failed to update 
renderer state for {} on applyCommit", szName));
@@ -1883,8 +1925,8 @@
 Aquamarine::CDRMOutput::~CDRMOutput() {
     if (backend && backend->backend)
         backend->backend->removeIdleEvent(frameIdle);
-    connector->isPageFlipPending   = false;
-    connector->frameEventScheduled = false;
+    connector->sched.onFrameComplete();
+    connector->sched.setFrameScheduled(false);
 }
 
 void Aquamarine::CDRMOutput::releaseMgpuResources() {
@@ -1998,31 +2040,16 @@
                 backend->backend->log(AQ_LOG_DEBUG, std::format("drm: 
Disabling output {}", name));
         }
 
-        if (STATE.enabled && (NEEDS_RECONFIG || (COMMITTED & 
COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER)) && 
connector->isPageFlipPending) {
-            // Check if the pending page-flip is stale (>500ms — well beyond
-            // any vblank interval, even at low refresh rates). Stale flips
-            // occur after S3/S4 suspend when page-flip completion events are
-            // lost because the display hardware was powered off.
-            struct timespec ts;
-            clock_gettime(CLOCK_BOOTTIME, &ts);
-            uint64_t nowMs     = ts.tv_sec * 1000ULL + ts.tv_nsec / 1000000ULL;
-            bool     staleFlip = (nowMs - connector->pageFlipPendingAtMs) > 
500;
-
-            if (NEEDS_RECONFIG && staleFlip) {
-                // A blocking modeset uses DRM_MODE_ATOMIC_ALLOW_MODESET which
-                // fully resets the CRTC, implicitly cancelling any stale
-                // page-flip at the kernel level. Clear the stale userspace
-                // bookkeeping to match.
-                backend->backend->log(AQ_LOG_DEBUG,
-                                      std::format("drm: Clearing stale 
page-flip state for {} during modeset (pending for {}ms)", name,
-                                                  nowMs - 
connector->pageFlipPendingAtMs));
-                connector->isPageFlipPending   = false;
-                connector->isFrameRunning      = false;
-                connector->frameEventScheduled = false;
-            } else {
-                backend->backend->log(AQ_LOG_ERROR, "drm: Cannot commit when a 
page-flip is awaiting");
-                return false;
-            }
+        if (STATE.enabled && NEEDS_RECONFIG && 
connector->sched.frameInFlight()) {
+            // ALLOW_MODESET resets the CRTC and cancels the in-flight flip; no
+            // completion event will arrive, so clear the userspace state.
+            backend->backend->log(AQ_LOG_DEBUG, std::format("drm: page-flip on 
{} cancelled by modeset, clearing flip state", name));
+            connector->sched.invalidate();
+        }
+
+        if (STATE.enabled && (COMMITTED & 
COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER) && 
connector->sched.frameInFlight()) {
+            backend->backend->log(AQ_LOG_ERROR, "drm: Cannot commit when a 
page-flip is awaiting");
+            return false;
         }
 
         if (STATE.enabled && (COMMITTED & 
COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER))
@@ -2141,15 +2168,35 @@
     if (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_HDR)
         data.hdrMetadata = STATE.hdrMetadata;
 
-    data.blocking = BLOCKING || formatMismatch;
-    data.modeset  = NEEDS_RECONFIG || lastCommitNoBuffer || formatMismatch;
-    data.flags    = flags;
-    data.test     = onlyTest;
+    data.blocking  = BLOCKING || formatMismatch;
+    data.modeset   = NEEDS_RECONFIG || lastCommitNoBuffer || formatMismatch;
+    data.flags     = flags;
+    data.test      = onlyTest;
+    data.enabled   = STATE.enabled;
+    data.committed = COMMITTED;
+    if (COMMITTED & 
COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_DAMAGE)
+        data.damage = STATE.damage.copy();
     if (MODE->modeInfo.has_value())
         data.modeInfo = *MODE->modeInfo;
     else
         data.calculateMode(connector);
 
+    // A modeset commit must carry a buffer sized for the new mode: the atomic 
path
+    // derives the plane geometry from it (and legacy SetCrtc has the same 
requirement),
+    // so drivers without plane scaling (e.g. virtio-gpu) reject stale-size 
buffers,
+    // making larger modes unreachable.
+    // If the consumer already reconfigured the swapchain to the new size, 
attach a fresh buffer from it.
+    bool acquiredModesetBuffer = false;
+    if (data.modeset && data.mainFB && swapchain && data.mainFB->buffer->size 
!= MODE->pixelSize && swapchain->currentOptions().size == MODE->pixelSize) {
+        if (auto newBuf = swapchain->next(nullptr); newBuf) {
+            if (auto newFB = CDRMFB::create(newBuf, backend, nullptr); newFB 
&& !newFB->dead) {
+                data.mainFB           = newFB;
+                acquiredModesetBuffer = true;
+            } else
+                swapchain->rollback();
+        }
+    }
+
     // Re-send CTM when it changes, when preserving a non-identity CTM over a 
modeset,
     // or when we need one identity blob to clear unknown kernel state from 
before us.
     // Once a real CTM commit succeeds, ordinary identity modesets can skip 
the blob.
@@ -2158,6 +2205,11 @@
 
     bool ok = connector->commitState(data);
 
+    // a buffer acquired only to validate/attempt the modeset isn't consumed 
by the
+    // consumer: rewind the swapchain so the acquire cursor stays in sync with 
it.
+    if (acquiredModesetBuffer && (onlyTest || !ok))
+        swapchain->rollback();
+
     if (!ok && !data.modeset && !connector->commitTainted) {
         // attempt to re-modeset, however, flip a tainted flag if the 
modesetting fails
         // to avoid doing this over and over.
@@ -2310,13 +2362,33 @@
 void Aquamarine::CDRMOutput::scheduleFrame(const scheduleFrameReason reason) {
     TRACE(backend->backend->log(AQ_LOG_TRACE,
                                 std::format("CDRMOutput::scheduleFrame: reason 
{}, needsFrame {}, isPageFlipPending {}, frameEventScheduled {}", 
(uint32_t)reason, needsFrame,
-                                            connector->isPageFlipPending, 
connector->frameEventScheduled)));
+                                            connector->sched.frameInFlight(), 
connector->sched.frameScheduled())));
     needsFrame = true;
 
-    if (connector->isPageFlipPending || connector->isFrameRunning || 
connector->frameEventScheduled || !enabledState)
+    if (!connector->sched.canSchedule() || !enabledState)
         return;
 
-    connector->frameEventScheduled = true;
+    connector->sched.setFrameScheduled(true);
+
+    if (!frameIdle) {
+        frameIdle = makeShared<std::function<void(void)>>([this, self_ = self, 
backend_ = backend]() {
+            if (!self)
+                return;
+
+            connector->sched.setFrameScheduled(false);
+            if (connector->sched.frameInFlight() || 
connector->sched.frameRunning())
+                return;
+
+            CFrameRunningGuard frameRunning(connector->sched);
+            connector->sched.frameReady.emit();
+
+            // above frame scheduled, and then committed, remove the idle 
frame. the pageflip will emit the frame.
+            if (backend_ && backend_->backend && 
connector->sched.frameScheduled() && connector->sched.frameInFlight()) {
+                backend_->backend->removeIdleEvent(frameIdle);
+                connector->sched.setFrameScheduled(false);
+            }
+        });
+    }
 
     backend->backend->addIdleEvent(frameIdle);
 }
@@ -2374,7 +2446,11 @@
 }
 
 bool Aquamarine::CDRMOutput::pendingPageFlip() {
-    return connector->isPageFlipPending;
+    return connector->sched.frameInFlight();
+}
+
+bool Aquamarine::CDRMOutput::pendingIdleFrame() {
+    return connector->sched.frameScheduled();
 }
 
 int Aquamarine::CDRMOutput::getConnectorID() {
@@ -2385,12 +2461,8 @@
     backend(backend_), connector(connector_) {
     name = name_;
 
-    frameIdle = makeShared<std::function<void(void)>>([this]() {
-        connector->frameEventScheduled = false;
-        if (connector->isPageFlipPending || connector->isFrameRunning)
-            return;
-        events.frame.emit();
-    });
+    // The scheduler's frameReady signal drives the public events.frame on 
this output.
+    frameReadyListener = connector->sched.frameReady.listen([this]() { 
events.frame.emit(); });
 }
 
 SP<CDRMFB> Aquamarine::CDRMFB::create(SP<IBuffer> buffer_, 
Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_, bool* isNew) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/drm/Props.cpp 
new/aquamarine-0.14.0/src/backend/drm/Props.cpp
--- old/aquamarine-0.12.1/src/backend/drm/Props.cpp     2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/src/backend/drm/Props.cpp     2026-07-27 
18:34:54.000000000 +0200
@@ -57,6 +57,7 @@
 
 static const struct prop_info plane_info[] = {
 #define INDEX(name) (offsetof(SDRMPlane::UDRMPlaneProps, values.name) / 
sizeof(uint32_t))
+    {.name = "COLOR_RANGE", .index = INDEX(color_range)},
     {.name = "CRTC_H", .index = INDEX(crtc_h)},
     {.name = "CRTC_ID", .index = INDEX(crtc_id)},
     {.name = "CRTC_W", .index = INDEX(crtc_w)},
@@ -77,6 +78,13 @@
 #undef INDEX
 };
 
+static const struct prop_info colorrange_info[] = {
+#define INDEX(name) (offsetof(SDRMPlane::UDRMPlaneColorRange, values.name) / 
sizeof(uint32_t))
+    {.name = "YCbCr full range", .index = INDEX(Full_YCbCr)},
+    {.name = "YCbCr limited range", .index = INDEX(Limited_YCbCr)},
+#undef INDEX
+};
+
 namespace Aquamarine {
 
     static int comparePropInfo(const void* arg1, const void* arg2) {
@@ -139,6 +147,10 @@
         return scanProperties(fd, id, DRM_MODE_OBJECT_PLANE, out->props, 
plane_info, sizeof(plane_info) / sizeof(plane_info[0]));
     }
 
+    bool getDRMPlaneColorRange(int fd, uint32_t id, 
SDRMPlane::UDRMPlaneColorRange* out) {
+        return scanPropertyEnum(fd, id, out->props, colorrange_info, 
sizeof(colorrange_info) / sizeof(colorrange_info[0]));
+    }
+
     bool getDRMProp(int fd, uint32_t obj, uint32_t prop, uint64_t* ret) {
         drmModeObjectProperties* props = drmModeObjectGetProperties(fd, obj, 
DRM_MODE_OBJECT_ANY);
         if (!props)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/drm/Props.hpp 
new/aquamarine-0.14.0/src/backend/drm/Props.hpp
--- old/aquamarine-0.12.1/src/backend/drm/Props.hpp     2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/src/backend/drm/Props.hpp     2026-07-27 
18:34:54.000000000 +0200
@@ -7,6 +7,7 @@
     bool  getDRMConnectorColorspace(int fd, uint32_t id, 
SDRMConnector::UDRMConnectorColorspace* out);
     bool  getDRMCRTCProps(int fd, uint32_t id, SDRMCRTC::UDRMCRTCProps* out);
     bool  getDRMPlaneProps(int fd, uint32_t id, SDRMPlane::UDRMPlaneProps* 
out);
+    bool  getDRMPlaneColorRange(int fd, uint32_t id, 
SDRMPlane::UDRMPlaneColorRange* out);
     bool  getDRMProp(int fd, uint32_t obj, uint32_t prop, uint64_t* ret);
     void* getDRMPropBlob(int fd, uint32_t obj, uint32_t prop, size_t* ret_len);
     char* getDRMPropEnum(int fd, uint32_t obj, uint32_t prop_id);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/drm/Renderer.cpp 
new/aquamarine-0.14.0/src/backend/drm/Renderer.cpp
--- old/aquamarine-0.12.1/src/backend/drm/Renderer.cpp  2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/src/backend/drm/Renderer.cpp  2026-07-27 
18:34:54.000000000 +0200
@@ -475,8 +475,15 @@
         attrs.push_back(EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); // or 
_FLUSH_KHR
     }
 
-    attrs.push_back(EGL_CONTEXT_OPENGL_DEBUG);
-    attrs.push_back(Aquamarine::isTrace() ? EGL_TRUE : EGL_FALSE);
+    if (Aquamarine::isTrace()) {
+        if (major > 1 || (major == 1 && minor >= 5)) {
+            attrs.push_back(EGL_CONTEXT_OPENGL_DEBUG);
+            attrs.push_back(EGL_TRUE);
+        } else if (EGLEXTENSIONS.contains("EGL_KHR_create_context")) {
+            attrs.push_back(EGL_CONTEXT_FLAGS_KHR);
+            attrs.push_back(EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR);
+        }
+    }
 
     auto attrsNoVer = attrs;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/drm/impl/Atomic.cpp 
new/aquamarine-0.14.0/src/backend/drm/impl/Atomic.cpp
--- old/aquamarine-0.12.1/src/backend/drm/impl/Atomic.cpp       2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/src/backend/drm/impl/Atomic.cpp       2026-07-27 
18:34:54.000000000 +0200
@@ -6,6 +6,7 @@
 #include <xf86drmMode.h>
 #include <sys/mman.h>
 #include <sstream>
+#include <optional>
 #include "Shared.hpp"
 #include "aquamarine/output/Output.hpp"
 
@@ -88,8 +89,8 @@
         TRACE(backend->log(AQ_LOG_TRACE, std::format("atomic planeProps: 
disabling plane {}", plane->id)));
         add(plane->id, plane->props.values.fb_id, 0);
         add(plane->id, plane->props.values.crtc_id, 0);
-        add(plane->id, plane->props.values.crtc_x, (uint64_t)pos.x);
-        add(plane->id, plane->props.values.crtc_y, (uint64_t)pos.y);
+        add(plane->id, plane->props.values.crtc_x, (uint64_t)(int64_t)pos.x);
+        add(plane->id, plane->props.values.crtc_y, (uint64_t)(int64_t)pos.y);
         return;
     }
 
@@ -107,6 +108,23 @@
     add(plane->id, plane->props.values.crtc_h, (uint32_t)fb->buffer->size.y);
     add(plane->id, plane->props.values.fb_id, fb->id);
     add(plane->id, plane->props.values.crtc_id, crtc);
+
+    if (plane->props.values.color_range) {
+        const auto              colorRange = conn && conn->output ? 
conn->output->state->state().colorRange : AQ_OUTPUT_COLOR_RANGE_AUTO;
+        std::optional<uint32_t> rangeVal;
+        switch (colorRange) {
+            case AQ_OUTPUT_COLOR_RANGE_FULL: rangeVal = 
plane->colorRange.values.Full_YCbCr; break;
+            case AQ_OUTPUT_COLOR_RANGE_LIMITED: rangeVal = 
plane->colorRange.values.Limited_YCbCr; break;
+            case AQ_OUTPUT_COLOR_RANGE_AUTO:
+                // 
https://github.com/NVIDIA/open-gpu-kernel-modules/discussions/1105
+                if (backend->gpuDriver() == AQ_BACKEND_GPU_DRIVER_NVIDIA)
+                    rangeVal = plane->colorRange.values.Full_YCbCr;
+                break;
+        }
+        if (rangeVal)
+            add(plane->id, plane->props.values.color_range, *rangeVal);
+    }
+
     planePropsPos(plane, pos);
 }
 
@@ -117,8 +135,8 @@
 
     TRACE(backend->log(AQ_LOG_TRACE, std::format("atomic planeProps: pos 
blobs: crtc_x {}, crtc_y {}", plane->props.values.crtc_x, 
plane->props.values.crtc_y)));
 
-    add(plane->id, plane->props.values.crtc_x, (uint64_t)pos.x);
-    add(plane->id, plane->props.values.crtc_y, (uint64_t)pos.y);
+    add(plane->id, plane->props.values.crtc_x, (uint64_t)(int64_t)pos.x);
+    add(plane->id, plane->props.values.crtc_y, (uint64_t)(int64_t)pos.y);
 }
 
 void 
Aquamarine::CDRMAtomicRequest::setConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector>
 connector) {
@@ -127,7 +145,7 @@
 
 void 
Aquamarine::CDRMAtomicRequest::addConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector>
 connector, SDRMConnectorCommitData& data) {
     const auto& STATE  = connector->output->state->state();
-    const bool  enable = STATE.enabled && data.mainFB;
+    const bool  enable = data.enabled && data.mainFB;
 
     TRACE(backend->log(AQ_LOG_TRACE,
                        std::format("atomic addConnector blobs: mode_id {}, 
active {}, crtc_id {}, link_status {}, content_type {}", 
connector->crtc->props.values.mode_id,
@@ -188,15 +206,15 @@
             add(connector->id, connector->props.values.content_type, 
newContentType);
     }
 
-    data.atomic.maxBpc       = newMaxBpc;
-    data.atomic.colorspace   = newColorspace;
-    data.atomic.contentType  = newContentType;
-    data.atomic.crtcID       = newCrtcID;
+    data.atomic.maxBpc      = newMaxBpc;
+    data.atomic.colorspace  = newColorspace;
+    data.atomic.contentType = newContentType;
+    data.atomic.crtcID      = newCrtcID;
 
     add(connector->crtc->id, connector->crtc->props.values.active, enable);
 
     if (enable) {
-        if (connector->output->supportsExplicit && STATE.committed & 
COutputState::AQ_OUTPUT_STATE_EXPLICIT_OUT_FENCE)
+        if (connector->output->supportsExplicit && data.committed & 
COutputState::AQ_OUTPUT_STATE_EXPLICIT_OUT_FENCE)
             add(connector->crtc->id, 
connector->crtc->props.values.out_fence_ptr, 
(uintptr_t)&STATE.explicitOutFence);
 
         if (connector->crtc->props.values.gamma_lut && data.atomic.gammad)
@@ -227,8 +245,7 @@
     if (!data.modeset)
         return;
 
-    const auto& STATE  = connector->output->state->state();
-    const bool  enable = STATE.enabled && data.mainFB;
+    const bool enable = data.enabled && data.mainFB;
 
     data.atomic.blobbed = true;
 
@@ -244,13 +261,12 @@
     if (!connector->crtc->cursor)
         return;
 
-    const auto& STATE  = connector->output->state->state();
-    const bool  enable = STATE.enabled && data.mainFB;
+    const bool enable = data.enabled && data.mainFB;
 
     if (enable) {
-        if (STATE.committed & COutputState::AQ_OUTPUT_STATE_CURSOR_SHAPE || 
STATE.committed & COutputState::AQ_OUTPUT_STATE_CURSOR_POS) {
-            TRACE(backend->log(AQ_LOG_TRACE, STATE.committed & 
COutputState::AQ_OUTPUT_STATE_CURSOR_SHAPE ? "atomic addConnector cursor shape" 
: "atomic addConnector cursor pos"));
-            if (STATE.committed & COutputState::AQ_OUTPUT_STATE_CURSOR_SHAPE) {
+        if (data.committed & COutputState::AQ_OUTPUT_STATE_CURSOR_SHAPE || 
data.committed & COutputState::AQ_OUTPUT_STATE_CURSOR_POS) {
+            TRACE(backend->log(AQ_LOG_TRACE, data.committed & 
COutputState::AQ_OUTPUT_STATE_CURSOR_SHAPE ? "atomic addConnector cursor shape" 
: "atomic addConnector cursor pos"));
+            if (data.committed & COutputState::AQ_OUTPUT_STATE_CURSOR_SHAPE) {
                 if (!connector->output->cursorVisible)
                     planeProps(connector->crtc->cursor, nullptr, 0, {});
                 else
@@ -350,7 +366,7 @@
 
 bool 
Aquamarine::CDRMAtomicImpl::prepareConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector>
 connector, SDRMConnectorCommitData& data) {
     const auto& STATE  = connector->output->state->state();
-    const bool  enable = STATE.enabled;
+    const bool  enable = data.enabled;
     const auto& MODE   = STATE.mode ? STATE.mode : STATE.customMode;
 
     if (data.modeset) {
@@ -400,10 +416,10 @@
     // clears the kernel state. only ride the modeset path when the prop 
actually
     // exists, otherwise we'd log a spurious "no gamma_lut prop" error per 
modeset.
     // (see #127)
-    if ((data.modeset && connector->crtc->props.values.gamma_lut) || 
(STATE.committed & COutputState::AQ_OUTPUT_STATE_GAMMA_LUT))
+    if ((data.modeset && connector->crtc->props.values.gamma_lut) || 
(data.committed & COutputState::AQ_OUTPUT_STATE_GAMMA_LUT))
         data.atomic.gammad = 
prepareGammaBlob(connector->crtc->props.values.gamma_lut, STATE.gammaLut, 
&data.atomic.gammaLut);
 
-    if ((data.modeset && connector->crtc->props.values.degamma_lut) || 
(STATE.committed & COutputState::AQ_OUTPUT_STATE_DEGAMMA_LUT))
+    if ((data.modeset && connector->crtc->props.values.degamma_lut) || 
(data.committed & COutputState::AQ_OUTPUT_STATE_DEGAMMA_LUT))
         data.atomic.degammad = 
prepareGammaBlob(connector->crtc->props.values.degamma_lut, STATE.degammaLut, 
&data.atomic.degammaLut);
 
     if (data.ctm.has_value()) {
@@ -430,7 +446,7 @@
         }
     }
 
-    if ((data.modeset || (STATE.committed & 
COutputState::AQ_OUTPUT_STATE_HDR)) && data.hdrMetadata.has_value()) {
+    if ((data.modeset || (data.committed & COutputState::AQ_OUTPUT_STATE_HDR)) 
&& data.hdrMetadata.has_value()) {
         if (!connector->props.values.hdr_output_metadata)
             connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed 
to commit hdr metadata: no HDR_OUTPUT_METADATA prop support");
         else {
@@ -457,12 +473,12 @@
         }
     }
 
-    if ((STATE.committed & COutputState::AQ_OUTPUT_STATE_DAMAGE) && 
connector->crtc->primary->props.values.fb_damage_clips && MODE) {
-        if (STATE.damage.empty())
+    if ((data.committed & COutputState::AQ_OUTPUT_STATE_DAMAGE) && 
connector->crtc->primary->props.values.fb_damage_clips && MODE) {
+        if (data.damage.empty())
             data.atomic.fbDamage = 0;
         else {
             TRACE(connector->backend->backend->log(AQ_LOG_TRACE, 
std::format("atomic drm: clipping damage to pixel size {}", MODE->pixelSize)));
-            std::vector<pixman_box32_t> rects = 
STATE.damage.copy().intersect(CBox{{}, MODE->pixelSize}).getRects();
+            std::vector<pixman_box32_t> rects = 
data.damage.copy().intersect(CBox{{}, MODE->pixelSize}).getRects();
             if (drmModeCreatePropertyBlob(connector->backend->gpu->fd, 
rects.data(), sizeof(pixman_box32_t) * rects.size(), &data.atomic.fbDamage)) {
                 connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: 
failed to create a damage blob");
                 return false;
@@ -504,12 +520,8 @@
             if (data.atomic.ctmd)
                 connector->crtc->atomic.ctmStateKnown = true;
 
-            if (data.mainFB && connector->output->state->state().enabled && 
(flags & DRM_MODE_PAGE_FLIP_EVENT)) {
-                connector->isPageFlipPending = true;
-                struct timespec ts;
-                clock_gettime(CLOCK_BOOTTIME, &ts);
-                connector->pageFlipPendingAtMs = ts.tv_sec * 1000ULL + 
ts.tv_nsec / 1000000ULL;
-            }
+            if (data.mainFB && data.enabled && (flags & 
DRM_MODE_PAGE_FLIP_EVENT))
+                connector->sched.onFrameSubmitted();
         }
     } else
         request.rollback(data);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/backend/drm/impl/Legacy.cpp 
new/aquamarine-0.14.0/src/backend/drm/impl/Legacy.cpp
--- old/aquamarine-0.12.1/src/backend/drm/impl/Legacy.cpp       2026-06-11 
18:17:06.000000000 +0200
+++ new/aquamarine-0.14.0/src/backend/drm/impl/Legacy.cpp       2026-07-27 
18:34:54.000000000 +0200
@@ -27,7 +27,7 @@
 bool 
Aquamarine::CDRMLegacyImpl::commitInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector>
 connector, SDRMConnectorCommitData& data) {
     const auto& STATE = connector->output->state->state();
     SP<CDRMFB>  mainFB;
-    bool        enable = STATE.enabled;
+    bool        enable = data.enabled;
 
     if (enable) {
         if (!data.mainFB)
@@ -65,7 +65,7 @@
         }
     }
 
-    if (STATE.committed & 
COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_ADAPTIVE_SYNC) {
+    if (data.committed & 
COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_ADAPTIVE_SYNC) {
         if (STATE.adaptiveSync && !connector->canDoVrr) {
             connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy 
drm: connector {} can't do vrr", connector->id));
             return false;
@@ -87,7 +87,7 @@
     // TODO: gamma
 
     if (data.cursorFB && connector->crtc->cursor && 
connector->output->cursorVisible && enable &&
-        (STATE.committed & COutputState::AQ_OUTPUT_STATE_CURSOR_SHAPE || 
STATE.committed & COutputState::AQ_OUTPUT_STATE_CURSOR_POS)) {
+        (data.committed & COutputState::AQ_OUTPUT_STATE_CURSOR_SHAPE || 
data.committed & COutputState::AQ_OUTPUT_STATE_CURSOR_POS)) {
         uint32_t boHandle = 0;
         auto     attrs    = data.cursorFB->buffer->dmabuf();
 
@@ -137,10 +137,7 @@
         return false;
     }
 
-    connector->isPageFlipPending = true;
-    struct timespec ts;
-    clock_gettime(CLOCK_BOOTTIME, &ts);
-    connector->pageFlipPendingAtMs = ts.tv_sec * 1000ULL + ts.tv_nsec / 
1000000ULL;
+    connector->sched.onFrameSubmitted();
 
     return true;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/src/output/Output.cpp 
new/aquamarine-0.14.0/src/output/Output.cpp
--- old/aquamarine-0.12.1/src/output/Output.cpp 2026-06-11 18:17:06.000000000 
+0200
+++ new/aquamarine-0.14.0/src/output/Output.cpp 2026-07-27 18:34:54.000000000 
+0200
@@ -142,6 +142,10 @@
     internalState.contentType = drmContentType;
 }
 
+void Aquamarine::COutputState::setColorRange(eOutputColorRange range) {
+    internalState.colorRange = range;
+}
+
 void Aquamarine::COutputState::onCommit() {
     internalState.committed = 0;
     internalState.damage.clear();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aquamarine-0.12.1/tests/Attachments.cpp 
new/aquamarine-0.14.0/tests/Attachments.cpp
--- old/aquamarine-0.12.1/tests/Attachments.cpp 2026-06-11 18:17:06.000000000 
+0200
+++ new/aquamarine-0.14.0/tests/Attachments.cpp 2026-07-27 18:34:54.000000000 
+0200
@@ -17,16 +17,17 @@
     Aquamarine::CAttachmentManager attachments;
     int                            ret = 0;
     EXPECT(attachments.has<CFooAttachment>(), false);
-    EXPECT(attachments.get<CFooAttachment>(), nullptr);
+    EXPECT(!!attachments.get<CFooAttachment>(), false);
     EXPECT(attachments.has<CBarAttachment>(), false);
-    EXPECT(attachments.get<CBarAttachment>(), nullptr);
+    EXPECT(!!attachments.get<CBarAttachment>(), false);
 
     auto foo = Hyprutils::Memory::makeShared<CFooAttachment>();
     attachments.add(foo);
     EXPECT(attachments.has<CFooAttachment>(), true);
     EXPECT(attachments.has<CBarAttachment>(), false);
     foo->counter++;
-    EXPECT(attachments.get<CFooAttachment>(), foo);
+    // cant work for now
+    //EXPECT(attachments.get<CFooAttachment>(), foo);
     EXPECT(attachments.get<CFooAttachment>()->counter, 1);
 
     attachments.add(Hyprutils::Memory::makeShared<CBarAttachment>());

++++++ aquamarine.obsinfo ++++++
--- /var/tmp/diff_new_pack.i5w4ly/_old  2026-07-28 17:51:40.111378510 +0200
+++ /var/tmp/diff_new_pack.i5w4ly/_new  2026-07-28 17:51:40.147379769 +0200
@@ -1,5 +1,5 @@
 name: aquamarine
-version: 0.12.1
-mtime: 1781194626
-commit: 06669631175b4db2383b94e7f8c13f45a9d28757
+version: 0.14.0
+mtime: 1785170094
+commit: a79fb21b2e2a82dd061a6d071802bcf38bd5c383
 

Reply via email to