File aquamarine-0.3.3.obscpio of Package aquamarine

07070100000000000081A400000000000000000000000166C38D3F0000070E000000000000000000000000000000000000001F00000000aquamarine-0.3.3/.clang-format---
Language: Cpp
BasedOnStyle: LLVM

AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AlignEscapedNewlines: Right
AlignOperands: false
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: false
BreakConstructorInitializers: AfterColon
ColumnLimit: 180
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
IncludeBlocks: Preserve
IndentCaseLabels: true
IndentWidth: 4
PointerAlignment: Left
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 4
UseTab: Never

AllowShortEnumsOnASingleLine: false

BraceWrapping:
  AfterEnum: false

AlignConsecutiveDeclarations: AcrossEmptyLines

NamespaceIndentation: All
07070100000001000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001900000000aquamarine-0.3.3/.github07070100000002000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000002300000000aquamarine-0.3.3/.github/workflows07070100000003000081A400000000000000000000000166C38D3F0000025B000000000000000000000000000000000000002B00000000aquamarine-0.3.3/.github/workflows/nix.ymlname: Build & Test

on: [push, pull_request, workflow_dispatch]
jobs:
  nix:
    strategy:
      matrix:
        package:
          - aquamarine
          # - aquamarine-with-tests

    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - uses: cachix/install-nix-action@v26
    - uses: DeterminateSystems/magic-nix-cache-action@main

    # not needed (yet)
    # - uses: cachix/cachix-action@v12
    #   with:
    #     name: hyprland
    #     authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'

    - name: Build & Test
      run: nix build .#${{ matrix.package }} --print-build-logs

07070100000004000081A400000000000000000000000166C38D3F00000147000000000000000000000000000000000000001C00000000aquamarine-0.3.3/.gitignore# Prerequisites
*.d

# Compiled Object files
*.slo
*.lo
*.o
*.obj

# Precompiled Headers
*.gch
*.pch

# Compiled Dynamic libraries
*.so
*.dylib
*.dll

# Fortran module files
*.mod
*.smod

# Compiled Static libraries
*.lai
*.la
*.a
*.lib

# Executables
*.exe
*.out
*.app

build/
.vscode/
.cache/

protocols/*.cpp
protocols/*.hpp07070100000005000081A400000000000000000000000166C38D3F0000118B000000000000000000000000000000000000002000000000aquamarine-0.3.3/CMakeLists.txtcmake_minimum_required(VERSION 3.19)

file(READ "${CMAKE_SOURCE_DIR}/VERSION" VER_RAW)
string(STRIP ${VER_RAW} AQUAMARINE_VERSION)

add_compile_definitions(AQUAMARINE_VERSION="${AQUAMARINE_VERSION}")

project(
  aquamarine
  VERSION ${AQUAMARINE_VERSION}
  DESCRIPTION "A very light linux rendering backend library")

include(CTest)
include(CheckIncludeFile)
include(GNUInstallDirs)

set(PREFIX ${CMAKE_INSTALL_PREFIX})
set(INCLUDE ${CMAKE_INSTALL_FULL_INCLUDEDIR})
set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR})

find_package(PkgConfig REQUIRED)
find_package(OpenGL REQUIRED COMPONENTS "GLES2")
find_package(hyprwayland-scanner 0.4.0 REQUIRED)
pkg_check_modules(
  deps
  REQUIRED
  IMPORTED_TARGET
  libseat>=0.8.0
  libinput>=1.26.0
  wayland-client
  wayland-protocols
  hyprutils>=0.1.5
  pixman-1
  libdrm
  gbm
  libudev
  libdisplay-info
  hwdata)

configure_file(aquamarine.pc.in aquamarine.pc @ONLY)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)

if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
  message(STATUS "Configuring aquamarine in Debug")
  add_compile_definitions(AQUAMARINE_DEBUG)
else()
  add_compile_options(-O3)
  message(STATUS "Configuring aquamarine in Release")
endif()

file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/*.hpp")
file(GLOB_RECURSE PUBLIC_HEADERS CONFIGURE_DEPENDS "include/*.hpp")

add_library(aquamarine SHARED ${SRCFILES})
target_include_directories(
  aquamarine
  PUBLIC "./include"
  PRIVATE "./src" "./src/include" "./protocols" "${CMAKE_BINARY_DIR}")
set_target_properties(aquamarine PROPERTIES VERSION ${AQUAMARINE_VERSION}
                                            SOVERSION 2)
target_link_libraries(aquamarine OpenGL::EGL OpenGL::OpenGL PkgConfig::deps)

check_include_file("sys/timerfd.h" HAS_TIMERFD)
pkg_check_modules(epoll IMPORTED_TARGET epoll-shim)
if(NOT HAS_TIMERFD AND epoll_FOUND)
  target_link_libraries(aquamarine PkgConfig::epoll)
endif()

# Protocols
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
pkg_get_variable(WAYLAND_SCANNER_PKGDATA_DIR wayland-scanner pkgdatadir)
message(STATUS "Found wayland-scanner pkgdatadir at ${WAYLAND_SCANNER_PKGDATA_DIR}")

function(protocolNew protoPath protoName external)
  if(external)
    set(path ${CMAKE_SOURCE_DIR}/${protoPath})
  else()
    set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
  endif()
  add_custom_command(
    OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp
           ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp
    COMMAND hyprwayland-scanner --client ${path}/${protoName}.xml
            ${CMAKE_SOURCE_DIR}/protocols/
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
  target_sources(aquamarine PRIVATE protocols/${protoName}.cpp
                                    protocols/${protoName}.hpp)
endfunction()
function(protocolWayland)
  add_custom_command(
    OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
           ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
    COMMAND hyprwayland-scanner --wayland-enums --client
            ${WAYLAND_SCANNER_PKGDATA_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
  target_sources(aquamarine PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
endfunction()

protocolwayland()

protocolnew("stable/xdg-shell" "xdg-shell" false)
protocolnew("stable/linux-dmabuf" "linux-dmabuf-v1" false)

# Generate hwdata info
pkg_get_variable(HWDATA_DIR hwdata pkgdatadir)
message(
  STATUS "Running ${CMAKE_SOURCE_DIR}/data/hwdata.sh < ${HWDATA_DIR}/pnp.ids")
execute_process(
  COMMAND /bin/sh -c
          "${CMAKE_SOURCE_DIR}/data/hwdata.sh < ${HWDATA_DIR}/pnp.ids"
  RESULT_VARIABLE HWDATA_PNP_RESULT
  OUTPUT_VARIABLE HWDATA_PNP_IDS ENCODING UTF8)

if(NOT HWDATA_PNP_RESULT MATCHES 0)
  message(WARNING "hwdata gathering pnps failed")
endif()

configure_file(data/hwdata.hpp.in hwdata.hpp @ONLY)

# tests
add_custom_target(tests)

add_executable(simpleWindow "tests/SimpleWindow.cpp")
target_link_libraries(simpleWindow PRIVATE PkgConfig::deps aquamarine)
add_test(
  NAME "simpleWindow"
  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
  COMMAND simpleWindow "simpleWindow")
add_dependencies(tests simpleWindow)

# Installation
install(TARGETS aquamarine)
install(DIRECTORY "include/aquamarine" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES ${CMAKE_BINARY_DIR}/aquamarine.pc
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
07070100000006000081A400000000000000000000000166C38D3F000005DF000000000000000000000000000000000000001900000000aquamarine-0.3.3/LICENSEBSD 3-Clause License

Copyright (c) 2024, Hypr Development

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
07070100000007000081A400000000000000000000000166C38D3F000003A2000000000000000000000000000000000000001B00000000aquamarine-0.3.3/README.md## Aquamarine

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.

It is agnostic of the rendering API (Vulkan/OpenGL) and designed to be lightweight, performant, and
minimal.

Aquamarine provides no bindings for other languages. It is C++-only.

## Stability

Aquamarine depends on the ABI stability of the stdlib implementation of your compiler. Sover bumps will be done only for aquamarine ABI breaks, not stdlib.

## Building

```sh
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -S . -B ./build
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf _NPROCESSORS_CONF`
```

## TODOs

 - [x] Wayland backend
 - [x] DRM backend (DRM / KMS / libinput)
 - [x] Virtual backend (aka. Headless)
 - [ ] Hardware plane support


07070100000008000081A400000000000000000000000166C38D3F00000006000000000000000000000000000000000000001900000000aquamarine-0.3.3/VERSION0.3.3
07070100000009000081A400000000000000000000000166C38D3F00000100000000000000000000000000000000000000002200000000aquamarine-0.3.3/aquamarine.pc.inprefix=@PREFIX@
includedir=@INCLUDE@
libdir=@LIBDIR@

Name: aquamarine
URL: https://github.com/hyprwm/aquamarine
Description: A very light linux rendering backend library
Version: @AQUAMARINE_VERSION@
Cflags: -I${includedir}
Libs: -L${libdir} -laquamarine
0707010000000A000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001600000000aquamarine-0.3.3/data0707010000000B000081A400000000000000000000000166C38D3F000000C1000000000000000000000000000000000000002400000000aquamarine-0.3.3/data/hwdata.hpp.in#include <unordered_map>
#include <string>

#define __AQ_PNP_PROP(pnp, manu) {pnp, manu}
inline std::unordered_map<std::string, std::string> PNPIDS = {
@HWDATA_PNP_IDS@
};
#undef __AQ_PNP_PROP
0707010000000C000081ED00000000000000000000000166C38D3F00000087000000000000000000000000000000000000002000000000aquamarine-0.3.3/data/hwdata.sh#!/bin/sh

while read -r id vendor; do
	[ "${#id}" = 3 ] || exit 1

	printf "\t__AQ_PNP_PROP(\"%s\", \"%s\"),\n" "$id" "$vendor"
done

0707010000000D000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001600000000aquamarine-0.3.3/docs0707010000000E000081A400000000000000000000000166C38D3F000001E1000000000000000000000000000000000000001D00000000aquamarine-0.3.3/docs/env.md## Environment variables

Unless specified otherwise, a variable is enabled if and only if it's set to `1`

### DRM

`AQ_DRM_DEVICES` -> Set an explicit list of DRM devices (GPUs) to use. It's a colon-separated list of paths, with the first being the primary. E.g. `/dev/dri/card1:/dev/dri/card0`
`AQ_NO_ATOMIC` -> Disables drm atomic modesetting
`AQ_MGPU_NO_EXPLICIT` -> Disables explicit syncing on mgpu buffers

### Debugging

`AQ_TRACE` -> Enables trace (very verbose) logging
0707010000000F000081A400000000000000000000000166C38D3F000008B3000000000000000000000000000000000000001C00000000aquamarine-0.3.3/flake.lock{
  "nodes": {
    "hyprutils": {
      "inputs": {
        "nixpkgs": [
          "nixpkgs"
        ],
        "systems": [
          "systems"
        ]
      },
      "locked": {
        "lastModified": 1721324102,
        "narHash": "sha256-WAZ0X6yJW1hFG6otkHBfyJDKRpNP5stsRqdEuHrFRpk=",
        "owner": "hyprwm",
        "repo": "hyprutils",
        "rev": "962582a090bc233c4de9d9897f46794280288989",
        "type": "github"
      },
      "original": {
        "owner": "hyprwm",
        "repo": "hyprutils",
        "type": "github"
      }
    },
    "hyprwayland-scanner": {
      "inputs": {
        "nixpkgs": [
          "nixpkgs"
        ],
        "systems": [
          "systems"
        ]
      },
      "locked": {
        "lastModified": 1721324119,
        "narHash": "sha256-SOOqIT27/X792+vsLSeFdrNTF+OSRp5qXv6Te+fb2Qg=",
        "owner": "hyprwm",
        "repo": "hyprwayland-scanner",
        "rev": "a048a6cb015340bd82f97c1f40a4b595ca85cc30",
        "type": "github"
      },
      "original": {
        "owner": "hyprwm",
        "repo": "hyprwayland-scanner",
        "type": "github"
      }
    },
    "nixpkgs": {
      "locked": {
        "lastModified": 1721138476,
        "narHash": "sha256-+W5eZOhhemLQxelojLxETfbFbc19NWawsXBlapYpqIA=",
        "owner": "NixOS",
        "repo": "nixpkgs",
        "rev": "ad0b5eed1b6031efaed382844806550c3dcb4206",
        "type": "github"
      },
      "original": {
        "owner": "NixOS",
        "ref": "nixos-unstable",
        "repo": "nixpkgs",
        "type": "github"
      }
    },
    "root": {
      "inputs": {
        "hyprutils": "hyprutils",
        "hyprwayland-scanner": "hyprwayland-scanner",
        "nixpkgs": "nixpkgs",
        "systems": "systems"
      }
    },
    "systems": {
      "locked": {
        "lastModified": 1689347949,
        "narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
        "owner": "nix-systems",
        "repo": "default-linux",
        "rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
        "type": "github"
      },
      "original": {
        "owner": "nix-systems",
        "repo": "default-linux",
        "type": "github"
      }
    }
  },
  "root": "root",
  "version": 7
}
07070100000010000081A400000000000000000000000166C38D3F00000A38000000000000000000000000000000000000001B00000000aquamarine-0.3.3/flake.nix{
  description = "A very light linux rendering backend library";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    systems.url = "github:nix-systems/default-linux";

    hyprutils = {
      url = "github:hyprwm/hyprutils";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.systems.follows = "systems";
    };

    hyprwayland-scanner = {
      url = "github:hyprwm/hyprwayland-scanner";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.systems.follows = "systems";
    };
  };

  outputs = {
    self,
    nixpkgs,
    systems,
    ...
  } @ inputs: let
    inherit (nixpkgs) lib;
    eachSystem = lib.genAttrs (import systems);
    pkgsFor = eachSystem (system:
      import nixpkgs {
        localSystem.system = system;
        overlays = with self.overlays; [aquamarine];
      });
    pkgsCrossFor = eachSystem (system: crossSystem:
      import nixpkgs {
        localSystem = system;
        crossSystem = crossSystem;
        overlays = with self.overlays; [aquamarine];
      });
    mkDate = longDate: (lib.concatStringsSep "-" [
      (builtins.substring 0 4 longDate)
      (builtins.substring 4 2 longDate)
      (builtins.substring 6 2 longDate)
    ]);
    version = lib.removeSuffix "\n" (builtins.readFile ./VERSION);
  in {
    overlays = {
      default = self.overlays.aquamarine;

      aquamarine = lib.composeManyExtensions [
        self.overlays.libinput
        inputs.hyprutils.overlays.default
        inputs.hyprwayland-scanner.overlays.default
        (final: prev: {
          aquamarine = final.callPackage ./nix/default.nix {
            stdenv = final.gcc13Stdenv;
            version = version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
          };
          aquamarine-with-tests = final.aquamarine.override {doCheck = true;};
        })
      ];

      libinput = final: prev: {
        libinput = prev.libinput.overrideAttrs (self: super: {
          version = "1.26.0";

          src = final.fetchFromGitLab {
            domain = "gitlab.freedesktop.org";
            owner = "libinput";
            repo = "libinput";
            rev = self.version;
            hash = "sha256-mlxw4OUjaAdgRLFfPKMZDMOWosW9yKAkzDccwuLGCwQ=";
          };
        });
      };
    };

    packages = eachSystem (system: {
      default = self.packages.${system}.aquamarine;
      inherit (pkgsFor.${system}) aquamarine aquamarine-with-tests;
      aquamarine-cross = (pkgsCrossFor.${system} "aarch64-linux").aquamarine;
    });

    formatter = eachSystem (system: pkgsFor.${system}.alejandra);
  };
}
07070100000011000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001900000000aquamarine-0.3.3/include07070100000012000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000002400000000aquamarine-0.3.3/include/aquamarine07070100000013000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000002E00000000aquamarine-0.3.3/include/aquamarine/allocator07070100000014000081A400000000000000000000000166C38D3F0000055E000000000000000000000000000000000000003C00000000aquamarine-0.3.3/include/aquamarine/allocator/Allocator.hpp#pragma once

#include <hyprutils/memory/SharedPtr.hpp>
#include "../buffer/Buffer.hpp"
#include <drm_fourcc.h>

namespace Aquamarine {
    class CBackend;
    class CSwapchain;

    struct SAllocatorBufferParams {
        Hyprutils::Math::Vector2D size;
        uint32_t                  format  = DRM_FORMAT_INVALID;
        bool                      scanout = false, cursor = false, multigpu = false;
    };

    enum eAllocatorType {
        AQ_ALLOCATOR_TYPE_GBM = 0,
    };

    class IAllocator {
      public:
        virtual ~IAllocator()                                                                                                                                      = default;
        virtual Hyprutils::Memory::CSharedPointer<IBuffer>  acquire(const SAllocatorBufferParams& params, Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain) = 0;
        virtual Hyprutils::Memory::CSharedPointer<CBackend> getBackend()                                                                                           = 0;
        virtual int                                         drmFD()                                                                                                = 0;
        virtual eAllocatorType                              type()                                                                                                 = 0;
    };
};
07070100000015000081A400000000000000000000000166C38D3F00000A72000000000000000000000000000000000000003600000000aquamarine-0.3.3/include/aquamarine/allocator/GBM.hpp#pragma once

#include "Allocator.hpp"

struct gbm_device;
struct gbm_bo;

namespace Aquamarine {
    class CGBMAllocator;
    class CBackend;
    class CSwapchain;

    class CGBMBuffer : public IBuffer {
      public:
        virtual ~CGBMBuffer();

        virtual eBufferCapability                      caps();
        virtual eBufferType                            type();
        virtual void                                   update(const Hyprutils::Math::CRegion& damage);
        virtual bool                                   isSynchronous();
        virtual bool                                   good();
        virtual SDMABUFAttrs                           dmabuf();
        virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
        virtual void                                   endDataPtr();

      private:
        CGBMBuffer(const SAllocatorBufferParams& params, Hyprutils::Memory::CWeakPointer<CGBMAllocator> allocator_, Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain);

        Hyprutils::Memory::CWeakPointer<CGBMAllocator> allocator;

        // gbm stuff
        gbm_bo*      bo         = nullptr;
        void*        boBuffer   = nullptr;
        void*        gboMapping = nullptr;
        SDMABUFAttrs attrs{.success = false};

        friend class CGBMAllocator;
    };

    class CGBMAllocator : public IAllocator {
      public:
        ~CGBMAllocator();
        static Hyprutils::Memory::CSharedPointer<CGBMAllocator> create(int drmfd_, Hyprutils::Memory::CWeakPointer<CBackend> backend_);

        virtual Hyprutils::Memory::CSharedPointer<IBuffer>      acquire(const SAllocatorBufferParams& params, Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain_);
        virtual Hyprutils::Memory::CSharedPointer<CBackend>     getBackend();
        virtual int                                             drmFD();
        virtual eAllocatorType                                  type();

        //
        Hyprutils::Memory::CWeakPointer<CGBMAllocator> self;

      private:
        CGBMAllocator(int fd_, Hyprutils::Memory::CWeakPointer<CBackend> backend_);

        // a vector purely for tracking (debugging) the buffers and nothing more
        std::vector<Hyprutils::Memory::CWeakPointer<CGBMBuffer>> buffers;

        int                                                      fd = -1;
        Hyprutils::Memory::CWeakPointer<CBackend>                backend;

        // gbm stuff
        gbm_device* gbmDevice            = nullptr;
        std::string gbmDeviceBackendName = "";
        std::string drmName              = "";

        friend class CGBMBuffer;
        friend class CDRMRenderer;
    };
};
07070100000016000081A400000000000000000000000166C38D3F00000910000000000000000000000000000000000000003C00000000aquamarine-0.3.3/include/aquamarine/allocator/Swapchain.hpp#pragma once

#include "Allocator.hpp"

namespace Aquamarine {

    class IBackendImplementation;

    struct SSwapchainOptions {
        size_t                    length = 0;
        Hyprutils::Math::Vector2D size;
        uint32_t                  format  = DRM_FORMAT_INVALID; // if you leave this on invalid, the swapchain will choose an appropriate format (and modifier) for you.
        bool                      scanout = false, cursor = false /* requires scanout = true */, multigpu = false /* if true, will force linear */;
    };

    class CSwapchain {
      public:
        static Hyprutils::Memory::CSharedPointer<CSwapchain> create(Hyprutils::Memory::CSharedPointer<IAllocator>             allocator_,
                                                                    Hyprutils::Memory::CSharedPointer<IBackendImplementation> backendImpl_);

        bool                                                 reconfigure(const SSwapchainOptions& options_);

        bool                                                 contains(Hyprutils::Memory::CSharedPointer<IBuffer> buffer);
        Hyprutils::Memory::CSharedPointer<IBuffer>           next(int* age);
        const SSwapchainOptions&                             currentOptions();
        Hyprutils::Memory::CSharedPointer<IAllocator>        getAllocator();

        // rolls the buffers back, marking the last consumed as the next valid.
        // useful if e.g. a commit fails and we don't wanna write to the previous buffer that is
        // in use.
        void rollback();

      private:
        CSwapchain(Hyprutils::Memory::CSharedPointer<IAllocator> allocator_, Hyprutils::Memory::CSharedPointer<IBackendImplementation> backendImpl_);

        bool fullReconfigure(const SSwapchainOptions& options_);
        bool resize(size_t newSize);

        //
        Hyprutils::Memory::CWeakPointer<CSwapchain>             self;
        SSwapchainOptions                                       options;
        Hyprutils::Memory::CSharedPointer<IAllocator>           allocator;
        Hyprutils::Memory::CWeakPointer<IBackendImplementation> backendImpl;
        std::vector<Hyprutils::Memory::CSharedPointer<IBuffer>> buffers;
        int                                                     lastAcquired = 0;

        friend class CGBMBuffer;
    };
};
07070100000017000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000002C00000000aquamarine-0.3.3/include/aquamarine/backend07070100000018000081A400000000000000000000000166C38D3F000019AE000000000000000000000000000000000000003800000000aquamarine-0.3.3/include/aquamarine/backend/Backend.hpp#pragma once

#include <hyprutils/memory/SharedPtr.hpp>
#include <hyprutils/signal/Signal.hpp>
#include <vector>
#include <functional>
#include <mutex>
#include <condition_variable>
#include "../allocator/Allocator.hpp"
#include "Misc.hpp"
#include "Session.hpp"

namespace Aquamarine {
    enum eBackendType : uint32_t {
        AQ_BACKEND_WAYLAND = 0,
        AQ_BACKEND_DRM,
        AQ_BACKEND_HEADLESS,
    };

    enum eBackendRequestMode : uint32_t {
        /*
            Require the provided backend, will error out if it's not available.
        */
        AQ_BACKEND_REQUEST_MANDATORY = 0,
        /*
            Start the backend if it's available
        */
        AQ_BACKEND_REQUEST_IF_AVAILABLE,
        /*
            If any IF_AVAILABLE backend fails, use this one
        */
        AQ_BACKEND_REQUEST_FALLBACK,
    };

    enum eBackendLogLevel : uint32_t {
        AQ_LOG_TRACE = 0,
        AQ_LOG_DEBUG,
        AQ_LOG_WARNING,
        AQ_LOG_ERROR,
        AQ_LOG_CRITICAL,
    };

    struct SBackendImplementationOptions {
        explicit SBackendImplementationOptions();
        eBackendType        backendType;
        eBackendRequestMode backendRequestMode;
    };

    struct SBackendOptions {
        explicit SBackendOptions();
        std::function<void(eBackendLogLevel, std::string)> logFunction;
    };

    struct SPollFD {
        int                       fd = -1;
        std::function<void(void)> onSignal; /* call this when signaled */
    };

    class IBackendImplementation {
      public:
        virtual ~IBackendImplementation() {
            ;
        }

        enum eBackendCapabilities : uint32_t {
            AQ_BACKEND_CAPABILITY_POINTER = (1 << 0),
        };

        virtual eBackendType                                            type()                                     = 0;
        virtual bool                                                    start()                                    = 0;
        virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs()                                  = 0;
        virtual int                                                     drmFD()                                    = 0;
        virtual bool                                                    dispatchEvents()                           = 0;
        virtual uint32_t                                                capabilities()                             = 0;
        virtual void                                                    onReady()                                  = 0;
        virtual std::vector<SDRMFormat>                                 getRenderFormats()                         = 0;
        virtual std::vector<SDRMFormat>                                 getCursorFormats()                         = 0;
        virtual bool                                                    createOutput(const std::string& name = "") = 0; // "" means auto
        virtual Hyprutils::Memory::CSharedPointer<IAllocator>           preferredAllocator()                       = 0;
        virtual std::vector<SDRMFormat>                                 getRenderableFormats(); // empty = use getRenderFormats
    };

    class CBackend {
      public:
        /* Create a backend, with the provided options. May return a single or a multi-backend. */
        static Hyprutils::Memory::CSharedPointer<CBackend> create(const std::vector<SBackendImplementationOptions>& backends, const SBackendOptions& options);

        ~CBackend();

        /* start the backend. Initializes all the stuff, and will return true on success, false on fail. */
        bool start();

        void log(eBackendLogLevel level, const std::string& msg);

        /* Gets all the FDs you have to poll. When any single one fires, call its onPoll */
        std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> getPollFDs();

        /* Checks if the backend has a session - iow if it's a DRM backend */
        bool hasSession();

        /* Get the primary DRM FD */
        int drmFD();

        /* Get the render formats the primary backend supports */
        std::vector<SDRMFormat> getPrimaryRenderFormats();

        /* get a vector of the backend implementations available */
        const std::vector<Hyprutils::Memory::CSharedPointer<IBackendImplementation>>& getImplementations();

        /* push an idle event to the queue */
        void addIdleEvent(Hyprutils::Memory::CSharedPointer<std::function<void(void)>> fn);

        /* remove an idle event from the queue */
        void removeIdleEvent(Hyprutils::Memory::CSharedPointer<std::function<void(void)>> pfn);

        // utils
        int reopenDRMNode(int drmFD, bool allowRenderNode = true);

        struct {
            Hyprutils::Signal::CSignal newOutput;
            Hyprutils::Signal::CSignal newPointer;
            Hyprutils::Signal::CSignal newKeyboard;
            Hyprutils::Signal::CSignal newTouch;
            Hyprutils::Signal::CSignal newSwitch;
            Hyprutils::Signal::CSignal newTablet;
            Hyprutils::Signal::CSignal newTabletTool;
            Hyprutils::Signal::CSignal newTabletPad;
        } events;

        Hyprutils::Memory::CSharedPointer<IAllocator> primaryAllocator;
        bool                                          ready = false;
        Hyprutils::Memory::CSharedPointer<CSession>   session;

      private:
        CBackend();

        bool                                                                   terminate = false;

        std::vector<SBackendImplementationOptions>                             implementationOptions;
        std::vector<Hyprutils::Memory::CSharedPointer<IBackendImplementation>> implementations;
        SBackendOptions                                                        options;
        Hyprutils::Memory::CWeakPointer<CBackend>                              self;
        std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>>                sessionFDs;

        struct {
            int                                                                       fd = -1;
            std::vector<Hyprutils::Memory::CSharedPointer<std::function<void(void)>>> pending;
        } idle;

        void dispatchIdle();
        void updateIdleTimer();

        //
        struct {
            std::condition_variable loopSignal;
            std::mutex              loopMutex;
            std::atomic<bool>       shouldProcess = false;
            std::mutex              loopRequestMutex;
            std::mutex              eventLock;
        } m_sEventLoopInternals;
    };
};
07070100000019000081A400000000000000000000000166C38D3F000047E1000000000000000000000000000000000000003400000000aquamarine-0.3.3/include/aquamarine/backend/DRM.hpp#pragma once

#include "./Backend.hpp"
#include "../allocator/Swapchain.hpp"
#include "../output/Output.hpp"
#include "../input/Input.hpp"
#include <hyprutils/memory/WeakPtr.hpp>
#include <wayland-client.h>
#include <xf86drmMode.h>

namespace Aquamarine {
    class CDRMBackend;
    class CDRMFB;
    class CDRMOutput;
    struct SDRMConnector;
    class CDRMRenderer;

    typedef std::function<void(void)> FIdleCallback;

    class CDRMBufferAttachment : public IAttachment {
      public:
        CDRMBufferAttachment(Hyprutils::Memory::CSharedPointer<CDRMFB> fb_);
        virtual ~CDRMBufferAttachment() {
            ;
        }
        virtual eAttachmentType type() {
            return AQ_ATTACHMENT_DRM_BUFFER;
        }

        Hyprutils::Memory::CSharedPointer<CDRMFB> fb;
    };

    class CDRMBufferUnimportable : public IAttachment {
      public:
        CDRMBufferUnimportable() {
            ;
        }
        virtual ~CDRMBufferUnimportable() {
            ;
        }
        virtual eAttachmentType type() {
            return AQ_ATTACHMENT_DRM_KMS_UNIMPORTABLE;
        }
    };

    class CDRMLease {
      public:
        static Hyprutils::Memory::CSharedPointer<CDRMLease> create(std::vector<Hyprutils::Memory::CSharedPointer<IOutput>> outputs);
        ~CDRMLease();

        void                                                     terminate();

        int                                                      leaseFD  = -1;
        uint32_t                                                 lesseeID = 0;
        Hyprutils::Memory::CWeakPointer<CDRMBackend>             backend;
        std::vector<Hyprutils::Memory::CWeakPointer<CDRMOutput>> outputs;
        bool                                                     active = true;

        struct {
            Hyprutils::Signal::CSignal destroy;
        } events;

      private:
        CDRMLease() = default;

        void destroy();

        friend class CDRMBackend;
    };

    class CDRMFB {
      public:
        ~CDRMFB();

        static Hyprutils::Memory::CSharedPointer<CDRMFB> create(Hyprutils::Memory::CSharedPointer<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_,
                                                                bool* isNew = nullptr);

        void                                             closeHandles();
        // drops the buffer from KMS
        void drop();

        // re-imports the buffer into KMS. Essentially drop and import.
        void                                         reimport();

        uint32_t                                     id = 0;
        Hyprutils::Memory::CWeakPointer<IBuffer>     buffer;
        Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
        std::array<uint32_t, 4>                      boHandles = {0, 0, 0, 0};

        // true if the original buffer is gone and this has been released.
        bool dead = false;

      private:
        CDRMFB(Hyprutils::Memory::CSharedPointer<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_);
        uint32_t submitBuffer();
        void     import();

        bool     dropped = false, handlesClosed = false;

        struct {
            Hyprutils::Signal::CHyprSignalListener destroyBuffer;
        } listeners;
    };

    struct SDRMLayer {
        // we expect the consumers to use double-buffering, so we keep the 2 last FBs around. If any of these goes out of
        // scope, the DRM FB will be destroyed, but the IBuffer will stay, as long as it's ref'd somewhere.
        Hyprutils::Memory::CSharedPointer<CDRMFB>    front /* currently displaying */, back /* submitted */, last /* keep just in case */;
        Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
    };

    struct SDRMPlane {
        bool                                         init(drmModePlane* plane);

        uint64_t                                     type      = 0;
        uint32_t                                     id        = 0;
        uint32_t                                     initialID = 0;

        Hyprutils::Memory::CSharedPointer<CDRMFB>    front /* currently displaying */, back /* submitted */, last /* keep just in case */;
        Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
        Hyprutils::Memory::CWeakPointer<SDRMPlane>   self;
        std::vector<SDRMFormat>                      formats;

        union UDRMPlaneProps {
            struct {
                uint32_t type;
                uint32_t rotation;   // Not guaranteed to exist
                uint32_t in_formats; // Not guaranteed to exist

                // atomic-modesetting only

                uint32_t src_x;
                uint32_t src_y;
                uint32_t src_w;
                uint32_t src_h;
                uint32_t crtc_x;
                uint32_t crtc_y;
                uint32_t crtc_w;
                uint32_t crtc_h;
                uint32_t fb_id;
                uint32_t crtc_id;
                uint32_t fb_damage_clips;
                uint32_t hotspot_x;
                uint32_t hotspot_y;
                uint32_t in_fence_fd;
            };
            uint32_t props[17] = {0};
        };
        UDRMPlaneProps props;
    };

    struct SDRMCRTC {
        uint32_t               id = 0;
        std::vector<SDRMLayer> layers;
        int32_t                refresh = 0; // unused

        struct {
            int gammaSize = 0;
        } legacy;

        struct {
            bool     ownModeID = false;
            uint32_t modeID    = 0;
            uint32_t gammaLut  = 0;
        } atomic;

        Hyprutils::Memory::CSharedPointer<SDRMPlane> primary;
        Hyprutils::Memory::CSharedPointer<SDRMPlane> cursor;
        Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
        Hyprutils::Memory::CSharedPointer<CDRMFB>    pendingCursor;

        union UDRMCRTCProps {
            struct {
                // None of these are guaranteed to exist
                uint32_t vrr_enabled;
                uint32_t gamma_lut;
                uint32_t gamma_lut_size;

                // atomic-modesetting only

                uint32_t active;
                uint32_t mode_id;
                uint32_t out_fence_ptr;
            };
            uint32_t props[7] = {0};
        };
        UDRMCRTCProps props;
    };

    class CDRMOutput : public IOutput {
      public:
        virtual ~CDRMOutput();
        virtual bool                                                      commit();
        virtual bool                                                      test();
        virtual Hyprutils::Memory::CSharedPointer<IBackendImplementation> getBackend();
        virtual bool                                                      setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
        virtual void                                                      moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipShedule = false);
        virtual void                                                      scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN);
        virtual void                                                      setCursorVisible(bool visible);
        virtual Hyprutils::Math::Vector2D                                 cursorPlaneSize();
        virtual size_t                                                    getGammaSize();
        virtual std::vector<SDRMFormat>                                   getRenderFormats();

        int                                                               getConnectorID();

        Hyprutils::Memory::CWeakPointer<CDRMOutput>                       self;
        Hyprutils::Memory::CWeakPointer<CDRMLease>                        lease;
        bool                                                              cursorVisible = true;
        Hyprutils::Math::Vector2D                                         cursorPos; // without hotspot
        Hyprutils::Math::Vector2D                                         cursorHotspot;

        bool enabledState = true; // actual enabled state. Should be synced with state->state().enabled after a new frame

      private:
        CDRMOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_, Hyprutils::Memory::CSharedPointer<SDRMConnector> connector_);

        bool                                                         commitState(bool onlyTest = false);

        Hyprutils::Memory::CWeakPointer<CDRMBackend>                 backend;
        Hyprutils::Memory::CSharedPointer<SDRMConnector>             connector;
        Hyprutils::Memory::CSharedPointer<std::function<void(void)>> frameIdle;

        struct {
            Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain;
            Hyprutils::Memory::CSharedPointer<CSwapchain> cursorSwapchain;
        } mgpu;

        bool lastCommitNoBuffer = true;

        friend struct SDRMConnector;
        friend class CDRMLease;
    };

    struct SDRMPageFlip {
        Hyprutils::Memory::CWeakPointer<SDRMConnector> connector;
    };

    struct SDRMConnectorCommitData {
        Hyprutils::Memory::CSharedPointer<CDRMFB> mainFB, cursorFB;
        bool                                      modeset  = false;
        bool                                      blocking = false;
        uint32_t                                  flags    = 0;
        bool                                      test     = false;
        drmModeModeInfo                           modeInfo;

        struct {
            uint32_t gammaLut = 0;
            uint32_t fbDamage = 0;
            uint32_t modeBlob = 0;
            bool     blobbed  = false;
            bool     gammad   = false;
        } atomic;

        void calculateMode(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector);
    };

    struct SDRMConnector {
        ~SDRMConnector();

        bool                                           init(drmModeConnector* connector);
        void                                           connect(drmModeConnector* connector);
        void                                           disconnect();
        Hyprutils::Memory::CSharedPointer<SDRMCRTC>    getCurrentCRTC(const drmModeConnector* connector);
        drmModeModeInfo*                               getCurrentMode();
        void                                           parseEDID(std::vector<uint8_t> data);
        bool                                           commitState(SDRMConnectorCommitData& data);
        void                                           applyCommit(const SDRMConnectorCommitData& data);
        void                                           rollbackCommit(const SDRMConnectorCommitData& data);
        void                                           onPresent();
        void                                           recheckCRTCProps();

        Hyprutils::Memory::CSharedPointer<CDRMOutput>  output;
        Hyprutils::Memory::CWeakPointer<CDRMBackend>   backend;
        Hyprutils::Memory::CWeakPointer<SDRMConnector> self;
        std::string                                    szName;
        drmModeConnection                              status       = DRM_MODE_DISCONNECTED;
        uint32_t                                       id           = 0;
        std::array<uint64_t, 2>                        maxBpcBounds = {0, 0};
        Hyprutils::Memory::CSharedPointer<SDRMCRTC>    crtc;
        int32_t                                        refresh       = 0;
        uint32_t                                       possibleCrtcs = 0;
        std::string                                    make, serial, model;
        bool                                           canDoVrr = false;

        bool                                           cursorEnabled = false;
        Hyprutils::Math::Vector2D                      cursorPos, cursorSize, cursorHotspot;
        Hyprutils::Memory::CSharedPointer<CDRMFB>      pendingCursorFB;

        bool                                           isPageFlipPending = false;
        SDRMPageFlip                                   pendingPageFlip;
        bool                                           frameEventScheduled = false;

        // the current state is invalid and won't commit, don't try to modeset.
        bool                                           commitTainted = false;

        Hyprutils::Memory::CSharedPointer<SOutputMode> fallbackMode;

        struct {
            bool vrrEnabled = false;
        } atomic;

        union UDRMConnectorProps {
            struct {
                uint32_t edid;
                uint32_t dpms;
                uint32_t link_status; // not guaranteed to exist
                uint32_t path;
                uint32_t vrr_capable;  // not guaranteed to exist
                uint32_t subconnector; // not guaranteed to exist
                uint32_t non_desktop;
                uint32_t panel_orientation; // not guaranteed to exist
                uint32_t content_type;      // not guaranteed to exist
                uint32_t max_bpc;           // not guaranteed to exist

                // atomic-modesetting only

                uint32_t crtc_id;
            };
            uint32_t props[4] = {0};
        };
        UDRMConnectorProps props;
    };

    class IDRMImplementation {
      public:
        virtual bool commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data) = 0;
        virtual bool reset()                                                                                           = 0;

        // moving a cursor IIRC is almost instant on most hardware so we don't have to wait for a commit.
        virtual bool moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, bool skipShedule = false) = 0;
    };

    class CDRMBackend : public IBackendImplementation {
      public:
        virtual ~CDRMBackend();
        virtual eBackendType                                            type();
        virtual bool                                                    start();
        virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs();
        virtual int                                                     drmFD();
        virtual bool                                                    dispatchEvents();
        virtual uint32_t                                                capabilities();
        virtual bool                                                    setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
        virtual void                                                    onReady();
        virtual std::vector<SDRMFormat>                                 getRenderFormats();
        virtual std::vector<SDRMFormat>                                 getCursorFormats();
        virtual bool                                                    createOutput(const std::string& name = "");
        virtual Hyprutils::Memory::CSharedPointer<IAllocator>           preferredAllocator();
        virtual std::vector<SDRMFormat>                                 getRenderableFormats();

        Hyprutils::Memory::CWeakPointer<CDRMBackend>                    self;

        void                                                            log(eBackendLogLevel, const std::string&);
        bool                                                            sessionActive();
        int                                                             getNonMasterFD();

        std::vector<FIdleCallback>                                      idleCallbacks;
        std::string                                                     gpuName;

      private:
        CDRMBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend);

        static std::vector<Hyprutils::Memory::CSharedPointer<CDRMBackend>> attempt(Hyprutils::Memory::CSharedPointer<CBackend> backend);
        bool registerGPU(Hyprutils::Memory::CSharedPointer<CSessionDevice> gpu_, Hyprutils::Memory::CSharedPointer<CDRMBackend> primary_ = {});
        bool checkFeatures();
        bool initResources();
        bool initMgpu();
        bool grabFormats();
        bool shouldBlit();
        void scanConnectors();
        void scanLeases();
        void restoreAfterVT();
        void recheckCRTCs();
        void buildGlFormats(const std::vector<SGLFormat>& fmts);

        Hyprutils::Memory::CSharedPointer<CSessionDevice>     gpu;
        Hyprutils::Memory::CSharedPointer<IDRMImplementation> impl;
        Hyprutils::Memory::CWeakPointer<CDRMBackend>          primary;

        struct {
            Hyprutils::Memory::CSharedPointer<IAllocator>   allocator;
            Hyprutils::Memory::CSharedPointer<CDRMRenderer> renderer; // may be null if creation fails
        } rendererState;

        Hyprutils::Memory::CWeakPointer<CBackend>                     backend;

        std::vector<Hyprutils::Memory::CSharedPointer<SDRMCRTC>>      crtcs;
        std::vector<Hyprutils::Memory::CSharedPointer<SDRMPlane>>     planes;
        std::vector<Hyprutils::Memory::CSharedPointer<SDRMConnector>> connectors;
        std::vector<SDRMFormat>                                       formats;
        std::vector<SDRMFormat>                                       glFormats;

        bool                                                          atomic = false;

        struct {
            Hyprutils::Math::Vector2D cursorSize;
            bool                      supportsAsyncCommit     = false;
            bool                      supportsAddFb2Modifiers = false;
            bool                      supportsTimelines       = false;
        } drmProps;

        struct {
            Hyprutils::Signal::CHyprSignalListener sessionActivate;
            Hyprutils::Signal::CHyprSignalListener gpuChange;
            Hyprutils::Signal::CHyprSignalListener gpuRemove;
        } listeners;

        friend class CBackend;
        friend class CDRMFB;
        friend class CDRMFBAttachment;
        friend struct SDRMConnector;
        friend struct SDRMCRTC;
        friend struct SDRMPlane;
        friend class CDRMOutput;
        friend struct SDRMPageFlip;
        friend class CDRMLegacyImpl;
        friend class CDRMAtomicImpl;
        friend class CDRMAtomicRequest;
        friend class CDRMLease;
        friend class CGBMBuffer;
    };
};
0707010000001A000081A400000000000000000000000166C38D3F00000DFC000000000000000000000000000000000000003900000000aquamarine-0.3.3/include/aquamarine/backend/Headless.hpp#pragma once

#include "./Backend.hpp"
#include "../allocator/Swapchain.hpp"
#include "../output/Output.hpp"
#include <hyprutils/memory/WeakPtr.hpp>

namespace Aquamarine {
    class CBackend;
    class CHeadlessBackend;

    class CHeadlessOutput : public IOutput {
      public:
        virtual ~CHeadlessOutput();
        virtual bool                                                      commit();
        virtual bool                                                      test();
        virtual Hyprutils::Memory::CSharedPointer<IBackendImplementation> getBackend();
        virtual void                                                      scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN);
        virtual bool                                                      destroy();
        virtual std::vector<SDRMFormat>                                   getRenderFormats();

        Hyprutils::Memory::CWeakPointer<CHeadlessOutput>                  self;

      private:
        CHeadlessOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CHeadlessBackend> backend_);

        Hyprutils::Memory::CWeakPointer<CHeadlessBackend>        backend;

        Hyprutils::Memory::CSharedPointer<std::function<void()>> framecb;
        bool                                                     frameScheduled = false;

        friend class CHeadlessBackend;
    };

    class CHeadlessBackend : public IBackendImplementation {
      public:
        virtual ~CHeadlessBackend();
        virtual eBackendType                                            type();
        virtual bool                                                    start();
        virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs();
        virtual int                                                     drmFD();
        virtual bool                                                    dispatchEvents();
        virtual uint32_t                                                capabilities();
        virtual bool                                                    setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
        virtual void                                                    onReady();
        virtual std::vector<SDRMFormat>                                 getRenderFormats();
        virtual std::vector<SDRMFormat>                                 getCursorFormats();
        virtual bool                                                    createOutput(const std::string& name = "");
        virtual Hyprutils::Memory::CSharedPointer<IAllocator>           preferredAllocator();

        Hyprutils::Memory::CWeakPointer<CHeadlessBackend>               self;

      private:
        CHeadlessBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend_);

        Hyprutils::Memory::CWeakPointer<CBackend>                       backend;
        std::vector<Hyprutils::Memory::CSharedPointer<CHeadlessOutput>> outputs;

        size_t                                                          outputIDCounter = 0;

        class CTimer {
          public:
            std::chrono::steady_clock::time_point when;
            std::function<void(void)>             what;
            bool                                  expired();
        };

        struct {
            int                 timerfd = -1;
            std::vector<CTimer> timers;
        } timers;

        void dispatchTimers();
        void updateTimerFD();

        friend class CBackend;
        friend class CHeadlessOutput;
    };
};
0707010000001B000081A400000000000000000000000166C38D3F0000015F000000000000000000000000000000000000003500000000aquamarine-0.3.3/include/aquamarine/backend/Misc.hpp#pragma once

#include <cstdint>
#include <vector>

namespace Aquamarine {
    struct SGLFormat {
        uint32_t drmFormat = 0;
        uint64_t modifier  = 0;
        bool     external  = false;
    };

    struct SDRMFormat {
        uint32_t              drmFormat = 0; /* DRM_FORMAT_INVALID */
        std::vector<uint64_t> modifiers;
    };
};
0707010000001C000081A400000000000000000000000166C38D3F0000228F000000000000000000000000000000000000003800000000aquamarine-0.3.3/include/aquamarine/backend/Session.hpp#pragma once

#include <sys/types.h>
#include <hyprutils/signal/Signal.hpp>
#include <hyprutils/memory/SharedPtr.hpp>
#include "../input/Input.hpp"
#include <vector>

struct udev;
struct udev_monitor;
struct udev_device;
struct libseat;
struct libinput;
struct libinput_event;
struct libinput_device;
struct libinput_tablet_tool;

namespace Aquamarine {
    class CBackend;
    class CSession;
    class CLibinputDevice;
    struct SPollFD;

    class CSessionDevice {
      public:
        CSessionDevice(Hyprutils::Memory::CSharedPointer<CSession> session_, const std::string& path_);
        ~CSessionDevice();

        static Hyprutils::Memory::CSharedPointer<CSessionDevice> openIfKMS(Hyprutils::Memory::CSharedPointer<CSession> session_, const std::string& path_);

        bool                                                     supportsKMS();

        int                                                      fd       = -1;
        int                                                      deviceID = -1;
        dev_t                                                    dev;
        std::string                                              path;

        enum eChangeEventType : uint32_t {
            AQ_SESSION_EVENT_CHANGE_HOTPLUG = 0,
            AQ_SESSION_EVENT_CHANGE_LEASE,
        };

        struct SChangeEvent {
            eChangeEventType type = AQ_SESSION_EVENT_CHANGE_HOTPLUG;

            struct {
                uint32_t connectorID = 0, propID = 0;
            } hotplug;
        };

        struct {
            Hyprutils::Signal::CSignal change;
            Hyprutils::Signal::CSignal remove;
        } events;

      private:
        Hyprutils::Memory::CWeakPointer<CSession> session;
    };

    class CLibinputKeyboard : public IKeyboard {
      public:
        CLibinputKeyboard(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev);
        virtual ~CLibinputKeyboard() {
            ;
        }

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName();
        virtual void               updateLEDs(uint32_t leds);

      private:
        Hyprutils::Memory::CWeakPointer<CLibinputDevice> device;

        friend class CLibinputDevice;
    };

    class CLibinputMouse : public IPointer {
      public:
        CLibinputMouse(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev);
        virtual ~CLibinputMouse() {
            ;
        }

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName();

      private:
        Hyprutils::Memory::CWeakPointer<CLibinputDevice> device;

        friend class CLibinputDevice;
    };

    class CLibinputTouch : public ITouch {
      public:
        CLibinputTouch(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev);
        virtual ~CLibinputTouch() {
            ;
        }

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName();

      private:
        Hyprutils::Memory::CWeakPointer<CLibinputDevice> device;

        friend class CLibinputDevice;
    };

    class CLibinputSwitch : public ISwitch {
      public:
        CLibinputSwitch(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev);
        virtual ~CLibinputSwitch() {
            ;
        }

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName();

        eSwitchType                type  = AQ_SWITCH_TYPE_UNKNOWN;
        bool                       state = false;

      private:
        Hyprutils::Memory::CWeakPointer<CLibinputDevice> device;

        friend class CLibinputDevice;
    };

    class CLibinputTablet : public ITablet {
      public:
        CLibinputTablet(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev);
        virtual ~CLibinputTablet() {
            ;
        }

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName();

      private:
        Hyprutils::Memory::CWeakPointer<CLibinputDevice> device;

        friend class CLibinputDevice;
    };

    class CLibinputTabletTool : public ITabletTool {
      public:
        CLibinputTabletTool(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev, libinput_tablet_tool* tool);
        virtual ~CLibinputTabletTool();

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName();

      private:
        Hyprutils::Memory::CWeakPointer<CLibinputDevice> device;
        libinput_tablet_tool*                            libinputTool = nullptr;

        friend class CLibinputDevice;
    };

    class CLibinputTabletPad : public ITabletPad {
      public:
        CLibinputTabletPad(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev);
        virtual ~CLibinputTabletPad();

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName();

      private:
        Hyprutils::Memory::CWeakPointer<CLibinputDevice>               device;

        Hyprutils::Memory::CSharedPointer<ITabletPad::STabletPadGroup> createGroupFromID(int id);

        friend class CLibinputDevice;
    };

    class CLibinputDevice {
      public:
        CLibinputDevice(libinput_device* device, Hyprutils::Memory::CWeakPointer<CSession> session_);
        ~CLibinputDevice();

        void                                                                init();

        libinput_device*                                                    device;
        Hyprutils::Memory::CWeakPointer<CLibinputDevice>                    self;
        Hyprutils::Memory::CWeakPointer<CSession>                           session;
        std::string                                                         name;

        Hyprutils::Memory::CSharedPointer<CLibinputKeyboard>                keyboard;
        Hyprutils::Memory::CSharedPointer<CLibinputMouse>                   mouse;
        Hyprutils::Memory::CSharedPointer<CLibinputTouch>                   touch;
        Hyprutils::Memory::CSharedPointer<CLibinputSwitch>                  switchy; // :)
        Hyprutils::Memory::CSharedPointer<CLibinputTablet>                  tablet;
        Hyprutils::Memory::CSharedPointer<CLibinputTabletPad>               tabletPad;
        std::vector<Hyprutils::Memory::CSharedPointer<CLibinputTabletTool>> tabletTools;

        Hyprutils::Memory::CSharedPointer<CLibinputTabletTool>              toolFrom(libinput_tablet_tool* tool);
    };

    class CSession {
      public:
        ~CSession();

        static Hyprutils::Memory::CSharedPointer<CSession>              attempt(Hyprutils::Memory::CSharedPointer<CBackend> backend_);

        bool                                                            active = true; // whether the current vt is ours
        uint32_t                                                        vt     = 0;    // 0 means unsupported
        std::string                                                     seatName;
        Hyprutils::Memory::CWeakPointer<CSession>                       self;

        std::vector<Hyprutils::Memory::CSharedPointer<CSessionDevice>>  sessionDevices;
        std::vector<Hyprutils::Memory::CSharedPointer<CLibinputDevice>> libinputDevices;

        udev*                                                           udevHandle     = nullptr;
        udev_monitor*                                                   udevMonitor    = nullptr;
        libseat*                                                        libseatHandle  = nullptr;
        libinput*                                                       libinputHandle = nullptr;

        std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>>         pollFDs();
        void                                                            dispatchPendingEventsAsync();
        bool                                                            switchVT(uint32_t vt);
        void                                                            onReady();

        struct SAddDrmCardEvent {
            std::string path;
        };

        struct {
            Hyprutils::Signal::CSignal changeActive;
            Hyprutils::Signal::CSignal addDrmCard;
            Hyprutils::Signal::CSignal destroy;
        } events;

      private:
        Hyprutils::Memory::CWeakPointer<CBackend>               backend;
        std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> polls;

        void                                                    dispatchUdevEvents();
        void                                                    dispatchLibinputEvents();
        void                                                    dispatchLibseatEvents();
        void                                                    handleLibinputEvent(libinput_event* e);

        friend class CSessionDevice;
        friend class CLibinputDevice;
    };
};
0707010000001D000081A400000000000000000000000166C38D3F000020C2000000000000000000000000000000000000003800000000aquamarine-0.3.3/include/aquamarine/backend/Wayland.hpp#pragma once

#include "./Backend.hpp"
#include "../allocator/Swapchain.hpp"
#include "../output/Output.hpp"
#include "../input/Input.hpp"
#include <hyprutils/memory/WeakPtr.hpp>
#include <wayland-client.h>
#include <wayland.hpp>
#include <xdg-shell.hpp>
#include <linux-dmabuf-v1.hpp>
#include <tuple>

namespace Aquamarine {
    class CBackend;
    class CWaylandBackend;
    class CWaylandOutput;
    class CWaylandPointer;

    typedef std::function<void(void)> FIdleCallback;

    class CWaylandBuffer {
      public:
        CWaylandBuffer(Hyprutils::Memory::CSharedPointer<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_);
        ~CWaylandBuffer();
        bool good();

        bool pendingRelease = false;

      private:
        struct {
            Hyprutils::Memory::CSharedPointer<CCWlBuffer> buffer;
        } waylandState;

        Hyprutils::Memory::CWeakPointer<IBuffer>         buffer;
        Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend;

        friend class CWaylandOutput;
    };

    class CWaylandOutput : public IOutput {
      public:
        virtual ~CWaylandOutput();
        virtual bool                                                      commit();
        virtual bool                                                      test();
        virtual Hyprutils::Memory::CSharedPointer<IBackendImplementation> getBackend();
        virtual bool                                                      setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
        virtual void                                                      moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipShedule = false);
        virtual void                                                      scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN);
        virtual Hyprutils::Math::Vector2D                                 cursorPlaneSize();
        virtual bool                                                      destroy();
        virtual std::vector<SDRMFormat>                                   getRenderFormats();

        Hyprutils::Memory::CWeakPointer<CWaylandOutput>                   self;

      private:
        CWaylandOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_);

        Hyprutils::Memory::CWeakPointer<CWaylandBackend>  backend;

        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;

        struct {
            std::vector<std::pair<Hyprutils::Memory::CWeakPointer<IBuffer>, Hyprutils::Memory::CSharedPointer<CWaylandBuffer>>> buffers;
        } backendState;

        struct {
            Hyprutils::Memory::CSharedPointer<IBuffer>     cursorBuffer;
            Hyprutils::Memory::CSharedPointer<CCWlSurface> cursorSurface;
            Hyprutils::Memory::CSharedPointer<CCWlBuffer>  cursorWlBuffer;
            uint32_t                                       serial = 0;
            Hyprutils::Math::Vector2D                      hotspot;
        } cursorState;

        struct {
            Hyprutils::Memory::CSharedPointer<CCWlSurface>   surface;
            Hyprutils::Memory::CSharedPointer<CCXdgSurface>  xdgSurface;
            Hyprutils::Memory::CSharedPointer<CCXdgToplevel> xdgToplevel;
            Hyprutils::Memory::CSharedPointer<CCWlCallback>  frameCallback;
        } waylandState;

        friend class CWaylandBackend;
        friend class CWaylandPointer;
    };

    class CWaylandKeyboard : public IKeyboard {
      public:
        CWaylandKeyboard(Hyprutils::Memory::CSharedPointer<CCWlKeyboard> keyboard_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_);
        virtual ~CWaylandKeyboard();

        virtual const std::string&                       getName();

        Hyprutils::Memory::CSharedPointer<CCWlKeyboard>  keyboard;
        Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend;

      private:
        const std::string name = "wl_keyboard";
    };

    class CWaylandPointer : public IPointer {
      public:
        CWaylandPointer(Hyprutils::Memory::CSharedPointer<CCWlPointer> pointer_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_);
        virtual ~CWaylandPointer();

        virtual const std::string&                       getName();

        Hyprutils::Memory::CSharedPointer<CCWlPointer>   pointer;
        Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend;

      private:
        const std::string name = "wl_pointer";
    };

    class CWaylandBackend : public IBackendImplementation {
      public:
        virtual ~CWaylandBackend();
        virtual eBackendType                                            type();
        virtual bool                                                    start();
        virtual std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> pollFDs();
        virtual int                                                     drmFD();
        virtual bool                                                    dispatchEvents();
        virtual uint32_t                                                capabilities();
        virtual bool                                                    setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
        virtual void                                                    onReady();
        virtual std::vector<SDRMFormat>                                 getRenderFormats();
        virtual std::vector<SDRMFormat>                                 getCursorFormats();
        virtual bool                                                    createOutput(const std::string& name = "");
        virtual Hyprutils::Memory::CSharedPointer<IAllocator>           preferredAllocator();

        Hyprutils::Memory::CWeakPointer<CWaylandBackend>                self;

      private:
        CWaylandBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend);

        void initSeat();
        void initShell();
        bool initDmabuf();

        //
        Hyprutils::Memory::CWeakPointer<CBackend>                        backend;
        std::vector<Hyprutils::Memory::CSharedPointer<CWaylandOutput>>   outputs;
        std::vector<Hyprutils::Memory::CSharedPointer<CWaylandKeyboard>> keyboards;
        std::vector<Hyprutils::Memory::CSharedPointer<CWaylandPointer>>  pointers;
        std::vector<FIdleCallback>                                       idleCallbacks;

        // pointer focus
        Hyprutils::Memory::CWeakPointer<CWaylandOutput> focusedOutput;
        uint32_t                                        lastEnterSerial = 0;

        // state
        size_t lastOutputID = 0;

        // dmabuf formats
        std::vector<SDRMFormat> dmabufFormats;

        struct {
            wl_display* display = nullptr;

            // hw-s types
            Hyprutils::Memory::CSharedPointer<CCWlRegistry>               registry;
            Hyprutils::Memory::CSharedPointer<CCWlSeat>                   seat;
            Hyprutils::Memory::CSharedPointer<CCWlShm>                    shm;
            Hyprutils::Memory::CSharedPointer<CCXdgWmBase>                xdg;
            Hyprutils::Memory::CSharedPointer<CCWlCompositor>             compositor;
            Hyprutils::Memory::CSharedPointer<CCZwpLinuxDmabufV1>         dmabuf;
            Hyprutils::Memory::CSharedPointer<CCZwpLinuxDmabufFeedbackV1> dmabufFeedback;

            // control
            bool dmabufFailed = false;
        } waylandState;

        struct {
            int         fd       = -1;
            std::string nodeName = "";
        } drmState;

        friend class CBackend;
        friend class CWaylandKeyboard;
        friend class CWaylandPointer;
        friend class CWaylandOutput;
        friend class CWaylandBuffer;
    };
};
0707010000001E000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000003000000000aquamarine-0.3.3/include/aquamarine/backend/drm0707010000001F000081A400000000000000000000000166C38D3F000007AF000000000000000000000000000000000000003B00000000aquamarine-0.3.3/include/aquamarine/backend/drm/Atomic.hpp#pragma once

#include "../DRM.hpp"

namespace Aquamarine {
    class CDRMAtomicImpl : public IDRMImplementation {
      public:
        CDRMAtomicImpl(Hyprutils::Memory::CSharedPointer<CDRMBackend> backend_);
        virtual bool commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);
        virtual bool reset();
        virtual bool moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, bool skipShedule = false);

      private:
        bool                                         prepareConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);

        Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;

        friend class CDRMAtomicRequest;
    };

    class CDRMAtomicRequest {
      public:
        CDRMAtomicRequest(Hyprutils::Memory::CWeakPointer<CDRMBackend> backend);
        ~CDRMAtomicRequest();

        void addConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);
        bool commit(uint32_t flagssss);
        void add(uint32_t id, uint32_t prop, uint64_t val);
        void planeProps(Hyprutils::Memory::CSharedPointer<SDRMPlane> plane, Hyprutils::Memory::CSharedPointer<CDRMFB> fb, uint32_t crtc, Hyprutils::Math::Vector2D pos);

        void rollback(SDRMConnectorCommitData& data);
        void apply(SDRMConnectorCommitData& data);

        bool failed = false;

      private:
        void                                             destroyBlob(uint32_t id);
        void                                             commitBlob(uint32_t* current, uint32_t next);
        void                                             rollbackBlob(uint32_t* current, uint32_t next);

        Hyprutils::Memory::CWeakPointer<CDRMBackend>     backend;
        drmModeAtomicReq*                                req = nullptr;
        Hyprutils::Memory::CSharedPointer<SDRMConnector> conn;
    };
};
07070100000020000081A400000000000000000000000166C38D3F00000376000000000000000000000000000000000000003B00000000aquamarine-0.3.3/include/aquamarine/backend/drm/Legacy.hpp#pragma once

#include "../DRM.hpp"

namespace Aquamarine {
    class CDRMLegacyImpl : public IDRMImplementation {
      public:
        CDRMLegacyImpl(Hyprutils::Memory::CSharedPointer<CDRMBackend> backend_);
        virtual bool commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);
        virtual bool reset();
        virtual bool moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, bool skipShedule = false);

      private:
        bool                                         commitInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);
        bool                                         testInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data);

        Hyprutils::Memory::CWeakPointer<CDRMBackend> backend;
    };
};
07070100000021000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000002B00000000aquamarine-0.3.3/include/aquamarine/buffer07070100000022000081A400000000000000000000000166C38D3F00000B51000000000000000000000000000000000000003600000000aquamarine-0.3.3/include/aquamarine/buffer/Buffer.hpp#pragma once

#include <array>
#include <tuple>
#include <hyprutils/signal/Signal.hpp>
#include <hyprutils/math/Region.hpp>
#include "../misc/Attachment.hpp"

namespace Aquamarine {
    enum eBufferCapability : uint32_t {
        BUFFER_CAPABILITY_NONE    = 0,
        BUFFER_CAPABILITY_DATAPTR = (1 << 0),
    };

    enum eBufferType : uint32_t {
        BUFFER_TYPE_DMABUF = 0,
        BUFFER_TYPE_SHM,
        BUFFER_TYPE_MISC,
    };

    class CWLBufferResource;

    struct SDMABUFAttrs {
        bool                      success = false;
        Hyprutils::Math::Vector2D size;
        uint32_t                  format   = 0; // fourcc
        uint64_t                  modifier = 0;

        int                       planes  = 1;
        std::array<uint32_t, 4>   offsets = {0};
        std::array<uint32_t, 4>   strides = {0};
        std::array<int, 4>        fds     = {-1, -1, -1, -1};
    };

    struct SSHMAttrs {
        bool                      success = false;
        int                       fd      = 0;
        uint32_t                  format  = 0;
        Hyprutils::Math::Vector2D size;
        int                       stride = 0;
        int64_t                   offset = 0;
    };

    class IBuffer {
      public:
        virtual ~IBuffer() {
            attachments.clear();
        };

        virtual eBufferCapability                      caps()                                         = 0;
        virtual eBufferType                            type()                                         = 0;
        virtual void                                   update(const Hyprutils::Math::CRegion& damage) = 0;
        virtual bool                                   isSynchronous() = 0; // whether the updates to this buffer are synchronous, aka happen over cpu
        virtual bool                                   good()          = 0;
        virtual SDMABUFAttrs                           dmabuf();
        virtual SSHMAttrs                              shm();
        virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
        virtual void                                   endDataPtr();
        virtual void                                   sendRelease();
        virtual void                                   lock();
        virtual void                                   unlock();
        virtual bool                                   locked();

        Hyprutils::Math::Vector2D                      size;
        bool                                           opaque          = false;
        bool                                           lockedByBackend = false;

        CAttachmentManager                             attachments;

        struct {
            Hyprutils::Signal::CSignal destroy;
            Hyprutils::Signal::CSignal backendRelease;
        } events;

      private:
        int locks = 0;
    };

};
07070100000023000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000002A00000000aquamarine-0.3.3/include/aquamarine/input07070100000024000081A400000000000000000000000166C38D3F00002FD6000000000000000000000000000000000000003400000000aquamarine-0.3.3/include/aquamarine/input/Input.hpp#pragma once

#include <hyprutils/signal/Signal.hpp>
#include <hyprutils/math/Vector2D.hpp>

struct libinput_device;

namespace Aquamarine {
    class ITabletTool;

    class IKeyboard {
      public:
        virtual ~IKeyboard() {
            events.destroy.emit();
        }
        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName() = 0;
        virtual void               updateLEDs(uint32_t leds);

        struct SKeyEvent {
            uint32_t timeMs  = 0;
            uint32_t key     = 0;
            bool     pressed = false;
        };

        struct SModifiersEvent {
            uint32_t depressed = 0, latched = 0, locked = 0, group = 0;
        };

        struct {
            Hyprutils::Signal::CSignal destroy;
            Hyprutils::Signal::CSignal key;
            Hyprutils::Signal::CSignal modifiers;
        } events;
    };

    class IPointer {
      public:
        virtual ~IPointer() {
            events.destroy.emit();
        }

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName() = 0;

        enum ePointerAxis : uint32_t {
            AQ_POINTER_AXIS_VERTICAL = 0,
            AQ_POINTER_AXIS_HORIZONTAL,
        };

        enum ePointerAxisSource : uint32_t {
            AQ_POINTER_AXIS_SOURCE_WHEEL = 0,
            AQ_POINTER_AXIS_SOURCE_FINGER,
            AQ_POINTER_AXIS_SOURCE_CONTINUOUS,
            AQ_POINTER_AXIS_SOURCE_TILT,
        };

        enum ePointerAxisRelativeDirection : uint32_t {
            AQ_POINTER_AXIS_RELATIVE_IDENTICAL = 0,
            AQ_POINTER_AXIS_RELATIVE_INVERTED,
        };

        struct SMoveEvent {
            uint32_t                  timeMs = 0;
            Hyprutils::Math::Vector2D delta, unaccel;
        };

        struct SWarpEvent {
            uint32_t                  timeMs = 0;
            Hyprutils::Math::Vector2D absolute;
        };

        struct SButtonEvent {
            uint32_t timeMs  = 0;
            uint32_t button  = 0;
            bool     pressed = false;
        };

        struct SAxisEvent {
            uint32_t                      timeMs    = 0;
            ePointerAxis                  axis      = AQ_POINTER_AXIS_VERTICAL;
            ePointerAxisSource            source    = AQ_POINTER_AXIS_SOURCE_WHEEL;
            ePointerAxisRelativeDirection direction = AQ_POINTER_AXIS_RELATIVE_IDENTICAL;
            double                        delta = 0.0, discrete = 0.0;
        };

        struct SSwipeBeginEvent {
            uint32_t timeMs  = 0;
            uint32_t fingers = 0;
        };

        struct SSwipeUpdateEvent {
            uint32_t                  timeMs  = 0;
            uint32_t                  fingers = 0;
            Hyprutils::Math::Vector2D delta;
        };

        struct SSwipeEndEvent {
            uint32_t timeMs    = 0;
            bool     cancelled = false;
        };

        struct SPinchBeginEvent {
            uint32_t timeMs  = 0;
            uint32_t fingers = 0;
        };

        struct SPinchUpdateEvent {
            uint32_t                  timeMs  = 0;
            uint32_t                  fingers = 0;
            Hyprutils::Math::Vector2D delta;
            double                    scale = 1.0, rotation = 0.0;
        };

        struct SPinchEndEvent {
            uint32_t timeMs    = 0;
            bool     cancelled = false;
        };

        struct SHoldBeginEvent {
            uint32_t timeMs  = 0;
            uint32_t fingers = 0;
        };

        struct SHoldEndEvent {
            uint32_t timeMs    = 0;
            bool     cancelled = false;
        };

        struct {
            Hyprutils::Signal::CSignal destroy;
            Hyprutils::Signal::CSignal move;
            Hyprutils::Signal::CSignal warp;
            Hyprutils::Signal::CSignal button;
            Hyprutils::Signal::CSignal axis;
            Hyprutils::Signal::CSignal frame;

            Hyprutils::Signal::CSignal swipeBegin;
            Hyprutils::Signal::CSignal swipeUpdate;
            Hyprutils::Signal::CSignal swipeEnd;

            Hyprutils::Signal::CSignal pinchBegin;
            Hyprutils::Signal::CSignal pinchUpdate;
            Hyprutils::Signal::CSignal pinchEnd;

            Hyprutils::Signal::CSignal holdBegin;
            Hyprutils::Signal::CSignal holdEnd;
        } events;
    };

    class ITouch {
      public:
        virtual ~ITouch() {
            events.destroy.emit();
        }

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName() = 0;

        Hyprutils::Math::Vector2D  physicalSize; // in mm, 0,0 if unknown

        struct SDownEvent {
            uint32_t                  timeMs  = 0;
            int32_t                   touchID = 0;
            Hyprutils::Math::Vector2D pos;
        };

        struct SUpEvent {
            uint32_t timeMs  = 0;
            int32_t  touchID = 0;
        };

        struct SMotionEvent {
            uint32_t                  timeMs  = 0;
            int32_t                   touchID = 0;
            Hyprutils::Math::Vector2D pos;
        };

        struct SCancelEvent {
            uint32_t timeMs  = 0;
            int32_t  touchID = 0;
        };

        struct {
            Hyprutils::Signal::CSignal destroy;
            Hyprutils::Signal::CSignal move;
            Hyprutils::Signal::CSignal down;
            Hyprutils::Signal::CSignal up;
            Hyprutils::Signal::CSignal cancel;
            Hyprutils::Signal::CSignal frame;
        } events;
    };

    class ISwitch {
      public:
        virtual ~ISwitch() {
            events.destroy.emit();
        }

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName() = 0;

        enum eSwitchType : uint32_t {
            AQ_SWITCH_TYPE_UNKNOWN = 0,
            AQ_SWITCH_TYPE_LID,
            AQ_SWITCH_TYPE_TABLET_MODE,
        };

        struct SFireEvent {
            uint32_t    timeMs = 0;
            eSwitchType type   = AQ_SWITCH_TYPE_UNKNOWN;
            bool        enable = false;
        };

        struct {
            Hyprutils::Signal::CSignal destroy;
            Hyprutils::Signal::CSignal fire;
        } events;
    };

    enum eTabletToolAxes : uint32_t {
        AQ_TABLET_TOOL_AXIS_X        = (1 << 0),
        AQ_TABLET_TOOL_AXIS_Y        = (1 << 1),
        AQ_TABLET_TOOL_AXIS_DISTANCE = (1 << 2),
        AQ_TABLET_TOOL_AXIS_PRESSURE = (1 << 3),
        AQ_TABLET_TOOL_AXIS_TILT_X   = (1 << 4),
        AQ_TABLET_TOOL_AXIS_TILT_Y   = (1 << 5),
        AQ_TABLET_TOOL_AXIS_ROTATION = (1 << 6),
        AQ_TABLET_TOOL_AXIS_SLIDER   = (1 << 7),
        AQ_TABLET_TOOL_AXIS_WHEEL    = (1 << 8),
    };

    class ITablet {
      public:
        virtual ~ITablet() {
            events.destroy.emit();
        }

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName() = 0;

        uint16_t                   usbVendorID = 0, usbProductID = 0;
        Hyprutils::Math::Vector2D  physicalSize; // mm
        std::vector<std::string>   paths;

        struct SAxisEvent {
            Hyprutils::Memory::CSharedPointer<ITabletTool> tool;

            uint32_t                                       timeMs = 0, updatedAxes = 0;
            Hyprutils::Math::Vector2D                      absolute;
            Hyprutils::Math::Vector2D                      delta;
            Hyprutils::Math::Vector2D                      tilt;
            double                                         pressure = 0.0, distance = 0.0, rotation = 0.0, slider = 0.0, wheelDelta = 0.0;
        };

        struct SProximityEvent {
            Hyprutils::Memory::CSharedPointer<ITabletTool> tool;

            uint32_t                                       timeMs = 0;
            Hyprutils::Math::Vector2D                      absolute;
            bool                                           in = false;
        };

        struct STipEvent {
            Hyprutils::Memory::CSharedPointer<ITabletTool> tool;

            uint32_t                                       timeMs = 0;
            Hyprutils::Math::Vector2D                      absolute;
            bool                                           down = false;
        };

        struct SButtonEvent {
            Hyprutils::Memory::CSharedPointer<ITabletTool> tool;

            uint32_t                                       timeMs = 0, button = 0;
            bool                                           down = false;
        };

        struct {
            Hyprutils::Signal::CSignal axis;
            Hyprutils::Signal::CSignal proximity;
            Hyprutils::Signal::CSignal tip;
            Hyprutils::Signal::CSignal button;
            Hyprutils::Signal::CSignal destroy;
        } events;
    };

    class ITabletTool {
      public:
        virtual ~ITabletTool() {
            events.destroy.emit();
        }

        virtual libinput_device*   getLibinputHandle();
        virtual const std::string& getName() = 0;

        enum eTabletToolType : uint32_t {
            AQ_TABLET_TOOL_TYPE_INVALID = 0,
            AQ_TABLET_TOOL_TYPE_PEN,
            AQ_TABLET_TOOL_TYPE_ERASER,
            AQ_TABLET_TOOL_TYPE_BRUSH,
            AQ_TABLET_TOOL_TYPE_PENCIL,
            AQ_TABLET_TOOL_TYPE_AIRBRUSH,
            AQ_TABLET_TOOL_TYPE_MOUSE,
            AQ_TABLET_TOOL_TYPE_LENS,
            AQ_TABLET_TOOL_TYPE_TOTEM,
        };

        eTabletToolType type   = AQ_TABLET_TOOL_TYPE_INVALID;
        uint64_t        serial = 0, id = 0;

        enum eTabletToolCapabilities : uint32_t {
            AQ_TABLET_TOOL_CAPABILITY_TILT     = (1 << 0),
            AQ_TABLET_TOOL_CAPABILITY_PRESSURE = (1 << 1),
            AQ_TABLET_TOOL_CAPABILITY_DISTANCE = (1 << 2),
            AQ_TABLET_TOOL_CAPABILITY_ROTATION = (1 << 3),
            AQ_TABLET_TOOL_CAPABILITY_SLIDER   = (1 << 4),
            AQ_TABLET_TOOL_CAPABILITY_WHEEL    = (1 << 5),
        };

        uint32_t capabilities = 0; // enum eTabletToolCapabilities

        struct {
            Hyprutils::Signal::CSignal destroy;
        } events;
    };

    class ITabletPad {
      public:
        virtual ~ITabletPad() {
            events.destroy.emit();
        }

        struct STabletPadGroup {
            std::vector<uint32_t> buttons, strips, rings;
            uint16_t              modes = 0;
        };

        virtual libinput_device*                                        getLibinputHandle();
        virtual const std::string&                                      getName() = 0;

        uint16_t                                                        buttons = 0, rings = 0, strips = 0;

        std::vector<std::string>                                        paths;
        std::vector<Hyprutils::Memory::CSharedPointer<STabletPadGroup>> groups;

        //

        struct SButtonEvent {
            uint32_t timeMs = 0, button = 0;
            bool     down = false;
            uint16_t mode = 0, group = 0;
        };

        enum eTabletPadRingSource : uint16_t {
            AQ_TABLET_PAD_RING_SOURCE_UNKNOWN = 0,
            AQ_TABLET_PAD_RING_SOURCE_FINGER,
        };

        enum eTabletPadStripSource : uint16_t {
            AQ_TABLET_PAD_STRIP_SOURCE_UNKNOWN = 0,
            AQ_TABLET_PAD_STRIP_SOURCE_FINGER,
        };

        struct SRingEvent {
            uint32_t             timeMs = 0;
            eTabletPadRingSource source = AQ_TABLET_PAD_RING_SOURCE_UNKNOWN;
            uint16_t             ring   = 0;
            double               pos    = 0.0;
            uint16_t             mode   = 0;
        };

        struct SStripEvent {
            uint32_t              timeMs = 0;
            eTabletPadStripSource source = AQ_TABLET_PAD_STRIP_SOURCE_UNKNOWN;
            uint16_t              strip  = 0;
            double                pos    = 0.0;
            uint16_t              mode   = 0;
        };

        struct {
            Hyprutils::Signal::CSignal destroy;
            Hyprutils::Signal::CSignal button;
            Hyprutils::Signal::CSignal ring;
            Hyprutils::Signal::CSignal strip;
            Hyprutils::Signal::CSignal attach;
        } events;
    };
}07070100000025000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000002900000000aquamarine-0.3.3/include/aquamarine/misc07070100000026000081A400000000000000000000000166C38D3F00000467000000000000000000000000000000000000003800000000aquamarine-0.3.3/include/aquamarine/misc/Attachment.hpp#pragma once

#include <vector>
#include <hyprutils/memory/SharedPtr.hpp>

namespace Aquamarine {
    enum eAttachmentType : uint32_t {
        AQ_ATTACHMENT_DRM_BUFFER = 0,
        AQ_ATTACHMENT_DRM_KMS_UNIMPORTABLE,
        AQ_ATTACHMENT_DRM_RENDERER_DATA,
    };

    class IAttachment {
      public:
        virtual ~IAttachment() {
            ;
        }

        virtual eAttachmentType type() = 0;
    };

    class CAttachmentManager {
      public:
        bool                                           has(eAttachmentType type);
        Hyprutils::Memory::CSharedPointer<IAttachment> get(eAttachmentType type);
        void                                           add(Hyprutils::Memory::CSharedPointer<IAttachment> attachment);
        void                                           remove(Hyprutils::Memory::CSharedPointer<IAttachment> attachment);
        void                                           removeByType(eAttachmentType type);
        void                                           clear();

      private:
        std::vector<Hyprutils::Memory::CSharedPointer<IAttachment>> attachments;
    };
};
07070100000027000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000002B00000000aquamarine-0.3.3/include/aquamarine/output07070100000028000081A400000000000000000000000166C38D3F000020B8000000000000000000000000000000000000003600000000aquamarine-0.3.3/include/aquamarine/output/Output.hpp#pragma once

#include <vector>
#include <optional>
#include <hyprutils/signal/Signal.hpp>
#include <hyprutils/memory/SharedPtr.hpp>
#include <hyprutils/math/Region.hpp>
#include <drm_fourcc.h>
#include <xf86drmMode.h>
#include "../allocator/Swapchain.hpp"
#include "../buffer/Buffer.hpp"
#include "../backend/Misc.hpp"

namespace Aquamarine {

    class IBackendImplementation;

    struct SOutputMode {
        Hyprutils::Math::Vector2D      pixelSize;
        unsigned int                   refreshRate = 0 /* in mHz */;
        bool                           preferred   = false;
        std::optional<drmModeModeInfo> modeInfo; // if this is a drm mode, this will be populated.
    };

    enum eOutputPresentationMode : uint32_t {
        AQ_OUTPUT_PRESENTATION_VSYNC = 0,
        AQ_OUTPUT_PRESENTATION_IMMEDIATE, // likely tearing
    };

    enum eSubpixelMode : uint32_t {
        AQ_SUBPIXEL_UNKNOWN = 0,
        AQ_SUBPIXEL_NONE,
        AQ_SUBPIXEL_HORIZONTAL_RGB,
        AQ_SUBPIXEL_HORIZONTAL_BGR,
        AQ_SUBPIXEL_VERTICAL_RGB,
        AQ_SUBPIXEL_VERTICAL_BGR,
    };

    class IOutput;

    class COutputState {
      public:
        enum eOutputStateProperties : uint32_t {
            AQ_OUTPUT_STATE_DAMAGE             = (1 << 0),
            AQ_OUTPUT_STATE_ENABLED            = (1 << 1),
            AQ_OUTPUT_STATE_ADAPTIVE_SYNC      = (1 << 2),
            AQ_OUTPUT_STATE_PRESENTATION_MODE  = (1 << 3),
            AQ_OUTPUT_STATE_GAMMA_LUT          = (1 << 4),
            AQ_OUTPUT_STATE_MODE               = (1 << 5),
            AQ_OUTPUT_STATE_FORMAT             = (1 << 6),
            AQ_OUTPUT_STATE_BUFFER             = (1 << 7),
            AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE  = (1 << 8),
            AQ_OUTPUT_STATE_EXPLICIT_OUT_FENCE = (1 << 9),
        };

        struct SInternalState {
            uint32_t                                       committed = 0; // enum eOutputStateProperties

            Hyprutils::Math::CRegion                       damage;
            bool                                           enabled          = false;
            bool                                           adaptiveSync     = false;
            eOutputPresentationMode                        presentationMode = AQ_OUTPUT_PRESENTATION_VSYNC;
            std::vector<uint16_t>                          gammaLut; // Gamma lut in the format [r,g,b]+
            Hyprutils::Math::Vector2D                      lastModeSize;
            Hyprutils::Memory::CWeakPointer<SOutputMode>   mode;
            Hyprutils::Memory::CSharedPointer<SOutputMode> customMode;
            uint32_t                                       drmFormat = DRM_FORMAT_INVALID;
            Hyprutils::Memory::CSharedPointer<IBuffer>     buffer;
            int64_t                                        explicitInFence = -1, explicitOutFence = -1;
        };

        const SInternalState& state();

        void                  addDamage(const Hyprutils::Math::CRegion& region);
        void                  clearDamage();
        void                  setEnabled(bool enabled);
        void                  setAdaptiveSync(bool enabled);
        void                  setPresentationMode(eOutputPresentationMode mode);
        void                  setGammaLut(const std::vector<uint16_t>& lut);
        void                  setMode(Hyprutils::Memory::CSharedPointer<SOutputMode> mode);
        void                  setCustomMode(Hyprutils::Memory::CSharedPointer<SOutputMode> mode);
        void                  setFormat(uint32_t drmFormat);
        void                  setBuffer(Hyprutils::Memory::CSharedPointer<IBuffer> buffer);
        void                  setExplicitInFence(int64_t fenceFD);  // -1 removes
        void                  setExplicitOutFence(int64_t fenceFD); // -1 removes
        void                  resetExplicitFences();

      private:
        SInternalState internalState;

        void           onCommit(); // clears a few props like damage and committed.

        friend class IOutput;
        friend class CWaylandOutput;
        friend class CDRMOutput;
        friend class CHeadlessOutput;
    };

    class IOutput {
      public:
        virtual ~IOutput() {
            ;
        }

        enum scheduleFrameReason : uint32_t {
            AQ_SCHEDULE_UNKNOWN = 0,
            AQ_SCHEDULE_NEW_CONNECTOR,
            AQ_SCHEDULE_CURSOR_VISIBLE,
            AQ_SCHEDULE_CURSOR_SHAPE,
            AQ_SCHEDULE_CURSOR_MOVE,
            AQ_SCHEDULE_CLIENT_UNKNOWN,
            AQ_SCHEDULE_DAMAGE,
            AQ_SCHEDULE_NEW_MONITOR,
            AQ_SCHEDULE_RENDER_MONITOR,
            AQ_SCHEDULE_NEEDS_FRAME,
            AQ_SCHEDULE_ANIMATION,
            AQ_SCHEDULE_ANIMATION_DAMAGE,
        };

        virtual bool                                                      commit()           = 0;
        virtual bool                                                      test()             = 0;
        virtual Hyprutils::Memory::CSharedPointer<IBackendImplementation> getBackend()       = 0;
        virtual std::vector<SDRMFormat>                                   getRenderFormats() = 0;
        virtual Hyprutils::Memory::CSharedPointer<SOutputMode>            preferredMode();
        virtual bool                                                      setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot);
        virtual void                                                      moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipShedule = false); // includes the hotspot
        virtual void                                                      setCursorVisible(bool visible); // moving the cursor will make it visible again without this util
        virtual Hyprutils::Math::Vector2D                                 cursorPlaneSize();              // -1, -1 means no set size, 0, 0 means error
        virtual void                                                      scheduleFrame(const scheduleFrameReason reason = AQ_SCHEDULE_UNKNOWN);
        virtual size_t                                                    getGammaSize();
        virtual bool                                                      destroy(); // not all backends allow this!!!

        std::string                                                       name, description, make, model, serial;
        Hyprutils::Math::Vector2D                                         physicalSize;
        bool                                                              enabled    = false;
        bool                                                              nonDesktop = false;
        eSubpixelMode                                                     subpixel   = AQ_SUBPIXEL_NONE;
        bool                                                              vrrCapable = false, vrrActive = false;
        bool                                                              needsFrame       = false;
        bool                                                              supportsExplicit = false;

        //
        std::vector<Hyprutils::Memory::CSharedPointer<SOutputMode>> modes;
        Hyprutils::Memory::CSharedPointer<COutputState>             state = Hyprutils::Memory::makeShared<COutputState>();

        Hyprutils::Memory::CSharedPointer<CSwapchain>               swapchain;

        //

        enum eOutputPresentFlags : uint32_t {
            AQ_OUTPUT_PRESENT_VSYNC         = (1 << 0),
            AQ_OUTPUT_PRESENT_HW_CLOCK      = (1 << 1),
            AQ_OUTPUT_PRESENT_HW_COMPLETION = (1 << 2),
            AQ_OUTPUT_PRESENT_ZEROCOPY      = (1 << 3),
        };
        struct SStateEvent {
            Hyprutils::Math::Vector2D size; // if {0,0}, means it needs a reconfigure.
        };

        struct SPresentEvent {
            bool         presented = true;
            timespec*    when      = nullptr;
            unsigned int seq       = 0;
            int          refresh   = 0;
            uint32_t     flags     = 0;
        };

        struct {
            Hyprutils::Signal::CSignal destroy;
            Hyprutils::Signal::CSignal frame;
            Hyprutils::Signal::CSignal needsFrame;
            Hyprutils::Signal::CSignal present;
            Hyprutils::Signal::CSignal commit;
            Hyprutils::Signal::CSignal state;
        } events;
    };
}
07070100000029000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001500000000aquamarine-0.3.3/nix0707010000002A000081A400000000000000000000000166C38D3F000003A4000000000000000000000000000000000000002100000000aquamarine-0.3.3/nix/default.nix{
  lib,
  stdenv,
  cmake,
  hwdata,
  hyprutils,
  hyprwayland-scanner,
  libdisplay-info,
  libdrm,
  libffi,
  libGL,
  libinput,
  libseat,
  mesa,
  pixman,
  pkg-config,
  udev,
  wayland,
  wayland-protocols,
  version ? "git",
  doCheck ? false,
}:
stdenv.mkDerivation {
  pname = "aquamarine";
  inherit version doCheck;
  src = ../.;

  strictDeps = true;

  nativeBuildInputs = [
    cmake
    hyprwayland-scanner
    pkg-config
  ];

  buildInputs = [
    hwdata
    hyprutils
    libdisplay-info
    libdrm
    libffi
    libGL
    libinput
    libseat
    mesa
    pixman
    udev
    wayland
    wayland-protocols
  ];

  outputs = ["out" "dev"];

  cmakeBuildType = "RelWithDebInfo";

  dontStrip = true;

  meta = {
    homepage = "https://github.com/hyprwm/aquamarine";
    description = "A very light linux rendering backend library";
    license = lib.licenses.bsd3;
    platforms = lib.platforms.linux;
  };
}
0707010000002B000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001B00000000aquamarine-0.3.3/protocols0707010000002C000081A400000000000000000000000166C38D3F00000000000000000000000000000000000000000000002400000000aquamarine-0.3.3/protocols/.gitkeep0707010000002D000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001500000000aquamarine-0.3.3/src0707010000002E000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001F00000000aquamarine-0.3.3/src/allocator0707010000002F000081A400000000000000000000000166C38D3F00002FCF000000000000000000000000000000000000002700000000aquamarine-0.3.3/src/allocator/GBM.cpp#include <aquamarine/allocator/GBM.hpp>
#include <aquamarine/backend/Backend.hpp>
#include <aquamarine/backend/DRM.hpp>
#include <aquamarine/allocator/Swapchain.hpp>
#include "FormatUtils.hpp"
#include "Shared.hpp"
#include <xf86drm.h>
#include <gbm.h>
#include <unistd.h>
#include "../backend/drm/Renderer.hpp"

using namespace Aquamarine;
using namespace Hyprutils::Memory;
#define SP CSharedPointer

static SDRMFormat guessFormatFrom(std::vector<SDRMFormat> formats, bool cursor) {
    if (formats.empty())
        return SDRMFormat{};

    if (!cursor) {
        /*
            Try to find 10bpp formats first, as they offer better color precision.
            For cursors, don't, as these almost never support that.
        */
        if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_ARGB2101010; }); it != formats.end())
            return *it;

        if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_XRGB2101010; }); it != formats.end())
            return *it;
    }

    if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_ARGB8888; }); it != formats.end())
        return *it;

    if (auto it = std::find_if(formats.begin(), formats.end(), [](const auto& f) { return f.drmFormat == DRM_FORMAT_XRGB8888; }); it != formats.end())
        return *it;

    for (auto& f : formats) {
        auto name = fourccToName(f.drmFormat);

        /* 10 bpp RGB */
        if (name.contains("30"))
            return f;
    }

    for (auto& f : formats) {
        auto name = fourccToName(f.drmFormat);

        /* 8 bpp RGB */
        if (name.contains("24"))
            return f;
    }

    return formats.at(0);
}

Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hyprutils::Memory::CWeakPointer<CGBMAllocator> allocator_,
                                   Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain) : allocator(allocator_) {
    if (!allocator)
        return;

    attrs.size   = params.size;
    attrs.format = params.format;
    size         = attrs.size;

    const bool CURSOR   = params.cursor && params.scanout;
    const bool MULTIGPU = params.multigpu && params.scanout;

    TRACE(allocator->backend->log(AQ_LOG_TRACE,
                                  std::format("GBM: Allocating a buffer: size {}, format {}, cursor: {}, multigpu: {}, scanout: {}", attrs.size, fourccToName(attrs.format), CURSOR,
                                              MULTIGPU, params.scanout)));

    const auto            FORMATS    = CURSOR ? swapchain->backendImpl->getCursorFormats() : swapchain->backendImpl->getRenderFormats();
    const auto            RENDERABLE = swapchain->backendImpl->getRenderableFormats();

    std::vector<uint64_t> explicitModifiers;

    if (attrs.format == DRM_FORMAT_INVALID) {
        attrs.format = guessFormatFrom(FORMATS, CURSOR).drmFormat;
        if (attrs.format != DRM_FORMAT_INVALID)
            allocator->backend->log(AQ_LOG_DEBUG, std::format("GBM: Automatically selected format {} for new GBM buffer", fourccToName(attrs.format)));
    }

    if (attrs.format == DRM_FORMAT_INVALID) {
        allocator->backend->log(AQ_LOG_ERROR, "GBM: Failed to allocate a GBM buffer: no format found");
        return;
    }

    // check if we can use modifiers. If the requested support has any explicit modifier
    // supported by the primary backend, we can.
    for (auto& f : FORMATS) {
        if (f.drmFormat != attrs.format)
            continue;

        for (auto& m : f.modifiers) {
            if (m == DRM_FORMAT_MOD_INVALID)
                continue;

            if (!RENDERABLE.empty() && params.scanout && !CURSOR && !MULTIGPU) {
                // regular scanout plane, check if the format is renderable
                auto rformat = std::find_if(RENDERABLE.begin(), RENDERABLE.end(), [f](const auto& e) { return e.drmFormat == f.drmFormat; });

                if (rformat == RENDERABLE.end()) {
                    TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Dropping format {} as it's not renderable", fourccToName(f.drmFormat))));
                    break;
                }

                if (std::find(rformat->modifiers.begin(), rformat->modifiers.end(), m) == rformat->modifiers.end()) {
                    TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Dropping modifier 0x{:x} as it's not renderable", m)));
                    continue;
                }
            }

            explicitModifiers.push_back(m);
        }
    }

    // FIXME: Nvidia cannot render to linear buffers. What do?
    if (MULTIGPU) {
        allocator->backend->log(AQ_LOG_DEBUG, "GBM: Buffer is marked as multigpu, forcing linear");
        explicitModifiers = {DRM_FORMAT_MOD_LINEAR};
    }

    if (explicitModifiers.empty()) {
        // fall back to using a linear buffer.
        explicitModifiers.push_back(DRM_FORMAT_MOD_LINEAR);
    }

    uint32_t flags = GBM_BO_USE_RENDERING;
    if (params.scanout)
        flags |= GBM_BO_USE_SCANOUT;

    if (explicitModifiers.empty()) {
        allocator->backend->log(AQ_LOG_WARNING, "GBM: Using modifier-less allocation");
        bo = gbm_bo_create(allocator->gbmDevice, attrs.size.x, attrs.size.y, attrs.format, flags);
    } else {
        TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: Using modifier-based allocation, modifiers: {}", explicitModifiers.size())));
        for (auto& mod : explicitModifiers) {
            TRACE(allocator->backend->log(AQ_LOG_TRACE, std::format("GBM: | mod 0x{:x}", mod)));
        }
        bo = gbm_bo_create_with_modifiers2(allocator->gbmDevice, attrs.size.x, attrs.size.y, attrs.format, explicitModifiers.data(), explicitModifiers.size(), flags);

        if (!bo && CURSOR) {
            // allow non-renderable cursor buffer for nvidia
            allocator->backend->log(AQ_LOG_ERROR, "GBM: Allocating with modifiers and flags failed, falling back to modifiers without flags");
            bo = gbm_bo_create_with_modifiers(allocator->gbmDevice, attrs.size.x, attrs.size.y, attrs.format, explicitModifiers.data(), explicitModifiers.size());
        }

        if (!bo) {
            if (explicitModifiers.size() == 1 && explicitModifiers[0] == DRM_FORMAT_MOD_LINEAR) {
                flags |= GBM_BO_USE_LINEAR;
                allocator->backend->log(AQ_LOG_ERROR, "GBM: Allocating with modifiers failed, falling back to modifier-less allocation");
            } else
                allocator->backend->log(AQ_LOG_ERROR, "GBM: Allocating with modifiers failed, falling back to implicit");
            bo = gbm_bo_create(allocator->gbmDevice, attrs.size.x, attrs.size.y, attrs.format, flags);
        }
    }

    if (!bo) {
        allocator->backend->log(AQ_LOG_ERROR, "GBM: Failed to allocate a GBM buffer: bo null");
        return;
    }

    attrs.planes   = gbm_bo_get_plane_count(bo);
    attrs.modifier = (flags & GBM_BO_USE_LINEAR) ? DRM_FORMAT_MOD_LINEAR : gbm_bo_get_modifier(bo);

    for (size_t i = 0; i < (size_t)attrs.planes; ++i) {
        attrs.strides.at(i) = gbm_bo_get_stride_for_plane(bo, i);
        attrs.offsets.at(i) = gbm_bo_get_offset(bo, i);
        attrs.fds.at(i)     = gbm_bo_get_fd_for_plane(bo, i);

        if (attrs.fds.at(i) < 0) {
            allocator->backend->log(AQ_LOG_ERROR, std::format("GBM: Failed to query fd for plane {}", i));
            for (size_t j = 0; j < i; ++j) {
                close(attrs.fds.at(j));
            }
            attrs.planes = 0;
            return;
        }
    }

    attrs.success = true;

    auto modName = drmGetFormatModifierName(attrs.modifier);

    allocator->backend->log(AQ_LOG_DEBUG,
                            std::format("GBM: Allocated a new buffer with size {} and format {} with modifier {} aka {}", attrs.size, fourccToName(attrs.format), attrs.modifier,
                                        modName ? modName : "Unknown"));

    free(modName);

    if (params.scanout && swapchain->backendImpl->type() == AQ_BACKEND_DRM) {
        // clear the buffer using the DRM renderer to avoid uninitialized mem
        auto impl = (CDRMBackend*)swapchain->backendImpl.get();
        if (impl->rendererState.renderer)
            impl->rendererState.renderer->clearBuffer(this);
    }
}

Aquamarine::CGBMBuffer::~CGBMBuffer() {
    events.destroy.emit();
    if (bo) {
        if (gboMapping)
            gbm_bo_unmap(bo, gboMapping); // FIXME: is it needed before destroy?
        gbm_bo_destroy(bo);
    }
    for (size_t i = 0; i < (size_t)attrs.planes; i++)
        close(attrs.fds.at(i));
}

eBufferCapability Aquamarine::CGBMBuffer::caps() {
    return (Aquamarine::eBufferCapability)0;
}

eBufferType Aquamarine::CGBMBuffer::type() {
    return Aquamarine::eBufferType::BUFFER_TYPE_DMABUF;
}

void Aquamarine::CGBMBuffer::update(const Hyprutils::Math::CRegion& damage) {
    ;
}

bool Aquamarine::CGBMBuffer::isSynchronous() {
    return false;
}

bool Aquamarine::CGBMBuffer::good() {
    return true;
}

SDMABUFAttrs Aquamarine::CGBMBuffer::dmabuf() {
    return attrs;
}

std::tuple<uint8_t*, uint32_t, size_t> Aquamarine::CGBMBuffer::beginDataPtr(uint32_t flags) {
    uint32_t stride = 0;
    if (boBuffer)
        allocator->backend->log(AQ_LOG_ERROR, "beginDataPtr is called a second time without calling endDataPtr first. Returning old mapping");
    else
        boBuffer = gbm_bo_map(bo, 0, 0, attrs.size.x, attrs.size.y, flags, &stride, &gboMapping);

    return {(uint8_t*)boBuffer, attrs.format, stride * attrs.size.y};
}

void Aquamarine::CGBMBuffer::endDataPtr() {
    if (gboMapping) {
        gbm_bo_unmap(bo, gboMapping);
        gboMapping = nullptr;
        boBuffer   = nullptr;
    }
}

CGBMAllocator::~CGBMAllocator() {
    if (gbmDevice)
        gbm_device_destroy(gbmDevice);
}

SP<CGBMAllocator> Aquamarine::CGBMAllocator::create(int drmfd_, Hyprutils::Memory::CWeakPointer<CBackend> backend_) {
    uint64_t capabilities = 0;
    if (drmGetCap(drmfd_, DRM_CAP_PRIME, &capabilities) || !(capabilities & DRM_PRIME_CAP_EXPORT)) {
        backend_->log(AQ_LOG_ERROR, "Cannot create a GBM Allocator: PRIME export is not supported by the gpu.");
        return nullptr;
    }

    auto allocator = SP<CGBMAllocator>(new CGBMAllocator(drmfd_, backend_));

    if (!allocator->gbmDevice) {
        backend_->log(AQ_LOG_ERROR, "Cannot create a GBM Allocator: gbm failed to create a device.");
        return nullptr;
    }

    backend_->log(AQ_LOG_DEBUG, std::format("Created a GBM allocator with drm fd {}", drmfd_));

    allocator->self = allocator;

    return allocator;
}

Aquamarine::CGBMAllocator::CGBMAllocator(int fd_, Hyprutils::Memory::CWeakPointer<CBackend> backend_) : fd(fd_), backend(backend_) {
    gbmDevice = gbm_create_device(fd_);
    if (!gbmDevice) {
        backend->log(AQ_LOG_ERROR, std::format("Couldn't open a GBM device at fd {}", fd_));
        return;
    }

    gbmDeviceBackendName = gbm_device_get_backend_name(gbmDevice);
    auto drmName_        = drmGetDeviceNameFromFd2(fd_);
    drmName              = drmName_;
    free(drmName_);
}

SP<IBuffer> Aquamarine::CGBMAllocator::acquire(const SAllocatorBufferParams& params, Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain_) {
    if (params.size.x < 1 || params.size.y < 1) {
        backend->log(AQ_LOG_ERROR, std::format("Couldn't allocate a gbm buffer with invalid size {}", params.size));
        return nullptr;
    }

    auto newBuffer = SP<CGBMBuffer>(new CGBMBuffer(params, self, swapchain_));

    if (!newBuffer->good()) {
        backend->log(AQ_LOG_ERROR, std::format("Couldn't allocate a gbm buffer with size {} and format {}", params.size, fourccToName(params.format)));
        return nullptr;
    }

    buffers.emplace_back(newBuffer);
    std::erase_if(buffers, [](const auto& b) { return b.expired(); });
    return newBuffer;
}

Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CGBMAllocator::getBackend() {
    return backend.lock();
}

int Aquamarine::CGBMAllocator::drmFD() {
    return fd;
}

eAllocatorType Aquamarine::CGBMAllocator::type() {
    return AQ_ALLOCATOR_TYPE_GBM;
}
07070100000030000081A400000000000000000000000166C38D3F00001043000000000000000000000000000000000000002D00000000aquamarine-0.3.3/src/allocator/Swapchain.cpp#include <aquamarine/allocator/Swapchain.hpp>
#include <aquamarine/backend/Backend.hpp>
#include "FormatUtils.hpp"

using namespace Aquamarine;
using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#define SP CSharedPointer

SP<CSwapchain> Aquamarine::CSwapchain::create(SP<IAllocator> allocator_, SP<IBackendImplementation> backendImpl_) {
    auto p  = SP<CSwapchain>(new CSwapchain(allocator_, backendImpl_));
    p->self = p;
    return p;
}

Aquamarine::CSwapchain::CSwapchain(SP<IAllocator> allocator_, SP<IBackendImplementation> backendImpl_) : allocator(allocator_), backendImpl(backendImpl_) {
    if (!allocator || !backendImpl)
        return;
}

bool Aquamarine::CSwapchain::reconfigure(const SSwapchainOptions& options_) {
    if (!allocator)
        return false;

    if (options_.size == Vector2D{} || options_.length == 0) {
        // clear the swapchain
        allocator->getBackend()->log(AQ_LOG_DEBUG, "Swapchain: Clearing");
        buffers.clear();
        options = options_;
        return true;
    }

    if ((options_.format == options.format || options_.format == DRM_FORMAT_INVALID) && options_.size == options.size && options_.length == options.length)
        return true; // no need to reconfigure

    if ((options_.format == options.format || options_.format == DRM_FORMAT_INVALID) && options_.size == options.size) {
        bool ok = resize(options_.length);
        if (!ok)
            return false;

        options = options_;

        allocator->getBackend()->log(AQ_LOG_DEBUG, std::format("Swapchain: Resized a {} {} swapchain to length {}", options.size, fourccToName(options.format), options.length));
        return true;
    }

    bool ok = fullReconfigure(options_);
    if (!ok)
        return false;

    options = options_;
    if (options.format == DRM_FORMAT_INVALID)
        options.format = buffers.at(0)->dmabuf().format;

    allocator->getBackend()->log(AQ_LOG_DEBUG,
                                 std::format("Swapchain: Reconfigured a swapchain to {} {} of length {}", options.size, fourccToName(options.format), options.length));
    return true;
}

SP<IBuffer> Aquamarine::CSwapchain::next(int* age) {
    if (!allocator || options.length <= 0)
        return nullptr;

    lastAcquired = (lastAcquired + 1) % options.length;

    if (age)
        *age = 1;

    return buffers.at(lastAcquired);
}

bool Aquamarine::CSwapchain::fullReconfigure(const SSwapchainOptions& options_) {
    buffers.clear();
    for (size_t i = 0; i < options_.length; ++i) {
        auto buf = allocator->acquire(
            SAllocatorBufferParams{.size = options_.size, .format = options_.format, .scanout = options_.scanout, .cursor = options_.cursor, .multigpu = options_.multigpu},
            self.lock());
        if (!buf) {
            allocator->getBackend()->log(AQ_LOG_ERROR, "Swapchain: Failed acquiring a buffer");
            return false;
        }
        buffers.emplace_back(buf);
    }

    return true;
}

bool Aquamarine::CSwapchain::resize(size_t newSize) {
    if (newSize == buffers.size())
        return true;

    if (newSize < buffers.size()) {
        while (buffers.size() > newSize) {
            buffers.pop_back();
        }
    } else {
        while (buffers.size() < newSize) {
            auto buf =
                allocator->acquire(SAllocatorBufferParams{.size = options.size, .format = options.format, .scanout = options.scanout, .cursor = options.cursor}, self.lock());
            if (!buf) {
                allocator->getBackend()->log(AQ_LOG_ERROR, "Swapchain: Failed acquiring a buffer");
                return false;
            }
            buffers.emplace_back(buf);
        }
    }

    return true;
}

bool Aquamarine::CSwapchain::contains(SP<IBuffer> buffer) {
    return std::find(buffers.begin(), buffers.end(), buffer) != buffers.end();
}

const SSwapchainOptions& Aquamarine::CSwapchain::currentOptions() {
    return options;
}

void Aquamarine::CSwapchain::rollback() {
    lastAcquired--;
    if (lastAcquired < 0)
        lastAcquired = options.length - 1;
}

SP<IAllocator> Aquamarine::CSwapchain::getAllocator() {
    return allocator;
}
07070100000031000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001D00000000aquamarine-0.3.3/src/backend07070100000032000081A400000000000000000000000166C38D3F000028A9000000000000000000000000000000000000002900000000aquamarine-0.3.3/src/backend/Backend.cpp#include <aquamarine/backend/Backend.hpp>
#include <aquamarine/backend/Wayland.hpp>
#include <aquamarine/backend/Headless.hpp>
#include <aquamarine/backend/DRM.hpp>
#include <aquamarine/allocator/GBM.hpp>
#include <sys/poll.h>
#include <thread>
#include <chrono>
#include <sys/timerfd.h>
#include <time.h>
#include <string.h>
#include <xf86drm.h>
#include <fcntl.h>
#include <unistd.h>

using namespace Hyprutils::Memory;
using namespace Aquamarine;
#define SP CSharedPointer

#define TIMESPEC_NSEC_PER_SEC 1000000000L

static void timespecAddNs(timespec* pTimespec, int64_t delta) {
    int delta_ns_low = delta % TIMESPEC_NSEC_PER_SEC;
    int delta_s_high = delta / TIMESPEC_NSEC_PER_SEC;

    pTimespec->tv_sec += delta_s_high;

    pTimespec->tv_nsec += (long)delta_ns_low;
    if (pTimespec->tv_nsec >= TIMESPEC_NSEC_PER_SEC) {
        pTimespec->tv_nsec -= TIMESPEC_NSEC_PER_SEC;
        ++pTimespec->tv_sec;
    }
}

static const char* backendTypeToName(eBackendType type) {
    switch (type) {
        case AQ_BACKEND_DRM: return "drm";
        case AQ_BACKEND_HEADLESS: return "headless";
        case AQ_BACKEND_WAYLAND: return "wayland";
        default: break;
    }
    return "invalid";
}

Aquamarine::CBackend::CBackend() {
    ;
}

Aquamarine::SBackendImplementationOptions::SBackendImplementationOptions() {
    backendType        = AQ_BACKEND_WAYLAND;
    backendRequestMode = AQ_BACKEND_REQUEST_IF_AVAILABLE;
}

Aquamarine::SBackendOptions::SBackendOptions() {
    logFunction = nullptr;
}

Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CBackend::create(const std::vector<SBackendImplementationOptions>& backends, const SBackendOptions& options) {
    auto backend = SP<CBackend>(new CBackend());

    backend->options               = options;
    backend->implementationOptions = backends;
    backend->self                  = backend;

    if (backends.size() <= 0)
        return nullptr;

    backend->log(AQ_LOG_DEBUG, "Creating an Aquamarine backend!");

    for (auto& b : backends) {
        if (b.backendType == AQ_BACKEND_WAYLAND) {
            auto ref = SP<CWaylandBackend>(new CWaylandBackend(backend));
            backend->implementations.emplace_back(ref);
            ref->self = ref;
        } else if (b.backendType == AQ_BACKEND_DRM) {
            auto ref = CDRMBackend::attempt(backend);
            if (ref.empty()) {
                backend->log(AQ_LOG_ERROR, "DRM Backend failed");
                continue;
            }

            for (auto& r : ref) {
                backend->implementations.emplace_back(r);
            }
        } else if (b.backendType == AQ_BACKEND_HEADLESS) {
            auto ref = SP<CHeadlessBackend>(new CHeadlessBackend(backend));
            backend->implementations.emplace_back(ref);
            ref->self = ref;
        } else {
            backend->log(AQ_LOG_ERROR, std::format("Unknown backend id: {}", (int)b.backendType));
            continue;
        }
    }

    // create a timerfd for idle events
    backend->idle.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);

    return backend;
}

Aquamarine::CBackend::~CBackend() {
    ;
}

bool Aquamarine::CBackend::start() {
    log(AQ_LOG_DEBUG, "Starting the Aquamarine backend!");

    bool fallback = false;
    int  started  = 0;

    auto optionsForType = [this](eBackendType type) -> SBackendImplementationOptions {
        for (auto& o : implementationOptions) {
            if (o.backendType == type)
                return o;
        }
        return SBackendImplementationOptions{};
    };

    for (size_t i = 0; i < implementations.size(); ++i) {
        const bool ok = implementations.at(i)->start();

        if (!ok) {
            log(AQ_LOG_ERROR, std::format("Requested backend ({}) could not start, enabling fallbacks", backendTypeToName(implementations.at(i)->type())));
            fallback = true;
            if (optionsForType(implementations.at(i)->type()).backendRequestMode == AQ_BACKEND_REQUEST_MANDATORY) {
                log(AQ_LOG_CRITICAL, std::format("Requested backend ({}) could not start and it's mandatory, cannot continue!", backendTypeToName(implementations.at(i)->type())));
                implementations.clear();
                return false;
            }
        } else
            started++;
    }

    if (implementations.empty() || started <= 0) {
        log(AQ_LOG_CRITICAL, "No backend could be opened. Make sure there was a correct backend passed to CBackend, and that your environment supports at least one of them.");
        return false;
    }

    // erase failed impls
    std::erase_if(implementations, [this](const auto& i) {
        bool failed = i->pollFDs().empty();
        if (failed)
            log(AQ_LOG_ERROR, std::format("Implementation {} failed, erasing.", backendTypeToName(i->type())));
        return failed;
    });

    // TODO: obviously change this when (if) we add different allocators.
    for (auto& b : implementations) {
        if (b->drmFD() >= 0) {
            auto fd = reopenDRMNode(b->drmFD());
            if (fd < 0) {
                // this is critical, we cannot create an allocator properly
                log(AQ_LOG_CRITICAL, "Failed to create an allocator (reopenDRMNode failed)");
                return false;
            }
            primaryAllocator = CGBMAllocator::create(fd, self);
            break;
        }
    }

    if (!primaryAllocator)
        return false;

    ready = true;
    for (auto& b : implementations) {
        b->onReady();
    }

    sessionFDs = session ? session->pollFDs() : std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>>{};

    return true;
}

void Aquamarine::CBackend::log(eBackendLogLevel level, const std::string& msg) {
    if (!options.logFunction)
        return;

    options.logFunction(level, msg);
}

std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CBackend::getPollFDs() {
    std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> result;
    for (auto& i : implementations) {
        auto pollfds = i->pollFDs();
        for (auto& p : pollfds) {
            log(AQ_LOG_DEBUG, std::format("backend: poll fd {} for implementation {}", p->fd, backendTypeToName(i->type())));
            result.emplace_back(p);
        }
    }

    for (auto& sfd : sessionFDs) {
        log(AQ_LOG_DEBUG, std::format("backend: poll fd {} for session", sfd->fd));
        result.emplace_back(sfd);
    }

    log(AQ_LOG_DEBUG, std::format("backend: poll fd {} for idle", idle.fd));
    result.emplace_back(makeShared<SPollFD>(idle.fd, [this]() { dispatchIdle(); }));

    return result;
}

int Aquamarine::CBackend::drmFD() {
    for (auto& i : implementations) {
        int fd = i->drmFD();
        if (fd < 0)
            continue;

        return fd;
    }
    return -1;
}

bool Aquamarine::CBackend::hasSession() {
    return session;
}

std::vector<SDRMFormat> Aquamarine::CBackend::getPrimaryRenderFormats() {
    for (auto& b : implementations) {
        if (b->type() != AQ_BACKEND_DRM && b->type() != AQ_BACKEND_WAYLAND)
            continue;

        return b->getRenderFormats();
    }

    for (auto& b : implementations) {
        return b->getRenderFormats();
    }

    return {};
}

const std::vector<SP<IBackendImplementation>>& Aquamarine::CBackend::getImplementations() {
    return implementations;
}

void Aquamarine::CBackend::addIdleEvent(SP<std::function<void(void)>> fn) {
    auto r = idle.pending.emplace_back(fn);

    updateIdleTimer();
}

void Aquamarine::CBackend::updateIdleTimer() {
    uint64_t ADD_NS = idle.pending.empty() ? TIMESPEC_NSEC_PER_SEC * 240ULL /* 240s, 4 mins */ : 0;

    timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    timespecAddNs(&now, ADD_NS);

    itimerspec ts = {.it_value = now};

    if (timerfd_settime(idle.fd, TFD_TIMER_ABSTIME, &ts, nullptr))
        log(AQ_LOG_ERROR, std::format("backend: failed to arm timerfd: {}", strerror(errno)));
}

void Aquamarine::CBackend::removeIdleEvent(SP<std::function<void(void)>> pfn) {
    std::erase(idle.pending, pfn);
}

void Aquamarine::CBackend::dispatchIdle() {
    auto cpy = idle.pending;
    idle.pending.clear();

    for (auto& i : cpy) {
        if (i && *i)
            (*i)();
    }

    updateIdleTimer();
}

// Yoinked from wlroots, render/allocator/allocator.c
// Ref-counting reasons, see https://gitlab.freedesktop.org/mesa/drm/-/merge_requests/110
int Aquamarine::CBackend::reopenDRMNode(int drmFD, bool allowRenderNode) {

    if (drmIsMaster(drmFD)) {
        // Only recent kernels support empty leases
        uint32_t lesseeID = 0;
        int      leaseFD  = drmModeCreateLease(drmFD, nullptr, 0, O_CLOEXEC, &lesseeID);
        if (leaseFD >= 0) {
            return leaseFD;
        } else if (leaseFD != -EINVAL && leaseFD != -EOPNOTSUPP) {
            log(AQ_LOG_ERROR, "reopenDRMNode: drmModeCreateLease failed");
            return -1;
        }
        log(AQ_LOG_DEBUG, "reopenDRMNode: drmModeCreateLease failed, falling back to open");
    }

    char* name = nullptr;
    if (allowRenderNode)
        name = drmGetRenderDeviceNameFromFd(drmFD);

    if (!name) {
        // primary node or no name
        name = drmGetDeviceNameFromFd2(drmFD);

        if (!name) {
            log(AQ_LOG_ERROR, "reopenDRMNode: drmGetDeviceNameFromFd2 failed");
            return -1;
        }
    }

    log(AQ_LOG_DEBUG, std::format("reopenDRMNode: opening node {}", name));

    int newFD = open(name, O_RDWR | O_CLOEXEC);
    if (newFD < 0) {
        log(AQ_LOG_ERROR, std::format("reopenDRMNode: failed to open node {}", name));
        free(name);
        return -1;
    }

    free(name);

    // We need to authenticate if we are using a DRM primary node and are the master
    if (drmIsMaster(drmFD) && drmGetNodeTypeFromFd(newFD) == DRM_NODE_PRIMARY) {
        drm_magic_t magic;
        if (int ret = drmGetMagic(newFD, &magic); ret < 0) {
            log(AQ_LOG_ERROR, std::format("reopenDRMNode: drmGetMagic failed: {}", strerror(-ret)));
            close(newFD);
            return -1;
        }

        if (int ret = drmAuthMagic(drmFD, magic); ret < 0) {
            log(AQ_LOG_ERROR, std::format("reopenDRMNode: drmAuthMagic failed: {}", strerror(-ret)));
            close(newFD);
            return -1;
        }
    }

    return newFD;
}

std::vector<SDRMFormat> Aquamarine::IBackendImplementation::getRenderableFormats() {
    return {};
}
07070100000033000081A400000000000000000000000166C38D3F0000160C000000000000000000000000000000000000002A00000000aquamarine-0.3.3/src/backend/Headless.cpp#include <aquamarine/backend/Headless.hpp>
#include <fcntl.h>
#include <time.h>
#include <sys/timerfd.h>
#include <string.h>
#include "Shared.hpp"

using namespace Aquamarine;
using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#define SP CSharedPointer

#define TIMESPEC_NSEC_PER_SEC 1000000000L

static void timespecAddNs(timespec* pTimespec, int64_t delta) {
    int delta_ns_low = delta % TIMESPEC_NSEC_PER_SEC;
    int delta_s_high = delta / TIMESPEC_NSEC_PER_SEC;

    pTimespec->tv_sec += delta_s_high;

    pTimespec->tv_nsec += (long)delta_ns_low;
    if (pTimespec->tv_nsec >= TIMESPEC_NSEC_PER_SEC) {
        pTimespec->tv_nsec -= TIMESPEC_NSEC_PER_SEC;
        ++pTimespec->tv_sec;
    }
}

Aquamarine::CHeadlessOutput::CHeadlessOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CHeadlessBackend> backend_) : backend(backend_) {
    name = name_;

    framecb = makeShared<std::function<void()>>([this]() {
        frameScheduled = false;
        events.frame.emit();
    });
}

Aquamarine::CHeadlessOutput::~CHeadlessOutput() {
    backend->backend->removeIdleEvent(framecb);
    events.destroy.emit();
}

bool Aquamarine::CHeadlessOutput::commit() {
    events.commit.emit();
    state->onCommit();
    needsFrame = false;
    return true;
}

bool Aquamarine::CHeadlessOutput::test() {
    return true;
}

std::vector<SDRMFormat> Aquamarine::CHeadlessOutput::getRenderFormats() {
    // not sure if this is right but prob doest matter
    return backend->getRenderFormats();
}

Hyprutils::Memory::CSharedPointer<IBackendImplementation> Aquamarine::CHeadlessOutput::getBackend() {
    return backend.lock();
}

void Aquamarine::CHeadlessOutput::scheduleFrame(const scheduleFrameReason reason) {
    TRACE(backend->backend->log(AQ_LOG_TRACE,
                                std::format("CHeadlessOutput::scheduleFrame: reason {}, needsFrame {}, frameScheduled {}", (uint32_t)reason, needsFrame, frameScheduled)));
    // FIXME: limit fps to the committed framerate.
    needsFrame = true;

    if (frameScheduled)
        return;

    frameScheduled = true;
    backend->backend->addIdleEvent(framecb);
}

bool Aquamarine::CHeadlessOutput::destroy() {
    events.destroy.emit();
    std::erase(backend->outputs, self.lock());
    return true;
}

Aquamarine::CHeadlessBackend::~CHeadlessBackend() {
    ;
}

Aquamarine::CHeadlessBackend::CHeadlessBackend(SP<CBackend> backend_) : backend(backend_) {
    timers.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
}

eBackendType Aquamarine::CHeadlessBackend::type() {
    return eBackendType::AQ_BACKEND_HEADLESS;
}

bool Aquamarine::CHeadlessBackend::start() {
    return true;
}

std::vector<SP<SPollFD>> Aquamarine::CHeadlessBackend::pollFDs() {
    return {makeShared<SPollFD>(timers.timerfd, [this]() { dispatchTimers(); })};
}

int Aquamarine::CHeadlessBackend::drmFD() {
    return -1;
}

bool Aquamarine::CHeadlessBackend::dispatchEvents() {
    return true;
}

uint32_t Aquamarine::CHeadlessBackend::capabilities() {
    return 0;
}

bool Aquamarine::CHeadlessBackend::setCursor(SP<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot) {
    return false;
}

void Aquamarine::CHeadlessBackend::onReady() {
    ;
}

std::vector<SDRMFormat> Aquamarine::CHeadlessBackend::getRenderFormats() {
    // FIXME: allow any modifier. Maybe set INVALID to mean that? or a special value?
    return {SDRMFormat{.drmFormat = DRM_FORMAT_RGBA8888, .modifiers = {DRM_FORMAT_MOD_LINEAR}},
            SDRMFormat{.drmFormat = DRM_FORMAT_RGBA1010102, .modifiers = {DRM_FORMAT_MOD_LINEAR}}};
}

std::vector<SDRMFormat> Aquamarine::CHeadlessBackend::getCursorFormats() {
    return {}; // No cursor support
}

bool Aquamarine::CHeadlessBackend::createOutput(const std::string& name) {
    auto output = SP<CHeadlessOutput>(new CHeadlessOutput(name.empty() ? std::format("HEADLESS-{}", ++outputIDCounter) : name, self.lock()));
    outputs.emplace_back(output);
    output->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock());
    output->self      = output;
    backend->events.newOutput.emit(SP<IOutput>(output));

    return true;
}

void Aquamarine::CHeadlessBackend::dispatchTimers() {
    std::vector<CTimer> toFire;
    for (size_t i = 0; i < timers.timers.size(); ++i) {
        if (timers.timers.at(i).expired()) {
            toFire.emplace_back(timers.timers.at(i));
            timers.timers.erase(timers.timers.begin() + i);
            i--;
            continue;
        }
    }

    for (auto& copy : toFire) {
        if (copy.what)
            copy.what();
    }

    updateTimerFD();
}

void Aquamarine::CHeadlessBackend::updateTimerFD() {
    long long  lowestNs = TIMESPEC_NSEC_PER_SEC * 240 /* 240s, 4 mins */;
    const auto clocknow = std::chrono::steady_clock::now();
    bool       any      = false;

    for (auto& t : timers.timers) {
        auto delta = std::chrono::duration_cast<std::chrono::microseconds>(t.when - clocknow).count() * 1000 /* µs -> ns */;

        if (delta < lowestNs)
            lowestNs = delta;
    }

    if (lowestNs < 0)
        lowestNs = 0;

    timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    timespecAddNs(&now, lowestNs);

    itimerspec ts = {.it_value = now};

    if (timerfd_settime(timers.timerfd, TFD_TIMER_ABSTIME, &ts, nullptr))
        backend->log(AQ_LOG_ERROR, std::format("headless: failed to arm timerfd: {}", strerror(errno)));
}

SP<IAllocator> Aquamarine::CHeadlessBackend::preferredAllocator() {
    return backend->primaryAllocator;
}

bool Aquamarine::CHeadlessBackend::CTimer::expired() {
    return std::chrono::steady_clock::now() > when;
}
07070100000034000081A400000000000000000000000166C38D3F0000A522000000000000000000000000000000000000002900000000aquamarine-0.3.3/src/backend/Session.cpp#include <aquamarine/backend/Backend.hpp>

extern "C" {
#include <libseat.h>
#include <libinput.h>
#include <libudev.h>
#include <cstring>
#include <xf86drm.h>
#include <sys/stat.h>
#include <xf86drmMode.h>
#include <linux/input.h>
#include <unistd.h>
}

using namespace Aquamarine;
using namespace Hyprutils::Memory;
#define SP CSharedPointer

static const std::string AQ_UNKNOWN_DEVICE_NAME = "UNKNOWN";

// we can't really do better with libseat/libinput logs
// because they don't allow us to pass "data" or anything...
// Nobody should create multiple backends anyways really
Hyprutils::Memory::CSharedPointer<CBackend> backendInUse;

//
static Aquamarine::eBackendLogLevel logLevelFromLibseat(libseat_log_level level) {
    switch (level) {
        case LIBSEAT_LOG_LEVEL_ERROR: return AQ_LOG_ERROR;
        case LIBSEAT_LOG_LEVEL_SILENT: return AQ_LOG_TRACE;
        default: break;
    }

    return AQ_LOG_DEBUG;
}

static Aquamarine::eBackendLogLevel logLevelFromLibinput(libinput_log_priority level) {
    switch (level) {
        case LIBINPUT_LOG_PRIORITY_ERROR: return AQ_LOG_ERROR;
        default: break;
    }

    return AQ_LOG_DEBUG;
}

static void libseatLog(libseat_log_level level, const char* fmt, va_list args) {
    if (!backendInUse)
        return;

    static char string[1024];
    vsnprintf(string, sizeof(string), fmt, args);

    backendInUse->log(logLevelFromLibseat(level), std::format("[libseat] {}", string));
}

static void libinputLog(libinput*, libinput_log_priority level, const char* fmt, va_list args) {
    if (!backendInUse)
        return;

    static char string[1024];
    vsnprintf(string, sizeof(string), fmt, args);

    backendInUse->log(logLevelFromLibinput(level), std::format("[libinput] {}", string));
}

// ------------ Libseat

static void libseatEnableSeat(struct libseat* seat, void* data) {
    auto PSESSION    = (Aquamarine::CSession*)data;
    PSESSION->active = true;
    if (PSESSION->libinputHandle)
        libinput_resume(PSESSION->libinputHandle);
    PSESSION->events.changeActive.emit();
}

static void libseatDisableSeat(struct libseat* seat, void* data) {
    auto PSESSION    = (Aquamarine::CSession*)data;
    PSESSION->active = false;
    if (PSESSION->libinputHandle)
        libinput_suspend(PSESSION->libinputHandle);
    PSESSION->events.changeActive.emit();
    libseat_disable_seat(PSESSION->libseatHandle);
}

static const libseat_seat_listener libseatListener = {
    .enable_seat  = ::libseatEnableSeat,
    .disable_seat = ::libseatDisableSeat,
};

//  ------------ Libinput

static int libinputOpen(const char* path, int flags, void* data) {
    auto SESSION = (CSession*)data;

    auto dev = makeShared<CSessionDevice>(SESSION->self.lock(), path);
    if (!dev->dev)
        return -1;

    SESSION->sessionDevices.emplace_back(dev);
    return dev->fd;
}

static void libinputClose(int fd, void* data) {
    auto SESSION = (CSession*)data;

    std::erase_if(SESSION->sessionDevices, [fd](const auto& dev) {
        auto toRemove = dev->fd == fd;
        if (toRemove)
            dev->events.remove.emit();
        return toRemove;
    });
}

static const libinput_interface libinputListener = {
    .open_restricted  = ::libinputOpen,
    .close_restricted = ::libinputClose,
};

// ------------

Aquamarine::CSessionDevice::CSessionDevice(Hyprutils::Memory::CSharedPointer<CSession> session_, const std::string& path_) : session(session_), path(path_) {
    deviceID = libseat_open_device(session->libseatHandle, path.c_str(), &fd);
    if (deviceID < 0) {
        session->backend->log(AQ_LOG_ERROR, std::format("libseat: Couldn't open device at {}", path_));
        return;
    }

    struct stat stat_;
    if (fstat(fd, &stat_) < 0) {
        session->backend->log(AQ_LOG_ERROR, std::format("libseat: Couldn't stat device at {}", path_));
        deviceID = -1;
        return;
    }

    dev = stat_.st_rdev;
}

Aquamarine::CSessionDevice::~CSessionDevice() {
    if (deviceID >= 0)
        if (libseat_close_device(session->libseatHandle, deviceID) < 0)
            session->backend->log(AQ_LOG_ERROR, std::format("libseat: Couldn't close device at {}", path));
    if (fd >= 0)
        close(fd);
}

bool Aquamarine::CSessionDevice::supportsKMS() {
    if (deviceID < 0)
        return false;

    bool kms = drmIsKMS(fd);

    if (kms)
        session->backend->log(AQ_LOG_DEBUG, std::format("libseat: Device {} supports kms", path));
    else
        session->backend->log(AQ_LOG_DEBUG, std::format("libseat: Device {} does not support kms", path));

    return kms;
}

SP<CSessionDevice> Aquamarine::CSessionDevice::openIfKMS(SP<CSession> session_, const std::string& path_) {
    auto dev = makeShared<CSessionDevice>(session_, path_);
    if (!dev->supportsKMS())
        return nullptr;
    return dev;
}

SP<CSession> Aquamarine::CSession::attempt(Hyprutils::Memory::CSharedPointer<CBackend> backend_) {
    if (!backend_)
        return nullptr;

    auto session     = makeShared<CSession>();
    session->backend = backend_;
    session->self    = session;
    backendInUse     = backend_;

    // ------------ Libseat

    libseat_set_log_handler(libseatLog);
    libseat_set_log_level(LIBSEAT_LOG_LEVEL_INFO);

    session->libseatHandle = libseat_open_seat(&libseatListener, session.get());

    if (!session->libseatHandle) {
        session->backend->log(AQ_LOG_ERROR, "libseat: failed to open a seat");
        return nullptr;
    }

    auto seatName = libseat_seat_name(session->libseatHandle);
    if (!seatName) {
        session->backend->log(AQ_LOG_ERROR, "libseat: failed to get seat name");
        return nullptr;
    }

    session->seatName = seatName;

    // dispatch any already pending events
    session->dispatchPendingEventsAsync();

    // ----------- Udev

    session->udevHandle = udev_new();
    if (!session->udevHandle) {
        session->backend->log(AQ_LOG_ERROR, "udev: failed to create a new context");
        return nullptr;
    }

    session->udevMonitor = udev_monitor_new_from_netlink(session->udevHandle, "udev");
    if (!session->udevMonitor) {
        session->backend->log(AQ_LOG_ERROR, "udev: failed to create a new udevMonitor");
        return nullptr;
    }

    udev_monitor_filter_add_match_subsystem_devtype(session->udevMonitor, "drm", nullptr);
    udev_monitor_enable_receiving(session->udevMonitor);

    // ----------- Libinput

    session->libinputHandle = libinput_udev_create_context(&libinputListener, session.get(), session->udevHandle);
    if (!session->libinputHandle) {
        session->backend->log(AQ_LOG_ERROR, "libinput: failed to create a new context");
        return nullptr;
    }

    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;
}

Aquamarine::CSession::~CSession() {
    sessionDevices.clear();
    libinputDevices.clear();

    if (libinputHandle)
        libinput_unref(libinputHandle);
    if (libseatHandle)
        libseat_close_seat(libseatHandle);
    if (udevMonitor)
        udev_monitor_unref(udevMonitor);
    if (udevHandle)
        udev_unref(udevHandle);

    libseatHandle = nullptr;
    udevMonitor   = nullptr;
    udevHandle    = nullptr;
}

static bool isDRMCard(const char* sysname) {
    const char prefix[] = DRM_PRIMARY_MINOR_NAME;
    if (strncmp(sysname, prefix, strlen(prefix)) != 0)
        return false;

    for (size_t i = strlen(prefix); sysname[i] != '\0'; i++) {
        if (sysname[i] < '0' || sysname[i] > '9')
            return false;
    }

    return true;
}

void Aquamarine::CSession::onReady() {
    for (auto& d : libinputDevices) {
        if (d->keyboard)
            backend->events.newKeyboard.emit(SP<IKeyboard>(d->keyboard));
        if (d->mouse)
            backend->events.newPointer.emit(SP<IPointer>(d->mouse));
        if (d->touch)
            backend->events.newTouch.emit(SP<ITouch>(d->touch));
        if (d->switchy)
            backend->events.newSwitch.emit(SP<ITouch>(d->touch));
        if (d->tablet)
            backend->events.newTablet.emit(SP<ITablet>(d->tablet));
        if (d->tabletPad)
            backend->events.newTabletPad.emit(SP<ITabletPad>(d->tabletPad));

        for (auto& t : d->tabletTools) {
            backend->events.newTabletTool.emit(SP<ITabletTool>(t));
        }
    }
}

void Aquamarine::CSession::dispatchUdevEvents() {
    if (!udevHandle || !udevMonitor)
        return;

    auto device = udev_monitor_receive_device(udevMonitor);

    if (!device)
        return;

    auto sysname = udev_device_get_sysname(device);
    auto devnode = udev_device_get_devnode(device);
    auto action  = udev_device_get_action(device);

    backend->log(AQ_LOG_DEBUG, std::format("udev: new udev {} event for {}", action ? action : "unknown", sysname ? sysname : "unknown"));

    if (!isDRMCard(sysname) || !action || !devnode) {
        udev_device_unref(device);
        return;
    }

    dev_t              deviceNum = udev_device_get_devnum(device);
    SP<CSessionDevice> sessionDevice;
    for (auto& sDev : sessionDevices) {
        if (sDev->dev == deviceNum) {
            sessionDevice = sDev;
            break;
        }
    }

    if (!sessionDevice) {
        udev_device_unref(device);
        return;
    }

    if (action == std::string{"add"})
        events.addDrmCard.emit(SAddDrmCardEvent{.path = devnode});
    else if (action == std::string{"change"}) {
        backend->log(AQ_LOG_DEBUG, std::format("udev: DRM device {} changed", sysname ? sysname : "unknown"));

        CSessionDevice::SChangeEvent event;

        //
        auto prop = udev_device_get_property_value(device, "HOTPLUG");
        if (prop && prop == std::string{"1"}) {
            event.type = CSessionDevice::AQ_SESSION_EVENT_CHANGE_HOTPLUG;

            prop = udev_device_get_property_value(device, "CONNECTOR");
            if (prop)
                event.hotplug.connectorID = std::stoull(prop);

            prop = udev_device_get_property_value(device, "PROPERTY");
            if (prop)
                event.hotplug.propID = std::stoull(prop);
        } else if (prop = udev_device_get_property_value(device, "LEASE"); prop && prop == std::string{"1"}) {
            event.type = CSessionDevice::AQ_SESSION_EVENT_CHANGE_LEASE;
        } else {
            backend->log(AQ_LOG_DEBUG, std::format("udev: DRM device {} change event unrecognized", sysname ? sysname : "unknown"));
        }

        sessionDevice->events.change.emit(event);
    } else if (action == std::string{"remove"}) {
        backend->log(AQ_LOG_DEBUG, std::format("udev: DRM device {} removed", sysname ? sysname : "unknown"));
        sessionDevice->events.remove.emit();
    }

    udev_device_unref(device);
    return;
}

void Aquamarine::CSession::dispatchLibinputEvents() {
    if (!libinputHandle)
        return;

    if (int ret = libinput_dispatch(libinputHandle); ret) {
        backend->log(AQ_LOG_ERROR, std::format("Couldn't dispatch libinput events: {}", strerror(-ret)));
        return;
    }

    libinput_event* event = libinput_get_event(libinputHandle);
    while (event) {
        handleLibinputEvent(event);
        libinput_event_destroy(event);
        event = libinput_get_event(libinputHandle);
    }
}

void Aquamarine::CSession::dispatchLibseatEvents() {
    if (libseat_dispatch(libseatHandle, 0) == -1)
        backend->log(AQ_LOG_ERROR, "Couldn't dispatch libseat events");
}

void Aquamarine::CSession::dispatchPendingEventsAsync() {
    dispatchLibseatEvents();
    dispatchUdevEvents();
    dispatchLibinputEvents();
}

std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CSession::pollFDs() {
    // clang-format off
    return {
        makeShared<SPollFD>(libseat_get_fd(libseatHandle), [this](){    dispatchLibseatEvents();  }),
        makeShared<SPollFD>(udev_monitor_get_fd(udevMonitor), [this](){ dispatchUdevEvents();     }),
        makeShared<SPollFD>(libinput_get_fd(libinputHandle), [this](){  dispatchLibinputEvents(); })
    };
    // clang-format on
}

bool Aquamarine::CSession::switchVT(uint32_t vt) {
    return libseat_switch_session(libseatHandle, vt) == 0;
}

void Aquamarine::CSession::handleLibinputEvent(libinput_event* e) {
    auto device    = libinput_event_get_device(e);
    auto eventType = libinput_event_get_type(e);
    auto data      = libinput_device_get_user_data(device);

    backend->log(AQ_LOG_TRACE, std::format("libinput: Event {}", (int)eventType));

    if (!data && eventType != LIBINPUT_EVENT_DEVICE_ADDED) {
        backend->log(AQ_LOG_ERROR, "libinput: No aq device in event and not added");
        return;
    }

    if (!data) {
        auto dev  = libinputDevices.emplace_back(makeShared<CLibinputDevice>(device, self));
        dev->self = dev;
        dev->init();
        return;
    }

    auto hlDevice    = ((CLibinputDevice*)data)->self.lock();
    bool destroyTool = false;

    switch (eventType) {
        case LIBINPUT_EVENT_DEVICE_ADDED:
            /* shouldn't happen */
            break;
        case LIBINPUT_EVENT_DEVICE_REMOVED:
            std::erase_if(libinputDevices, [device](const auto& d) { return d->device == device; });
            break;

            // --------- keyboard

        case LIBINPUT_EVENT_KEYBOARD_KEY: {
            auto kbe = libinput_event_get_keyboard_event(e);
            hlDevice->keyboard->events.key.emit(IKeyboard::SKeyEvent{
                .timeMs  = (uint32_t)(libinput_event_keyboard_get_time_usec(kbe) / 1000),
                .key     = libinput_event_keyboard_get_key(kbe),
                .pressed = libinput_event_keyboard_get_key_state(kbe) == LIBINPUT_KEY_STATE_PRESSED,
            });
            break;
        }

            // --------- pointer

        case LIBINPUT_EVENT_POINTER_MOTION: {
            auto pe = libinput_event_get_pointer_event(e);
            hlDevice->mouse->events.move.emit(IPointer::SMoveEvent{
                .timeMs  = (uint32_t)(libinput_event_pointer_get_time_usec(pe) / 1000),
                .delta   = {libinput_event_pointer_get_dx(pe), libinput_event_pointer_get_dy(pe)},
                .unaccel = {libinput_event_pointer_get_dx_unaccelerated(pe), libinput_event_pointer_get_dy_unaccelerated(pe)},
            });
            hlDevice->mouse->events.frame.emit();
            break;
        }

        case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: {
            auto pe = libinput_event_get_pointer_event(e);
            hlDevice->mouse->events.warp.emit(IPointer::SWarpEvent{
                .timeMs   = (uint32_t)(libinput_event_pointer_get_time_usec(pe) / 1000),
                .absolute = {libinput_event_pointer_get_absolute_x_transformed(pe, 1), libinput_event_pointer_get_absolute_y_transformed(pe, 1)},
            });
            hlDevice->mouse->events.frame.emit();
            break;
        }

        case LIBINPUT_EVENT_POINTER_BUTTON: {
            auto       pe        = libinput_event_get_pointer_event(e);
            const auto SEATCOUNT = libinput_event_pointer_get_seat_button_count(pe);
            const bool PRESSED   = libinput_event_pointer_get_button_state(pe) == LIBINPUT_BUTTON_STATE_PRESSED;

            if ((PRESSED && SEATCOUNT != 1) || (!PRESSED && SEATCOUNT != 0))
                break;

            hlDevice->mouse->events.button.emit(IPointer::SButtonEvent{
                .timeMs  = (uint32_t)(libinput_event_pointer_get_time_usec(pe) / 1000),
                .button  = libinput_event_pointer_get_button(pe),
                .pressed = PRESSED,
            });
            hlDevice->mouse->events.frame.emit();
            break;
        }

        case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL:
        case LIBINPUT_EVENT_POINTER_SCROLL_FINGER:
        case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS: {
            auto                 pe = libinput_event_get_pointer_event(e);

            IPointer::SAxisEvent aqe = {
                .timeMs = (uint32_t)(libinput_event_pointer_get_time_usec(pe) / 1000),
            };

            switch (eventType) {
                case LIBINPUT_EVENT_POINTER_SCROLL_WHEEL: aqe.source = IPointer::AQ_POINTER_AXIS_SOURCE_WHEEL; break;
                case LIBINPUT_EVENT_POINTER_SCROLL_FINGER: aqe.source = IPointer::AQ_POINTER_AXIS_SOURCE_FINGER; break;
                case LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS: aqe.source = IPointer::AQ_POINTER_AXIS_SOURCE_CONTINUOUS; break;
                default: break; /* unreachable */
            }

            static const std::array<libinput_pointer_axis, 2> LAXES = {
                LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
                LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
            };

            for (auto& axis : LAXES) {
                if (!libinput_event_pointer_has_axis(pe, axis))
                    continue;

                aqe.axis      = axis == LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL ? IPointer::AQ_POINTER_AXIS_VERTICAL : IPointer::AQ_POINTER_AXIS_HORIZONTAL;
                aqe.delta     = libinput_event_pointer_get_scroll_value(pe, axis);
                aqe.direction = IPointer::AQ_POINTER_AXIS_RELATIVE_IDENTICAL;
                if (libinput_device_config_scroll_get_natural_scroll_enabled(device))
                    aqe.direction = IPointer::AQ_POINTER_AXIS_RELATIVE_INVERTED;

                if (aqe.source == IPointer::AQ_POINTER_AXIS_SOURCE_WHEEL)
                    aqe.discrete = libinput_event_pointer_get_scroll_value_v120(pe, axis);

                hlDevice->mouse->events.axis.emit(aqe);
            }

            hlDevice->mouse->events.frame.emit();
            break;
        }

        case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: {
            auto ge = libinput_event_get_gesture_event(e);
            hlDevice->mouse->events.swipeBegin.emit(IPointer::SSwipeBeginEvent{
                .timeMs  = (uint32_t)(libinput_event_gesture_get_time_usec(ge) / 1000),
                .fingers = (uint32_t)libinput_event_gesture_get_finger_count(ge),
            });
            break;
        }
        case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: {
            auto ge = libinput_event_get_gesture_event(e);
            hlDevice->mouse->events.swipeUpdate.emit(IPointer::SSwipeUpdateEvent{
                .timeMs  = (uint32_t)(libinput_event_gesture_get_time_usec(ge) / 1000),
                .fingers = (uint32_t)libinput_event_gesture_get_finger_count(ge),
                .delta   = {libinput_event_gesture_get_dx(ge), libinput_event_gesture_get_dy(ge)},
            });
            break;
        }
        case LIBINPUT_EVENT_GESTURE_SWIPE_END: {
            auto ge = libinput_event_get_gesture_event(e);
            hlDevice->mouse->events.swipeEnd.emit(IPointer::SSwipeEndEvent{
                .timeMs    = (uint32_t)(libinput_event_gesture_get_time_usec(ge) / 1000),
                .cancelled = (bool)libinput_event_gesture_get_cancelled(ge),
            });
            break;
        }

        case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: {
            auto ge = libinput_event_get_gesture_event(e);
            hlDevice->mouse->events.pinchBegin.emit(IPointer::SPinchBeginEvent{
                .timeMs  = (uint32_t)(libinput_event_gesture_get_time_usec(ge) / 1000),
                .fingers = (uint32_t)libinput_event_gesture_get_finger_count(ge),
            });
            break;
        }
        case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: {
            auto ge = libinput_event_get_gesture_event(e);
            hlDevice->mouse->events.pinchUpdate.emit(IPointer::SPinchUpdateEvent{
                .timeMs   = (uint32_t)(libinput_event_gesture_get_time_usec(ge) / 1000),
                .fingers  = (uint32_t)libinput_event_gesture_get_finger_count(ge),
                .delta    = {libinput_event_gesture_get_dx(ge), libinput_event_gesture_get_dy(ge)},
                .scale    = libinput_event_gesture_get_scale(ge),
                .rotation = libinput_event_gesture_get_angle_delta(ge),
            });
            break;
        }
        case LIBINPUT_EVENT_GESTURE_PINCH_END: {
            auto ge = libinput_event_get_gesture_event(e);
            hlDevice->mouse->events.pinchEnd.emit(IPointer::SPinchEndEvent{
                .timeMs    = (uint32_t)(libinput_event_gesture_get_time_usec(ge) / 1000),
                .cancelled = (bool)libinput_event_gesture_get_cancelled(ge),
            });
            break;
        }

        case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN: {
            auto ge = libinput_event_get_gesture_event(e);
            hlDevice->mouse->events.holdBegin.emit(IPointer::SHoldBeginEvent{
                .timeMs  = (uint32_t)(libinput_event_gesture_get_time_usec(ge) / 1000),
                .fingers = (uint32_t)libinput_event_gesture_get_finger_count(ge),
            });
            break;
        }
        case LIBINPUT_EVENT_GESTURE_HOLD_END: {
            auto ge = libinput_event_get_gesture_event(e);
            hlDevice->mouse->events.holdEnd.emit(IPointer::SHoldEndEvent{
                .timeMs    = (uint32_t)(libinput_event_gesture_get_time_usec(ge) / 1000),
                .cancelled = (bool)libinput_event_gesture_get_cancelled(ge),
            });
            break;
        }

            // --------- touch

        case LIBINPUT_EVENT_TOUCH_DOWN: {
            auto te = libinput_event_get_touch_event(e);
            hlDevice->touch->events.down.emit(ITouch::SDownEvent{
                .timeMs  = (uint32_t)(libinput_event_touch_get_time_usec(te) / 1000),
                .touchID = libinput_event_touch_get_seat_slot(te),
                .pos     = {libinput_event_touch_get_x_transformed(te, 1), libinput_event_touch_get_y_transformed(te, 1)},
            });
            break;
        }
        case LIBINPUT_EVENT_TOUCH_UP: {
            auto te = libinput_event_get_touch_event(e);
            hlDevice->touch->events.up.emit(ITouch::SUpEvent{
                .timeMs  = (uint32_t)(libinput_event_touch_get_time_usec(te) / 1000),
                .touchID = libinput_event_touch_get_seat_slot(te),
            });
            break;
        }
        case LIBINPUT_EVENT_TOUCH_MOTION: {
            auto te = libinput_event_get_touch_event(e);
            hlDevice->touch->events.move.emit(ITouch::SMotionEvent{
                .timeMs  = (uint32_t)(libinput_event_touch_get_time_usec(te) / 1000),
                .touchID = libinput_event_touch_get_seat_slot(te),
                .pos     = {libinput_event_touch_get_x_transformed(te, 1), libinput_event_touch_get_y_transformed(te, 1)},
            });
            break;
        }
        case LIBINPUT_EVENT_TOUCH_CANCEL: {
            auto te = libinput_event_get_touch_event(e);
            hlDevice->touch->events.cancel.emit(ITouch::SCancelEvent{
                .timeMs  = (uint32_t)(libinput_event_touch_get_time_usec(te) / 1000),
                .touchID = libinput_event_touch_get_seat_slot(te),
            });
            break;
        }
        case LIBINPUT_EVENT_TOUCH_FRAME: {
            auto te = libinput_event_get_touch_event(e);
            hlDevice->touch->events.frame.emit();
            break;
        }

            // --------- switch

        case LIBINPUT_EVENT_SWITCH_TOGGLE: {
            auto       se = libinput_event_get_switch_event(e);

            const bool ENABLED = libinput_event_switch_get_switch_state(se) == LIBINPUT_SWITCH_STATE_ON;

            if (ENABLED == hlDevice->switchy->state)
                return;
            hlDevice->switchy->state = ENABLED;

            switch (libinput_event_switch_get_switch(se)) {
                case LIBINPUT_SWITCH_LID: hlDevice->switchy->type = ISwitch::AQ_SWITCH_TYPE_LID; break;
                case LIBINPUT_SWITCH_TABLET_MODE: hlDevice->switchy->type = ISwitch::AQ_SWITCH_TYPE_TABLET_MODE; break;
            }

            hlDevice->switchy->events.fire.emit(ISwitch::SFireEvent{
                .timeMs = (uint32_t)(libinput_event_switch_get_time_usec(se) / 1000),
                .type   = hlDevice->switchy->type,
                .enable = ENABLED,
            });
            break;
        }

            // --------- tbalet

        case LIBINPUT_EVENT_TABLET_PAD_BUTTON: {
            auto tpe = libinput_event_get_tablet_pad_event(e);

            hlDevice->tabletPad->events.button.emit(ITabletPad::SButtonEvent{
                .timeMs = (uint32_t)(libinput_event_tablet_pad_get_time_usec(tpe) / 1000),
                .button = libinput_event_tablet_pad_get_button_number(tpe),
                .down   = libinput_event_tablet_pad_get_button_state(tpe) == LIBINPUT_BUTTON_STATE_PRESSED,
                .mode   = (uint16_t)libinput_event_tablet_pad_get_mode(tpe),
                .group  = (uint16_t)libinput_tablet_pad_mode_group_get_index(libinput_event_tablet_pad_get_mode_group(tpe)),
            });
            break;
        }
        case LIBINPUT_EVENT_TABLET_PAD_RING: {
            auto tpe = libinput_event_get_tablet_pad_event(e);

            hlDevice->tabletPad->events.ring.emit(ITabletPad::SRingEvent{
                .timeMs = (uint32_t)(libinput_event_tablet_pad_get_time_usec(tpe) / 1000),
                .source = libinput_event_tablet_pad_get_ring_source(tpe) == LIBINPUT_TABLET_PAD_RING_SOURCE_UNKNOWN ? ITabletPad::AQ_TABLET_PAD_RING_SOURCE_UNKNOWN :
                                                                                                                      ITabletPad::AQ_TABLET_PAD_RING_SOURCE_FINGER,
                .ring   = (uint16_t)libinput_event_tablet_pad_get_ring_number(tpe),
                .pos    = libinput_event_tablet_pad_get_ring_position(tpe),
                .mode   = (uint16_t)libinput_event_tablet_pad_get_mode(tpe),
            });
            break;
        }
        case LIBINPUT_EVENT_TABLET_PAD_STRIP: {
            auto tpe = libinput_event_get_tablet_pad_event(e);

            hlDevice->tabletPad->events.strip.emit(ITabletPad::SStripEvent{
                .timeMs = (uint32_t)(libinput_event_tablet_pad_get_time_usec(tpe) / 1000),
                .source = libinput_event_tablet_pad_get_strip_source(tpe) == LIBINPUT_TABLET_PAD_STRIP_SOURCE_UNKNOWN ? ITabletPad::AQ_TABLET_PAD_STRIP_SOURCE_UNKNOWN :
                                                                                                                        ITabletPad::AQ_TABLET_PAD_STRIP_SOURCE_FINGER,
                .strip  = (uint16_t)libinput_event_tablet_pad_get_strip_number(tpe),
                .pos    = libinput_event_tablet_pad_get_strip_position(tpe),
                .mode   = (uint16_t)libinput_event_tablet_pad_get_mode(tpe),
            });
            break;
        }

        case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: {
            auto tte  = libinput_event_get_tablet_tool_event(e);
            auto tool = hlDevice->toolFrom(libinput_event_tablet_tool_get_tool(tte));

            hlDevice->tablet->events.proximity.emit(ITablet::SProximityEvent{
                .tool     = tool,
                .timeMs   = (uint32_t)(libinput_event_tablet_tool_get_time_usec(tte) / 1000),
                .absolute = {libinput_event_tablet_tool_get_x_transformed(tte, 1), libinput_event_tablet_tool_get_y_transformed(tte, 1)},
                .in       = libinput_event_tablet_tool_get_proximity_state(tte) == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN,
            });

            destroyTool = !libinput_tablet_tool_is_unique(libinput_event_tablet_tool_get_tool(tte)) &&
                libinput_event_tablet_tool_get_proximity_state(tte) == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT;

            if (libinput_event_tablet_tool_get_proximity_state(tte) == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) {
                std::erase(hlDevice->tabletTools, tool);
                break;
            }

            // fallthrough. If this is proximity in, also process axis.
        }
        case LIBINPUT_EVENT_TABLET_TOOL_AXIS: {
            auto                tte  = libinput_event_get_tablet_tool_event(e);
            auto                tool = hlDevice->toolFrom(libinput_event_tablet_tool_get_tool(tte));

            ITablet::SAxisEvent event = {
                .tool   = tool,
                .timeMs = (uint32_t)(libinput_event_tablet_tool_get_time_usec(tte) / 1000),
            };

            if (libinput_event_tablet_tool_x_has_changed(tte)) {
                event.updatedAxes |= AQ_TABLET_TOOL_AXIS_X;
                event.absolute.x = libinput_event_tablet_tool_get_x_transformed(tte, 1);
                event.delta.x    = libinput_event_tablet_tool_get_dx(tte);
            }
            if (libinput_event_tablet_tool_y_has_changed(tte)) {
                event.updatedAxes |= AQ_TABLET_TOOL_AXIS_Y;
                event.absolute.y = libinput_event_tablet_tool_get_y_transformed(tte, 1);
                event.delta.y    = libinput_event_tablet_tool_get_dy(tte);
            }
            if (libinput_event_tablet_tool_pressure_has_changed(tte)) {
                event.updatedAxes |= AQ_TABLET_TOOL_AXIS_PRESSURE;
                event.pressure = libinput_event_tablet_tool_get_pressure(tte);
            }
            if (libinput_event_tablet_tool_distance_has_changed(tte)) {
                event.updatedAxes |= AQ_TABLET_TOOL_AXIS_DISTANCE;
                event.distance = libinput_event_tablet_tool_get_distance(tte);
            }
            if (libinput_event_tablet_tool_tilt_x_has_changed(tte)) {
                event.updatedAxes |= AQ_TABLET_TOOL_AXIS_TILT_X;
                event.tilt.x = libinput_event_tablet_tool_get_tilt_x(tte);
            }
            if (libinput_event_tablet_tool_tilt_y_has_changed(tte)) {
                event.updatedAxes |= AQ_TABLET_TOOL_AXIS_TILT_Y;
                event.tilt.y = libinput_event_tablet_tool_get_tilt_y(tte);
            }
            if (libinput_event_tablet_tool_rotation_has_changed(tte)) {
                event.updatedAxes |= AQ_TABLET_TOOL_AXIS_ROTATION;
                event.rotation = libinput_event_tablet_tool_get_rotation(tte);
            }
            if (libinput_event_tablet_tool_slider_has_changed(tte)) {
                event.updatedAxes |= AQ_TABLET_TOOL_AXIS_SLIDER;
                event.slider = libinput_event_tablet_tool_get_slider_position(tte);
            }
            if (libinput_event_tablet_tool_wheel_has_changed(tte)) {
                event.updatedAxes |= AQ_TABLET_TOOL_AXIS_WHEEL;
                event.wheelDelta = libinput_event_tablet_tool_get_wheel_delta(tte);
            }

            hlDevice->tablet->events.axis.emit(event);

            if (destroyTool)
                std::erase(hlDevice->tabletTools, tool);

            break;
        }
        case LIBINPUT_EVENT_TABLET_TOOL_TIP: {
            auto tte  = libinput_event_get_tablet_tool_event(e);
            auto tool = hlDevice->toolFrom(libinput_event_tablet_tool_get_tool(tte));

            hlDevice->tablet->events.tip.emit(ITablet::STipEvent{
                .tool     = tool,
                .timeMs   = (uint32_t)(libinput_event_tablet_tool_get_time_usec(tte) / 1000),
                .absolute = {libinput_event_tablet_tool_get_x_transformed(tte, 1), libinput_event_tablet_tool_get_y_transformed(tte, 1)},
                .down     = libinput_event_tablet_tool_get_tip_state(tte) == LIBINPUT_TABLET_TOOL_TIP_DOWN,
            });
            break;
        }
        case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: {
            auto tte  = libinput_event_get_tablet_tool_event(e);
            auto tool = hlDevice->toolFrom(libinput_event_tablet_tool_get_tool(tte));

            hlDevice->tablet->events.button.emit(ITablet::SButtonEvent{
                .tool   = tool,
                .timeMs = (uint32_t)(libinput_event_tablet_tool_get_time_usec(tte) / 1000),
                .button = libinput_event_tablet_tool_get_button(tte),
                .down   = libinput_event_tablet_tool_get_button_state(tte) == LIBINPUT_BUTTON_STATE_PRESSED,
            });
            break;
        }

        default: break;
    }
}

Aquamarine::CLibinputDevice::CLibinputDevice(libinput_device* device_, Hyprutils::Memory::CWeakPointer<CSession> session_) : device(device_), session(session_) {
    ;
}

void Aquamarine::CLibinputDevice::init() {
    const auto VENDOR  = libinput_device_get_id_vendor(device);
    const auto PRODUCT = libinput_device_get_id_product(device);
    const auto NAME    = libinput_device_get_name(device);

    session->backend->log(AQ_LOG_DEBUG, std::format("libinput: New device {}: {}-{}", NAME ? NAME : "Unknown", VENDOR, PRODUCT));

    name = NAME;

    libinput_device_ref(device);
    libinput_device_set_user_data(device, this);

    if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_KEYBOARD)) {
        keyboard = makeShared<CLibinputKeyboard>(self.lock());
        if (session->backend->ready)
            session->backend->events.newKeyboard.emit(SP<IKeyboard>(keyboard));
    }

    if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER)) {
        mouse = makeShared<CLibinputMouse>(self.lock());
        if (session->backend->ready)
            session->backend->events.newPointer.emit(SP<IPointer>(mouse));
    }

    if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TOUCH)) {
        touch = makeShared<CLibinputTouch>(self.lock());
        if (session->backend->ready)
            session->backend->events.newTouch.emit(SP<ITouch>(touch));
    }

    if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_SWITCH)) {
        switchy = makeShared<CLibinputSwitch>(self.lock());
        if (session->backend->ready)
            session->backend->events.newSwitch.emit(SP<ISwitch>(switchy));
    }

    if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_TOOL)) {
        tablet = makeShared<CLibinputTablet>(self.lock());
        if (session->backend->ready)
            session->backend->events.newTablet.emit(SP<ITablet>(tablet));
    }

    if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_TABLET_PAD)) {
        tabletPad = makeShared<CLibinputTabletPad>(self.lock());
        if (session->backend->ready)
            session->backend->events.newTabletPad.emit(SP<ITabletPad>(tabletPad));
    }
}

Aquamarine::CLibinputDevice::~CLibinputDevice() {
    libinput_device_set_user_data(device, nullptr);
    libinput_device_unref(device);
}

SP<CLibinputTabletTool> Aquamarine::CLibinputDevice::toolFrom(libinput_tablet_tool* tool) {
    for (auto& t : tabletTools) {
        if (t->libinputTool == tool)
            return t;
    }

    auto newt = makeShared<CLibinputTabletTool>(self.lock(), tool);
    tabletTools.emplace_back(newt);
    if (session->backend->ready)
        session->backend->events.newTabletTool.emit(SP<ITabletTool>(newt));
    return newt;
}

static ITabletTool::eTabletToolType aqTypeFromLibinput(libinput_tablet_tool_type value) {
    switch (value) {
        case LIBINPUT_TABLET_TOOL_TYPE_PEN: return ITabletTool::AQ_TABLET_TOOL_TYPE_PEN;
        case LIBINPUT_TABLET_TOOL_TYPE_ERASER: return ITabletTool::AQ_TABLET_TOOL_TYPE_ERASER;
        case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: return ITabletTool::AQ_TABLET_TOOL_TYPE_BRUSH;
        case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: return ITabletTool::AQ_TABLET_TOOL_TYPE_PENCIL;
        case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: return ITabletTool::AQ_TABLET_TOOL_TYPE_AIRBRUSH;
        case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: return ITabletTool::AQ_TABLET_TOOL_TYPE_MOUSE;
        case LIBINPUT_TABLET_TOOL_TYPE_LENS: return ITabletTool::AQ_TABLET_TOOL_TYPE_LENS;
        case LIBINPUT_TABLET_TOOL_TYPE_TOTEM: return ITabletTool::AQ_TABLET_TOOL_TYPE_TOTEM;
    }
    return ITabletTool::AQ_TABLET_TOOL_TYPE_INVALID;
}

Aquamarine::CLibinputKeyboard::CLibinputKeyboard(SP<CLibinputDevice> dev) : device(dev) {
    libinput_device_led_update(device->device, (libinput_led)0);
}

libinput_device* Aquamarine::CLibinputKeyboard::getLibinputHandle() {
    if (!device)
        return nullptr;
    return device->device;
}

const std::string& Aquamarine::CLibinputKeyboard::getName() {
    if (!device)
        return AQ_UNKNOWN_DEVICE_NAME;
    return device->name;
}

void Aquamarine::CLibinputKeyboard::updateLEDs(uint32_t leds) {
    libinput_device_led_update(device->device, (libinput_led)leds);
}

Aquamarine::CLibinputMouse::CLibinputMouse(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev) : device(dev) {
    ;
}

libinput_device* Aquamarine::CLibinputMouse::getLibinputHandle() {
    if (!device)
        return nullptr;
    return device->device;
}

const std::string& Aquamarine::CLibinputMouse::getName() {
    if (!device)
        return AQ_UNKNOWN_DEVICE_NAME;
    return device->name;
}

Aquamarine::CLibinputTouch::CLibinputTouch(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev) : device(dev) {
    double w = 0, h = 0;
    libinput_device_get_size(dev->device, &w, &h);
    physicalSize = {w, h};
}

libinput_device* Aquamarine::CLibinputTouch::getLibinputHandle() {
    if (!device)
        return nullptr;
    return device->device;
}

const std::string& Aquamarine::CLibinputTouch::getName() {
    if (!device)
        return AQ_UNKNOWN_DEVICE_NAME;
    return device->name;
}

Aquamarine::CLibinputSwitch::CLibinputSwitch(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev) : device(dev) {
    ;
}

libinput_device* Aquamarine::CLibinputSwitch::getLibinputHandle() {
    if (!device)
        return nullptr;
    return device->device;
}

const std::string& Aquamarine::CLibinputSwitch::getName() {
    if (!device)
        return AQ_UNKNOWN_DEVICE_NAME;
    return device->name;
}

Aquamarine::CLibinputTablet::CLibinputTablet(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev) : device(dev) {
    if (libinput_device_get_id_bustype(device->device) == BUS_USB) {
        usbVendorID  = libinput_device_get_id_vendor(device->device);
        usbProductID = libinput_device_get_id_product(device->device);
    }

    double w = 0, h = 0;
    libinput_device_get_size(dev->device, &w, &h);
    physicalSize = {w, h};

    auto udevice = libinput_device_get_udev_device(device->device);
    paths.emplace_back(udev_device_get_syspath(udevice));
}

libinput_device* Aquamarine::CLibinputTablet::getLibinputHandle() {
    if (!device)
        return nullptr;
    return device->device;
}

const std::string& Aquamarine::CLibinputTablet::getName() {
    if (!device)
        return AQ_UNKNOWN_DEVICE_NAME;
    return device->name;
}

Aquamarine::CLibinputTabletTool::CLibinputTabletTool(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev, libinput_tablet_tool* tool) : device(dev), libinputTool(tool) {
    type   = aqTypeFromLibinput(libinput_tablet_tool_get_type(libinputTool));
    serial = libinput_tablet_tool_get_serial(libinputTool);
    id     = libinput_tablet_tool_get_tool_id(libinputTool);

    libinput_tablet_tool_ref(tool);

    capabilities = 0;
    if (libinput_tablet_tool_has_distance(tool))
        capabilities |= AQ_TABLET_TOOL_CAPABILITY_DISTANCE;
    if (libinput_tablet_tool_has_pressure(tool))
        capabilities |= AQ_TABLET_TOOL_CAPABILITY_PRESSURE;
    if (libinput_tablet_tool_has_tilt(tool))
        capabilities |= AQ_TABLET_TOOL_CAPABILITY_TILT;
    if (libinput_tablet_tool_has_rotation(tool))
        capabilities |= AQ_TABLET_TOOL_CAPABILITY_ROTATION;
    if (libinput_tablet_tool_has_slider(tool))
        capabilities |= AQ_TABLET_TOOL_CAPABILITY_SLIDER;
    if (libinput_tablet_tool_has_wheel(tool))
        capabilities |= AQ_TABLET_TOOL_CAPABILITY_WHEEL;

    libinput_tablet_tool_set_user_data(tool, this);
}

Aquamarine::CLibinputTabletTool::~CLibinputTabletTool() {
    libinput_tablet_tool_unref(libinputTool);
}

libinput_device* Aquamarine::CLibinputTabletTool::getLibinputHandle() {
    if (!device)
        return nullptr;
    return device->device;
}

const std::string& Aquamarine::CLibinputTabletTool::getName() {
    if (!device)
        return AQ_UNKNOWN_DEVICE_NAME;
    return device->name;
}

Aquamarine::CLibinputTabletPad::CLibinputTabletPad(Hyprutils::Memory::CSharedPointer<CLibinputDevice> dev) : device(dev) {
    buttons = libinput_device_tablet_pad_get_num_buttons(device->device);
    rings   = libinput_device_tablet_pad_get_num_rings(device->device);
    strips  = libinput_device_tablet_pad_get_num_strips(device->device);

    auto udevice = libinput_device_get_udev_device(device->device);
    paths.emplace_back(udev_device_get_syspath(udevice));

    int groupsno = libinput_device_tablet_pad_get_num_mode_groups(device->device);
    for (size_t i = 0; i < groupsno; ++i) {
        auto g = createGroupFromID(i);
        if (g)
            groups.emplace_back(g);
    }
}

Aquamarine::CLibinputTabletPad::~CLibinputTabletPad() {
    int groups = libinput_device_tablet_pad_get_num_mode_groups(device->device);
    for (int i = 0; i < groups; ++i) {
        auto g = libinput_device_tablet_pad_get_mode_group(device->device, i);
        libinput_tablet_pad_mode_group_unref(g);
    }
}

libinput_device* Aquamarine::CLibinputTabletPad::getLibinputHandle() {
    if (!device)
        return nullptr;
    return device->device;
}

const std::string& Aquamarine::CLibinputTabletPad::getName() {
    if (!device)
        return AQ_UNKNOWN_DEVICE_NAME;
    return device->name;
}

SP<ITabletPad::STabletPadGroup> Aquamarine::CLibinputTabletPad::createGroupFromID(int id) {
    auto libinputGroup = libinput_device_tablet_pad_get_mode_group(device->device, id);

    auto group = makeShared<STabletPadGroup>();
    for (size_t i = 0; i < rings; ++i) {
        if (!libinput_tablet_pad_mode_group_has_ring(libinputGroup, i))
            continue;

        group->rings.push_back(i);
    }

    for (size_t i = 0; i < strips; ++i) {
        if (!libinput_tablet_pad_mode_group_has_strip(libinputGroup, i))
            continue;

        group->strips.push_back(i);
    }

    for (size_t i = 0; i < buttons; ++i) {
        if (!libinput_tablet_pad_mode_group_has_button(libinputGroup, i))
            continue;

        group->buttons.push_back(i);
    }

    group->modes = libinput_tablet_pad_mode_group_get_num_modes(libinputGroup);

    return group;
}
07070100000035000081A400000000000000000000000166C38D3F000073E9000000000000000000000000000000000000002900000000aquamarine-0.3.3/src/backend/Wayland.cpp#include <aquamarine/backend/Wayland.hpp>
#include <wayland.hpp>
#include <xdg-shell.hpp>
#include "Shared.hpp"
#include "FormatUtils.hpp"
#include <string.h>
#include <xf86drm.h>
#include <gbm.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

using namespace Aquamarine;
using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#define SP CSharedPointer

static std::pair<int, std::string> openExclusiveShm() {
    // Only absolute paths can be shared across different shm_open() calls
    srand(time(nullptr));
    std::string name = std::format("/aq{:x}", rand() % RAND_MAX);

    for (size_t i = 0; i < 69; ++i) {
        int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
        if (fd >= 0)
            return {fd, name};
    }

    return {-1, ""};
}

static int allocateSHMFile(size_t len) {
    auto [fd, name] = openExclusiveShm();
    if (fd < 0)
        return -1;

    shm_unlink(name.c_str());

    int ret;
    do {
        ret = ftruncate(fd, len);
    } while (ret < 0 && errno == EINTR);

    if (ret < 0) {
        close(fd);
        return -1;
    }

    return fd;
}

wl_shm_format shmFormatFromDRM(uint32_t drmFormat) {
    switch (drmFormat) {
        case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888;
        case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888;
        default: return (wl_shm_format)drmFormat;
    }

    return (wl_shm_format)drmFormat;
}

Aquamarine::CWaylandBackend::~CWaylandBackend() {
    if (drmState.fd >= 0)
        close(drmState.fd);
}

eBackendType Aquamarine::CWaylandBackend::type() {
    return AQ_BACKEND_WAYLAND;
}

Aquamarine::CWaylandBackend::CWaylandBackend(SP<CBackend> backend_) : backend(backend_) {
    ;
}

bool Aquamarine::CWaylandBackend::start() {
    backend->log(AQ_LOG_DEBUG, "Starting the Wayland backend!");

    waylandState.display = wl_display_connect(nullptr);

    if (!waylandState.display) {
        backend->log(AQ_LOG_ERROR, "Wayland backend cannot start: wl_display_connect failed (is a wayland compositor running?)");
        return false;
    }

    waylandState.registry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(waylandState.display));

    backend->log(AQ_LOG_DEBUG, std::format("Got registry at 0x{:x}", (uintptr_t)waylandState.registry->resource()));

    waylandState.registry->setGlobal([this](CCWlRegistry* r, uint32_t id, const char* name, uint32_t version) {
        TRACE(backend->log(AQ_LOG_TRACE, std::format(" | received global: {} (version {}) with id {}", name, version, id)));

        const std::string NAME = name;

        if (NAME == "wl_seat") {
            TRACE(backend->log(AQ_LOG_TRACE, std::format("  > binding to global: {} (version {}) with id {}", name, 9, id)));
            waylandState.seat = makeShared<CCWlSeat>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &wl_seat_interface, 9));
            initSeat();
        } else if (NAME == "xdg_wm_base") {
            TRACE(backend->log(AQ_LOG_TRACE, std::format("  > binding to global: {} (version {}) with id {}", name, 6, id)));
            waylandState.xdg = makeShared<CCXdgWmBase>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &xdg_wm_base_interface, 6));
            initShell();
        } else if (NAME == "wl_compositor") {
            TRACE(backend->log(AQ_LOG_TRACE, std::format("  > binding to global: {} (version {}) with id {}", name, 6, id)));
            waylandState.compositor = makeShared<CCWlCompositor>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &wl_compositor_interface, 6));
        } else if (NAME == "wl_shm") {
            TRACE(backend->log(AQ_LOG_TRACE, std::format("  > binding to global: {} (version {}) with id {}", name, 1, id)));
            waylandState.shm = makeShared<CCWlShm>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &wl_shm_interface, 1));
        } else if (NAME == "zwp_linux_dmabuf_v1") {
            TRACE(backend->log(AQ_LOG_TRACE, std::format("  > binding to global: {} (version {}) with id {}", name, 4, id)));
            waylandState.dmabuf =
                makeShared<CCZwpLinuxDmabufV1>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &zwp_linux_dmabuf_v1_interface, 4));
            if (!initDmabuf()) {
                backend->log(AQ_LOG_ERROR, "Wayland backend cannot start: zwp_linux_dmabuf_v1 init failed");
                waylandState.dmabufFailed = true;
            }
        }
    });
    waylandState.registry->setGlobalRemove([this](CCWlRegistry* r, uint32_t id) { backend->log(AQ_LOG_DEBUG, std::format("Global {} removed", id)); });

    wl_display_roundtrip(waylandState.display);

    if (!waylandState.xdg || !waylandState.compositor || !waylandState.seat || !waylandState.dmabuf || waylandState.dmabufFailed || !waylandState.shm) {
        backend->log(AQ_LOG_ERROR, "Wayland backend cannot start: Missing protocols");
        return false;
    }

    dispatchEvents();

    createOutput();

    return true;
}

int Aquamarine::CWaylandBackend::drmFD() {
    return drmState.fd;
}

bool Aquamarine::CWaylandBackend::createOutput(const std::string& szName) {
    auto o  = outputs.emplace_back(SP<CWaylandOutput>(new CWaylandOutput(szName.empty() ? std::format("WAYLAND-{}", ++lastOutputID) : szName, self)));
    o->self = o;
    if (backend->ready)
        o->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock());
    idleCallbacks.emplace_back([this, o]() { backend->events.newOutput.emit(SP<IOutput>(o)); });
    return true;
}

std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CWaylandBackend::pollFDs() {
    if (!waylandState.display)
        return {};

    return {makeShared<SPollFD>(wl_display_get_fd(waylandState.display), [this]() { dispatchEvents(); })};
}

bool Aquamarine::CWaylandBackend::dispatchEvents() {
    wl_display_flush(waylandState.display);

    if (wl_display_prepare_read(waylandState.display) == 0) {
        wl_display_read_events(waylandState.display);
        wl_display_dispatch_pending(waylandState.display);
    } else
        wl_display_dispatch(waylandState.display);

    int ret = 0;
    do {
        ret = wl_display_dispatch_pending(waylandState.display);
        wl_display_flush(waylandState.display);
    } while (ret > 0);

    // dispatch frames
    if (backend->ready) {
        for (auto& f : idleCallbacks) {
            f();
        }
        idleCallbacks.clear();
    }

    return true;
}

uint32_t Aquamarine::CWaylandBackend::capabilities() {
    return AQ_BACKEND_CAPABILITY_POINTER;
}

bool Aquamarine::CWaylandBackend::setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot) {
    // TODO:
    return true;
}

void Aquamarine::CWaylandBackend::onReady() {
    for (auto& o : outputs) {
        o->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock());
        if (!o->swapchain) {
            backend->log(AQ_LOG_ERROR, std::format("Output {} failed: swapchain creation failed", o->name));
            continue;
        }
    }
}

Aquamarine::CWaylandKeyboard::CWaylandKeyboard(SP<CCWlKeyboard> keyboard_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : keyboard(keyboard_), backend(backend_) {
    if (!keyboard->resource())
        return;

    backend->backend->log(AQ_LOG_DEBUG, "New wayland keyboard wl_keyboard");

    keyboard->setKey([this](CCWlKeyboard* r, uint32_t serial, uint32_t timeMs, uint32_t key, wl_keyboard_key_state state) {
        events.key.emit(SKeyEvent{
            .timeMs  = timeMs,
            .key     = key,
            .pressed = state == WL_KEYBOARD_KEY_STATE_PRESSED,
        });
    });

    keyboard->setModifiers([this](CCWlKeyboard* r, uint32_t serial, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
        events.modifiers.emit(SModifiersEvent{
            .depressed = depressed,
            .latched   = latched,
            .locked    = locked,
            .group     = group,
        });
    });
}

Aquamarine::CWaylandKeyboard::~CWaylandKeyboard() {
    ;
}

const std::string& Aquamarine::CWaylandKeyboard::getName() {
    return name;
}

Aquamarine::CWaylandPointer::CWaylandPointer(SP<CCWlPointer> pointer_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : pointer(pointer_), backend(backend_) {
    if (!pointer->resource())
        return;

    backend->backend->log(AQ_LOG_DEBUG, "New wayland pointer wl_pointer");

    pointer->setMotion([this](CCWlPointer* r, uint32_t serial, wl_fixed_t x, wl_fixed_t y) {
        const auto STATE = backend->focusedOutput->state->state();

        if (!backend->focusedOutput || (!STATE.mode && !STATE.customMode))
            return;

        const Vector2D size = STATE.customMode ? STATE.customMode->pixelSize : STATE.mode->pixelSize;

        Vector2D       local = {wl_fixed_to_double(x), wl_fixed_to_double(y)};
        local                = local / size;

        timespec now;
        clock_gettime(CLOCK_MONOTONIC, &now);

        events.warp.emit(SWarpEvent{
            .timeMs   = (uint32_t)(now.tv_sec * 1000 + now.tv_nsec / 1000000),
            .absolute = local,
        });
    });

    pointer->setEnter([this](CCWlPointer* r, uint32_t serial, wl_proxy* surface, wl_fixed_t x, wl_fixed_t y) {
        backend->lastEnterSerial = serial;

        for (auto& o : backend->outputs) {
            if (o->waylandState.surface->resource() != surface)
                continue;

            backend->focusedOutput = o;
            backend->backend->log(AQ_LOG_DEBUG, std::format("[wayland] focus changed: {}", o->name));
            o->onEnter(pointer, serial);
            break;
        }
    });

    pointer->setLeave([this](CCWlPointer* r, uint32_t serial, wl_proxy* surface) {
        for (auto& o : backend->outputs) {
            if (o->waylandState.surface->resource() != surface)
                continue;

            o->cursorState.serial = 0;
        }
    });

    pointer->setButton([this](CCWlPointer* r, uint32_t serial, uint32_t timeMs, uint32_t button, wl_pointer_button_state state) {
        events.button.emit(SButtonEvent{
            .timeMs  = timeMs,
            .button  = button,
            .pressed = state == WL_POINTER_BUTTON_STATE_PRESSED,
        });
    });

    pointer->setAxis([this](CCWlPointer* r, uint32_t timeMs, wl_pointer_axis axis, wl_fixed_t value) {
        events.axis.emit(SAxisEvent{
            .timeMs = timeMs,
            .axis   = axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL ? AQ_POINTER_AXIS_HORIZONTAL : AQ_POINTER_AXIS_VERTICAL,
            .delta  = wl_fixed_to_double(value),
        });
    });

    pointer->setFrame([this](CCWlPointer* r) { events.frame.emit(); });
}

Aquamarine::CWaylandPointer::~CWaylandPointer() {
    ;
}

const std::string& Aquamarine::CWaylandPointer::getName() {
    return name;
}

void Aquamarine::CWaylandBackend::initSeat() {
    waylandState.seat->setCapabilities([this](CCWlSeat* r, wl_seat_capability cap) {
        const bool HAS_KEYBOARD = ((uint32_t)cap) & WL_SEAT_CAPABILITY_KEYBOARD;
        const bool HAS_POINTER  = ((uint32_t)cap) & WL_SEAT_CAPABILITY_POINTER;

        if (HAS_KEYBOARD && keyboards.empty()) {
            auto k = keyboards.emplace_back(makeShared<CWaylandKeyboard>(makeShared<CCWlKeyboard>(waylandState.seat->sendGetKeyboard()), self));
            idleCallbacks.emplace_back([this, k]() { backend->events.newKeyboard.emit(SP<IKeyboard>(k)); });
        } else if (!HAS_KEYBOARD && !keyboards.empty())
            keyboards.clear();

        if (HAS_POINTER && pointers.empty()) {
            auto p = pointers.emplace_back(makeShared<CWaylandPointer>(makeShared<CCWlPointer>(waylandState.seat->sendGetPointer()), self));
            idleCallbacks.emplace_back([this, p]() { backend->events.newPointer.emit(SP<IPointer>(p)); });
        } else if (!HAS_POINTER && !pointers.empty())
            pointers.clear();
    });
}

void Aquamarine::CWaylandBackend::initShell() {
    waylandState.xdg->setPing([](CCXdgWmBase* r, uint32_t serial) { r->sendPong(serial); });
}

bool Aquamarine::CWaylandBackend::initDmabuf() {
    waylandState.dmabufFeedback = makeShared<CCZwpLinuxDmabufFeedbackV1>(waylandState.dmabuf->sendGetDefaultFeedback());
    if (!waylandState.dmabufFeedback) {
        backend->log(AQ_LOG_ERROR, "initDmabuf: failed to get default feedback");
        return false;
    }

    waylandState.dmabufFeedback->setDone([this](CCZwpLinuxDmabufFeedbackV1* r) {
        // no-op
        backend->log(AQ_LOG_DEBUG, "zwp_linux_dmabuf_v1: Got done");
    });

    waylandState.dmabufFeedback->setMainDevice([this](CCZwpLinuxDmabufFeedbackV1* r, wl_array* deviceArr) {
        backend->log(AQ_LOG_DEBUG, "zwp_linux_dmabuf_v1: Got main device");

        dev_t device;
        ASSERT(deviceArr->size == sizeof(device));
        memcpy(&device, deviceArr->data, sizeof(device));

        drmDevice* drmDev;
        if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0) {
            backend->log(AQ_LOG_ERROR, "zwp_linux_dmabuf_v1: drmGetDeviceFromDevId failed");
            return;
        }

        const char* name = nullptr;
        if (drmDev->available_nodes & (1 << DRM_NODE_RENDER))
            name = drmDev->nodes[DRM_NODE_RENDER];
        else {
            // Likely a split display/render setup. Pick the primary node and hope
            // Mesa will open the right render node under-the-hood.
            ASSERT(drmDev->available_nodes & (1 << DRM_NODE_PRIMARY));
            name = drmDev->nodes[DRM_NODE_PRIMARY];
            backend->log(AQ_LOG_WARNING, "zwp_linux_dmabuf_v1: DRM device has no render node, using primary.");
        }

        if (!name) {
            backend->log(AQ_LOG_ERROR, "zwp_linux_dmabuf_v1: no node name");
            return;
        }

        drmState.nodeName = name;

        drmFreeDevice(&drmDev);

        backend->log(AQ_LOG_DEBUG, std::format("zwp_linux_dmabuf_v1: Got node {}", drmState.nodeName));
    });

    waylandState.dmabufFeedback->setFormatTable([this](CCZwpLinuxDmabufFeedbackV1* r, int32_t fd, uint32_t size) {
#pragma pack(push, 1)
        struct wlDrmFormatMarshalled {
            uint32_t drmFormat;
            char     pad[4];
            uint64_t modifier;
        };
#pragma pack(pop)
        static_assert(sizeof(wlDrmFormatMarshalled) == 16);

        auto formatTable = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
        if (formatTable == MAP_FAILED) {
            backend->log(AQ_LOG_ERROR, std::format("zwp_linux_dmabuf_v1: Failed to mmap the format table"));
            return;
        }

        const auto FORMATS = (wlDrmFormatMarshalled*)formatTable;

        for (size_t i = 0; i < size / 16; ++i) {
            auto& fmt = FORMATS[i];

            auto  modName = drmGetFormatModifierName(fmt.modifier);
            backend->log(AQ_LOG_DEBUG, std::format("zwp_linux_dmabuf_v1: Got format {} with modifier {}", fourccToName(fmt.drmFormat), modName ? modName : "UNKNOWN"));
            free(modName);

            auto it = std::find_if(dmabufFormats.begin(), dmabufFormats.end(), [&fmt](const auto& e) { return e.drmFormat == fmt.drmFormat; });
            if (it == dmabufFormats.end()) {
                dmabufFormats.emplace_back(SDRMFormat{.drmFormat = fmt.drmFormat, .modifiers = {fmt.modifier}});
                continue;
            }

            it->modifiers.emplace_back(fmt.modifier);
        }

        munmap(formatTable, size);
    });

    wl_display_roundtrip(waylandState.display);

    if (!drmState.nodeName.empty()) {
        drmState.fd = open(drmState.nodeName.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC);
        if (drmState.fd < 0) {
            backend->log(AQ_LOG_ERROR, std::format("zwp_linux_dmabuf_v1: Failed to open node {}", drmState.nodeName));
            return false;
        }

        backend->log(AQ_LOG_DEBUG, std::format("zwp_linux_dmabuf_v1: opened node {} with fd {}", drmState.nodeName, drmState.fd));
    }

    return true;
}

std::vector<SDRMFormat> Aquamarine::CWaylandBackend::getRenderFormats() {
    return dmabufFormats;
}

std::vector<SDRMFormat> Aquamarine::CWaylandBackend::getCursorFormats() {
    return dmabufFormats;
}

SP<IAllocator> Aquamarine::CWaylandBackend::preferredAllocator() {
    return backend->primaryAllocator;
}

Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : backend(backend_) {
    name = name_;

    waylandState.surface = makeShared<CCWlSurface>(backend->waylandState.compositor->sendCreateSurface());

    if (!waylandState.surface->resource()) {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {} failed: no surface given. Errno: {}", name, errno));
        return;
    }

    waylandState.xdgSurface = makeShared<CCXdgSurface>(backend->waylandState.xdg->sendGetXdgSurface(waylandState.surface->resource()));

    if (!waylandState.xdgSurface->resource()) {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {} failed: no xdgSurface given. Errno: {}", name, errno));
        return;
    }

    waylandState.xdgSurface->setConfigure([this](CCXdgSurface* r, uint32_t serial) {
        backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: configure surface with {}", name, serial));
        r->sendAckConfigure(serial);
    });

    waylandState.xdgToplevel = makeShared<CCXdgToplevel>(waylandState.xdgSurface->sendGetToplevel());

    if (!waylandState.xdgToplevel->resource()) {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {} failed: no xdgToplevel given. Errno: {}", name, errno));
        return;
    }

    waylandState.xdgToplevel->setWmCapabilities(
        [this](CCXdgToplevel* r, wl_array* arr) { backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: wm_capabilities received", name)); });

    waylandState.xdgToplevel->setConfigure([this](CCXdgToplevel* r, int32_t w, int32_t h, wl_array* arr) {
        backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: configure toplevel with {}x{}", name, w, h));
        events.state.emit(SStateEvent{.size = {w, h}});
        sendFrameAndSetCallback();
    });

    waylandState.xdgToplevel->setClose([this](CCXdgToplevel* r) { destroy(); });

    waylandState.xdgToplevel->sendSetTitle(std::format("aquamarine - {}", name).c_str());
    waylandState.xdgToplevel->sendSetAppId("aquamarine");

    auto inputRegion = makeShared<CCWlRegion>(backend->waylandState.compositor->sendCreateRegion());
    inputRegion->sendAdd(0, 0, INT32_MAX, INT32_MAX);

    waylandState.surface->sendSetInputRegion(inputRegion.get());
    waylandState.surface->sendAttach(nullptr, 0, 0);
    waylandState.surface->sendCommit();

    inputRegion->sendDestroy();

    backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: initialized", name));
}

Aquamarine::CWaylandOutput::~CWaylandOutput() {
    backend->idleCallbacks.clear(); // FIXME: mega hack to avoid a UAF in frame events
    events.destroy.emit();
    if (waylandState.xdgToplevel)
        waylandState.xdgToplevel->sendDestroy();
    if (waylandState.xdgSurface)
        waylandState.xdgSurface->sendDestroy();
    if (waylandState.surface)
        waylandState.surface->sendDestroy();
}

std::vector<SDRMFormat> Aquamarine::CWaylandOutput::getRenderFormats() {
    // TODO
    // this is technically wrong because this returns the format table formats
    // the actually supported formats are given by tranche formats
    return backend->getRenderFormats();
}

bool Aquamarine::CWaylandOutput::destroy() {
    events.destroy.emit();
    waylandState.surface->sendAttach(nullptr, 0, 0);
    waylandState.surface->sendCommit();
    waylandState.frameCallback.reset();
    std::erase(backend->outputs, self.lock());
    return true;
}

bool Aquamarine::CWaylandOutput::test() {
    return true; // TODO:
}

bool Aquamarine::CWaylandOutput::commit() {
    Vector2D pixelSize   = {};
    uint32_t refreshRate = 0;

    if (state->internalState.customMode)
        pixelSize = state->internalState.customMode->pixelSize;
    else if (state->internalState.mode)
        pixelSize = state->internalState.mode->pixelSize;
    else {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: pending state rejected: invalid mode", name));
        return false;
    }

    uint32_t format = state->internalState.drmFormat;

    if (format == DRM_FORMAT_INVALID) {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: pending state rejected: invalid format", name));
        return false;
    }

    if (!swapchain) {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: no swapchain, lying because it will soon be here", name));
        return true;
    }

    if (!swapchain->reconfigure(SSwapchainOptions{.length = 2, .size = pixelSize, .format = format})) {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: pending state rejected: swapchain failed reconfiguring", name));
        return false;
    }

    if (!state->internalState.buffer) {
        // if the consumer explicitly committed a null buffer, that's a violation.
        if (state->internalState.committed & COutputState::AQ_OUTPUT_STATE_BUFFER) {
            backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: pending state rejected: no buffer", name));
            return false;
        }

        events.commit.emit();
        state->onCommit();
        return true;
    }

    auto wlBuffer = wlBufferFromBuffer(state->internalState.buffer);

    if (!wlBuffer) {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: pending state rejected: no wlBuffer??", name));
        return false;
    }

    if (wlBuffer->pendingRelease)
        backend->backend->log(AQ_LOG_WARNING, std::format("Output {}: pending state has a non-released buffer??", name));

    wlBuffer->pendingRelease = true;

    waylandState.surface->sendAttach(wlBuffer->waylandState.buffer.get(), 0, 0);
    waylandState.surface->sendDamageBuffer(0, 0, INT32_MAX, INT32_MAX);
    waylandState.surface->sendCommit();

    readyForFrameCallback = true;

    events.commit.emit();
    state->onCommit();
    needsFrame = false;

    return true;
}

SP<IBackendImplementation> Aquamarine::CWaylandOutput::getBackend() {
    return SP<IBackendImplementation>(backend.lock());
}

SP<CWaylandBuffer> Aquamarine::CWaylandOutput::wlBufferFromBuffer(SP<IBuffer> buffer) {
    std::erase_if(backendState.buffers, [this](const auto& el) { return el.first.expired() || !swapchain->contains(el.first.lock()); });

    for (auto& [k, v] : backendState.buffers) {
        if (k != buffer)
            continue;

        return v;
    }

    // create a new one
    auto wlBuffer = makeShared<CWaylandBuffer>(buffer, backend);

    if (!wlBuffer->good())
        return nullptr;

    backendState.buffers.emplace_back(std::make_pair<>(buffer, wlBuffer));

    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(); });
}

void Aquamarine::CWaylandOutput::onFrameDone() {
    waylandState.frameCallback.reset();
    readyForFrameCallback = false;

    // FIXME: this is wrong, but otherwise we get bugs.
    // thanks @phonetic112
    scheduleFrame(AQ_SCHEDULE_NEEDS_FRAME);

    if (frameScheduledWhileWaiting)
        sendFrameAndSetCallback();
    else
        events.frame.emit();

    frameScheduledWhileWaiting = false;
}

bool Aquamarine::CWaylandOutput::setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot) {
    if (!cursorState.cursorSurface)
        cursorState.cursorSurface = makeShared<CCWlSurface>(backend->waylandState.compositor->sendCreateSurface());

    if (!cursorState.cursorSurface) {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: Failed to create a wl_surface for the cursor", name));
        return false;
    }

    if (!buffer) {
        cursorState.cursorBuffer.reset();
        cursorState.cursorWlBuffer.reset();
        backend->pointers.at(0)->pointer->sendSetCursor(cursorState.serial, nullptr, cursorState.hotspot.x, cursorState.hotspot.y);
        return true;
    }

    cursorState.cursorBuffer = buffer;
    cursorState.hotspot      = hotspot;

    if (buffer->shm().success) {
        auto attrs                    = buffer->shm();
        auto [pixelData, fmt, bufLen] = buffer->beginDataPtr(0);

        int fd = allocateSHMFile(bufLen);
        if (fd < 0) {
            backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: Failed to allocate a shm file", name));
            return false;
        }

        void* data = mmap(nullptr, bufLen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        if (data == MAP_FAILED) {
            backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: Failed to mmap the cursor pixel data", name));
            close(fd);
            return false;
        }

        memcpy(data, pixelData, bufLen);
        munmap(data, bufLen);

        auto pool = makeShared<CCWlShmPool>(backend->waylandState.shm->sendCreatePool(fd, bufLen));
        if (!pool) {
            backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: Failed to submit a wl_shm pool", name));
            close(fd);
            return false;
        }

        cursorState.cursorWlBuffer = makeShared<CCWlBuffer>(pool->sendCreateBuffer(0, attrs.size.x, attrs.size.y, attrs.stride, shmFormatFromDRM(attrs.format)));

        pool.reset();

        close(fd);
    } else if (auto attrs = buffer->dmabuf(); attrs.success) {
        auto params = makeShared<CCZwpLinuxBufferParamsV1>(backend->waylandState.dmabuf->sendCreateParams());

        for (int i = 0; i < attrs.planes; ++i) {
            params->sendAdd(attrs.fds.at(i), i, attrs.offsets.at(i), attrs.strides.at(i), attrs.modifier >> 32, attrs.modifier & 0xFFFFFFFF);
        }

        cursorState.cursorWlBuffer = makeShared<CCWlBuffer>(params->sendCreateImmed(attrs.size.x, attrs.size.y, attrs.format, (zwpLinuxBufferParamsV1Flags)0));
    } else {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: Failed to create a buffer for cursor: No known attrs (tried dmabuf / shm)", name));
        return false;
    }

    if (!cursorState.cursorWlBuffer) {
        backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: Failed to create a buffer for cursor", name));
        return false;
    }

    cursorState.cursorSurface->sendSetBufferScale(1);
    cursorState.cursorSurface->sendSetBufferTransform(WL_OUTPUT_TRANSFORM_NORMAL);
    cursorState.cursorSurface->sendAttach(cursorState.cursorWlBuffer.get(), 0, 0);
    cursorState.cursorSurface->sendDamage(0, 0, INT32_MAX, INT32_MAX);
    cursorState.cursorSurface->sendCommit();

    // this may fail if we are not in focus
    if (!backend->pointers.empty() && cursorState.serial)
        backend->pointers.at(0)->pointer->sendSetCursor(cursorState.serial, cursorState.cursorSurface.get(), cursorState.hotspot.x, cursorState.hotspot.y);

    return true;
}

void Aquamarine::CWaylandOutput::moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipShedule) {
    return;
}

void Aquamarine::CWaylandOutput::onEnter(SP<CCWlPointer> pointer, uint32_t serial) {
    cursorState.serial = serial;

    if (!cursorState.cursorSurface)
        return;

    pointer->sendSetCursor(serial, cursorState.cursorSurface.get(), cursorState.hotspot.x, cursorState.hotspot.y);
}

Hyprutils::Math::Vector2D Aquamarine::CWaylandOutput::cursorPlaneSize() {
    return {-1, -1}; // no limit
}

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)));
    needsFrame = true;

    if (frameScheduled)
        return;

    frameScheduled = true;

    if (waylandState.frameCallback)
        frameScheduledWhileWaiting = true;
    else
        backend->idleCallbacks.emplace_back([this]() { sendFrameAndSetCallback(); });
}

Aquamarine::CWaylandBuffer::CWaylandBuffer(SP<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : buffer(buffer_), backend(backend_) {
    auto params = makeShared<CCZwpLinuxBufferParamsV1>(backend->waylandState.dmabuf->sendCreateParams());

    if (!params) {
        backend->backend->log(AQ_LOG_ERROR, "WaylandBuffer: failed to query params");
        return;
    }

    auto attrs = buffer->dmabuf();

    for (size_t i = 0; i < attrs.planes; ++i) {
        params->sendAdd(attrs.fds.at(i), i, attrs.offsets.at(i), attrs.strides.at(i), attrs.modifier >> 32, attrs.modifier & 0xFFFFFFFF);
    }

    waylandState.buffer = makeShared<CCWlBuffer>(params->sendCreateImmed(attrs.size.x, attrs.size.y, attrs.format, (zwpLinuxBufferParamsV1Flags)0));

    waylandState.buffer->setRelease([this](CCWlBuffer* r) { pendingRelease = false; });

    params->sendDestroy();
}

Aquamarine::CWaylandBuffer::~CWaylandBuffer() {
    if (waylandState.buffer && waylandState.buffer->resource())
        waylandState.buffer->sendDestroy();
}

bool Aquamarine::CWaylandBuffer::good() {
    return waylandState.buffer && waylandState.buffer->resource();
}
07070100000036000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000002100000000aquamarine-0.3.3/src/backend/drm07070100000037000081A400000000000000000000000166C38D3F0001186E000000000000000000000000000000000000002900000000aquamarine-0.3.3/src/backend/drm/DRM.cpp#include "aquamarine/output/Output.hpp"
#include <aquamarine/backend/DRM.hpp>
#include <aquamarine/backend/drm/Legacy.hpp>
#include <aquamarine/backend/drm/Atomic.hpp>
#include <aquamarine/allocator/GBM.hpp>
#include <hyprutils/string/VarList.hpp>
#include <chrono>
#include <thread>
#include <deque>
#include <cstring>
#include <filesystem>
#include <system_error>
#include <sys/mman.h>
#include <fcntl.h>

extern "C" {
#include <libseat.h>
#include <libudev.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <libdisplay-info/cvt.h>
#include <libdisplay-info/info.h>
#include <libdisplay-info/edid.h>
}

#include "Props.hpp"
#include "FormatUtils.hpp"
#include "Shared.hpp"
#include "hwdata.hpp"
#include "Renderer.hpp"

using namespace Aquamarine;
using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#define SP CSharedPointer

Aquamarine::CDRMBackend::CDRMBackend(SP<CBackend> backend_) : backend(backend_) {
    listeners.sessionActivate = backend->session->events.changeActive.registerListener([this](std::any d) {
        if (backend->session->active) {
            // session got activated, we need to restore
            restoreAfterVT();
        }
    });
}

static udev_enumerate* enumDRMCards(udev* udev) {
    auto enumerate = udev_enumerate_new(udev);
    if (!enumerate)
        return nullptr;

    udev_enumerate_add_match_subsystem(enumerate, "drm");
    udev_enumerate_add_match_sysname(enumerate, DRM_PRIMARY_MINOR_NAME "[0-9]*");

    if (udev_enumerate_scan_devices(enumerate)) {
        udev_enumerate_unref(enumerate);
        return nullptr;
    }

    return enumerate;
}

static std::vector<SP<CSessionDevice>> scanGPUs(SP<CBackend> backend) {
    auto enumerate = enumDRMCards(backend->session->udevHandle);

    if (!enumerate) {
        backend->log(AQ_LOG_ERROR, "drm: couldn't enumerate gpus with udev");
        return {};
    }

    if (!udev_enumerate_get_list_entry(enumerate)) {
        // TODO: wait for them.
        backend->log(AQ_LOG_ERROR, "drm: No gpus in scanGPUs.");
        return {};
    }

    udev_list_entry*               entry = nullptr;
    size_t                         i     = 0;

    std::deque<SP<CSessionDevice>> devices;

    udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(enumerate)) {
        auto path   = udev_list_entry_get_name(entry);
        auto device = udev_device_new_from_syspath(backend->session->udevHandle, path);
        if (!device) {
            backend->log(AQ_LOG_WARNING, std::format("drm: Skipping device {}", path ? path : "unknown"));
            continue;
        }

        backend->log(AQ_LOG_DEBUG, std::format("drm: Enumerated device {}", path ? path : "unknown"));

        auto seat = udev_device_get_property_value(device, "ID_SEAT");
        if (!seat)
            seat = "seat0";

        if (!backend->session->seatName.empty() && backend->session->seatName != seat) {
            backend->log(AQ_LOG_WARNING, std::format("drm: Skipping device {} because seat {} doesn't match our {}", path ? path : "unknown", seat, backend->session->seatName));
            udev_device_unref(device);
            continue;
        }

        auto pciDevice = udev_device_get_parent_with_subsystem_devtype(device, "pci", nullptr);
        bool isBootVGA = false;
        if (pciDevice) {
            auto id   = udev_device_get_sysattr_value(pciDevice, "boot_vga");
            isBootVGA = id && id == std::string{"1"};
        }

        if (!udev_device_get_devnode(device)) {
            backend->log(AQ_LOG_ERROR, std::format("drm: Skipping device {}, no devnode", path ? path : "unknown"));
            udev_device_unref(device);
            continue;
        }

        auto sessionDevice = CSessionDevice::openIfKMS(backend->session, udev_device_get_devnode(device));
        if (!sessionDevice) {
            backend->log(AQ_LOG_ERROR, std::format("drm: Skipping device {}, not a KMS device", path ? path : "unknown"));
            udev_device_unref(device);
            continue;
        }

        udev_device_unref(device);

        if (isBootVGA)
            devices.push_front(sessionDevice);
        else
            devices.push_back(sessionDevice);

        ++i;
    }

    udev_enumerate_unref(enumerate);

    std::vector<SP<CSessionDevice>> vecDevices;

    auto                            explicitGpus = getenv("AQ_DRM_DEVICES");
    if (explicitGpus) {
        backend->log(AQ_LOG_DEBUG, std::format("drm: Explicit device list {}", explicitGpus));
        Hyprutils::String::CVarList explicitDevices(explicitGpus, 0, ':', true);

        // Iterate over GPUs and canonicalize the paths
        for (auto& d : explicitDevices) {
            std::error_code ec;

            auto            canonicalFilePath = std::filesystem::canonical(d, ec);

            // If there is an error, log and continue.
            // TODO: Verify that the path is a valid DRM device. (https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/backend/session/session.c?ref_type=heads#L369-387)
            if (ec) {
                backend->log(AQ_LOG_ERROR, std::format("drm: Failed to canonicalize path {}", d));
                continue;
            }

            d = canonicalFilePath.string();
        }

        for (auto& d : explicitDevices) {
            bool found = false;
            for (auto& vd : devices) {
                if (vd->path == d) {
                    vecDevices.emplace_back(vd);
                    found = true;
                    break;
                }
            }

            if (found)
                backend->log(AQ_LOG_DEBUG, std::format("drm: Explicit device {} found", d));
            else
                backend->log(AQ_LOG_ERROR, std::format("drm: Explicit device {} not found", d));
        }
    } else {
        for (auto& d : devices) {
            vecDevices.push_back(d);
        }
    }

    return vecDevices;
}

std::vector<SP<CDRMBackend>> Aquamarine::CDRMBackend::attempt(SP<CBackend> backend) {
    if (!backend->session)
        backend->session = CSession::attempt(backend);

    if (!backend->session) {
        backend->log(AQ_LOG_ERROR, "Failed to open a session");
        return {};
    }

    if (!backend->session->active) {
        backend->log(AQ_LOG_DEBUG, "Session is not active, waiting for 5s");

        auto started = std::chrono::system_clock::now();

        while (!backend->session->active) {
            std::this_thread::sleep_for(std::chrono::milliseconds(250));
            backend->session->dispatchPendingEventsAsync();

            if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - started).count() >= 5000) {
                backend->log(AQ_LOG_DEBUG, "Session timeout reached");
                break;
            }
        }

        if (!backend->session->active) {
            backend->log(AQ_LOG_DEBUG, "Session could not be activated in time");
            return {};
        }
    }

    auto gpus = scanGPUs(backend);

    if (gpus.empty()) {
        backend->log(AQ_LOG_ERROR, "drm: Found no gpus to use, cannot continue");
        return {};
    }

    backend->log(AQ_LOG_DEBUG, std::format("drm: Found {} GPUs", gpus.size()));

    std::vector<SP<CDRMBackend>> backends;
    SP<CDRMBackend>              newPrimary;

    for (auto& gpu : gpus) {
        auto drmBackend  = SP<CDRMBackend>(new CDRMBackend(backend));
        drmBackend->self = drmBackend;

        if (!drmBackend->registerGPU(gpu, newPrimary)) {
            backend->log(AQ_LOG_ERROR, std::format("drm: Failed to register gpu {}", gpu->path));
            continue;
        } else
            backend->log(AQ_LOG_DEBUG, std::format("drm: Registered gpu {}", gpu->path));

        // TODO: consider listening for new devices
        // But if you expect me to handle gpu hotswaps you are probably insane LOL

        if (!drmBackend->checkFeatures()) {
            backend->log(AQ_LOG_ERROR, "drm: Failed checking features");
            continue;
        }

        if (!drmBackend->initResources()) {
            backend->log(AQ_LOG_ERROR, "drm: Failed initializing resources");
            continue;
        }

        backend->log(AQ_LOG_DEBUG, std::format("drm: Basic init pass for gpu {}", gpu->path));

        drmBackend->grabFormats();

        drmBackend->scanConnectors();

        drmBackend->recheckCRTCs();

        if (!newPrimary) {
            backend->log(AQ_LOG_DEBUG, std::format("drm: gpu {} becomes primary drm", gpu->path));
            newPrimary = drmBackend;
        }

        backends.emplace_back(drmBackend);

        // so that session can handle udev change/remove events for this gpu
        backend->session->sessionDevices.push_back(gpu);
    }

    return backends;
}

Aquamarine::CDRMBackend::~CDRMBackend() {
    ;
}

void Aquamarine::CDRMBackend::log(eBackendLogLevel l, const std::string& s) {
    backend->log(l, s);
}

bool Aquamarine::CDRMBackend::sessionActive() {
    return backend->session->active;
}

void Aquamarine::CDRMBackend::restoreAfterVT() {
    backend->log(AQ_LOG_DEBUG, "drm: Restoring after VT switch");

    scanConnectors();
    recheckCRTCs();

    backend->log(AQ_LOG_DEBUG, "drm: Rescanned connectors");

    if (!impl->reset())
        backend->log(AQ_LOG_ERROR, "drm: failed reset");

    std::vector<SP<SDRMConnector>> noMode;

    for (auto& c : connectors) {
        if (!c->crtc || !c->output)
            continue;

        SDRMConnectorCommitData data = {
            .mainFB   = nullptr,
            .modeset  = true,
            .blocking = true,
            .flags    = 0,
            .test     = false,
        };

        auto& STATE = c->output->state->state();
        auto& MODE  = STATE.customMode ? STATE.customMode : STATE.mode;

        if (!MODE) {
            backend->log(AQ_LOG_WARNING, "drm: Connector {} has output but state has no mode, will send a reset state event later.");
            noMode.emplace_back(c);
            continue;
        }

        if (MODE->modeInfo.has_value())
            data.modeInfo = *MODE->modeInfo;
        else
            data.calculateMode(c);

        if (STATE.buffer) {
            SP<CDRMFB> drmFB;
            auto       buf   = STATE.buffer;
            bool       isNew = false;

            drmFB = CDRMFB::create(buf, self, &isNew);

            if (!drmFB)
                backend->log(AQ_LOG_ERROR, "drm: Buffer failed to import to KMS");

            data.mainFB = drmFB;
        }

        if (c->crtc->pendingCursor)
            data.cursorFB = c->crtc->pendingCursor;

        if (data.cursorFB && data.cursorFB->buffer->dmabuf().modifier == DRM_FORMAT_MOD_INVALID)
            data.cursorFB = nullptr;

        backend->log(AQ_LOG_DEBUG,
                     std::format("drm: Restoring crtc {} with clock {} hdisplay {} vdisplay {} vrefresh {}", c->crtc->id, data.modeInfo.clock, data.modeInfo.hdisplay,
                                 data.modeInfo.vdisplay, data.modeInfo.vrefresh));

        if (!impl->commit(c, data))
            backend->log(AQ_LOG_ERROR, std::format("drm: crtc {} failed restore", c->crtc->id));
    }

    for (auto& c : noMode) {
        if (!c->output)
            continue;

        // tell the consumer to re-set a state because we had no mode
        c->output->events.state.emit(IOutput::SStateEvent{});
    }
}

bool Aquamarine::CDRMBackend::checkFeatures() {
    uint64_t curW = 0, curH = 0;
    if (drmGetCap(gpu->fd, DRM_CAP_CURSOR_WIDTH, &curW))
        curW = 64;
    if (drmGetCap(gpu->fd, DRM_CAP_CURSOR_HEIGHT, &curH))
        curH = 64;

    drmProps.cursorSize = Hyprutils::Math::Vector2D{(double)curW, (double)curH};

    uint64_t cap = 0;
    if (drmGetCap(gpu->fd, DRM_CAP_PRIME, &cap) || !(cap & DRM_PRIME_CAP_IMPORT)) {
        backend->log(AQ_LOG_ERROR, std::format("drm: DRM_PRIME_CAP_IMPORT unsupported"));
        return false;
    }

    if (drmGetCap(gpu->fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap) || !cap) {
        backend->log(AQ_LOG_ERROR, std::format("drm: DRM_CAP_CRTC_IN_VBLANK_EVENT unsupported"));
        return false;
    }

    if (drmGetCap(gpu->fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap) || !cap) {
        backend->log(AQ_LOG_ERROR, std::format("drm: DRM_PRIME_CAP_IMPORT unsupported"));
        return false;
    }

    if (drmSetClientCap(gpu->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) {
        backend->log(AQ_LOG_ERROR, std::format("drm: DRM_CLIENT_CAP_UNIVERSAL_PLANES unsupported"));
        return false;
    }

    drmProps.supportsAsyncCommit     = drmGetCap(gpu->fd, DRM_CAP_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1;
    drmProps.supportsAddFb2Modifiers = drmGetCap(gpu->fd, DRM_CAP_ADDFB2_MODIFIERS, &cap) == 0 && cap == 1;
    drmProps.supportsTimelines       = drmGetCap(gpu->fd, DRM_CAP_SYNCOBJ_TIMELINE, &cap) == 0 && cap == 1;

    if (envEnabled("AQ_NO_ATOMIC")) {
        backend->log(AQ_LOG_WARNING, "drm: AQ_NO_ATOMIC enabled, using the legacy drm iface");
        impl = makeShared<CDRMLegacyImpl>(self.lock());
    } else if (drmSetClientCap(gpu->fd, DRM_CLIENT_CAP_ATOMIC, 1)) {
        backend->log(AQ_LOG_WARNING, "drm: failed to set DRM_CLIENT_CAP_ATOMIC, falling back to legacy");
        impl = makeShared<CDRMLegacyImpl>(self.lock());
    } else {
        backend->log(AQ_LOG_DEBUG, "drm: Atomic supported, using atomic for modesetting");
        impl                         = makeShared<CDRMAtomicImpl>(self.lock());
        drmProps.supportsAsyncCommit = drmGetCap(gpu->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &cap) == 0 && cap == 1;
        atomic                       = true;
    }

    backend->log(AQ_LOG_DEBUG, std::format("drm: drmProps.supportsAsyncCommit: {}", drmProps.supportsAsyncCommit));
    backend->log(AQ_LOG_DEBUG, std::format("drm: drmProps.supportsAddFb2Modifiers: {}", drmProps.supportsAddFb2Modifiers));
    backend->log(AQ_LOG_DEBUG, std::format("drm: drmProps.supportsTimelines: {}", drmProps.supportsTimelines));

    // TODO: allow no-modifiers?

    return true;
}

bool Aquamarine::CDRMBackend::initResources() {
    auto resources = drmModeGetResources(gpu->fd);
    if (!resources) {
        backend->log(AQ_LOG_ERROR, std::format("drm: drmModeGetResources failed"));
        return false;
    }

    backend->log(AQ_LOG_DEBUG, std::format("drm: found {} CRTCs", resources->count_crtcs));

    for (size_t i = 0; i < resources->count_crtcs; ++i) {
        auto CRTC     = makeShared<SDRMCRTC>();
        CRTC->id      = resources->crtcs[i];
        CRTC->backend = self;

        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;
        }

        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;
        }

        crtcs.emplace_back(CRTC);
    }

    if (crtcs.size() > 32) {
        backend->log(AQ_LOG_CRITICAL, "drm: Cannot support more than 32 CRTCs");
        return false;
    }

    // initialize planes
    auto planeResources = drmModeGetPlaneResources(gpu->fd);
    if (!planeResources) {
        backend->log(AQ_LOG_ERROR, std::format("drm: drmModeGetPlaneResources failed"));
        return false;
    }

    backend->log(AQ_LOG_DEBUG, std::format("drm: found {} planes", planeResources->count_planes));

    for (uint32_t i = 0; i < planeResources->count_planes; ++i) {
        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)) {
            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);

    return true;
}

bool Aquamarine::CDRMBackend::shouldBlit() {
    return primary;
}

bool Aquamarine::CDRMBackend::initMgpu() {
    SP<CGBMAllocator> newAllocator;
    if (primary || backend->primaryAllocator->type() != AQ_ALLOCATOR_TYPE_GBM) {
        newAllocator            = CGBMAllocator::create(backend->reopenDRMNode(gpu->fd), backend);
        rendererState.allocator = newAllocator;
    } else {
        newAllocator            = ((CGBMAllocator*)backend->primaryAllocator.get())->self.lock();
        rendererState.allocator = newAllocator;
    }

    if (!rendererState.allocator) {
        backend->log(AQ_LOG_ERROR, "drm: initMgpu: no allocator");
        return false;
    }

    rendererState.renderer = CDRMRenderer::attempt(newAllocator, backend.lock());

    if (!rendererState.renderer) {
        backend->log(AQ_LOG_ERROR, "drm: initMgpu: no renderer");
        return false;
    }

    rendererState.renderer->self = rendererState.renderer;

    buildGlFormats(rendererState.renderer->formats);

    return true;
}

void Aquamarine::CDRMBackend::buildGlFormats(const std::vector<SGLFormat>& fmts) {
    std::vector<SDRMFormat> result;

    for (auto& fmt : fmts) {
        if (fmt.external)
            continue;

        if (auto it = std::find_if(result.begin(), result.end(), [fmt](const auto& e) { return fmt.drmFormat == e.drmFormat; }); it != result.end()) {
            it->modifiers.emplace_back(fmt.modifier);
            continue;
        }

        result.emplace_back(SDRMFormat{
            fmt.drmFormat,
            {fmt.modifier},
        });
    }

    glFormats = result;
}

void Aquamarine::CDRMBackend::recheckCRTCs() {
    if (connectors.empty() || crtcs.empty())
        return;

    backend->log(AQ_LOG_DEBUG, "drm: Rechecking CRTCs");

    std::vector<SP<SDRMConnector>> recheck, changed;
    for (auto& c : connectors) {
        if (c->crtc && c->status == DRM_MODE_CONNECTED) {
            backend->log(AQ_LOG_DEBUG, std::format("drm: Skipping connector {}, has crtc {} and is connected", c->szName, c->crtc->id));
            continue;
        }

        recheck.emplace_back(c);
        backend->log(AQ_LOG_DEBUG, std::format("drm: connector {}, has crtc {}, will be rechecked", c->szName, c->crtc ? (int)c->crtc->id : -1));
    }

    for (size_t i = 0; i < crtcs.size(); ++i) {
        bool taken = false;
        for (auto& c : connectors) {
            if (c->crtc != crtcs.at(i))
                continue;

            if (c->status != DRM_MODE_CONNECTED)
                continue;

            backend->log(AQ_LOG_DEBUG, std::format("drm: slot {} crtc {} taken by {}, skipping", i, c->crtc->id, c->szName));
            taken = true;
            break;
        }

        if (taken)
            continue;

        bool assigned = false;

        // try to use a connected connector
        for (auto& c : recheck) {
            if (!(c->possibleCrtcs & (1 << i)))
                continue;

            if (c->status != DRM_MODE_CONNECTED)
                continue;

            // deactivate old output
            if (c->output && c->output->state && c->output->state->state().enabled) {
                c->output->state->setEnabled(false);
                c->output->commit();
            }

            backend->log(AQ_LOG_DEBUG,
                         std::format("drm: connected slot {} crtc {} assigned to {}{}", i, crtcs.at(i)->id, c->szName, c->crtc ? std::format(" (old {})", c->crtc->id) : ""));
            c->crtc  = crtcs.at(i);
            assigned = true;
            changed.emplace_back(c);
            std::erase(recheck, c);
            break;
        }

        if (!assigned)
            backend->log(AQ_LOG_DEBUG, std::format("drm: slot {} crtc {} unassigned", i, crtcs.at(i)->id));
    }

    for (auto& c : connectors) {
        if (c->status == DRM_MODE_CONNECTED)
            continue;

        backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} is not connected{}", c->szName, c->crtc ? std::format(", removing old crtc {}", c->crtc->id) : ""));
    }

    // if any connectors get a crtc and are connected, we need to rescan to assign them outputs.
    bool rescan = false;
    for (auto& c : changed) {
        if (!c->output && c->status == DRM_MODE_CONNECTED) {
            rescan = true;
            continue;
        }

        // tell the user to re-assign a valid mode etc
        if (c->output)
            c->output->events.state.emit(IOutput::SStateEvent{});
    }

    backend->log(AQ_LOG_DEBUG, "drm: rescanning after realloc");
    scanConnectors();
}

bool Aquamarine::CDRMBackend::grabFormats() {
    // FIXME: do this properly maybe?
    return true;
}

bool Aquamarine::CDRMBackend::registerGPU(SP<CSessionDevice> gpu_, SP<CDRMBackend> primary_) {
    gpu     = gpu_;
    primary = primary_;

    auto drmName = drmGetDeviceNameFromFd2(gpu->fd);
    auto drmVer  = drmGetVersion(gpu->fd);

    gpuName = drmName;

    auto drmVerName = drmVer->name ? drmVer->name : "unknown";
    if (std::string_view(drmVerName) == "evdi")
        primary = {};

    backend->log(AQ_LOG_DEBUG,
                 std::format("drm: Starting backend for {}, with driver {}{}", drmName ? drmName : "unknown", drmVerName,
                             (primary ? std::format(" with primary {}", primary->gpu->path) : "")));

    drmFreeVersion(drmVer);

    listeners.gpuChange = gpu->events.change.registerListener([this](std::any d) {
        auto E = std::any_cast<CSessionDevice::SChangeEvent>(d);
        if (E.type == CSessionDevice::AQ_SESSION_EVENT_CHANGE_HOTPLUG) {
            backend->log(AQ_LOG_DEBUG, std::format("drm: Got a hotplug event for {}", gpuName));
            scanConnectors();
            recheckCRTCs();
        } else if (E.type == CSessionDevice::AQ_SESSION_EVENT_CHANGE_LEASE) {
            backend->log(AQ_LOG_DEBUG, std::format("drm: Got a lease event for {}", gpuName));
            scanLeases();
        }
    });

    listeners.gpuRemove = gpu->events.remove.registerListener(
        [this](std::any d) { backend->log(AQ_LOG_ERROR, std::format("drm: !!!!FIXME: Got a remove event for {}, this is not handled properly!!!!!", gpuName)); });

    return true;
}

eBackendType Aquamarine::CDRMBackend::type() {
    return eBackendType::AQ_BACKEND_DRM;
}

void Aquamarine::CDRMBackend::scanConnectors() {
    backend->log(AQ_LOG_DEBUG, std::format("drm: Scanning connectors for {}", gpu->path));

    auto resources = drmModeGetResources(gpu->fd);
    if (!resources) {
        backend->log(AQ_LOG_ERROR, std::format("drm: Scanning connectors for {} failed", gpu->path));
        return;
    }

    for (size_t i = 0; i < resources->count_connectors; ++i) {
        uint32_t          connectorID = resources->connectors[i];

        SP<SDRMConnector> conn;
        auto              drmConn = drmModeGetConnector(gpu->fd, connectorID);

        backend->log(AQ_LOG_DEBUG, std::format("drm: Scanning connector id {}", connectorID));

        if (!drmConn) {
            backend->log(AQ_LOG_ERROR, std::format("drm: Failed to get connector id {}", connectorID));
            continue;
        }

        auto it = std::find_if(connectors.begin(), connectors.end(), [connectorID](const auto& e) { return e->id == connectorID; });
        if (it == connectors.end()) {
            backend->log(AQ_LOG_DEBUG, std::format("drm: Initializing connector id {}", connectorID));
            conn          = connectors.emplace_back(SP<SDRMConnector>(new SDRMConnector()));
            conn->self    = conn;
            conn->backend = self;
            conn->id      = connectorID;
            if (!conn->init(drmConn)) {
                backend->log(AQ_LOG_ERROR, std::format("drm: Connector id {} failed initializing", connectorID));
                connectors.pop_back();
                continue;
            }
        } else {
            backend->log(AQ_LOG_DEBUG, std::format("drm: Connector id {} already initialized", connectorID));
            conn = *it;
        }

        conn->status = drmConn->connection;

        if (conn->crtc)
            conn->recheckCRTCProps();

        backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connection state: {}", connectorID, (int)drmConn->connection));

        if (conn->status == DRM_MODE_CONNECTED && !conn->output) {
            backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} connected", conn->szName));
            conn->connect(drmConn);
        } else if (conn->status != DRM_MODE_CONNECTED && conn->output) {
            backend->log(AQ_LOG_DEBUG, std::format("drm: Connector {} disconnected", conn->szName));
            conn->disconnect();
        }

        drmModeFreeConnector(drmConn);
    }

    drmModeFreeResources(resources);
}

void Aquamarine::CDRMBackend::scanLeases() {
    auto lessees = drmModeListLessees(gpu->fd);
    if (!lessees) {
        backend->log(AQ_LOG_ERROR, "drmModeListLessees failed");
        return;
    }

    for (auto& c : connectors) {
        if (!c->output || !c->output->lease)
            continue;

        bool has = false;
        for (size_t i = 0; i < lessees->count; ++i) {
            if (lessees->lessees[i] == c->output->lease->lesseeID) {
                has = true;
                break;
            }
        }

        if (has)
            continue;

        backend->log(AQ_LOG_DEBUG, std::format("lessee {} gone, removing", c->output->lease->lesseeID));

        // don't terminate
        c->output->lease->active = false;

        auto l = c->output->lease;

        for (auto& c2 : connectors) {
            if (!c2->output || c2->output->lease != c->output->lease)
                continue;

            c2->output->lease.reset();
        }

        l->destroy();
    }

    drmFree(lessees);
}

bool Aquamarine::CDRMBackend::start() {
    impl->reset();
    return true;
}

std::vector<Hyprutils::Memory::CSharedPointer<SPollFD>> Aquamarine::CDRMBackend::pollFDs() {
    return {makeShared<SPollFD>(gpu->fd, [this]() { dispatchEvents(); })};
}

int Aquamarine::CDRMBackend::drmFD() {
    return gpu->fd;
}

static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void* data) {
    auto pageFlip = (SDRMPageFlip*)data;

    if (!pageFlip->connector)
        return;

    pageFlip->connector->isPageFlipPending = false;

    const auto& BACKEND = pageFlip->connector->backend;

    TRACE(BACKEND->log(AQ_LOG_TRACE, std::format("drm: pf event seq {} sec {} usec {} crtc {}", seq, tv_sec, tv_usec, crtc_id)));

    if (pageFlip->connector->status != DRM_MODE_CONNECTED || !pageFlip->connector->crtc) {
        BACKEND->log(AQ_LOG_DEBUG, "drm: Ignoring a pf event from a disabled crtc / connector");
        return;
    }

    pageFlip->connector->onPresent();

    uint32_t flags = IOutput::AQ_OUTPUT_PRESENT_VSYNC | IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK | IOutput::AQ_OUTPUT_PRESENT_HW_COMPLETION | IOutput::AQ_OUTPUT_PRESENT_ZEROCOPY;

    timespec presented = {.tv_sec = (time_t)tv_sec, .tv_nsec = (long)(tv_usec * 1000)};

    pageFlip->connector->output->events.present.emit(IOutput::SPresentEvent{
        .presented = BACKEND->sessionActive(),
        .when      = &presented,
        .seq       = seq,
        .refresh   = (int)(pageFlip->connector->refresh ? (1000000000000LL / pageFlip->connector->refresh) : 0),
        .flags     = flags,
    });

    if (BACKEND->sessionActive() && !pageFlip->connector->frameEventScheduled && pageFlip->connector->output->enabledState)
        pageFlip->connector->output->events.frame.emit();
}

bool Aquamarine::CDRMBackend::dispatchEvents() {
    drmEventContext event = {
        .version            = 3,
        .page_flip_handler2 = ::handlePF,
    };

    if (drmHandleEvent(gpu->fd, &event) != 0)
        backend->log(AQ_LOG_ERROR, std::format("drm: Failed to handle event on fd {}", gpu->fd));

    return true;
}

uint32_t Aquamarine::CDRMBackend::capabilities() {
    if (getCursorFormats().empty())
        return 0;
    return eBackendCapabilities::AQ_BACKEND_CAPABILITY_POINTER;
}

bool Aquamarine::CDRMBackend::setCursor(SP<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot) {
    return false;
}

void Aquamarine::CDRMBackend::onReady() {
    backend->log(AQ_LOG_DEBUG, std::format("drm: Connectors size2 {}", connectors.size()));

    // init a drm renderer to gather gl formats.
    // if we are secondary, initMgpu will have done that
    if (!primary) {
        auto a = CGBMAllocator::create(backend->reopenDRMNode(gpu->fd), backend);
        if (!a)
            backend->log(AQ_LOG_ERROR, "drm: onReady: no renderer for gl formats");
        else {
            auto r = CDRMRenderer::attempt(a, backend.lock());
            if (!r)
                backend->log(AQ_LOG_ERROR, "drm: onReady: no renderer for gl formats");
            else {
                TRACE(backend->log(AQ_LOG_TRACE, std::format("drm: onReady: gathered {} gl formats", r->formats.size())));
                buildGlFormats(r->formats);
                r.reset();
                a.reset();
            }
        }
    }

    for (auto& c : connectors) {
        backend->log(AQ_LOG_DEBUG, std::format("drm: onReady: connector {}", c->id));
        if (!c->output)
            continue;

        backend->log(AQ_LOG_DEBUG, std::format("drm: onReady: connector {} has output name {}", c->id, c->output->name));

        // swapchain has to be created here because allocator is absent in connect if not ready
        c->output->swapchain = CSwapchain::create(backend->primaryAllocator, self.lock());
        c->output->swapchain->reconfigure(SSwapchainOptions{.length = 0, .scanout = true, .multigpu = !!primary}); // mark the swapchain for scanout
        c->output->needsFrame = true;

        backend->events.newOutput.emit(SP<IOutput>(c->output));
    }

    if (!initMgpu()) {
        backend->log(AQ_LOG_ERROR, "drm: Failed initializing mgpu");
        return;
    }
}

std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderFormats() {
    for (auto& p : planes) {
        if (p->type != DRM_PLANE_TYPE_PRIMARY)
            continue;

        return p->formats;
    }

    return {};
}

std::vector<SDRMFormat> Aquamarine::CDRMBackend::getRenderableFormats() {
    return glFormats;
}

std::vector<SDRMFormat> Aquamarine::CDRMBackend::getCursorFormats() {
    for (auto& p : planes) {
        if (p->type != DRM_PLANE_TYPE_CURSOR)
            continue;

        if (primary) {
            TRACE(backend->log(AQ_LOG_TRACE, std::format("drm: getCursorFormats on secondary {}", gpu->path)));

            // this is a secondary GPU renderer. In order to receive buffers,
            // we'll force linear modifiers.
            // TODO: don't. Find a common maybe?
            auto fmts = p->formats;
            for (auto& fmt : fmts) {
                fmt.modifiers = {DRM_FORMAT_MOD_LINEAR};
            }
            return fmts;
        }

        return p->formats;
    }

    return {};
}

bool Aquamarine::CDRMBackend::createOutput(const std::string&) {
    return false;
}

int Aquamarine::CDRMBackend::getNonMasterFD() {
    int fd = open(gpuName.c_str(), O_RDWR | O_CLOEXEC);

    if (fd < 0) {
        backend->log(AQ_LOG_ERROR, "drm: couldn't dupe fd for non master");
        return -1;
    }

    if (drmIsMaster(fd) && drmDropMaster(fd) < 0) {
        backend->log(AQ_LOG_ERROR, "drm: couldn't drop master from duped fd");
        return -1;
    }

    return fd;
}

SP<IAllocator> Aquamarine::CDRMBackend::preferredAllocator() {
    return backend->primaryAllocator;
}

bool Aquamarine::SDRMPlane::init(drmModePlane* plane) {
    id = plane->plane_id;

    if (!getDRMPlaneProps(backend->gpu->fd, id, &props))
        return false;

    if (!getDRMProp(backend->gpu->fd, id, props.type, &type))
        return false;

    initialID = id;

    backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Plane {} has type {}", id, (int)type));

    backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Plane {} has {} formats", id, plane->count_formats));

    for (size_t i = 0; i < plane->count_formats; ++i) {
        if (type != DRM_PLANE_TYPE_CURSOR)
            formats.emplace_back(SDRMFormat{.drmFormat = plane->formats[i], .modifiers = {DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID}});
        else
            formats.emplace_back(SDRMFormat{.drmFormat = plane->formats[i], .modifiers = {DRM_FORMAT_MOD_LINEAR}});

        TRACE(backend->backend->log(AQ_LOG_TRACE, std::format("drm: | Format {}", fourccToName(plane->formats[i]))));
    }

    if (props.in_formats && backend->drmProps.supportsAddFb2Modifiers) {
        backend->backend->log(AQ_LOG_DEBUG, "drm: Plane: checking for modifiers");

        uint64_t blobID = 0;
        if (!getDRMProp(backend->gpu->fd, id, props.in_formats, &blobID)) {
            backend->backend->log(AQ_LOG_ERROR, "drm: Plane: No blob id");
            return false;
        }

        auto blob = drmModeGetPropertyBlob(backend->gpu->fd, blobID);
        if (!blob) {
            backend->backend->log(AQ_LOG_ERROR, "drm: Plane: No property");
            return false;
        }

        drmModeFormatModifierIterator iter = {0};
        while (drmModeFormatModifierBlobIterNext(blob, &iter)) {
            auto it = std::find_if(formats.begin(), formats.end(), [iter](const auto& e) { return e.drmFormat == iter.fmt; });

            TRACE(backend->backend->log(AQ_LOG_TRACE, std::format("drm: | Modifier {} with format {}", iter.mod, fourccToName(iter.fmt))));

            if (it == formats.end())
                formats.emplace_back(SDRMFormat{.drmFormat = iter.fmt, .modifiers = {iter.mod}});
            else
                it->modifiers.emplace_back(iter.mod);
        }

        drmModeFreePropertyBlob(blob);
    }

    for (size_t i = 0; i < backend->crtcs.size(); ++i) {
        uint32_t crtcBit = (1 << i);
        if (!(plane->possible_crtcs & crtcBit))
            continue;

        auto CRTC = backend->crtcs.at(i);
        if (type == DRM_PLANE_TYPE_PRIMARY && !CRTC->primary) {
            CRTC->primary = self.lock();
            break;
        }

        if (type == DRM_PLANE_TYPE_CURSOR && !CRTC->cursor) {
            CRTC->cursor = self.lock();
            break;
        }
    }

    return true;
}

SP<SDRMCRTC> Aquamarine::SDRMConnector::getCurrentCRTC(const drmModeConnector* connector) {
    uint32_t crtcID = 0;
    if (props.crtc_id) {
        TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Using crtc_id for finding crtc"));
        uint64_t value = 0;
        if (!getDRMProp(backend->gpu->fd, id, props.crtc_id, &value)) {
            backend->backend->log(AQ_LOG_ERROR, "drm: Failed to get CRTC_ID");
            return nullptr;
        }
        crtcID = static_cast<uint32_t>(value);
    } else if (connector->encoder_id) {
        TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Using encoder_id for finding crtc"));
        auto encoder = drmModeGetEncoder(backend->gpu->fd, connector->encoder_id);
        if (!encoder) {
            backend->backend->log(AQ_LOG_ERROR, "drm: drmModeGetEncoder failed");
            return nullptr;
        }
        crtcID = encoder->crtc_id;
        drmModeFreeEncoder(encoder);
    } else {
        backend->backend->log(AQ_LOG_ERROR, "drm: Connector has neither crtc_id nor encoder_id");
        return nullptr;
    }

    if (crtcID == 0) {
        backend->backend->log(AQ_LOG_ERROR, "drm: getCurrentCRTC: No CRTC 0");
        return nullptr;
    }

    auto it = std::find_if(backend->crtcs.begin(), backend->crtcs.end(), [crtcID](const auto& e) { return e->id == crtcID; });

    if (it == backend->crtcs.end()) {
        backend->backend->log(AQ_LOG_ERROR, std::format("drm: Failed to find a CRTC with ID {}", crtcID));
        return nullptr;
    }

    return *it;
}

bool Aquamarine::SDRMConnector::init(drmModeConnector* connector) {
    pendingPageFlip.connector = self.lock();

    if (!getDRMConnectorProps(backend->gpu->fd, id, &props))
        return false;

    auto name = drmModeGetConnectorTypeName(connector->connector_type);
    if (!name)
        name = "ERROR";

    szName = std::format("{}-{}", name, connector->connector_type_id);
    backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Connector gets name {}", szName));

    possibleCrtcs = drmModeConnectorGetPossibleCrtcs(backend->gpu->fd, connector);
    if (!possibleCrtcs)
        backend->backend->log(AQ_LOG_ERROR, "drm: No CRTCs possible");

    crtc = getCurrentCRTC(connector);

    return true;
}

Aquamarine::SDRMConnector::~SDRMConnector() {
    disconnect();
}

static int32_t calculateRefresh(const drmModeModeInfo& mode) {
    int32_t refresh = (mode.clock * 1000000LL / mode.htotal + mode.vtotal / 2) / mode.vtotal;

    if (mode.flags & DRM_MODE_FLAG_INTERLACE)
        refresh *= 2;

    if (mode.flags & DRM_MODE_FLAG_DBLSCAN)
        refresh /= 2;

    if (mode.vscan > 1)
        refresh /= mode.vscan;

    return refresh;
}

drmModeModeInfo* Aquamarine::SDRMConnector::getCurrentMode() {
    if (!crtc)
        return nullptr;

    if (crtc->props.mode_id) {
        size_t size = 0;
        return (drmModeModeInfo*)getDRMPropBlob(backend->gpu->fd, crtc->id, crtc->props.mode_id, &size);
    }

    auto drmCrtc = drmModeGetCrtc(backend->gpu->fd, crtc->id);
    if (!drmCrtc)
        return nullptr;
    if (!drmCrtc->mode_valid) {
        drmModeFreeCrtc(drmCrtc);
        return nullptr;
    }

    drmModeModeInfo* modeInfo = (drmModeModeInfo*)malloc(sizeof(drmModeModeInfo));
    if (!modeInfo) {
        drmModeFreeCrtc(drmCrtc);
        return nullptr;
    }

    *modeInfo = drmCrtc->mode;
    drmModeFreeCrtc(drmCrtc);

    return modeInfo;
}

void Aquamarine::SDRMConnector::parseEDID(std::vector<uint8_t> data) {
    auto info = di_info_parse_edid(data.data(), data.size());
    if (!info) {
        backend->backend->log(AQ_LOG_ERROR, "drm: failed to parse edid");
        return;
    }

    auto edid       = di_info_get_edid(info);
    auto venProduct = di_edid_get_vendor_product(edid);
    auto pnpID      = std::string{venProduct->manufacturer, 3};
    if (PNPIDS.contains(pnpID))
        make = PNPIDS.at(pnpID);
    else
        make = pnpID;

    auto mod = di_info_get_model(info);
    auto ser = di_info_get_serial(info);

    model  = mod ? mod : "";
    serial = ser ? ser : "";

    di_info_destroy(info);
}

void Aquamarine::SDRMConnector::recheckCRTCProps() {
    if (!crtc || !output)
        return;

    uint64_t prop      = 0;
    canDoVrr           = props.vrr_capable && crtc->props.vrr_enabled && getDRMProp(backend->gpu->fd, id, props.vrr_capable, &prop) && prop;
    output->vrrCapable = canDoVrr;

    backend->backend->log(AQ_LOG_DEBUG,
                          std::format("drm: connector {} crtc is {} of vrr: props.vrr_capable -> {}, crtc->props.vrr_enabled -> {}", szName, (canDoVrr ? "capable" : "incapable"),
                                      props.vrr_capable, crtc->props.vrr_enabled));

    output->supportsExplicit = backend->drmProps.supportsTimelines && crtc->props.out_fence_ptr && crtc->primary->props.in_fence_fd;

    backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Explicit sync {}", output->supportsExplicit ? "supported" : "unsupported"));
}

void Aquamarine::SDRMConnector::connect(drmModeConnector* connector) {
    if (output) {
        backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Not connecting connector {} because it's already connected", szName));
        return;
    }

    backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Connecting connector {}, {}", szName, crtc ? std::format("CRTC ID {}", crtc->id) : "no CRTC"));

    output            = SP<CDRMOutput>(new CDRMOutput(szName, backend, self.lock()));
    output->self      = output;
    output->connector = self.lock();

    backend->backend->log(AQ_LOG_DEBUG, "drm: Dumping detected modes:");

    auto currentModeInfo = getCurrentMode();

    for (int i = 0; i < connector->count_modes; ++i) {
        auto& drmMode = connector->modes[i];

        if (drmMode.flags & DRM_MODE_FLAG_INTERLACE) {
            backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Skipping mode {} because it's interlaced", i));
            continue;
        }

        auto aqMode         = makeShared<SOutputMode>();
        aqMode->pixelSize   = {drmMode.hdisplay, drmMode.vdisplay};
        aqMode->refreshRate = calculateRefresh(drmMode);
        aqMode->preferred   = (drmMode.type & DRM_MODE_TYPE_PREFERRED);
        aqMode->modeInfo    = drmMode;

        if (i == 1)
            fallbackMode = aqMode;

        output->modes.emplace_back(aqMode);

        if (currentModeInfo && std::memcmp(&drmMode, currentModeInfo, sizeof(drmModeModeInfo))) {
            output->state->setMode(aqMode);

            //uint64_t modeID = 0;
            // getDRMProp(backend->gpu->fd, crtc->id, crtc->props.mode_id, &modeID);
        }

        backend->backend->log(AQ_LOG_DEBUG,
                              std::format("drm: Mode {}: {}x{}@{:.2f}Hz {}", i, (int)aqMode->pixelSize.x, (int)aqMode->pixelSize.y, aqMode->refreshRate / 1000.0,
                                          aqMode->preferred ? " (preferred)" : ""));
    }

    if (!currentModeInfo && fallbackMode)
        output->state->setMode(fallbackMode);

    output->physicalSize = {(double)connector->mmWidth, (double)connector->mmHeight};

    backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Physical size {} (mm)", output->physicalSize));

    switch (connector->subpixel) {
        case DRM_MODE_SUBPIXEL_NONE: output->subpixel = eSubpixelMode::AQ_SUBPIXEL_NONE; break;
        case DRM_MODE_SUBPIXEL_UNKNOWN: output->subpixel = eSubpixelMode::AQ_SUBPIXEL_UNKNOWN; break;
        case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: output->subpixel = eSubpixelMode::AQ_SUBPIXEL_HORIZONTAL_RGB; break;
        case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: output->subpixel = eSubpixelMode::AQ_SUBPIXEL_HORIZONTAL_BGR; break;
        case DRM_MODE_SUBPIXEL_VERTICAL_RGB: output->subpixel = eSubpixelMode::AQ_SUBPIXEL_VERTICAL_RGB; break;
        case DRM_MODE_SUBPIXEL_VERTICAL_BGR: output->subpixel = eSubpixelMode::AQ_SUBPIXEL_VERTICAL_BGR; break;
        default: output->subpixel = eSubpixelMode::AQ_SUBPIXEL_UNKNOWN;
    }

    uint64_t prop = 0;
    if (getDRMProp(backend->gpu->fd, id, props.non_desktop, &prop)) {
        if (prop == 1)
            backend->backend->log(AQ_LOG_DEBUG, "drm: Non-desktop connector");
        output->nonDesktop = prop;
    }

    maxBpcBounds.fill(0);

    if (props.max_bpc && !introspectDRMPropRange(backend->gpu->fd, props.max_bpc, maxBpcBounds.data(), &maxBpcBounds[1]))
        backend->backend->log(AQ_LOG_ERROR, "drm: Failed to check max_bpc");

    size_t               edidLen  = 0;
    uint8_t*             edidData = (uint8_t*)getDRMPropBlob(backend->gpu->fd, id, props.edid, &edidLen);

    std::vector<uint8_t> edid{edidData, edidData + edidLen};
    parseEDID(edid);

    free(edidData);
    edid = {};

    // TODO: subconnectors

    output->make        = make;
    output->model       = model;
    output->serial      = serial;
    output->description = std::format("{} {} {} ({})", make, model, serial, szName);
    output->needsFrame  = true;

    backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Description {}", output->description));

    status = DRM_MODE_CONNECTED;

    recheckCRTCProps();

    if (!backend->backend->ready)
        return;

    output->swapchain = CSwapchain::create(backend->backend->primaryAllocator, backend->self.lock());
    backend->backend->events.newOutput.emit(SP<IOutput>(output));
    output->scheduleFrame(IOutput::AQ_SCHEDULE_NEW_CONNECTOR);
}

void Aquamarine::SDRMConnector::disconnect() {
    if (!output) {
        backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Not disconnecting connector {} because it's already disconnected", szName));
        return;
    }

    output->events.destroy.emit();
    output.reset();

    status = DRM_MODE_DISCONNECTED;
}

bool Aquamarine::SDRMConnector::commitState(SDRMConnectorCommitData& data) {
    const bool ok = backend->impl->commit(self.lock(), data);

    if (ok && !data.test)
        applyCommit(data);
    else
        rollbackCommit(data);

    return ok;
}

void Aquamarine::SDRMConnector::applyCommit(const SDRMConnectorCommitData& data) {
    crtc->primary->back = data.mainFB;
    if (crtc->cursor && data.cursorFB)
        crtc->cursor->back = data.cursorFB;

    if (data.mainFB)
        data.mainFB->buffer->lockedByBackend = true;
    if (crtc->cursor && data.cursorFB)
        data.cursorFB->buffer->lockedByBackend = true;

    pendingCursorFB.reset();

    if (output->state->state().committed & COutputState::AQ_OUTPUT_STATE_MODE)
        refresh = calculateRefresh(data.modeInfo);

    output->enabledState = output->state->state().enabled;
}

void Aquamarine::SDRMConnector::rollbackCommit(const SDRMConnectorCommitData& data) {
    // cursors are applied regardless,
    // unless this was a test
    if (data.test)
        return;

    if (crtc->cursor && data.cursorFB)
        crtc->cursor->back = data.cursorFB;

    crtc->pendingCursor.reset();
}

void Aquamarine::SDRMConnector::onPresent() {
    crtc->primary->last  = crtc->primary->front;
    crtc->primary->front = crtc->primary->back;
    if (crtc->primary->last && crtc->primary->last->buffer) {
        crtc->primary->last->buffer->lockedByBackend = false;
        crtc->primary->last->buffer->events.backendRelease.emit();
    }

    if (crtc->cursor) {
        crtc->cursor->last  = crtc->cursor->front;
        crtc->cursor->front = crtc->cursor->back;
        if (crtc->cursor->last && crtc->cursor->last->buffer) {
            crtc->cursor->last->buffer->lockedByBackend = false;
            crtc->cursor->last->buffer->events.backendRelease.emit();
        }
    }
}

Aquamarine::CDRMOutput::~CDRMOutput() {
    backend->backend->removeIdleEvent(frameIdle);
    connector->isPageFlipPending   = false;
    connector->frameEventScheduled = false;
}

bool Aquamarine::CDRMOutput::commit() {
    return commitState();
}

bool Aquamarine::CDRMOutput::test() {
    return commitState(true);
}

void Aquamarine::CDRMOutput::setCursorVisible(bool visible) {
    cursorVisible = visible;
    scheduleFrame(AQ_SCHEDULE_CURSOR_VISIBLE);
}

bool Aquamarine::CDRMOutput::commitState(bool onlyTest) {
    if (!backend->backend->session->active) {
        backend->backend->log(AQ_LOG_ERROR, "drm: Session inactive");
        return false;
    }

    if (!connector->crtc) {
        backend->backend->log(AQ_LOG_ERROR, "drm: No CRTC attached to output");
        return false;
    }

    const auto&    STATE     = state->state();
    const uint32_t COMMITTED = STATE.committed;

    if ((COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_ENABLED) && STATE.enabled) {
        if (!STATE.mode && !STATE.customMode) {
            backend->backend->log(AQ_LOG_ERROR, "drm: No mode on enable commit");
            return false;
        }
    }

    if (STATE.adaptiveSync && !connector->canDoVrr) {
        backend->backend->log(AQ_LOG_ERROR, "drm: No Adaptive sync support for output");
        return false;
    }

    if (STATE.presentationMode == AQ_OUTPUT_PRESENTATION_IMMEDIATE && !backend->drmProps.supportsAsyncCommit) {
        backend->backend->log(AQ_LOG_ERROR, "drm: No Immediate presentation support in the backend");
        return false;
    }

    if ((COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER) && !STATE.buffer) {
        backend->backend->log(AQ_LOG_ERROR, "drm: No buffer committed");
        return false;
    }

    if ((COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER) && STATE.buffer->attachments.has(AQ_ATTACHMENT_DRM_KMS_UNIMPORTABLE)) {
        TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Cannot commit a KMS-unimportable buffer."));
        return false;
    }

    // If we are changing the rendering format, we may need to reconfigure the output (aka modeset)
    // which may result in some glitches
    const bool NEEDS_RECONFIG = COMMITTED &
        (COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_ENABLED | COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_FORMAT |
         COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_MODE);

    const bool BLOCKING = NEEDS_RECONFIG || !(COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER);

    const auto MODE = STATE.mode ? STATE.mode : STATE.customMode;

    if (!MODE) // modeless commits are invalid
        return false;

    uint32_t flags = 0;

    if (!onlyTest) {
        if (NEEDS_RECONFIG) {
            if (STATE.enabled)
                backend->backend->log(AQ_LOG_DEBUG,
                                      std::format("drm: Modesetting {} with {}x{}@{:.2f}Hz", name, (int)MODE->pixelSize.x, (int)MODE->pixelSize.y, MODE->refreshRate / 1000.F));
            else
                backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Disabling output {}", name));
        }

        if ((NEEDS_RECONFIG || (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER)) && connector->isPageFlipPending) {
            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))
            flags |= DRM_MODE_PAGE_FLIP_EVENT;
        if (STATE.presentationMode == AQ_OUTPUT_PRESENTATION_IMMEDIATE && (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_BUFFER)) {
            flags |= DRM_MODE_PAGE_FLIP_ASYNC;
            flags &= ~DRM_MODE_PAGE_FLIP_EVENT; // Do not request an event for immediate page flips, as it makes no sense.
        }
    }

    // we can't go further without a blit
    if (backend->primary && onlyTest)
        return true;

    SDRMConnectorCommitData data;

    if (STATE.buffer) {
        TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Committed a buffer, updating state"));

        SP<CDRMFB> drmFB;

        if (backend->shouldBlit()) {
            TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Backend requires blit, blitting"));

            if (!mgpu.swapchain) {
                TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: No swapchain for blit, creating"));
                mgpu.swapchain = CSwapchain::create(backend->rendererState.allocator, backend.lock());
            }

            auto OPTIONS = swapchain->currentOptions();
            auto bufDma  = STATE.buffer->dmabuf();
            OPTIONS.size = STATE.buffer->size;
            if (OPTIONS.format == DRM_FORMAT_INVALID)
                OPTIONS.format = bufDma.format;
            OPTIONS.multigpu = false; // this is not a shared swapchain, and additionally, don't make it linear, nvidia would be mad
            OPTIONS.cursor   = false;
            OPTIONS.scanout  = true;
            if (!mgpu.swapchain->reconfigure(OPTIONS)) {
                backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but the mgpu swapchain failed reconfiguring");
                return false;
            }

            auto NEWAQBUF   = mgpu.swapchain->next(nullptr);
            auto blitResult = backend->rendererState.renderer->blit(
                STATE.buffer, NEWAQBUF, (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE) ? STATE.explicitInFence : -1);
            if (!blitResult.success) {
                backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but blit failed");
                return false;
            }

            // replace the explicit in fence if the blitting backend returned one, otherwise discard old. Passed fence from the client is wrong.
            // if the commit doesn't have an explicit fence, don't use the one we created, just fallback to implicit
            static auto NO_EXPLICIT = envEnabled("AQ_MGPU_NO_EXPLICIT");
            if (blitResult.syncFD.has_value() && !NO_EXPLICIT && (COMMITTED & COutputState::eOutputStateProperties::AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE))
                state->setExplicitInFence(blitResult.syncFD.value());
            else
                state->setExplicitInFence(-1);

            drmFB = CDRMFB::create(NEWAQBUF, backend, nullptr); // will return attachment if present
        } else
            drmFB = CDRMFB::create(STATE.buffer, backend, nullptr); // will return attachment if present

        if (!drmFB) {
            backend->backend->log(AQ_LOG_ERROR, "drm: Buffer failed to import to KMS");
            return false;
        }

        if (drmFB->dead) {
            backend->backend->log(AQ_LOG_ERROR, "drm: KMS buffer is dead?!");
            return false;
        }

        data.mainFB = drmFB;
    }

    // sometimes, our consumer could f up the swapchain format and change it without the state changing
    bool formatMismatch = false;
    if (data.mainFB) {
        if (const auto params = data.mainFB->buffer->dmabuf(); params.success && params.format != STATE.drmFormat) {
            // formats mismatch. Update the state format and roll with it
            backend->backend->log(AQ_LOG_WARNING,
                                  std::format("drm: Formats mismatch in commit, buffer is {} but output is set to {}. Modesetting to {}", fourccToName(params.format),
                                              fourccToName(STATE.drmFormat), fourccToName(params.format)));
            state->setFormat(params.format);
            formatMismatch = true;
            // TODO: reject if tearing? We will miss a frame event!
            flags &= ~DRM_MODE_PAGE_FLIP_ASYNC; // we cannot modeset with async pf
        }
    }

    if (connector->crtc->pendingCursor)
        data.cursorFB = connector->crtc->pendingCursor;
    else if (connector->crtc->cursor)
        data.cursorFB = connector->crtc->cursor->front;

    if (data.cursorFB) {
        // verify cursor format. This might be wrong on NVIDIA where linear buffers
        // fail to be created from gbm
        // TODO: add an API to detect this and request drm_dumb linear buffers. Or do something,
        // idk
        if (data.cursorFB->dead || data.cursorFB->buffer->dmabuf().modifier == DRM_FORMAT_MOD_INVALID) {
            TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Dropping invalid buffer for cursor plane"));
            data.cursorFB = nullptr;
        }
    }

    data.blocking = BLOCKING || formatMismatch;
    data.modeset  = NEEDS_RECONFIG || lastCommitNoBuffer || formatMismatch;
    data.flags    = flags;
    data.test     = onlyTest;
    if (MODE->modeInfo.has_value())
        data.modeInfo = *MODE->modeInfo;
    else
        data.calculateMode(connector);

    bool ok = connector->commitState(data);

    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.
        data.modeset  = true;
        data.blocking = true;
        data.flags    = onlyTest ? 0 : DRM_MODE_PAGE_FLIP_EVENT;
        ok            = connector->commitState(data);

        if (!ok)
            connector->commitTainted = true;
    }

    if (onlyTest || !ok)
        return ok;

    events.commit.emit();
    state->onCommit();

    lastCommitNoBuffer = !data.mainFB;
    needsFrame         = false;

    if (ok)
        connector->commitTainted = false;

    if (data.flags & DRM_MODE_PAGE_FLIP_ASYNC) {
        // for tearing commits, we will send presentation feedback instantly, and rotate
        // drm framebuffers to properly send backendRelease events.
        // the last FB should already be gone from KMS because it's been immediately replaced

        // no completion and no vsync, because tearing
        uint32_t flags = IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK | IOutput::AQ_OUTPUT_PRESENT_ZEROCOPY;

        timespec presented;
        clock_gettime(CLOCK_MONOTONIC, &presented);

        connector->output->events.present.emit(IOutput::SPresentEvent{
            .presented = backend->sessionActive(),
            .when      = &presented,
            .seq       = 0, /* unknown sequence for tearing */
            .refresh   = (int)(connector->refresh ? (1000000000000LL / connector->refresh) : 0),
            .flags     = flags,
        });

        connector->onPresent();
    }

    return ok;
}

SP<IBackendImplementation> Aquamarine::CDRMOutput::getBackend() {
    return backend.lock();
}

bool Aquamarine::CDRMOutput::setCursor(SP<IBuffer> buffer, const Vector2D& hotspot) {
    if (buffer && !buffer->dmabuf().success) {
        backend->backend->log(AQ_LOG_ERROR, "drm: Cursor buffer has to be a dmabuf");
        return false;
    }

    if (!buffer)
        setCursorVisible(false);
    else {
        SP<CDRMFB> fb;

        if (backend->primary) {
            TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: Backend requires cursor blit, blitting"));

            if (!mgpu.cursorSwapchain) {
                TRACE(backend->backend->log(AQ_LOG_TRACE, "drm: No cursorSwapchain for blit, creating"));
                mgpu.cursorSwapchain = CSwapchain::create(backend->rendererState.allocator, backend.lock());
            }

            auto OPTIONS     = mgpu.cursorSwapchain->currentOptions();
            OPTIONS.multigpu = false;
            OPTIONS.scanout  = true;
            OPTIONS.cursor   = true;
            OPTIONS.format   = buffer->dmabuf().format;
            OPTIONS.size     = buffer->dmabuf().size;
            OPTIONS.length   = 2;

            if (!mgpu.cursorSwapchain->reconfigure(OPTIONS)) {
                backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but the mgpu cursorSwapchain failed reconfiguring");
                return false;
            }

            auto NEWAQBUF = mgpu.cursorSwapchain->next(nullptr);
            if (!backend->rendererState.renderer->blit(buffer, NEWAQBUF).success) {
                backend->backend->log(AQ_LOG_ERROR, "drm: Backend requires blit, but cursor blit failed");
                return false;
            }

            fb = CDRMFB::create(NEWAQBUF, backend, nullptr); // will return attachment if present
        } else
            fb = CDRMFB::create(buffer, backend, nullptr);

        if (!fb) {
            backend->backend->log(AQ_LOG_ERROR, "drm: Cursor buffer failed to import to KMS");
            return false;
        }

        cursorHotspot = hotspot;

        backend->backend->log(AQ_LOG_DEBUG, std::format("drm: Cursor buffer imported into KMS with id {}", fb->id));

        connector->crtc->pendingCursor = fb;

        cursorVisible = true;
    }

    scheduleFrame(AQ_SCHEDULE_CURSOR_SHAPE);
    return true;
}

void Aquamarine::CDRMOutput::moveCursor(const Vector2D& coord, bool skipShedule) {
    cursorPos = coord;
    // cursorVisible = true;
    backend->impl->moveCursor(connector, skipShedule);
}

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)));
    needsFrame = true;

    if (connector->isPageFlipPending || connector->frameEventScheduled || !enabledState)
        return;

    connector->frameEventScheduled = true;

    backend->backend->addIdleEvent(frameIdle);
}

Vector2D Aquamarine::CDRMOutput::cursorPlaneSize() {
    return backend->drmProps.cursorSize;
}

size_t Aquamarine::CDRMOutput::getGammaSize() {
    if (!backend->atomic) {
        backend->log(AQ_LOG_ERROR, "No support for gamma on the legacy iface");
        return 0;
    }

    uint64_t size = 0;
    if (!getDRMProp(backend->gpu->fd, connector->crtc->id, connector->crtc->props.gamma_lut_size, &size)) {
        backend->log(AQ_LOG_ERROR, "Couldn't get the gamma_size prop");
        return 0;
    }

    return size;
}

std::vector<SDRMFormat> Aquamarine::CDRMOutput::getRenderFormats() {
    return connector->crtc->primary->formats;
}

int Aquamarine::CDRMOutput::getConnectorID() {
    return connector->id;
}

Aquamarine::CDRMOutput::CDRMOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_, SP<SDRMConnector> connector_) :
    backend(backend_), connector(connector_) {
    name = name_;

    frameIdle = makeShared<std::function<void(void)>>([this]() {
        connector->frameEventScheduled = false;
        if (connector->isPageFlipPending)
            return;
        events.frame.emit();
    });
}

SP<CDRMFB> Aquamarine::CDRMFB::create(SP<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_, bool* isNew) {

    SP<CDRMFB> fb;

    if (isNew)
        *isNew = true;

    if (buffer_->attachments.has(AQ_ATTACHMENT_DRM_BUFFER)) {
        auto at = (CDRMBufferAttachment*)buffer_->attachments.get(AQ_ATTACHMENT_DRM_BUFFER).get();
        fb      = at->fb;
        TRACE(backend_->log(AQ_LOG_TRACE, std::format("drm: CDRMFB: buffer has drmfb attachment with fb {:x}", (uintptr_t)fb.get())));
    }

    if (fb) {
        if (isNew)
            *isNew = false;
        return fb;
    }

    fb = SP<CDRMFB>(new CDRMFB(buffer_, backend_));

    if (!fb->id)
        return nullptr;

    buffer_->attachments.add(makeShared<CDRMBufferAttachment>(fb));

    return fb;
}

Aquamarine::CDRMFB::CDRMFB(SP<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_) : buffer(buffer_), backend(backend_) {
    import();
}

void Aquamarine::CDRMFB::import() {
    auto attrs = buffer->dmabuf();
    if (!attrs.success) {
        backend->backend->log(AQ_LOG_ERROR, "drm: Buffer submitted has no dmabuf");
        return;
    }

    if (buffer->attachments.has(AQ_ATTACHMENT_DRM_KMS_UNIMPORTABLE)) {
        backend->backend->log(AQ_LOG_ERROR, "drm: Buffer submitted is unimportable");
        return;
    }

    // TODO: check format

    for (int i = 0; i < attrs.planes; ++i) {
        int ret = drmPrimeFDToHandle(backend->gpu->fd, attrs.fds.at(i), &boHandles[i]);
        if (ret) {
            backend->backend->log(AQ_LOG_ERROR, "drm: drmPrimeFDToHandle failed");
            drop();
            return;
        }

        TRACE(backend->backend->log(AQ_LOG_TRACE, std::format("drm: CDRMFB: plane {} has fd {}, got handle {}", i, attrs.fds.at(i), boHandles.at(i))));
    }

    id = submitBuffer();
    if (!id) {
        backend->backend->log(AQ_LOG_ERROR, "drm: Failed to submit a buffer to KMS");
        buffer->attachments.add(makeShared<CDRMBufferUnimportable>());
        drop();
        return;
    }

    TRACE(backend->backend->log(AQ_LOG_TRACE, std::format("drm: new buffer {}", id)));

    // FIXME: why does this implode when it doesnt on wlroots or kwin?
    closeHandles();

    listeners.destroyBuffer = buffer->events.destroy.registerListener([this](std::any d) {
        drop();
        dead      = true;
        id        = 0;
        boHandles = {0, 0, 0, 0};
    });
}

void Aquamarine::CDRMFB::reimport() {
    drop();
    dropped       = false;
    handlesClosed = false;
    boHandles     = {0, 0, 0, 0};

    import();
}

Aquamarine::CDRMFB::~CDRMFB() {
    drop();
}

void Aquamarine::CDRMFB::closeHandles() {
    if (handlesClosed)
        return;

    handlesClosed = true;

    std::vector<uint32_t> closed;

    for (size_t i = 0; i < 4; ++i) {
        if (boHandles.at(i) == 0)
            continue;

        bool exists = false;
        for (size_t j = 0; j < i; ++j) {
            if (boHandles.at(i) == boHandles.at(j)) {
                exists = true;
                break;
            }
        }
        if (exists)
            continue;

        if (drmCloseBufferHandle(backend->gpu->fd, boHandles.at(i)))
            backend->backend->log(AQ_LOG_ERROR, "drm: drmCloseBufferHandle failed");
    }

    boHandles = {0, 0, 0, 0};
}

void Aquamarine::CDRMFB::drop() {
    if (dropped)
        return;

    dropped = true;

    if (!id)
        return;

    closeHandles();

    TRACE(backend->backend->log(AQ_LOG_TRACE, std::format("drm: dropping buffer {}", id)));

    int ret = drmModeCloseFB(backend->gpu->fd, id);
    if (ret == -EINVAL)
        ret = drmModeRmFB(backend->gpu->fd, id);

    if (ret)
        backend->backend->log(AQ_LOG_ERROR, std::format("drm: Failed to close a buffer: {}", strerror(-ret)));
}

uint32_t Aquamarine::CDRMFB::submitBuffer() {
    auto                    attrs = buffer->dmabuf();
    uint32_t                newID = 0;
    std::array<uint64_t, 4> mods  = {0, 0, 0, 0};
    for (size_t i = 0; i < attrs.planes; ++i) {
        mods[i] = attrs.modifier;
    }

    if (backend->drmProps.supportsAddFb2Modifiers && attrs.modifier != DRM_FORMAT_MOD_INVALID) {
        TRACE(backend->backend->log(AQ_LOG_TRACE,
                                    std::format("drm: Using drmModeAddFB2WithModifiers to import buffer into KMS: Size {} with format {} and mod {}", attrs.size,
                                                fourccToName(attrs.format), attrs.modifier)));
        if (drmModeAddFB2WithModifiers(backend->gpu->fd, attrs.size.x, attrs.size.y, attrs.format, boHandles.data(), attrs.strides.data(), attrs.offsets.data(), mods.data(),
                                       &newID, DRM_MODE_FB_MODIFIERS)) {
            backend->backend->log(AQ_LOG_ERROR, "drm: Failed to submit a buffer with drmModeAddFB2WithModifiers");
            return 0;
        }
    } else {
        if (attrs.modifier != DRM_FORMAT_MOD_INVALID && attrs.modifier != DRM_FORMAT_MOD_LINEAR) {
            backend->backend->log(AQ_LOG_ERROR, "drm: drmModeAddFB2WithModifiers unsupported and buffer has explicit modifiers");
            return 0;
        }

        TRACE(backend->backend->log(
            AQ_LOG_TRACE,
            std::format("drm: Using drmModeAddFB2 to import buffer into KMS: Size {} with format {} and mod {}", attrs.size, fourccToName(attrs.format), attrs.modifier)));

        if (drmModeAddFB2(backend->gpu->fd, attrs.size.x, attrs.size.y, attrs.format, boHandles.data(), attrs.strides.data(), attrs.offsets.data(), &newID, 0)) {
            backend->backend->log(AQ_LOG_ERROR, "drm: Failed to submit a buffer with drmModeAddFB2");
            return 0;
        }
    }

    return newID;
}

void Aquamarine::SDRMConnectorCommitData::calculateMode(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector) {
    if (!connector || !connector->output || !connector->output->state)
        return;

    const auto& STATE = connector->output->state->state();
    const auto  MODE  = STATE.mode ? STATE.mode : STATE.customMode;

    if (!MODE) {
        connector->backend->log(AQ_LOG_ERROR, "drm: no mode in calculateMode??");
        return;
    }

    di_cvt_options options = {
        .red_blank_ver = DI_CVT_REDUCED_BLANKING_NONE,
        .h_pixels      = (int)MODE->pixelSize.x,
        .v_lines       = (int)MODE->pixelSize.y,
        .ip_freq_rqd   = MODE->refreshRate ? MODE->refreshRate / 1000.0 : 60.0,
    };
    di_cvt_timing timing;

    di_cvt_compute(&timing, &options);

    uint16_t hsync_start = (int)MODE->pixelSize.x + timing.h_front_porch;
    uint16_t vsync_start = timing.v_lines_rnd + timing.v_front_porch;
    uint16_t hsync_end   = hsync_start + timing.h_sync;
    uint16_t vsync_end   = vsync_start + timing.v_sync;

    modeInfo = (drmModeModeInfo){
        .clock       = (uint32_t)std::round(timing.act_pixel_freq * 1000),
        .hdisplay    = (uint16_t)MODE->pixelSize.x,
        .hsync_start = hsync_start,
        .hsync_end   = hsync_end,
        .htotal      = (uint16_t)(hsync_end + timing.h_back_porch),
        .vdisplay    = (uint16_t)timing.v_lines_rnd,
        .vsync_start = vsync_start,
        .vsync_end   = vsync_end,
        .vtotal      = (uint16_t)(vsync_end + timing.v_back_porch),
        .vrefresh    = (uint32_t)std::round(timing.act_frame_rate),
        .flags       = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
    };
    snprintf(modeInfo.name, sizeof(modeInfo.name), "%dx%d", (int)MODE->pixelSize.x, (int)MODE->pixelSize.y);

    TRACE(connector->backend->log(AQ_LOG_TRACE,
                                  std::format("drm: calculateMode: modeline dump: {} {} {} {} {} {} {} {} {} {} {}", modeInfo.clock, modeInfo.hdisplay, modeInfo.hsync_start,
                                              modeInfo.hsync_end, modeInfo.htotal, modeInfo.vdisplay, modeInfo.vsync_start, modeInfo.vsync_end, modeInfo.vtotal, modeInfo.vrefresh,
                                              modeInfo.flags)));
}

Aquamarine::CDRMBufferAttachment::CDRMBufferAttachment(SP<CDRMFB> fb_) : fb(fb_) {
    ;
}

SP<CDRMLease> Aquamarine::CDRMLease::create(std::vector<SP<IOutput>> outputs) {
    if (outputs.empty())
        return nullptr;

    if (outputs.at(0)->getBackend()->type() != AQ_BACKEND_DRM)
        return nullptr;

    auto backend = ((CDRMBackend*)outputs.at(0)->getBackend().get())->self.lock();

    for (auto& o : outputs) {
        if (o->getBackend() != backend) {
            backend->log(AQ_LOG_ERROR, "drm lease: Mismatched backends");
            return nullptr;
        }
    }

    std::vector<uint32_t> objects;

    auto                  lease = SP<CDRMLease>(new CDRMLease);

    for (auto& o : outputs) {
        auto drmo = ((CDRMOutput*)o.get())->self.lock();
        backend->log(AQ_LOG_DEBUG, std::format("drm lease: output {}, connector {}", drmo->name, drmo->connector->id));

        // FIXME: do we have to alloc a crtc here?
        if (!drmo->connector->crtc) {
            backend->log(AQ_LOG_ERROR, std::format("drm lease: output {} has no crtc", drmo->name));
            return nullptr;
        }

        backend->log(AQ_LOG_DEBUG, std::format("drm lease: crtc {}, primary {}", drmo->connector->crtc->id, drmo->connector->crtc->primary->id));

        objects.push_back(drmo->connector->id);
        objects.push_back(drmo->connector->crtc->id);
        objects.push_back(drmo->connector->crtc->primary->id);
        if (drmo->connector->crtc->cursor)
            objects.push_back(drmo->connector->crtc->cursor->id);

        lease->outputs.emplace_back(drmo);
    }

    backend->log(AQ_LOG_DEBUG, "drm lease: issuing a lease");

    int leaseFD = drmModeCreateLease(backend->gpu->fd, objects.data(), objects.size(), O_CLOEXEC, &lease->lesseeID);
    if (leaseFD < 0) {
        backend->log(AQ_LOG_ERROR, "drm lease: drm rejected a lease");
        return nullptr;
    }

    for (auto& o : lease->outputs) {
        o->lease = lease;
    }

    lease->leaseFD = leaseFD;

    backend->log(AQ_LOG_DEBUG, std::format("drm lease: lease granted with lessee id {}", lease->lesseeID));

    return lease;
}

Aquamarine::CDRMLease::~CDRMLease() {
    if (active)
        terminate();
    else
        destroy();
}

void Aquamarine::CDRMLease::terminate() {
    active = false;

    if (drmModeRevokeLease(backend->gpu->fd, lesseeID) < 0)
        backend->log(AQ_LOG_ERROR, "drm lease: Failed to revoke lease");

    destroy();
}

void Aquamarine::CDRMLease::destroy() {
    events.destroy.emit();
}
07070100000038000081A400000000000000000000000166C38D3F00001241000000000000000000000000000000000000002A00000000aquamarine-0.3.3/src/backend/drm/Math.cpp#include "Math.hpp"
#include <unordered_map>
#include <cstring>
#include <cmath>

void matrixIdentity(float mat[9]) {
    static const float identity[9] = {
        1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
    };
    memcpy(mat, identity, sizeof(identity));
}

void matrixMultiply(float mat[9], const float a[9], const float b[9]) {
    float product[9];

    product[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
    product[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
    product[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];

    product[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
    product[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
    product[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];

    product[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
    product[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
    product[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];

    memcpy(mat, product, sizeof(product));
}

void matrixTranspose(float mat[9], const float a[9]) {
    float transposition[9] = {
        a[0], a[3], a[6], a[1], a[4], a[7], a[2], a[5], a[8],
    };
    memcpy(mat, transposition, sizeof(transposition));
}

void matrixTranslate(float mat[9], float x, float y) {
    float translate[9] = {
        1.0f, 0.0f, x, 0.0f, 1.0f, y, 0.0f, 0.0f, 1.0f,
    };
    matrixMultiply(mat, mat, translate);
}

void matrixScale(float mat[9], float x, float y) {
    float scale[9] = {
        x, 0.0f, 0.0f, 0.0f, y, 0.0f, 0.0f, 0.0f, 1.0f,
    };
    matrixMultiply(mat, mat, scale);
}

void matrixRotate(float mat[9], float rad) {
    float rotate[9] = {
        (float)cos(rad), (float)-sin(rad), 0.0f, (float)sin(rad), (float)cos(rad), 0.0f, 0.0f, 0.0f, 1.0f,
    };
    matrixMultiply(mat, mat, rotate);
}

std::unordered_map<eTransform, std::array<float, 9>> transforms = {
    {HYPRUTILS_TRANSFORM_NORMAL,
     {
         1.0f,
         0.0f,
         0.0f,
         0.0f,
         1.0f,
         0.0f,
         0.0f,
         0.0f,
         1.0f,
     }},
    {HYPRUTILS_TRANSFORM_90,
     {
         0.0f,
         1.0f,
         0.0f,
         -1.0f,
         0.0f,
         0.0f,
         0.0f,
         0.0f,
         1.0f,
     }},
    {HYPRUTILS_TRANSFORM_180,
     {
         -1.0f,
         0.0f,
         0.0f,
         0.0f,
         -1.0f,
         0.0f,
         0.0f,
         0.0f,
         1.0f,
     }},
    {HYPRUTILS_TRANSFORM_270,
     {
         0.0f,
         -1.0f,
         0.0f,
         1.0f,
         0.0f,
         0.0f,
         0.0f,
         0.0f,
         1.0f,
     }},
    {HYPRUTILS_TRANSFORM_FLIPPED,
     {
         -1.0f,
         0.0f,
         0.0f,
         0.0f,
         1.0f,
         0.0f,
         0.0f,
         0.0f,
         1.0f,
     }},
    {HYPRUTILS_TRANSFORM_FLIPPED_90,
     {
         0.0f,
         1.0f,
         0.0f,
         1.0f,
         0.0f,
         0.0f,
         0.0f,
         0.0f,
         1.0f,
     }},
    {HYPRUTILS_TRANSFORM_FLIPPED_180,
     {
         1.0f,
         0.0f,
         0.0f,
         0.0f,
         -1.0f,
         0.0f,
         0.0f,
         0.0f,
         1.0f,
     }},
    {HYPRUTILS_TRANSFORM_FLIPPED_270,
     {
         0.0f,
         -1.0f,
         0.0f,
         -1.0f,
         0.0f,
         0.0f,
         0.0f,
         0.0f,
         1.0f,
     }},
};

void matrixTransform(float mat[9], eTransform transform) {
    matrixMultiply(mat, mat, transforms.at(transform).data());
}

void matrixProjection(float mat[9], int width, int height, eTransform transform) {
    memset(mat, 0, sizeof(*mat) * 9);

    const float* t = transforms.at(transform).data();
    float        x = 2.0f / width;
    float        y = 2.0f / height;

    // Rotation + reflection
    mat[0] = x * t[0];
    mat[1] = x * t[1];
    mat[3] = y * -t[3];
    mat[4] = y * -t[4];

    // Translation
    mat[2] = -copysign(1.0f, mat[0] + mat[1]);
    mat[5] = -copysign(1.0f, mat[3] + mat[4]);

    // Identity
    mat[8] = 1.0f;
}

void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]) {
    double x      = box.x;
    double y      = box.y;
    double width  = box.width;
    double height = box.height;

    matrixIdentity(mat);
    matrixTranslate(mat, x, y);

    if (rotation != 0) {
        matrixTranslate(mat, width / 2, height / 2);
        matrixRotate(mat, rotation);
        matrixTranslate(mat, -width / 2, -height / 2);
    }

    matrixScale(mat, width, height);

    if (transform != HYPRUTILS_TRANSFORM_NORMAL) {
        matrixTranslate(mat, 0.5, 0.5);
        matrixTransform(mat, transform);
        matrixTranslate(mat, -0.5, -0.5);
    }

    matrixMultiply(mat, projection, mat);
}
07070100000039000081A400000000000000000000000166C38D3F000002C5000000000000000000000000000000000000002A00000000aquamarine-0.3.3/src/backend/drm/Math.hpp#pragma once

// FIXME: migrate this to utils

// includes box and vector as well
#include <hyprutils/math/Region.hpp>

using namespace Hyprutils::Math;

void projectBox(float mat[9], CBox& box, eTransform transform, float rotation, const float projection[9]);
void matrixProjection(float mat[9], int width, int height, eTransform transform);
void matrixTransform(float mat[9], eTransform transform);
void matrixRotate(float mat[9], float rad);
void matrixScale(float mat[9], float x, float y);
void matrixTranslate(float mat[9], float x, float y);
void matrixTranspose(float mat[9], const float a[9]);
void matrixMultiply(float mat[9], const float a[9], const float b[9]);
void matrixIdentity(float mat[9]);
0707010000003A000081A400000000000000000000000166C38D3F0000175C000000000000000000000000000000000000002B00000000aquamarine-0.3.3/src/backend/drm/Props.cpp#include <aquamarine/backend/DRM.hpp>

extern "C" {
#include <libseat.h>
#include <libudev.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
}

#include <cstring>

using namespace Aquamarine;
using namespace Hyprutils::Memory;
#define SP CSharedPointer

struct prop_info {
    const char* name;
    size_t      index;
};

static const struct prop_info connector_info[] = {
#define INDEX(name) (offsetof(SDRMConnector::UDRMConnectorProps, name) / sizeof(uint32_t))
    {"CRTC_ID", INDEX(crtc_id)},
    {"DPMS", INDEX(dpms)},
    {"EDID", INDEX(edid)},
    {"PATH", INDEX(path)},
    {"content type", INDEX(content_type)},
    {"link-status", INDEX(link_status)},
    {"max bpc", INDEX(max_bpc)},
    {"non-desktop", INDEX(non_desktop)},
    {"panel orientation", INDEX(panel_orientation)},
    {"subconnector", INDEX(subconnector)},
    {"vrr_capable", INDEX(vrr_capable)},
#undef INDEX
};

static const struct prop_info crtc_info[] = {
#define INDEX(name) (offsetof(SDRMCRTC::UDRMCRTCProps, name) / sizeof(uint32_t))
    {"ACTIVE", INDEX(active)},   {"GAMMA_LUT", INDEX(gamma_lut)},         {"GAMMA_LUT_SIZE", INDEX(gamma_lut_size)},
    {"MODE_ID", INDEX(mode_id)}, {"OUT_FENCE_PTR", INDEX(out_fence_ptr)}, {"VRR_ENABLED", INDEX(vrr_enabled)},
#undef INDEX
};

static const struct prop_info plane_info[] = {
#define INDEX(name) (offsetof(SDRMPlane::UDRMPlaneProps, name) / sizeof(uint32_t))
    {"CRTC_H", INDEX(crtc_h)},
    {"CRTC_ID", INDEX(crtc_id)},
    {"CRTC_W", INDEX(crtc_w)},
    {"CRTC_X", INDEX(crtc_x)},
    {"CRTC_Y", INDEX(crtc_y)},
    {"FB_DAMAGE_CLIPS", INDEX(fb_damage_clips)},
    {"FB_ID", INDEX(fb_id)},
    {"HOTSPOT_X", INDEX(hotspot_x)},
    {"HOTSPOT_Y", INDEX(hotspot_y)},
    {"IN_FENCE_FD", INDEX(in_fence_fd)},
    {"IN_FORMATS", INDEX(in_formats)},
    {"SRC_H", INDEX(src_h)},
    {"SRC_W", INDEX(src_w)},
    {"SRC_X", INDEX(src_x)},
    {"SRC_Y", INDEX(src_y)},
    {"rotation", INDEX(rotation)},
    {"type", INDEX(type)},
#undef INDEX
};

namespace Aquamarine {

    static int comparePropInfo(const void* arg1, const void* arg2) {
        const char*      key  = (const char*)arg1;
        const prop_info* elem = (prop_info*)arg2;

        return strcmp(key, elem->name);
    }

    static bool scanProperties(int fd, uint32_t id, uint32_t type, uint32_t* result, const prop_info* info, size_t info_len) {
        drmModeObjectProperties* props = drmModeObjectGetProperties(fd, id, type);
        if (!props)
            return false;

        for (uint32_t i = 0; i < props->count_props; ++i) {
            drmModePropertyRes* prop = drmModeGetProperty(fd, props->props[i]);
            if (!prop)
                continue;

            const prop_info* p = (prop_info*)bsearch(prop->name, info, info_len, sizeof(info[0]), comparePropInfo);
            if (p)
                result[p->index] = prop->prop_id;

            drmModeFreeProperty(prop);
        }

        drmModeFreeObjectProperties(props);
        return true;
    }

    bool getDRMConnectorProps(int fd, uint32_t id, SDRMConnector::UDRMConnectorProps* out) {
        return scanProperties(fd, id, DRM_MODE_OBJECT_CONNECTOR, out->props, connector_info, sizeof(connector_info) / sizeof(connector_info[0]));
    }

    bool getDRMCRTCProps(int fd, uint32_t id, SDRMCRTC::UDRMCRTCProps* out) {
        return scanProperties(fd, id, DRM_MODE_OBJECT_CRTC, out->props, crtc_info, sizeof(crtc_info) / sizeof(crtc_info[0]));
    }

    bool getDRMPlaneProps(int fd, uint32_t id, SDRMPlane::UDRMPlaneProps* out) {
        return scanProperties(fd, id, DRM_MODE_OBJECT_PLANE, out->props, plane_info, sizeof(plane_info) / sizeof(plane_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)
            return false;

        bool found = false;

        for (uint32_t i = 0; i < props->count_props; ++i) {
            if (props->props[i] == prop) {
                *ret  = props->prop_values[i];
                found = true;
                break;
            }
        }

        drmModeFreeObjectProperties(props);
        return found;
    }

    void* getDRMPropBlob(int fd, uint32_t obj, uint32_t prop, size_t* ret_len) {
        uint64_t blob_id;
        if (!getDRMProp(fd, obj, prop, &blob_id))
            return nullptr;

        drmModePropertyBlobRes* blob = drmModeGetPropertyBlob(fd, blob_id);
        if (!blob)
            return nullptr;

        void* ptr = malloc(blob->length);
        if (!ptr) {
            drmModeFreePropertyBlob(blob);
            return nullptr;
        }

        memcpy(ptr, blob->data, blob->length);
        *ret_len = blob->length;

        drmModeFreePropertyBlob(blob);
        return ptr;
    }

    char* getDRMPropEnum(int fd, uint32_t obj, uint32_t prop_id) {
        uint64_t value;
        if (!getDRMProp(fd, obj, prop_id, &value))
            return nullptr;

        drmModePropertyRes* prop = drmModeGetProperty(fd, prop_id);
        if (!prop)
            return nullptr;

        char* str = nullptr;
        for (int i = 0; i < prop->count_enums; i++) {
            if (prop->enums[i].value == value) {
                str = strdup(prop->enums[i].name);
                break;
            }
        }

        drmModeFreeProperty(prop);

        return str;
    }

    bool introspectDRMPropRange(int fd, uint32_t prop_id, uint64_t* min, uint64_t* max) {
        drmModePropertyRes* prop = drmModeGetProperty(fd, prop_id);
        if (!prop)
            return false;

        if (drmModeGetPropertyType(prop) != DRM_MODE_PROP_RANGE) {
            drmModeFreeProperty(prop);
            return false;
        }

        if (prop->count_values != 2)
            abort();

        if (min != nullptr)
            *min = prop->values[0];
        if (max != nullptr)
            *max = prop->values[1];

        drmModeFreeProperty(prop);
        return true;
    }

};
0707010000003B000081A400000000000000000000000166C38D3F00000281000000000000000000000000000000000000002B00000000aquamarine-0.3.3/src/backend/drm/Props.hpp#pragma once

#include <aquamarine/backend/DRM.hpp>

namespace Aquamarine {
    bool  getDRMConnectorProps(int fd, uint32_t id, SDRMConnector::UDRMConnectorProps* out);
    bool  getDRMCRTCProps(int fd, uint32_t id, SDRMCRTC::UDRMCRTCProps* out);
    bool  getDRMPlaneProps(int fd, uint32_t id, SDRMPlane::UDRMPlaneProps* 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);
    bool  introspectDRMPropRange(int fd, uint32_t prop_id, uint64_t* min, uint64_t* max);
};
0707010000003C000081A400000000000000000000000166C38D3F00007A3E000000000000000000000000000000000000002E00000000aquamarine-0.3.3/src/backend/drm/Renderer.cpp#include "Renderer.hpp"
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
#include "Math.hpp"
#include "Shared.hpp"
#include "FormatUtils.hpp"
#include <xf86drm.h>
#include <aquamarine/allocator/GBM.hpp>

using namespace Aquamarine;
using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#define SP CSharedPointer
#define WP CWeakPointer

// static funcs
WP<CBackend> gBackend;

// ------------------- shader utils

GLuint compileShader(const GLuint& type, std::string src) {
    auto shader = glCreateShader(type);

    auto shaderSource = src.c_str();

    glShaderSource(shader, 1, (const GLchar**)&shaderSource, nullptr);
    glCompileShader(shader);

    GLint ok;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);

    if (ok == GL_FALSE)
        return 0;

    return shader;
}

GLuint createProgram(const std::string& vert, const std::string& frag) {
    auto vertCompiled = compileShader(GL_VERTEX_SHADER, vert);
    if (vertCompiled == 0)
        return 0;

    auto fragCompiled = compileShader(GL_FRAGMENT_SHADER, frag);
    if (fragCompiled == 0)
        return 0;

    auto prog = glCreateProgram();
    glAttachShader(prog, vertCompiled);
    glAttachShader(prog, fragCompiled);
    glLinkProgram(prog);

    glDetachShader(prog, vertCompiled);
    glDetachShader(prog, fragCompiled);
    glDeleteShader(vertCompiled);
    glDeleteShader(fragCompiled);

    GLint ok;
    glGetProgramiv(prog, GL_LINK_STATUS, &ok);
    if (ok == GL_FALSE)
        return 0;

    return prog;
}

inline const std::string VERT_SRC = R"#(
uniform mat3 proj;
attribute vec2 pos;
attribute vec2 texcoord;
varying vec2 v_texcoord;

void main() {
    gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);
    v_texcoord = texcoord;
})#";

inline const std::string FRAG_SRC = R"#(
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform sampler2D tex;

void main() {
    gl_FragColor = texture2D(tex, v_texcoord);
})#";

inline const std::string FRAG_SRC_EXT = R"#(
#extension GL_OES_EGL_image_external : require
precision highp float;
varying vec2 v_texcoord; // is in 0-1
uniform samplerExternalOES texture0;

void main() {
    gl_FragColor = texture2D(texture0, v_texcoord);
})#";

// ------------------- egl stuff

inline void loadGLProc(void* pProc, const char* name) {
    void* proc = (void*)eglGetProcAddress(name);
    if (proc == NULL) {
        gBackend->log(AQ_LOG_ERROR, std::format("eglGetProcAddress({}) failed", name));
        abort();
    }
    *(void**)pProc = proc;
}

// -------------------

std::optional<std::vector<std::pair<uint64_t, bool>>> CDRMRenderer::getModsForFormat(EGLint format) {
    // TODO: return std::expected when clang supports it

    EGLint len = 0;
    if (!egl.eglQueryDmaBufModifiersEXT(egl.display, format, 0, nullptr, nullptr, &len)) {
        backend->log(AQ_LOG_ERROR, std::format("EGL: eglQueryDmaBufModifiersEXT failed for format {}", fourccToName(format)));
        return std::nullopt;
    }

    if (len <= 0)
        return std::vector<std::pair<uint64_t, bool>>{};

    std::vector<uint64_t>   mods;
    std::vector<EGLBoolean> external;

    mods.resize(len);
    external.resize(len);

    egl.eglQueryDmaBufModifiersEXT(egl.display, format, len, mods.data(), external.data(), &len);

    std::vector<std::pair<uint64_t, bool>> result;
    for (size_t i = 0; i < mods.size(); ++i) {
        result.push_back({mods.at(i), external.at(i)});
    }

    if (std::find(mods.begin(), mods.end(), DRM_FORMAT_MOD_LINEAR) == mods.end() && mods.size() == 0)
        result.push_back({DRM_FORMAT_MOD_LINEAR, true});

    return result;
}

bool CDRMRenderer::initDRMFormats() {
    std::vector<EGLint> formats;

    EGLint              len = 0;
    egl.eglQueryDmaBufFormatsEXT(egl.display, 0, nullptr, &len);
    formats.resize(len);
    egl.eglQueryDmaBufFormatsEXT(egl.display, len, formats.data(), &len);

    if (formats.size() == 0) {
        backend->log(AQ_LOG_ERROR, "EGL: Failed to get formats");
        return false;
    }

    TRACE(backend->log(AQ_LOG_TRACE, "EGL: Supported formats:"));

    std::vector<SGLFormat> dmaFormats;

    for (auto& fmt : formats) {
        std::vector<std::pair<uint64_t, bool>> mods;

        auto                                   ret = getModsForFormat(fmt);
        if (!ret.has_value())
            continue;

        mods = *ret;

        hasModifiers = hasModifiers || mods.size() > 0;

        // EGL can always do implicit modifiers.
        mods.push_back({DRM_FORMAT_MOD_INVALID, true});

        for (auto& [mod, external] : mods) {
            dmaFormats.push_back(SGLFormat{
                .drmFormat = (uint32_t)fmt,
                .modifier  = mod,
                .external  = external,
            });
        }

        TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL: GPU Supports Format {} (0x{:x})", fourccToName((uint32_t)fmt), fmt)));
        for (auto& [mod, external] : mods) {
            auto modName = drmGetFormatModifierName(mod);
            TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL:  | {}with modifier 0x{:x}: {}", (external ? "external only " : ""), mod, modName ? modName : "?unknown?")));
            free(modName);
        }
    }

    TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL: Found {} formats", dmaFormats.size())));

    if (dmaFormats.empty()) {
        backend->log(AQ_LOG_ERROR, "EGL: No formats");
        return false;
    }

    this->formats = dmaFormats;
    return true;
}

SP<CDRMRenderer> CDRMRenderer::attempt(Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_, SP<CBackend> backend_) {
    SP<CDRMRenderer> renderer = SP<CDRMRenderer>(new CDRMRenderer());
    renderer->drmFD           = allocator_->drmFD();
    renderer->backend         = backend_;
    gBackend                  = backend_;

    const std::string EGLEXTENSIONS = (const char*)eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);

    if (!EGLEXTENSIONS.contains("KHR_platform_gbm")) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no gbm support");
        return nullptr;
    }

    // init egl

    if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, eglBindAPI failed");
        return nullptr;
    }

    loadGLProc(&renderer->egl.eglGetPlatformDisplayEXT, "eglGetPlatformDisplayEXT");
    loadGLProc(&renderer->egl.eglCreateImageKHR, "eglCreateImageKHR");
    loadGLProc(&renderer->egl.eglDestroyImageKHR, "eglDestroyImageKHR");
    loadGLProc(&renderer->egl.glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES");
    loadGLProc(&renderer->egl.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES");
    loadGLProc(&renderer->egl.eglQueryDmaBufFormatsEXT, "eglQueryDmaBufFormatsEXT");
    loadGLProc(&renderer->egl.eglQueryDmaBufModifiersEXT, "eglQueryDmaBufModifiersEXT");
    loadGLProc(&renderer->egl.eglDestroySyncKHR, "eglDestroySyncKHR");
    loadGLProc(&renderer->egl.eglWaitSyncKHR, "eglWaitSyncKHR");
    loadGLProc(&renderer->egl.eglCreateSyncKHR, "eglCreateSyncKHR");
    loadGLProc(&renderer->egl.eglDupNativeFenceFDANDROID, "eglDupNativeFenceFDANDROID");

    if (!renderer->egl.eglCreateSyncKHR) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglCreateSyncKHR");
        return nullptr;
    }

    if (!renderer->egl.eglDupNativeFenceFDANDROID) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglDupNativeFenceFDANDROID");
        return nullptr;
    }

    if (!renderer->egl.eglGetPlatformDisplayEXT) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglGetPlatformDisplayEXT");
        return nullptr;
    }

    if (!renderer->egl.eglCreateImageKHR) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglCreateImageKHR");
        return nullptr;
    }

    if (!renderer->egl.eglQueryDmaBufFormatsEXT) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglQueryDmaBufFormatsEXT");
        return nullptr;
    }

    if (!renderer->egl.eglQueryDmaBufModifiersEXT) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no eglQueryDmaBufModifiersEXT");
        return nullptr;
    }

    std::vector<EGLint> attrs = {EGL_NONE};
    renderer->egl.display     = renderer->egl.eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, allocator_->gbmDevice, attrs.data());
    if (renderer->egl.display == EGL_NO_DISPLAY) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, eglGetPlatformDisplayEXT failed");
        return nullptr;
    }

    EGLint major, minor;
    if (eglInitialize(renderer->egl.display, &major, &minor) == EGL_FALSE) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, eglInitialize failed");
        return nullptr;
    }

    attrs.clear();

    const std::string EGLEXTENSIONS2 = (const char*)eglQueryString(renderer->egl.display, EGL_EXTENSIONS);

    if (EGLEXTENSIONS2.contains("IMG_context_priority")) {
        attrs.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
        attrs.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
    }

    if (EGLEXTENSIONS2.contains("EXT_create_context_robustness")) {
        attrs.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
        attrs.push_back(EGL_LOSE_CONTEXT_ON_RESET_EXT);
    }

    if (!EGLEXTENSIONS2.contains("EXT_image_dma_buf_import_modifiers")) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no EXT_image_dma_buf_import_modifiers ext");
        return nullptr;
    }

    if (!EGLEXTENSIONS2.contains("EXT_image_dma_buf_import")) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, no EXT_image_dma_buf_import ext");
        return nullptr;
    }

    attrs.push_back(EGL_CONTEXT_MAJOR_VERSION);
    attrs.push_back(2);
    attrs.push_back(EGL_CONTEXT_MINOR_VERSION);
    attrs.push_back(0);
    attrs.push_back(EGL_CONTEXT_OPENGL_DEBUG);
    attrs.push_back(EGL_FALSE);

    attrs.push_back(EGL_NONE);

    renderer->egl.context = eglCreateContext(renderer->egl.display, EGL_NO_CONFIG_KHR, EGL_NO_CONTEXT, attrs.data());
    if (renderer->egl.context == EGL_NO_CONTEXT) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, eglCreateContext failed");
        return nullptr;
    }

    if (EGLEXTENSIONS2.contains("IMG_context_priority")) {
        EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG;
        eglQueryContext(renderer->egl.display, renderer->egl.context, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority);
        if (priority != EGL_CONTEXT_PRIORITY_HIGH_IMG)
            backend_->log(AQ_LOG_DEBUG, "CDRMRenderer: didnt get a high priority context");
        else
            backend_->log(AQ_LOG_DEBUG, "CDRMRenderer: got a high priority context");
    }

    // init shaders

    renderer->setEGL();

    if (!renderer->initDRMFormats()) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, initDRMFormats failed");
        return nullptr;
    }

    renderer->gl.shader.program = createProgram(VERT_SRC, FRAG_SRC);
    if (renderer->gl.shader.program == 0) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, shader failed");
        return nullptr;
    }

    renderer->gl.shader.proj      = glGetUniformLocation(renderer->gl.shader.program, "proj");
    renderer->gl.shader.posAttrib = glGetAttribLocation(renderer->gl.shader.program, "pos");
    renderer->gl.shader.texAttrib = glGetAttribLocation(renderer->gl.shader.program, "texcoord");
    renderer->gl.shader.tex       = glGetUniformLocation(renderer->gl.shader.program, "tex");

    renderer->gl.shaderExt.program = createProgram(VERT_SRC, FRAG_SRC_EXT);
    if (renderer->gl.shaderExt.program == 0) {
        backend_->log(AQ_LOG_ERROR, "CDRMRenderer: fail, shaderExt failed");
        return nullptr;
    }

    renderer->gl.shaderExt.proj      = glGetUniformLocation(renderer->gl.shaderExt.program, "proj");
    renderer->gl.shaderExt.posAttrib = glGetAttribLocation(renderer->gl.shaderExt.program, "pos");
    renderer->gl.shaderExt.texAttrib = glGetAttribLocation(renderer->gl.shaderExt.program, "texcoord");
    renderer->gl.shaderExt.tex       = glGetUniformLocation(renderer->gl.shaderExt.program, "tex");

    renderer->restoreEGL();

    backend_->log(AQ_LOG_DEBUG, "CDRMRenderer: success");

    return renderer;
}

void CDRMRenderer::setEGL() {
    savedEGLState.display = eglGetCurrentDisplay();
    savedEGLState.context = eglGetCurrentContext();
    savedEGLState.draw    = eglGetCurrentSurface(EGL_DRAW);
    savedEGLState.read    = eglGetCurrentSurface(EGL_READ);

    if (!eglMakeCurrent(egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl.context))
        backend->log(AQ_LOG_WARNING, "CDRMRenderer: setEGL eglMakeCurrent failed");
}

void CDRMRenderer::restoreEGL() {
    EGLDisplay dpy = savedEGLState.display ? savedEGLState.display : egl.display;

    // egl can't handle this
    if (dpy == EGL_NO_DISPLAY)
        return;

    if (!eglMakeCurrent(dpy, savedEGLState.draw, savedEGLState.read, savedEGLState.context))
        backend->log(AQ_LOG_WARNING, "CDRMRenderer: restoreEGL eglMakeCurrent failed");
}

EGLImageKHR CDRMRenderer::createEGLImage(const SDMABUFAttrs& attrs) {
    std::vector<uint32_t> attribs;

    attribs.push_back(EGL_WIDTH);
    attribs.push_back(attrs.size.x);
    attribs.push_back(EGL_HEIGHT);
    attribs.push_back(attrs.size.y);
    attribs.push_back(EGL_LINUX_DRM_FOURCC_EXT);
    attribs.push_back(attrs.format);

    TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL: createEGLImage: size {} with format {} and modifier 0x{:x}", attrs.size, fourccToName(attrs.format), attrs.modifier)));

    struct {
        EGLint fd;
        EGLint offset;
        EGLint pitch;
        EGLint modlo;
        EGLint modhi;
    } attrNames[4] = {
        {EGL_DMA_BUF_PLANE0_FD_EXT, EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGL_DMA_BUF_PLANE0_PITCH_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT},
        {EGL_DMA_BUF_PLANE1_FD_EXT, EGL_DMA_BUF_PLANE1_OFFSET_EXT, EGL_DMA_BUF_PLANE1_PITCH_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT},
        {EGL_DMA_BUF_PLANE2_FD_EXT, EGL_DMA_BUF_PLANE2_OFFSET_EXT, EGL_DMA_BUF_PLANE2_PITCH_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT},
        {EGL_DMA_BUF_PLANE3_FD_EXT, EGL_DMA_BUF_PLANE3_OFFSET_EXT, EGL_DMA_BUF_PLANE3_PITCH_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT}};

    for (int i = 0; i < attrs.planes; i++) {
        attribs.push_back(attrNames[i].fd);
        attribs.push_back(attrs.fds[i]);
        attribs.push_back(attrNames[i].offset);
        attribs.push_back(attrs.offsets[i]);
        attribs.push_back(attrNames[i].pitch);
        attribs.push_back(attrs.strides[i]);
        if (hasModifiers && attrs.modifier != DRM_FORMAT_MOD_INVALID) {
            attribs.push_back(attrNames[i].modlo);
            attribs.push_back(attrs.modifier & 0xFFFFFFFF);
            attribs.push_back(attrNames[i].modhi);
            attribs.push_back(attrs.modifier >> 32);
        }
    }

    attribs.push_back(EGL_IMAGE_PRESERVED_KHR);
    attribs.push_back(EGL_TRUE);

    attribs.push_back(EGL_NONE);

    EGLImageKHR image = egl.eglCreateImageKHR(egl.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, (int*)attribs.data());
    if (image == EGL_NO_IMAGE_KHR) {
        backend->log(AQ_LOG_ERROR, std::format("EGL: EGLCreateImageKHR failed: {}", eglGetError()));
        return EGL_NO_IMAGE_KHR;
    }

    return image;
}

#define GLCALL(__CALL__)                                                                                                                                                           \
    {                                                                                                                                                                              \
        __CALL__;                                                                                                                                                                  \
        auto err = glGetError();                                                                                                                                                   \
        if (err != GL_NO_ERROR) {                                                                                                                                                  \
            backend->log(AQ_LOG_ERROR,                                                                                                                                             \
                         std::format("[GLES] Error in call at {}@{}: 0x{:x}", __LINE__,                                                                                            \
                                     ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })(), err));               \
        }                                                                                                                                                                          \
    }

SGLTex CDRMRenderer::glTex(Hyprutils::Memory::CSharedPointer<IBuffer> buffa) {
    SGLTex     tex;

    const auto dma = buffa->dmabuf();

    tex.image = createEGLImage(dma);
    if (tex.image == EGL_NO_IMAGE_KHR) {
        backend->log(AQ_LOG_ERROR, std::format("EGL (glTex): createEGLImage failed: {}", eglGetError()));
        return tex;
    }

    bool external = false;
    for (auto& fmt : formats) {
        if (fmt.drmFormat != dma.format || fmt.modifier != dma.modifier)
            continue;

        backend->log(AQ_LOG_DEBUG, std::format("CDRMRenderer::glTex: found format+mod, external = {}", fmt.external));
        external = fmt.external;
        break;
    }

    tex.target = external ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;

    GLCALL(glGenTextures(1, &tex.texid));

    GLCALL(glBindTexture(tex.target, tex.texid));
    GLCALL(glTexParameteri(tex.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
    GLCALL(glTexParameteri(tex.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
    GLCALL(egl.glEGLImageTargetTexture2DOES(tex.target, tex.image));
    GLCALL(glBindTexture(tex.target, 0));

    return tex;
}

inline const float fullVerts[] = {
    1, 0, // top right
    0, 0, // top left
    1, 1, // bottom right
    0, 1, // bottom left
};

void CDRMRenderer::waitOnSync(int fd) {
    TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (waitOnSync): attempting to wait on fd {}", fd)));

    std::vector<EGLint> attribs;
    int                 dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
    if (dupFd < 0) {
        backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to dup fd for wait");
        return;
    }

    attribs.push_back(EGL_SYNC_NATIVE_FENCE_FD_ANDROID);
    attribs.push_back(dupFd);
    attribs.push_back(EGL_NONE);

    EGLSyncKHR sync = egl.eglCreateSyncKHR(egl.display, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs.data());
    if (sync == EGL_NO_SYNC_KHR) {
        TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to create an egl sync for explicit"));
        if (dupFd >= 0)
            close(dupFd);
        return;
    }

    // we got a sync, now we just tell egl to wait before sampling
    if (egl.eglWaitSyncKHR(egl.display, sync, 0) != EGL_TRUE) {
        if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE)
            TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to destroy sync"));

        TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to wait on the sync object"));
        return;
    }

    if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE)
        TRACE(backend->log(AQ_LOG_TRACE, "EGL (waitOnSync): failed to destroy sync"));
}

int CDRMRenderer::recreateBlitSync() {
    TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): recreating blit sync"));

    if (egl.lastBlitSync) {
        TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (recreateBlitSync): cleaning up old sync (fd {})", egl.lastBlitSyncFD)));

        // cleanup last sync
        if (egl.eglDestroySyncKHR(egl.display, egl.lastBlitSync) != EGL_TRUE)
            TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to destroy old sync"));

        if (egl.lastBlitSyncFD >= 0)
            close(egl.lastBlitSyncFD);

        egl.lastBlitSyncFD = -1;
        egl.lastBlitSync   = nullptr;
    }

    EGLSyncKHR sync = egl.eglCreateSyncKHR(egl.display, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
    if (sync == EGL_NO_SYNC_KHR) {
        TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to create an egl sync for explicit"));
        return -1;
    }

    // we need to flush otherwise we might not get a valid fd
    glFlush();

    int fd = egl.eglDupNativeFenceFDANDROID(egl.display, sync);
    if (fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
        TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to dup egl fence fd"));
        if (egl.eglDestroySyncKHR(egl.display, sync) != EGL_TRUE)
            TRACE(backend->log(AQ_LOG_TRACE, "EGL (recreateBlitSync): failed to destroy new sync"));
        return -1;
    }

    egl.lastBlitSync   = sync;
    egl.lastBlitSyncFD = fd;

    TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (recreateBlitSync): success, new fence exported with fd {}", fd)));

    return fd;
}

void CDRMRenderer::clearBuffer(IBuffer* buf) {
    setEGL();

    auto   dmabuf = buf->dmabuf();
    GLuint rboID = 0, fboID = 0;

    if (!dmabuf.success) {
        backend->log(AQ_LOG_ERROR, "EGL (clear): cannot clear a non-dmabuf");
        return;
    }

    auto rboImage = createEGLImage(dmabuf);
    if (rboImage == EGL_NO_IMAGE_KHR) {
        backend->log(AQ_LOG_ERROR, std::format("EGL (clear): createEGLImage failed: {}", eglGetError()));
        return;
    }

    GLCALL(glGenRenderbuffers(1, &rboID));
    GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID));
    GLCALL(egl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)rboImage));
    GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));

    GLCALL(glGenFramebuffers(1, &fboID));
    GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID));
    GLCALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboID));

    GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID));
    GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID));

    TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (clear): fbo {} rbo {}", fboID, rboID)));

    glClearColor(0.F, 0.F, 0.F, 1.F);
    glClear(GL_COLOR_BUFFER_BIT);

    glFlush();

    GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
    GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));

    glDeleteFramebuffers(1, &fboID);
    glDeleteRenderbuffers(1, &rboID);
    egl.eglDestroyImageKHR(egl.display, rboImage);

    restoreEGL();
}

CDRMRenderer::SBlitResult CDRMRenderer::blit(SP<IBuffer> from, SP<IBuffer> to, int waitFD) {
    setEGL();

    if (from->dmabuf().size != to->dmabuf().size) {
        backend->log(AQ_LOG_ERROR, "EGL (blit): buffer sizes mismatched");
        return {};
    }

    if (waitFD >= 0) {
        // wait on a provided explicit fence
        waitOnSync(waitFD);
    }

    // firstly, get a texture from the from buffer
    // if it has an attachment, use that
    // both from and to have the same AQ_ATTACHMENT_DRM_RENDERER_DATA.
    // Those buffers always come from different swapchains, so it's OK.

    SGLTex fromTex;
    {
        auto attachment = from->attachments.get(AQ_ATTACHMENT_DRM_RENDERER_DATA);
        if (attachment) {
            TRACE(backend->log(AQ_LOG_TRACE, "EGL (blit): From attachment found"));
            auto att = (CDRMRendererBufferAttachment*)attachment.get();
            fromTex  = att->tex;
        }

        if (!fromTex.image) {
            backend->log(AQ_LOG_DEBUG, "EGL (blit): No attachment in from, creating a new image");
            fromTex = glTex(from);

            // should never remove anything, but JIC. We'll leak an EGLImage if this removes anything.
            from->attachments.removeByType(AQ_ATTACHMENT_DRM_RENDERER_DATA);
            from->attachments.add(makeShared<CDRMRendererBufferAttachment>(self, from, nullptr, 0, 0, fromTex));
        }
    }

    TRACE(backend->log(AQ_LOG_TRACE,
                       std::format("EGL (blit): fromTex id {}, image 0x{:x}, target {}", fromTex.texid, (uintptr_t)fromTex.image,
                                   fromTex.target == GL_TEXTURE_2D ? "GL_TEXTURE_2D" : "GL_TEXTURE_EXTERNAL_OES")));

    // then, get a rbo from our to buffer
    // if it has an attachment, use that

    EGLImageKHR rboImage = nullptr;
    GLuint      rboID = 0, fboID = 0;
    auto        toDma = to->dmabuf();

    if (!verifyDestinationDMABUF(toDma)) {
        backend->log(AQ_LOG_ERROR, "EGL (blit): failed to blit: destination dmabuf unsupported");
        return {};
    }

    {
        auto attachment = to->attachments.get(AQ_ATTACHMENT_DRM_RENDERER_DATA);
        if (attachment) {
            TRACE(backend->log(AQ_LOG_TRACE, "EGL (blit): To attachment found"));
            auto att = (CDRMRendererBufferAttachment*)attachment.get();
            rboImage = att->eglImage;
            fboID    = att->fbo;
            rboID    = att->rbo;
        }

        if (!rboImage) {
            backend->log(AQ_LOG_DEBUG, "EGL (blit): No attachment in to, creating a new image");

            rboImage = createEGLImage(toDma);
            if (rboImage == EGL_NO_IMAGE_KHR) {
                backend->log(AQ_LOG_ERROR, std::format("EGL (blit): createEGLImage failed: {}", eglGetError()));
                return {};
            }

            GLCALL(glGenRenderbuffers(1, &rboID));
            GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID));
            GLCALL(egl.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, (GLeglImageOES)rboImage));
            GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));

            GLCALL(glGenFramebuffers(1, &fboID));
            GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID));
            GLCALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rboID));

            if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
                backend->log(AQ_LOG_ERROR, std::format("EGL (blit): glCheckFramebufferStatus failed: {}", glGetError()));
                return {};
            }

            // should never remove anything, but JIC. We'll leak an RBO and FBO if this removes anything.
            to->attachments.removeByType(AQ_ATTACHMENT_DRM_RENDERER_DATA);
            to->attachments.add(makeShared<CDRMRendererBufferAttachment>(self, to, rboImage, fboID, rboID, SGLTex{}));
        }
    }

    glFlush();

    TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (blit): rboImage 0x{:x}", (uintptr_t)rboImage)));

    GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, rboID));
    GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, fboID));

    TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (blit): fbo {} rbo {}", fboID, rboID)));

    glClearColor(0.77F, 0.F, 0.74F, 1.F);
    glClear(GL_COLOR_BUFFER_BIT);

    // done, let's render the texture to the rbo
    CBox renderBox = {{}, toDma.size};

    TRACE(backend->log(AQ_LOG_TRACE, std::format("EGL (blit): box size {}", renderBox.size())));

    float mtx[9];
    float base[9];
    float monitorProj[9];
    matrixIdentity(base);

    auto& SHADER = fromTex.target == GL_TEXTURE_2D ? gl.shader : gl.shaderExt;

    // KMS uses flipped y, we have to do FLIPPED_180
    matrixTranslate(base, toDma.size.x / 2.0, toDma.size.y / 2.0);
    matrixTransform(base, HYPRUTILS_TRANSFORM_FLIPPED_180);
    matrixTranslate(base, -toDma.size.x / 2.0, -toDma.size.y / 2.0);

    projectBox(mtx, renderBox, HYPRUTILS_TRANSFORM_FLIPPED_180, 0, base);

    matrixProjection(monitorProj, toDma.size.x, toDma.size.y, HYPRUTILS_TRANSFORM_FLIPPED_180);

    float glMtx[9];
    matrixMultiply(glMtx, monitorProj, mtx);

    GLCALL(glViewport(0, 0, toDma.size.x, toDma.size.y));

    GLCALL(glActiveTexture(GL_TEXTURE0));
    GLCALL(glBindTexture(fromTex.target, fromTex.texid));

    GLCALL(glTexParameteri(fromTex.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
    GLCALL(glTexParameteri(fromTex.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST));

    GLCALL(glUseProgram(SHADER.program));
    GLCALL(glDisable(GL_BLEND));
    GLCALL(glDisable(GL_SCISSOR_TEST));

    matrixTranspose(glMtx, glMtx);
    GLCALL(glUniformMatrix3fv(SHADER.proj, 1, GL_FALSE, glMtx));

    GLCALL(glUniform1i(SHADER.tex, 0));

    GLCALL(glVertexAttribPointer(SHADER.posAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts));
    GLCALL(glVertexAttribPointer(SHADER.texAttrib, 2, GL_FLOAT, GL_FALSE, 0, fullVerts));

    GLCALL(glEnableVertexAttribArray(SHADER.posAttrib));
    GLCALL(glEnableVertexAttribArray(SHADER.texAttrib));

    GLCALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));

    GLCALL(glDisableVertexAttribArray(SHADER.posAttrib));
    GLCALL(glDisableVertexAttribArray(SHADER.texAttrib));

    GLCALL(glBindTexture(fromTex.target, 0));

    // rendered, cleanup
    glFlush();

    // get an explicit sync fd for the secondary gpu.
    // when we pass buffers between gpus we should always use explicit sync,
    // as implicit is not guaranteed at all
    int explicitFD = recreateBlitSync();

    GLCALL(glBindFramebuffer(GL_FRAMEBUFFER, 0));
    GLCALL(glBindRenderbuffer(GL_RENDERBUFFER, 0));

    restoreEGL();

    return {true, explicitFD == -1 ? std::nullopt : std::optional<int>{explicitFD}};
}

void CDRMRenderer::onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment) {
    setEGL();

    TRACE(backend->log(AQ_LOG_TRACE,
                       std::format("EGL (onBufferAttachmentDrop): dropping fbo {} rbo {} image 0x{:x}", attachment->fbo, attachment->rbo, (uintptr_t)attachment->eglImage)));

    if (attachment->tex.texid)
        GLCALL(glDeleteTextures(1, &attachment->tex.texid));
    if (attachment->rbo)
        GLCALL(glDeleteRenderbuffers(1, &attachment->rbo));
    if (attachment->fbo)
        GLCALL(glDeleteFramebuffers(1, &attachment->fbo));
    if (attachment->eglImage)
        egl.eglDestroyImageKHR(egl.display, attachment->eglImage);
    if (attachment->tex.image)
        egl.eglDestroyImageKHR(egl.display, attachment->tex.image);

    restoreEGL();
}

bool CDRMRenderer::verifyDestinationDMABUF(const SDMABUFAttrs& attrs) {
    for (auto& fmt : formats) {
        if (fmt.drmFormat != attrs.format)
            continue;

        if (fmt.modifier != attrs.modifier)
            continue;

        if (fmt.external) {
            backend->log(AQ_LOG_ERROR, "EGL (verifyDestinationDMABUF): FAIL, format is external-only");
            return false;
        }

        return true;
    }

    backend->log(AQ_LOG_ERROR, "EGL (verifyDestinationDMABUF): FAIL, format is unsupported by EGL");
    return false;
}

CDRMRendererBufferAttachment::CDRMRendererBufferAttachment(Hyprutils::Memory::CWeakPointer<CDRMRenderer> renderer_, Hyprutils::Memory::CSharedPointer<IBuffer> buffer,
                                                           EGLImageKHR image, GLuint fbo_, GLuint rbo_, SGLTex tex_) :
    eglImage(image), fbo(fbo_), rbo(rbo_), renderer(renderer_), tex(tex_) {
    bufferDestroy = buffer->events.destroy.registerListener([this](std::any d) { renderer->onBufferAttachmentDrop(this); });
}
0707010000003D000081A400000000000000000000000166C38D3F000013ED000000000000000000000000000000000000002E00000000aquamarine-0.3.3/src/backend/drm/Renderer.hpp#pragma once

#include <aquamarine/backend/DRM.hpp>
#include "FormatUtils.hpp"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <gbm.h>
#include <optional>
#include <tuple>
#include <vector>

namespace Aquamarine {

    class CGBMAllocator;

    struct SGLTex {
        EGLImage image  = nullptr;
        GLuint   texid  = 0;
        GLuint   target = GL_TEXTURE_2D;
    };

    class CDRMRendererBufferAttachment : public IAttachment {
      public:
        CDRMRendererBufferAttachment(Hyprutils::Memory::CWeakPointer<CDRMRenderer> renderer_, Hyprutils::Memory::CSharedPointer<IBuffer> buffer, EGLImageKHR image, GLuint fbo_,
                                     GLuint rbo_, SGLTex tex);
        virtual ~CDRMRendererBufferAttachment() {
            ;
        }
        virtual eAttachmentType type() {
            return AQ_ATTACHMENT_DRM_RENDERER_DATA;
        }

        EGLImageKHR                                   eglImage = nullptr;
        GLuint                                        fbo = 0, rbo = 0;
        SGLTex                                        tex;
        Hyprutils::Signal::CHyprSignalListener        bufferDestroy;

        Hyprutils::Memory::CWeakPointer<CDRMRenderer> renderer;
    };

    class CDRMRenderer {
      public:
        static Hyprutils::Memory::CSharedPointer<CDRMRenderer> attempt(Hyprutils::Memory::CSharedPointer<CGBMAllocator> allocator_,
                                                                       Hyprutils::Memory::CSharedPointer<CBackend>      backend_);

        int                                                    drmFD = -1;

        struct SBlitResult {
            bool               success = false;
            std::optional<int> syncFD;
        };

        SBlitResult blit(Hyprutils::Memory::CSharedPointer<IBuffer> from, Hyprutils::Memory::CSharedPointer<IBuffer> to, int waitFD = -1);
        // can't be a SP<> because we call it from buf's ctor...
        void clearBuffer(IBuffer* buf);

        void setEGL();
        void restoreEGL();

        void onBufferAttachmentDrop(CDRMRendererBufferAttachment* attachment);

        struct {
            struct SShader {
                GLuint program = 0;
                GLint  proj = -1, tex = -1, posAttrib = -1, texAttrib = -1;
            } shader, shaderExt;
        } gl;

        struct {
            EGLDisplay                                    display        = nullptr;
            EGLContext                                    context        = nullptr;
            EGLSync                                       lastBlitSync   = nullptr;
            int                                           lastBlitSyncFD = -1;

            PFNEGLGETPLATFORMDISPLAYEXTPROC               eglGetPlatformDisplayEXT               = nullptr;
            PFNEGLCREATEIMAGEKHRPROC                      eglCreateImageKHR                      = nullptr;
            PFNEGLDESTROYIMAGEKHRPROC                     eglDestroyImageKHR                     = nullptr;
            PFNGLEGLIMAGETARGETTEXTURE2DOESPROC           glEGLImageTargetTexture2DOES           = nullptr;
            PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES = nullptr;
            PFNEGLQUERYDMABUFFORMATSEXTPROC               eglQueryDmaBufFormatsEXT               = nullptr;
            PFNEGLQUERYDMABUFMODIFIERSEXTPROC             eglQueryDmaBufModifiersEXT             = nullptr;
            PFNEGLDESTROYSYNCKHRPROC                      eglDestroySyncKHR                      = nullptr;
            PFNEGLWAITSYNCKHRPROC                         eglWaitSyncKHR                         = nullptr;
            PFNEGLCREATESYNCKHRPROC                       eglCreateSyncKHR                       = nullptr;
            PFNEGLDUPNATIVEFENCEFDANDROIDPROC             eglDupNativeFenceFDANDROID             = nullptr;
        } egl;

        struct {
            EGLDisplay display = nullptr;
            EGLContext context = nullptr;
            EGLSurface draw = nullptr, read = nullptr;
        } savedEGLState;

        SGLTex                                        glTex(Hyprutils::Memory::CSharedPointer<IBuffer> buf);

        Hyprutils::Memory::CWeakPointer<CDRMRenderer> self;
        std::vector<SGLFormat>                        formats;

      private:
        CDRMRenderer() = default;

        EGLImageKHR                                           createEGLImage(const SDMABUFAttrs& attrs);
        std::optional<std::vector<std::pair<uint64_t, bool>>> getModsForFormat(EGLint format);
        bool                                                  initDRMFormats();
        bool                                                  verifyDestinationDMABUF(const SDMABUFAttrs& attrs);
        void                                                  waitOnSync(int fd);
        int                                                   recreateBlitSync();
        bool                                                  hasModifiers = false;

        Hyprutils::Memory::CWeakPointer<CBackend>             backend;
    };
};0707010000003E000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000002600000000aquamarine-0.3.3/src/backend/drm/impl0707010000003F000081A400000000000000000000000166C38D3F0000367E000000000000000000000000000000000000003100000000aquamarine-0.3.3/src/backend/drm/impl/Atomic.cpp#include <aquamarine/backend/drm/Atomic.hpp>
#include <cstring>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <sys/mman.h>
#include "Shared.hpp"
#include "aquamarine/output/Output.hpp"

using namespace Aquamarine;
using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#define SP CSharedPointer

Aquamarine::CDRMAtomicRequest::CDRMAtomicRequest(Hyprutils::Memory::CWeakPointer<CDRMBackend> backend_) : backend(backend_) {
    req = drmModeAtomicAlloc();
    if (!req)
        failed = true;
}

Aquamarine::CDRMAtomicRequest::~CDRMAtomicRequest() {
    if (req)
        drmModeAtomicFree(req);
}

void Aquamarine::CDRMAtomicRequest::add(uint32_t id, uint32_t prop, uint64_t val) {
    if (failed)
        return;

    TRACE(backend->log(AQ_LOG_TRACE, std::format("atomic drm request: adding id {} prop {} with value {}", id, prop, val)));

    if (id == 0 || prop == 0) {
        backend->log(AQ_LOG_ERROR, "atomic drm request: failed to add prop: id / prop == 0");
        return;
    }

    if (drmModeAtomicAddProperty(req, id, prop, val) < 0) {
        backend->log(AQ_LOG_ERROR, "atomic drm request: failed to add prop");
        failed = true;
    }
}

void Aquamarine::CDRMAtomicRequest::planeProps(Hyprutils::Memory::CSharedPointer<SDRMPlane> plane, Hyprutils::Memory::CSharedPointer<CDRMFB> fb, uint32_t crtc,
                                               Hyprutils::Math::Vector2D pos) {

    if (failed)
        return;

    if (!fb || !crtc) {
        // Disable the plane
        TRACE(backend->log(AQ_LOG_TRACE, std::format("atomic planeProps: disabling plane {}", plane->id)));
        add(plane->id, plane->props.fb_id, 0);
        add(plane->id, plane->props.crtc_id, 0);
        add(plane->id, plane->props.crtc_x, (uint64_t)pos.x);
        add(plane->id, plane->props.crtc_y, (uint64_t)pos.y);
        return;
    }

    TRACE(backend->log(AQ_LOG_TRACE,
                       std::format("atomic planeProps: prop blobs: src_x {}, src_y {}, src_w {}, src_h {}, crtc_w {}, crtc_h {}, fb_id {}, crtc_id {}, crtc_x {}, crtc_y {}",
                                   plane->props.src_x, plane->props.src_y, plane->props.src_w, plane->props.src_h, plane->props.crtc_w, plane->props.crtc_h, plane->props.fb_id,
                                   plane->props.crtc_id, plane->props.crtc_x, plane->props.crtc_y)));

    // src_ are 16.16 fixed point (lol)
    add(plane->id, plane->props.src_x, 0);
    add(plane->id, plane->props.src_y, 0);
    add(plane->id, plane->props.src_w, ((uint64_t)fb->buffer->size.x) << 16);
    add(plane->id, plane->props.src_h, ((uint64_t)fb->buffer->size.y) << 16);
    add(plane->id, plane->props.crtc_w, (uint32_t)fb->buffer->size.x);
    add(plane->id, plane->props.crtc_h, (uint32_t)fb->buffer->size.y);
    add(plane->id, plane->props.fb_id, fb->id);
    add(plane->id, plane->props.crtc_id, crtc);
    add(plane->id, plane->props.crtc_x, (uint64_t)pos.x);
    add(plane->id, plane->props.crtc_y, (uint64_t)pos.y);
}

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;

    TRACE(backend->log(AQ_LOG_TRACE,
                       std::format("atomic addConnector blobs: mode_id {}, active {}, crtc_id {}, link_status {}, content_type {}", connector->crtc->props.mode_id,
                                   connector->crtc->props.active, connector->props.crtc_id, connector->props.link_status, connector->props.content_type)));

    TRACE(backend->log(AQ_LOG_TRACE, std::format("atomic addConnector values: CRTC {}, mode {}", enable ? connector->crtc->id : 0, data.atomic.modeBlob)));

    add(connector->id, connector->props.crtc_id, enable ? connector->crtc->id : 0);

    if (data.modeset) {
        add(connector->crtc->id, connector->crtc->props.mode_id, data.atomic.modeBlob);
        data.atomic.blobbed = true;
    }

    if (data.modeset && enable && connector->props.link_status)
        add(connector->id, connector->props.link_status, DRM_MODE_LINK_STATUS_GOOD);

    // TODO: allow to send aq a content type, maybe? Wayland has a protocol for this.
    if (enable && connector->props.content_type)
        add(connector->id, connector->props.content_type, DRM_MODE_CONTENT_TYPE_GRAPHICS);

    if (data.modeset && enable && connector->props.max_bpc && connector->maxBpcBounds.at(1))
        add(connector->id, connector->props.max_bpc, 8); // FIXME: this isnt always 8

    add(connector->crtc->id, connector->crtc->props.active, enable);

    if (enable) {
        if (connector->output->supportsExplicit && STATE.committed & COutputState::AQ_OUTPUT_STATE_EXPLICIT_OUT_FENCE)
            add(connector->crtc->id, connector->crtc->props.out_fence_ptr, (uintptr_t)&STATE.explicitOutFence);

        if (connector->crtc->props.gamma_lut && data.atomic.gammad)
            add(connector->crtc->id, connector->crtc->props.gamma_lut, data.atomic.gammaLut);

        if (connector->crtc->props.vrr_enabled)
            add(connector->crtc->id, connector->crtc->props.vrr_enabled, (uint64_t)STATE.adaptiveSync);

        planeProps(connector->crtc->primary, data.mainFB, connector->crtc->id, {});

        if (connector->output->supportsExplicit && STATE.explicitInFence >= 0)
            add(connector->crtc->primary->id, connector->crtc->primary->props.in_fence_fd, STATE.explicitInFence);

        if (connector->crtc->primary->props.fb_damage_clips)
            add(connector->crtc->primary->id, connector->crtc->primary->props.fb_damage_clips, data.atomic.fbDamage);

        if (connector->crtc->cursor) {
            if (!connector->output->cursorVisible)
                planeProps(connector->crtc->cursor, nullptr, 0, {});
            else
                planeProps(connector->crtc->cursor, data.cursorFB, connector->crtc->id, connector->output->cursorPos - connector->output->cursorHotspot);
        }

    } else {
        planeProps(connector->crtc->primary, nullptr, 0, {});
        if (connector->crtc->cursor)
            planeProps(connector->crtc->cursor, nullptr, 0, {});
    }

    conn = connector;
}

bool Aquamarine::CDRMAtomicRequest::commit(uint32_t flagssss) {
    static auto flagsToStr = [](uint32_t flags) {
        std::string result;
        if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)
            result += "ATOMIC_ALLOW_MODESET ";
        if (flags & DRM_MODE_ATOMIC_NONBLOCK)
            result += "ATOMIC_NONBLOCK ";
        if (flags & DRM_MODE_ATOMIC_TEST_ONLY)
            result += "ATOMIC_TEST_ONLY ";
        if (flags & DRM_MODE_PAGE_FLIP_EVENT)
            result += "PAGE_FLIP_EVENT ";
        if (flags & DRM_MODE_PAGE_FLIP_ASYNC)
            result += "PAGE_FLIP_ASYNC ";
        if (flags & (~DRM_MODE_ATOMIC_FLAGS))
            result += " + invalid...";
        return result;
    };

    if (failed) {
        backend->log((flagssss & DRM_MODE_ATOMIC_TEST_ONLY) ? AQ_LOG_DEBUG : AQ_LOG_ERROR, std::format("atomic drm request: failed to commit, failed flag set to true"));
        return false;
    }

    if (auto ret = drmModeAtomicCommit(backend->gpu->fd, req, flagssss, &conn->pendingPageFlip); ret) {
        backend->log((flagssss & DRM_MODE_ATOMIC_TEST_ONLY) ? AQ_LOG_DEBUG : AQ_LOG_ERROR,
                     std::format("atomic drm request: failed to commit: {}, flags: {}", strerror(-ret), flagsToStr(flagssss)));
        return false;
    }

    return true;
}

void Aquamarine::CDRMAtomicRequest::destroyBlob(uint32_t id) {
    if (!id)
        return;

    if (drmModeDestroyPropertyBlob(backend->gpu->fd, id))
        backend->log(AQ_LOG_ERROR, "atomic drm request: failed to destroy a blob");
}

void Aquamarine::CDRMAtomicRequest::commitBlob(uint32_t* current, uint32_t next) {
    if (*current == next)
        return;
    destroyBlob(*current);
    *current = next;
}

void Aquamarine::CDRMAtomicRequest::rollbackBlob(uint32_t* current, uint32_t next) {
    if (*current == next)
        return;
    destroyBlob(next);
}

void Aquamarine::CDRMAtomicRequest::rollback(SDRMConnectorCommitData& data) {
    if (!conn)
        return;

    conn->crtc->atomic.ownModeID = true;
    if (data.atomic.blobbed)
        rollbackBlob(&conn->crtc->atomic.modeID, data.atomic.modeBlob);
    rollbackBlob(&conn->crtc->atomic.gammaLut, data.atomic.gammaLut);
    destroyBlob(data.atomic.fbDamage);
}

void Aquamarine::CDRMAtomicRequest::apply(SDRMConnectorCommitData& data) {
    if (!conn)
        return;

    if (!conn->crtc->atomic.ownModeID)
        conn->crtc->atomic.modeID = 0;

    conn->crtc->atomic.ownModeID = true;
    if (data.atomic.blobbed)
        commitBlob(&conn->crtc->atomic.modeID, data.atomic.modeBlob);
    commitBlob(&conn->crtc->atomic.gammaLut, data.atomic.gammaLut);
    destroyBlob(data.atomic.fbDamage);
}

Aquamarine::CDRMAtomicImpl::CDRMAtomicImpl(Hyprutils::Memory::CSharedPointer<CDRMBackend> backend_) : backend(backend_) {
    ;
}

bool Aquamarine::CDRMAtomicImpl::prepareConnector(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data) {
    const auto& STATE  = connector->output->state->state();
    const bool  enable = STATE.enabled;
    const auto& MODE   = STATE.mode ? STATE.mode : STATE.customMode;

    if (data.modeset) {
        if (!enable)
            data.atomic.modeBlob = 0;
        else {
            if (drmModeCreatePropertyBlob(connector->backend->gpu->fd, (drmModeModeInfo*)&data.modeInfo, sizeof(drmModeModeInfo), &data.atomic.modeBlob)) {
                connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to create a modeset blob");
                return false;
            }

            TRACE(connector->backend->log(AQ_LOG_TRACE,
                                          std::format("Connector blob id {}: clock {}, {}x{}, vrefresh {}, name: {}", data.atomic.modeBlob, data.modeInfo.clock,
                                                      data.modeInfo.hdisplay, data.modeInfo.vdisplay, data.modeInfo.vrefresh, data.modeInfo.name)));
        }
    }

    if (STATE.committed & COutputState::AQ_OUTPUT_STATE_GAMMA_LUT) {
        if (!connector->crtc->props.gamma_lut) // TODO: allow this with legacy gamma, perhaps.
            connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to commit gamma: no gamma_lut prop");
        else if (STATE.gammaLut.empty()) {
            data.atomic.gammaLut = 0;
            data.atomic.gammad   = true;
        } else {
            std::vector<drm_color_lut> lut;
            lut.resize(STATE.gammaLut.size() / 3); // [r,g,b]+

            for (size_t i = 0; i < lut.size(); ++i) {
                lut.at(i).red      = STATE.gammaLut.at(i * 3 + 0);
                lut.at(i).green    = STATE.gammaLut.at(i * 3 + 1);
                lut.at(i).blue     = STATE.gammaLut.at(i * 3 + 2);
                lut.at(i).reserved = 0;
            }

            if (drmModeCreatePropertyBlob(connector->backend->gpu->fd, lut.data(), lut.size() * sizeof(drm_color_lut), &data.atomic.gammaLut)) {
                connector->backend->backend->log(AQ_LOG_ERROR, "atomic drm: failed to create a gamma blob");
                data.atomic.gammaLut = 0;
            } else
                data.atomic.gammad = true;
        }
    }

    if ((STATE.committed & COutputState::AQ_OUTPUT_STATE_DAMAGE) && connector->crtc->primary->props.fb_damage_clips && MODE) {
        if (STATE.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();
            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;
            }
        }
    }

    return true;
}

bool Aquamarine::CDRMAtomicImpl::commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data) {
    if (!prepareConnector(connector, data))
        return false;

    CDRMAtomicRequest request(backend);

    request.addConnector(connector, data);

    uint32_t flags = data.flags;
    if (data.test)
        flags |= DRM_MODE_ATOMIC_TEST_ONLY;
    if (data.modeset)
        flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
    if (!data.blocking && !data.test)
        flags |= DRM_MODE_ATOMIC_NONBLOCK;

    const bool ok = request.commit(flags);

    if (ok) {
        request.apply(data);
        if (!data.test && data.mainFB && connector->output->state->state().enabled && (flags & DRM_MODE_PAGE_FLIP_EVENT))
            connector->isPageFlipPending = true;
    } else
        request.rollback(data);

    return ok;
}

bool Aquamarine::CDRMAtomicImpl::reset() {
    CDRMAtomicRequest request(backend);

    for (auto& crtc : backend->crtcs) {
        request.add(crtc->id, crtc->props.mode_id, 0);
        request.add(crtc->id, crtc->props.active, 0);
    }

    for (auto& conn : backend->connectors) {
        request.add(conn->id, conn->props.crtc_id, 0);
    }

    for (auto& plane : backend->planes) {
        request.planeProps(plane, nullptr, 0, {});
    }

    return request.commit(DRM_MODE_ATOMIC_ALLOW_MODESET);
}

bool Aquamarine::CDRMAtomicImpl::moveCursor(SP<SDRMConnector> connector, bool skipShedule) {
    if (!connector->output->cursorVisible || !connector->output->state->state().enabled || !connector->crtc || !connector->crtc->cursor)
        return true;

    if (!skipShedule) {
        TRACE(connector->backend->log(AQ_LOG_TRACE, "atomic moveCursor"));
        connector->output->scheduleFrame(IOutput::AQ_SCHEDULE_CURSOR_MOVE);
    }

    return true;
}07070100000040000081A400000000000000000000000166C38D3F00001C4A000000000000000000000000000000000000003100000000aquamarine-0.3.3/src/backend/drm/impl/Legacy.cpp#include "aquamarine/output/Output.hpp"
#include <aquamarine/backend/drm/Legacy.hpp>
#include <cstring>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <sys/mman.h>

using namespace Aquamarine;
using namespace Hyprutils::Memory;
using namespace Hyprutils::Math;
#define SP CSharedPointer

Aquamarine::CDRMLegacyImpl::CDRMLegacyImpl(Hyprutils::Memory::CSharedPointer<CDRMBackend> backend_) : backend(backend_) {
    ;
}

bool Aquamarine::CDRMLegacyImpl::moveCursor(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, bool skipShedule) {
    if (!connector->output->cursorVisible || !connector->output->state->state().enabled || !connector->crtc || !connector->crtc->cursor)
        return true;

    if (!skipShedule)
        connector->output->scheduleFrame(IOutput::AQ_SCHEDULE_CURSOR_MOVE);

    return true;
}

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;

    if (enable) {
        if (!data.mainFB)
            connector->backend->backend->log(AQ_LOG_WARNING, "legacy drm: No buffer, will fall back to only modeset (if present)");
        else
            mainFB = data.mainFB;
    }

    if (data.modeset) {
        connector->backend->backend->log(AQ_LOG_DEBUG, std::format("legacy drm: Modesetting CRTC {}", connector->crtc->id));

        uint32_t dpms = enable ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
        if (drmModeConnectorSetProperty(connector->backend->gpu->fd, connector->id, connector->props.dpms, dpms)) {
            connector->backend->backend->log(AQ_LOG_ERROR, "legacy drm: Failed to set dpms");
            return false;
        }

        std::vector<uint32_t> connectors;
        drmModeModeInfo*      mode = nullptr;
        if (enable) {
            connectors.push_back(connector->id);
            mode = (drmModeModeInfo*)&data.modeInfo;
        }

        if (mode) {
            connector->backend->backend->log(
                AQ_LOG_DEBUG,
                std::format("legacy drm: Modesetting CRTC, mode: clock {} hdisplay {} vdisplay {} vrefresh {}", mode->clock, mode->hdisplay, mode->vdisplay, mode->vrefresh));
        } else
            connector->backend->backend->log(AQ_LOG_DEBUG, "legacy drm: Modesetting CRTC, mode null");

        if (auto ret = drmModeSetCrtc(connector->backend->gpu->fd, connector->crtc->id, mainFB ? mainFB->id : -1, 0, 0, connectors.data(), connectors.size(), mode); ret) {
            connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: drmModeSetCrtc failed: {}", strerror(-ret)));
            return false;
        }
    }

    if (STATE.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;
        }

        if (connector->crtc->props.vrr_enabled) {
            if (auto ret = drmModeObjectSetProperty(backend->gpu->fd, connector->crtc->id, DRM_MODE_OBJECT_CRTC, connector->crtc->props.vrr_enabled, (uint64_t)STATE.adaptiveSync);
                ret) {
                connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: drmModeObjectSetProperty: vrr -> {} failed: {}", STATE.adaptiveSync, strerror(-ret)));
                return false;
            }
        }

        connector->output->vrrActive = STATE.adaptiveSync;
        connector->backend->backend->log(AQ_LOG_DEBUG, std::format("legacy drm: connector {} vrr -> {}", connector->id, STATE.adaptiveSync));
    }

    // TODO: gamma

    if (data.cursorFB && connector->crtc->cursor && connector->output->cursorVisible && enable) {
        uint32_t boHandle = 0;
        auto     attrs    = data.cursorFB->buffer->dmabuf();

        if (int ret = drmPrimeFDToHandle(connector->backend->gpu->fd, attrs.fds.at(0), &boHandle); ret) {
            connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: drmPrimeFDToHandle failed: {}", strerror(-ret)));
            return false;
        }

        connector->backend->backend->log(AQ_LOG_DEBUG,
                                         std::format("legacy drm: cursor fb: {} with bo handle {} from fd {}, size {}", connector->backend->gpu->fd, boHandle,
                                                     data.cursorFB->buffer->dmabuf().fds.at(0), data.cursorFB->buffer->size));

        Vector2D                cursorPos = connector->output->cursorPos;

        struct drm_mode_cursor2 request = {
            .flags   = DRM_MODE_CURSOR_BO | DRM_MODE_CURSOR_MOVE,
            .crtc_id = connector->crtc->id,
            .x       = (int32_t)cursorPos.x,
            .y       = (int32_t)cursorPos.y,
            .width   = (uint32_t)data.cursorFB->buffer->size.x,
            .height  = (uint32_t)data.cursorFB->buffer->size.y,
            .handle  = boHandle,
            .hot_x   = (int32_t)connector->output->cursorHotspot.x,
            .hot_y   = (int32_t)connector->output->cursorHotspot.y,
        };

        int ret = drmIoctl(connector->backend->gpu->fd, DRM_IOCTL_MODE_CURSOR2, &request);

        if (boHandle && drmCloseBufferHandle(connector->backend->gpu->fd, boHandle))
            connector->backend->backend->log(AQ_LOG_ERROR, "legacy drm: drmCloseBufferHandle in cursor failed");

        if (ret) {
            connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: cursor drmIoctl failed: {}", strerror(errno)));
            return false;
        }
    } else if (drmModeSetCursor(connector->backend->gpu->fd, connector->crtc->id, 0, 0, 0))
        connector->backend->backend->log(AQ_LOG_ERROR, "legacy drm: cursor null failed");

    if (!enable)
        return true;

    if (!(data.flags & DRM_MODE_PAGE_FLIP_EVENT))
        return true;

    if (int ret = drmModePageFlip(connector->backend->gpu->fd, connector->crtc->id, mainFB ? mainFB->id : -1, data.flags, &connector->pendingPageFlip); ret) {
        connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: drmModePageFlip failed: {}", strerror(-ret)));
        return false;
    }

    connector->isPageFlipPending = true;

    return true;
}

bool Aquamarine::CDRMLegacyImpl::testInternal(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data) {
    return true; // TODO: lol
}

bool Aquamarine::CDRMLegacyImpl::commit(Hyprutils::Memory::CSharedPointer<SDRMConnector> connector, SDRMConnectorCommitData& data) {
    if (!testInternal(connector, data))
        return false;

    return commitInternal(connector, data);
}

bool Aquamarine::CDRMLegacyImpl::reset() {
    bool ok = true;
    for (auto& connector : backend->connectors) {
        if (!connector->crtc)
            continue;

        if (int ret = drmModeSetCrtc(backend->gpu->fd, connector->crtc->id, 0, 0, 0, nullptr, 0, nullptr); ret) {
            connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: reset failed: {}", strerror(-ret)));
            ok = false;
        }
    }

    return ok;
}07070100000041000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001C00000000aquamarine-0.3.3/src/buffer07070100000042000081A400000000000000000000000166C38D3F000002B7000000000000000000000000000000000000002700000000aquamarine-0.3.3/src/buffer/Buffer.cpp#include <aquamarine/buffer/Buffer.hpp>
#include "Shared.hpp"

using namespace Aquamarine;

SDMABUFAttrs Aquamarine::IBuffer::dmabuf() {
    return SDMABUFAttrs{};
}

SSHMAttrs Aquamarine::IBuffer::shm() {
    return SSHMAttrs{};
}

std::tuple<uint8_t*, uint32_t, size_t> Aquamarine::IBuffer::beginDataPtr(uint32_t flags) {
    return {nullptr, 0, 0};
}

void Aquamarine::IBuffer::endDataPtr() {
    ; // empty
}

void Aquamarine::IBuffer::sendRelease() {
    ;
}

void Aquamarine::IBuffer::lock() {
    locks++;
}

void Aquamarine::IBuffer::unlock() {
    locks--;

    ASSERT(locks >= 0);

    if (locks <= 0)
        sendRelease();
}

bool Aquamarine::IBuffer::locked() {
    return locks;
}
07070100000043000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001D00000000aquamarine-0.3.3/src/include07070100000044000081A400000000000000000000000166C38D3F00000061000000000000000000000000000000000000002D00000000aquamarine-0.3.3/src/include/FormatUtils.hpp#pragma once

#include <string>
#include <cstdint>

std::string fourccToName(uint32_t drmFormat);07070100000045000081A400000000000000000000000166C38D3F00000957000000000000000000000000000000000000002800000000aquamarine-0.3.3/src/include/Shared.hpp#pragma once

#include <iostream>
#include <format>
#include <signal.h>

namespace Aquamarine {
    bool envEnabled(const std::string& env);
    bool isTrace();
};

#define RASSERT(expr, reason, ...)                                                                                                                                                 \
    if (!(expr)) {                                                                                                                                                                 \
        std::cout << std::format("\n==========================================================================================\nASSERTION FAILED! \n\n{}\n\nat: line {} in {}",    \
                                 std::format(reason, ##__VA_ARGS__), __LINE__,                                                                                                     \
                                 ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })());                         \
        std::cout << "[Aquamarine] Assertion failed!";                                                                                                                             \
        raise(SIGABRT);                                                                                                                                                            \
    }

#define ASSERT(expr) RASSERT(expr, "?")

#define TRACE(expr)                                                                                                                                                                \
    {                                                                                                                                                                              \
        if (Aquamarine::isTrace()) {                                                                                                                                               \
            expr;                                                                                                                                                                  \
        }                                                                                                                                                                          \
    }
07070100000046000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001B00000000aquamarine-0.3.3/src/input07070100000047000081A400000000000000000000000166C38D3F000002B5000000000000000000000000000000000000002500000000aquamarine-0.3.3/src/input/Input.cpp#include <aquamarine/input/Input.hpp>

libinput_device* Aquamarine::IPointer::getLibinputHandle() {
    return nullptr;
}

libinput_device* Aquamarine::IKeyboard::getLibinputHandle() {
    return nullptr;
}

libinput_device* Aquamarine::ITouch::getLibinputHandle() {
    return nullptr;
}

libinput_device* Aquamarine::ISwitch::getLibinputHandle() {
    return nullptr;
}

libinput_device* Aquamarine::ITabletTool::getLibinputHandle() {
    return nullptr;
}

libinput_device* Aquamarine::ITablet::getLibinputHandle() {
    return nullptr;
}

libinput_device* Aquamarine::ITabletPad::getLibinputHandle() {
    return nullptr;
}

void Aquamarine::IKeyboard::updateLEDs(uint32_t leds) {
    ;
}
07070100000048000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001A00000000aquamarine-0.3.3/src/misc07070100000049000081A400000000000000000000000166C38D3F000003D1000000000000000000000000000000000000002900000000aquamarine-0.3.3/src/misc/Attachment.cpp#include <aquamarine/misc/Attachment.hpp>

using namespace Aquamarine;
using namespace Hyprutils::Memory;
#define SP CSharedPointer

bool Aquamarine::CAttachmentManager::has(eAttachmentType type) {
    for (auto& a : attachments) {
        if (a->type() == type)
            return true;
    }
    return false;
}

SP<IAttachment> Aquamarine::CAttachmentManager::get(eAttachmentType type) {
    for (auto& a : attachments) {
        if (a->type() == type)
            return a;
    }
    return nullptr;
}

void Aquamarine::CAttachmentManager::add(SP<IAttachment> attachment) {
    attachments.emplace_back(attachment);
}

void Aquamarine::CAttachmentManager::remove(SP<IAttachment> attachment) {
    std::erase(attachments, attachment);
}

void Aquamarine::CAttachmentManager::removeByType(eAttachmentType type) {
    std::erase_if(attachments, [type](const auto& e) { return e->type() == type; });
}

void Aquamarine::CAttachmentManager::clear() {
    attachments.clear();
}
0707010000004A000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001C00000000aquamarine-0.3.3/src/output0707010000004B000081A400000000000000000000000166C38D3F00000DBB000000000000000000000000000000000000002700000000aquamarine-0.3.3/src/output/Output.cpp#include <aquamarine/output/Output.hpp>

using namespace Aquamarine;

Hyprutils::Memory::CSharedPointer<SOutputMode> Aquamarine::IOutput::preferredMode() {
    for (auto& m : modes) {
        if (m->preferred)
            return m;
    }

    return nullptr;
}

void Aquamarine::IOutput::moveCursor(const Hyprutils::Math::Vector2D& coord, bool skipShedule) {
    ;
}

bool Aquamarine::IOutput::setCursor(Hyprutils::Memory::CSharedPointer<IBuffer> buffer, const Hyprutils::Math::Vector2D& hotspot) {
    return false;
}

void Aquamarine::IOutput::setCursorVisible(bool visible) {
    ;
}

void Aquamarine::IOutput::scheduleFrame(const scheduleFrameReason reason) {
    ;
}

Hyprutils::Math::Vector2D Aquamarine::IOutput::cursorPlaneSize() {
    return {}; // error
}

size_t Aquamarine::IOutput::getGammaSize() {
    return 0;
}

bool Aquamarine::IOutput::destroy() {
    return false;
}

const Aquamarine::COutputState::SInternalState& Aquamarine::COutputState::state() {
    return internalState;
}

void Aquamarine::COutputState::addDamage(const Hyprutils::Math::CRegion& region) {
    internalState.damage.add(region);
    internalState.committed |= AQ_OUTPUT_STATE_DAMAGE;
}

void Aquamarine::COutputState::clearDamage() {
    internalState.damage.clear();
    internalState.committed |= AQ_OUTPUT_STATE_DAMAGE;
}

void Aquamarine::COutputState::setEnabled(bool enabled) {
    internalState.enabled = enabled;
    internalState.committed |= AQ_OUTPUT_STATE_ENABLED;
}

void Aquamarine::COutputState::setAdaptiveSync(bool enabled) {
    internalState.adaptiveSync = enabled;
    internalState.committed |= AQ_OUTPUT_STATE_ADAPTIVE_SYNC;
}

void Aquamarine::COutputState::setPresentationMode(eOutputPresentationMode mode) {
    internalState.presentationMode = mode;
    internalState.committed |= AQ_OUTPUT_STATE_PRESENTATION_MODE;
}

void Aquamarine::COutputState::setGammaLut(const std::vector<uint16_t>& lut) {
    internalState.gammaLut = lut;
    internalState.committed |= AQ_OUTPUT_STATE_GAMMA_LUT;
}

void Aquamarine::COutputState::setMode(Hyprutils::Memory::CSharedPointer<SOutputMode> mode) {
    internalState.mode       = mode;
    internalState.customMode = nullptr;
    internalState.committed |= AQ_OUTPUT_STATE_MODE;
}

void Aquamarine::COutputState::setCustomMode(Hyprutils::Memory::CSharedPointer<SOutputMode> mode) {
    internalState.mode.reset();
    internalState.customMode = mode;
    internalState.committed |= AQ_OUTPUT_STATE_MODE;
}

void Aquamarine::COutputState::setFormat(uint32_t drmFormat) {
    internalState.drmFormat = drmFormat;
    internalState.committed |= AQ_OUTPUT_STATE_FORMAT;
}

void Aquamarine::COutputState::setBuffer(Hyprutils::Memory::CSharedPointer<IBuffer> buffer) {
    internalState.buffer = buffer;
    internalState.committed |= AQ_OUTPUT_STATE_BUFFER;
}

void Aquamarine::COutputState::setExplicitInFence(int64_t fenceFD) {
    internalState.explicitInFence = fenceFD;
    internalState.committed |= AQ_OUTPUT_STATE_EXPLICIT_IN_FENCE;
}

void Aquamarine::COutputState::setExplicitOutFence(int64_t fenceFD) {
    // internalState.explicitOutFence = fenceFD;
    internalState.committed |= AQ_OUTPUT_STATE_EXPLICIT_OUT_FENCE;
}

void Aquamarine::COutputState::resetExplicitFences() {
    // fences are now used, let's reset them to not confuse ourselves later.
    internalState.explicitInFence  = -1;
    internalState.explicitOutFence = -1;
}

void Aquamarine::COutputState::onCommit() {
    internalState.committed = 0;
    internalState.damage.clear();
}
0707010000004C000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001B00000000aquamarine-0.3.3/src/utils0707010000004D000081A400000000000000000000000166C38D3F0000010F000000000000000000000000000000000000002B00000000aquamarine-0.3.3/src/utils/FormatUtils.cpp#include "FormatUtils.hpp"
#include <drm_fourcc.h>
#include <xf86drm.h>
#include <cstdlib>

std::string fourccToName(uint32_t drmFormat) {
    auto        fmt  = drmGetFormatName(drmFormat);
    std::string name = fmt ? fmt : "unknown";
    free(fmt);
    return name;
}
0707010000004E000081A400000000000000000000000166C38D3F00000138000000000000000000000000000000000000002600000000aquamarine-0.3.3/src/utils/Shared.cpp#include "Shared.hpp"
#include <cstdlib>

bool Aquamarine::envEnabled(const std::string& env) {
    auto e = getenv(env.c_str());
    return e && e == std::string{"1"};
}

static bool trace = []() -> bool { return Aquamarine::envEnabled("AQ_TRACE"); }();

bool        Aquamarine::isTrace() {
    return trace;
}
0707010000004F000041ED00000000000000000000000266C38D3F00000000000000000000000000000000000000000000001700000000aquamarine-0.3.3/tests07070100000050000081A400000000000000000000000166C38D3F00000F25000000000000000000000000000000000000002800000000aquamarine-0.3.3/tests/SimpleWindow.cpp#include <aquamarine/backend/Backend.hpp>
#include <aquamarine/output/Output.hpp>
#include <aquamarine/input/Input.hpp>
#include <iostream>
#include <wayland-server.h>

using namespace Hyprutils::Signal;
using namespace Hyprutils::Memory;
#define SP CSharedPointer

static const char* aqLevelToString(Aquamarine::eBackendLogLevel level) {
    switch (level) {
        case Aquamarine::eBackendLogLevel::AQ_LOG_TRACE: return "TRACE";
        case Aquamarine::eBackendLogLevel::AQ_LOG_DEBUG: return "DEBUG";
        case Aquamarine::eBackendLogLevel::AQ_LOG_ERROR: return "ERROR";
        case Aquamarine::eBackendLogLevel::AQ_LOG_WARNING: return "WARNING";
        case Aquamarine::eBackendLogLevel::AQ_LOG_CRITICAL: return "CRITICAL";
        default: break;
    }

    return "UNKNOWN";
}

void aqLog(Aquamarine::eBackendLogLevel level, std::string msg) {
    std::cout << "[AQ] [" << aqLevelToString(level) << "] " << msg << "\n";
}

CHyprSignalListener     newOutputListener, outputFrameListener, outputStateListener, mouseMotionListener, keyboardKeyListener, newMouseListener, newKeyboardListener;
SP<Aquamarine::IOutput> output;

//
void onFrame() {
    std::cout << "[Client] onFrame\n";

    auto buf = output->swapchain->next(nullptr);

    output->state->setBuffer(buf);
    output->commit();
}

void onState(const Aquamarine::IOutput::SStateEvent& event) {
    std::cout << "[Client] onState with size " << std::format("{}", event.size) << "\n";

    output->state->setEnabled(true);
    output->state->setCustomMode(makeShared<Aquamarine::SOutputMode>(Aquamarine::SOutputMode{.pixelSize = event.size}));
    output->state->setFormat(DRM_FORMAT_XRGB8888);

    output->commit();
}

int main(int argc, char** argv, char** envp) {
    Aquamarine::SBackendOptions options;
    options.logFunction = aqLog;

    std::vector<Aquamarine::SBackendImplementationOptions> implementations;
    Aquamarine::SBackendImplementationOptions              waylandOptions;
    waylandOptions.backendType        = Aquamarine::eBackendType::AQ_BACKEND_WAYLAND;
    waylandOptions.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_IF_AVAILABLE;
    implementations.emplace_back(waylandOptions);

    auto aqBackend = Aquamarine::CBackend::create(implementations, options);

    newOutputListener = aqBackend->events.newOutput.registerListener([](std::any data) {
        output = std::any_cast<SP<Aquamarine::IOutput>>(data);

        std::cout << "[Client] Got a new output named " << output->name << "\n";

        outputFrameListener = output->events.frame.registerListener([](std::any data) { onFrame(); });
        outputStateListener = output->events.state.registerListener([](std::any data) { onState(std::any_cast<Aquamarine::IOutput::SStateEvent>(data)); });
    });

    newMouseListener = aqBackend->events.newPointer.registerListener([] (std::any pointer) {
        auto p = std::any_cast<SP<Aquamarine::IPointer>>(pointer);
        mouseMotionListener = p->events.warp.registerListener([] (std::any data) {
            auto e = std::any_cast<Aquamarine::IPointer::SWarpEvent>(data);
            std::cout << "[Client] Mouse warped to " << std::format("{}", e.absolute) << "\n";
        });
    });

    newKeyboardListener = aqBackend->events.newKeyboard.registerListener([] (std::any keeb) {
        auto k = std::any_cast<SP<Aquamarine::IKeyboard>>(keeb);
        keyboardKeyListener = k->events.key.registerListener([] (std::any data) {
            auto e = std::any_cast<Aquamarine::IKeyboard::SKeyEvent>(data);
            std::cout << "[Client] Key " << std::format("{}", e.key) << " state: " << e.pressed << " \n";
        });
    });

    if (!aqBackend || !aqBackend->start()) {
        std::cout << "Failed to start the aq backend\n";
        return 1;
    }

    // FIXME: write an event loop.
    // aqBackend->enterLoop();

    return 0;
}07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!718 blocks
openSUSE Build Service is sponsored by