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-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 ) {
allocator = CGBMAllocator : : create ( b - > drmFD ( ) , self ) ;
break ;
}
}
if ( ! allocator )
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 ) {
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-06-27 00:07:59 +02:00
result . emplace_back ( sfd ) ;
2024-06-23 19:40:40 +02:00
}
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 ) ;
// update timerfd
timespec now ;
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
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 ) ( ) ;
}
}