2024-06-18 11:38:26 +02:00
# include <aquamarine/backend/Backend.hpp>
2024-06-18 18:45:05 +02:00
# include <aquamarine/backend/Wayland.hpp>
2024-07-01 14:33:05 +02:00
# include <aquamarine/backend/Headless.hpp>
2024-06-23 19:40:40 +02:00
# include <aquamarine/backend/DRM.hpp>
2024-06-19 22:40:23 +02:00
# include <aquamarine/allocator/GBM.hpp>
2024-06-18 18:45:05 +02:00
# include <sys/poll.h>
# include <thread>
# include <chrono>
2024-06-30 19:04:14 +02:00
# include <sys/timerfd.h>
# include <time.h>
# include <string.h>
2024-07-12 20:53:11 +02:00
# include <xf86drm.h>
# include <fcntl.h>
2024-07-16 11:53:20 +02:00
# include <unistd.h>
2024-06-18 11:38:26 +02:00
using namespace Hyprutils : : Memory ;
using namespace Aquamarine ;
# define SP CSharedPointer
2024-06-30 19:04:14 +02:00
# 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 ;
}
}
2024-06-18 18:45:05 +02:00
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 " ;
}
2024-06-18 11:38:26 +02:00
Aquamarine : : CBackend : : CBackend ( ) {
;
}
2024-06-18 18:45:05 +02:00
Aquamarine : : SBackendImplementationOptions : : SBackendImplementationOptions ( ) {
backendType = AQ_BACKEND_WAYLAND ;
backendRequestMode = AQ_BACKEND_REQUEST_IF_AVAILABLE ;
}
Aquamarine : : SBackendOptions : : SBackendOptions ( ) {
logFunction = nullptr ;
}
2024-06-18 11:38:26 +02:00
Hyprutils : : Memory : : CSharedPointer < CBackend > Aquamarine : : CBackend : : create ( const std : : vector < SBackendImplementationOptions > & backends , const SBackendOptions & options ) {
auto backend = SP < CBackend > ( new CBackend ( ) ) ;
2024-06-18 18:45:05 +02:00
backend - > options = options ;
backend - > implementationOptions = backends ;
2024-06-19 22:40:23 +02:00
backend - > self = backend ;
if ( backends . size ( ) < = 0 )
return nullptr ;
2024-06-18 11:38:26 +02:00
2024-06-18 18:45:05 +02:00
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 ;
2024-06-23 19:40:40 +02:00
} else if ( b . backendType = = AQ_BACKEND_DRM ) {
auto ref = CDRMBackend : : attempt ( backend ) ;
2024-06-28 17:13:03 +02:00
if ( ref . empty ( ) ) {
2024-06-23 19:40:40 +02:00
backend - > log ( AQ_LOG_ERROR , " DRM Backend failed " ) ;
continue ;
}
2024-06-28 17:13:03 +02:00
for ( auto & r : ref ) {
backend - > implementations . emplace_back ( r ) ;
}
2024-07-01 14:33:05 +02:00
} else if ( b . backendType = = AQ_BACKEND_HEADLESS ) {
auto ref = SP < CHeadlessBackend > ( new CHeadlessBackend ( backend ) ) ;
backend - > implementations . emplace_back ( ref ) ;
ref - > self = ref ;
2024-06-18 18:45:05 +02:00
} else {
backend - > log ( AQ_LOG_ERROR , std : : format ( " Unknown backend id: {} " , ( int ) b . backendType ) ) ;
continue ;
}
}
2024-06-18 11:38:26 +02:00
2024-06-30 19:04:14 +02:00
// create a timerfd for idle events
backend - > idle . fd = timerfd_create ( CLOCK_MONOTONIC , TFD_CLOEXEC ) ;
2024-06-18 11:38:26 +02:00
return backend ;
}
Aquamarine : : CBackend : : ~ CBackend ( ) {
2024-06-18 18:45:05 +02:00
;
2024-06-18 11:38:26 +02:00
}
bool Aquamarine : : CBackend : : start ( ) {
2024-06-18 18:45:05 +02:00
log ( AQ_LOG_DEBUG , " Starting the Aquamarine backend! " ) ;
bool fallback = false ;
int started = 0 ;
2024-06-30 19:04:14 +02:00
auto optionsForType = [ this ] ( eBackendType type ) - > SBackendImplementationOptions {
2024-06-28 17:35:43 +02:00
for ( auto & o : implementationOptions ) {
if ( o . backendType = = type )
return o ;
}
return SBackendImplementationOptions { } ;
} ;
2024-06-18 18:45:05 +02:00
for ( size_t i = 0 ; i < implementations . size ( ) ; + + i ) {
const bool ok = implementations . at ( i ) - > start ( ) ;
if ( ! ok ) {
2024-06-28 17:35:43 +02:00
log ( AQ_LOG_ERROR , std : : format ( " Requested backend ({}) could not start, enabling fallbacks " , backendTypeToName ( implementations . at ( i ) - > type ( ) ) ) ) ;
2024-06-18 18:45:05 +02:00
fallback = true ;
2024-06-28 17:35:43 +02:00
if ( optionsForType ( implementations . at ( i ) - > type ( ) ) . backendRequestMode = = AQ_BACKEND_REQUEST_MANDATORY ) {
2024-06-30 19:04:14 +02:00
log ( AQ_LOG_CRITICAL , std : : format ( " Requested backend ({}) could not start and it's mandatory, cannot continue! " , backendTypeToName ( implementations . at ( i ) - > type ( ) ) ) ) ;
2024-06-18 18:45:05 +02:00
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
2024-06-24 23:22:02 +02:00
std : : erase_if ( implementations , [ this ] ( const auto & i ) {
2024-06-27 00:07:59 +02:00
bool failed = i - > pollFDs ( ) . empty ( ) ;
2024-06-24 23:22:02 +02:00
if ( failed )
log ( AQ_LOG_ERROR , std : : format ( " Implementation {} failed, erasing. " , backendTypeToName ( i - > type ( ) ) ) ) ;
return failed ;
} ) ;
2024-06-18 18:45:05 +02:00
2024-06-19 22:40:23 +02:00
// TODO: obviously change this when (if) we add different allocators.
for ( auto & b : implementations ) {
if ( b - > drmFD ( ) > = 0 ) {
2024-07-12 20:53:11 +02:00
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 ) ;
2024-07-11 20:41:53 +02:00
break ;
2024-06-19 22:40:23 +02:00
}
}
2024-07-11 20:41:53 +02:00
if ( ! primaryAllocator )
2024-06-19 22:40:23 +02:00
return false ;
2024-06-21 15:49:28 +02:00
ready = true ;
2024-06-20 19:24:43 +02:00
for ( auto & b : implementations ) {
b - > onReady ( ) ;
}
2024-06-27 00:07:59 +02:00
sessionFDs = session ? session - > pollFDs ( ) : std : : vector < Hyprutils : : Memory : : CSharedPointer < SPollFD > > { } ;
2024-06-23 19:40:40 +02:00
2024-06-18 11:38:26 +02:00
return true ;
}
void Aquamarine : : CBackend : : log ( eBackendLogLevel level , const std : : string & msg ) {
if ( ! options . logFunction )
return ;
options . logFunction ( level , msg ) ;
}
2024-06-18 18:45:05 +02:00
2024-06-27 00:07:59 +02:00
std : : vector < Hyprutils : : Memory : : CSharedPointer < SPollFD > > Aquamarine : : CBackend : : getPollFDs ( ) {
std : : vector < Hyprutils : : Memory : : CSharedPointer < SPollFD > > result ;
2024-06-18 18:45:05 +02:00
for ( auto & i : implementations ) {
2024-06-27 00:07:59 +02:00
auto pollfds = i - > pollFDs ( ) ;
for ( auto & p : pollfds ) {
2024-07-02 14:32:41 +02:00
log ( AQ_LOG_DEBUG , std : : format ( " backend: poll fd {} for implementation {} " , p - > fd , backendTypeToName ( i - > type ( ) ) ) ) ;
2024-06-27 00:07:59 +02:00
result . emplace_back ( p ) ;
2024-06-18 18:45:05 +02:00
}
}
2024-06-20 19:24:43 +02:00
2024-06-23 19:40:40 +02:00
for ( auto & sfd : sessionFDs ) {
2024-07-02 14:32:41 +02:00
log ( AQ_LOG_DEBUG , std : : format ( " backend: poll fd {} for session " , sfd - > fd ) ) ;
2024-06-27 00:07:59 +02:00
result . emplace_back ( sfd ) ;
2024-06-23 19:40:40 +02:00
}
2024-07-02 14:32:41 +02:00
log ( AQ_LOG_DEBUG , std : : format ( " backend: poll fd {} for idle " , idle . fd ) ) ;
2024-06-30 19:04:14 +02:00
result . emplace_back ( makeShared < SPollFD > ( idle . fd , [ this ] ( ) { dispatchIdle ( ) ; } ) ) ;
2024-06-20 19:24:43 +02:00
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 ( ) {
2024-06-24 23:22:02 +02:00
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 { } ;
2024-06-18 18:45:05 +02:00
}
2024-06-26 22:35:14 +02:00
2024-06-30 19:04:14 +02:00
const std : : vector < SP < IBackendImplementation > > & Aquamarine : : CBackend : : getImplementations ( ) {
2024-06-26 22:35:14 +02:00
return implementations ;
}
2024-06-30 19:04:14 +02:00
void Aquamarine : : CBackend : : addIdleEvent ( SP < std : : function < void ( void ) > > fn ) {
auto r = idle . pending . emplace_back ( fn ) ;
2024-07-02 14:32:41 +02:00
updateIdleTimer ( ) ;
}
void Aquamarine : : CBackend : : updateIdleTimer ( ) {
uint64_t ADD_NS = idle . pending . empty ( ) ? TIMESPEC_NSEC_PER_SEC * 240ULL /* 240s, 4 mins */ : 0 ;
2024-06-30 19:04:14 +02:00
timespec now ;
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
2024-07-02 14:32:41 +02:00
timespecAddNs ( & now , ADD_NS ) ;
2024-06-30 19:04:14 +02:00
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 ) ( ) ;
}
2024-07-02 14:32:41 +02:00
updateIdleTimer ( ) ;
2024-06-30 19:04:14 +02:00
}
2024-07-12 20:53:11 +02:00
// 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
2024-07-16 11:53:50 +02:00
if ( drmIsMaster ( drmFD ) & & drmGetNodeTypeFromFd ( newFD ) = = DRM_NODE_PRIMARY ) {
2024-07-12 20:53:11 +02:00
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 ;
}
2024-07-16 11:53:50 +02:00
if ( int ret = drmAuthMagic ( drmFD , magic ) ; ret < 0 ) {
2024-07-12 20:53:11 +02:00
log ( AQ_LOG_ERROR , std : : format ( " reopenDRMNode: drmAuthMagic failed: {} " , strerror ( - ret ) ) ) ;
close ( newFD ) ;
return - 1 ;
}
}
return newFD ;
}
2024-07-24 18:41:13 +02:00
std : : vector < SDRMFormat > Aquamarine : : IBackendImplementation : : getRenderableFormats ( ) {
return { } ;
}