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>
# include <sys/poll.h>
# include <thread>
# include <chrono>
2024-06-18 11:38:26 +02:00
using namespace Hyprutils : : Memory ;
using namespace Aquamarine ;
# define SP CSharedPointer
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-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 ;
} else {
backend - > log ( AQ_LOG_ERROR , std : : format ( " Unknown backend id: {} " , ( int ) b . backendType ) ) ;
continue ;
}
}
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 ;
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 ( implementationOptions . at ( i ) . backendType ) ) ) ;
fallback = true ;
if ( implementationOptions . at ( i ) . backendRequestMode = = AQ_BACKEND_REQUEST_MANDATORY ) {
log ( AQ_LOG_CRITICAL ,
std : : format ( " Requested backend ({}) could not start and it's mandatory, cannot continue! " , backendTypeToName ( implementationOptions . at ( i ) . backendType ) ) ) ;
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 , [ ] ( const auto & i ) { return i - > pollFD ( ) < 0 ; } ) ;
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
void Aquamarine : : CBackend : : enterLoop ( ) {
std : : vector < pollfd > pollFDs ;
for ( auto & i : implementations ) {
auto fd = i - > pollFD ( ) ;
pollFDs . emplace_back ( pollfd { . fd = fd , . events = POLLIN , . revents = 0 } ) ;
}
std : : thread pollThr ( [ this , & pollFDs ] ( ) {
int ret = 0 ;
while ( 1 ) {
ret = poll ( pollFDs . data ( ) , pollFDs . size ( ) , 5000 /* 5 seconds, reasonable. It's because we might need to terminate */ ) ;
if ( ret < 0 ) {
log ( AQ_LOG_CRITICAL , std : : format ( " Polling fds failed with {} " , errno ) ) ;
terminate = true ;
exit ( 1 ) ;
}
for ( size_t i = 0 ; i < pollFDs . size ( ) ; + + i ) {
if ( pollFDs [ i ] . revents & POLLHUP ) {
log ( AQ_LOG_CRITICAL , std : : format ( " disconnected from pollfd {} " , i ) ) ;
terminate = true ;
exit ( 1 ) ;
}
}
if ( terminate )
break ;
if ( ret ! = 0 ) {
std : : lock_guard < std : : mutex > lg ( m_sEventLoopInternals . loopRequestMutex ) ;
m_sEventLoopInternals . shouldProcess = true ;
m_sEventLoopInternals . loopSignal . notify_all ( ) ;
}
}
} ) ;
while ( 1 ) {
m_sEventLoopInternals . loopRequestMutex . unlock ( ) ; // unlock, we are ready to take events
std : : unique_lock lk ( m_sEventLoopInternals . loopMutex ) ;
if ( m_sEventLoopInternals . shouldProcess = = false ) // avoid a lock if a thread managed to request something already since we .unlock()ed
m_sEventLoopInternals . loopSignal . wait_for ( lk , std : : chrono : : seconds ( 5 ) , [ this ] { return m_sEventLoopInternals . shouldProcess = = true ; } ) ; // wait for events
m_sEventLoopInternals . loopRequestMutex . lock ( ) ; // lock incoming events
if ( terminate )
break ;
m_sEventLoopInternals . shouldProcess = false ;
std : : lock_guard < std : : mutex > lg ( m_sEventLoopInternals . eventLock ) ;
for ( size_t i = 0 ; i < pollFDs . size ( ) ; + + i ) {
implementations . at ( i ) - > dispatchEvents ( ) ;
}
}
}