2022-07-01 23:05:58 +02:00
# include "Hyprpaper.hpp"
CHyprpaper : : CHyprpaper ( ) { }
void CHyprpaper : : init ( ) {
2022-07-17 13:59:43 +02:00
removeOldHyprpaperImages ( ) ;
2022-07-01 23:05:58 +02:00
g_pConfigManager = std : : make_unique < CConfigManager > ( ) ;
2022-07-02 18:26:59 +02:00
g_pIPCSocket = std : : make_unique < CIPCSocket > ( ) ;
2022-07-01 23:05:58 +02:00
m_sDisplay = ( wl_display * ) wl_display_connect ( NULL ) ;
if ( ! m_sDisplay ) {
Debug : : log ( CRIT , " No wayland compositor running! " ) ;
exit ( 1 ) ;
return ;
}
preloadAllWallpapersFromConfig ( ) ;
2022-07-31 17:28:37 +02:00
if ( m_bIPCEnabled )
g_pIPCSocket - > initialize ( ) ;
2022-07-02 18:26:59 +02:00
2022-07-01 23:05:58 +02:00
// run
wl_registry * registry = wl_display_get_registry ( m_sDisplay ) ;
wl_registry_add_listener ( registry , & Events : : registryListener , nullptr ) ;
2022-07-31 17:28:37 +02:00
if ( m_bIPCEnabled ) {
std : : thread ( [ & ] ( ) { // we dispatch wl events cuz we have to
while ( wl_display_dispatch ( m_sDisplay ) ! = - 1 ) {
tick ( ) ;
}
2022-07-02 18:26:59 +02:00
2022-07-31 17:28:37 +02:00
m_bShouldExit = true ;
} ) . detach ( ) ;
2022-07-02 18:26:59 +02:00
2022-07-31 17:28:37 +02:00
while ( 1 ) { // we also tick every 1ms for socket and other shit's updates
tick ( ) ;
2022-07-01 23:05:58 +02:00
2022-07-31 17:28:37 +02:00
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1 ) ) ;
2022-07-02 18:26:59 +02:00
2022-07-31 17:28:37 +02:00
if ( m_bShouldExit )
break ;
}
} else {
while ( wl_display_dispatch ( m_sDisplay ) ! = - 1 ) {
tick ( ) ;
}
2022-07-01 23:05:58 +02:00
}
}
2022-07-02 18:26:59 +02:00
void CHyprpaper : : tick ( ) {
2022-07-31 17:28:37 +02:00
std : : lock_guard < std : : mutex > lg ( m_mtTickMutex ) ;
bool reload = g_pIPCSocket - > mainThreadParseRequest ( ) ;
if ( ! reload )
return ;
2022-07-02 18:26:59 +02:00
preloadAllWallpapersFromConfig ( ) ;
2022-07-02 19:10:50 +02:00
ensurePoolBuffersPresent ( ) ;
recheckAllMonitors ( ) ;
2022-07-02 18:26:59 +02:00
}
bool CHyprpaper : : isPreloaded ( const std : : string & path ) {
for ( auto & [ pt , wt ] : m_mWallpaperTargets ) {
if ( pt = = path )
return true ;
}
return false ;
}
2022-07-17 14:17:54 +02:00
void CHyprpaper : : unloadWallpaper ( const std : : string & path ) {
bool found = false ;
for ( auto & [ ewp , cls ] : m_mWallpaperTargets ) {
if ( ewp = = path ) {
// found
found = true ;
break ;
}
}
if ( ! found ) {
Debug : : log ( LOG , " Cannot unload a target that was not loaded! " ) ;
return ;
}
// clean buffers
2022-07-18 12:31:20 +02:00
for ( auto it = m_vBuffers . begin ( ) ; it ! = m_vBuffers . end ( ) ; ) {
2022-07-17 14:17:54 +02:00
2022-07-18 12:31:20 +02:00
if ( it - > get ( ) - > target ! = path ) {
it + + ;
2022-07-17 14:17:54 +02:00
continue ;
2022-07-18 12:31:20 +02:00
}
2022-07-17 14:17:54 +02:00
const auto PRELOADPATH = it - > get ( ) - > name ;
Debug : : log ( LOG , " Unloading target %s, preload path %s " , path . c_str ( ) , PRELOADPATH . c_str ( ) ) ;
std : : filesystem : : remove ( PRELOADPATH ) ;
destroyBuffer ( it - > get ( ) ) ;
it = m_vBuffers . erase ( it ) ;
}
m_mWallpaperTargets . erase ( path ) ; // will free the cairo surface
}
2022-07-01 23:05:58 +02:00
void CHyprpaper : : preloadAllWallpapersFromConfig ( ) {
2022-07-02 18:26:59 +02:00
if ( g_pConfigManager - > m_dRequestedPreloads . empty ( ) )
return ;
2022-07-01 23:05:58 +02:00
for ( auto & wp : g_pConfigManager - > m_dRequestedPreloads ) {
2022-07-07 15:26:38 +02:00
// check if it doesnt exist
bool exists = false ;
for ( auto & [ ewp , cls ] : m_mWallpaperTargets ) {
if ( ewp = = wp ) {
Debug : : log ( LOG , " Ignoring request to preload %s as it already is preloaded! " , ewp . c_str ( ) ) ;
exists = true ;
break ;
}
}
if ( exists )
continue ;
2022-07-01 23:05:58 +02:00
m_mWallpaperTargets [ wp ] = CWallpaperTarget ( ) ;
m_mWallpaperTargets [ wp ] . create ( wp ) ;
}
2022-07-02 18:26:59 +02:00
g_pConfigManager - > m_dRequestedPreloads . clear ( ) ;
2022-07-01 23:05:58 +02:00
}
void CHyprpaper : : recheckAllMonitors ( ) {
for ( auto & m : m_vMonitors ) {
2022-07-02 18:26:59 +02:00
recheckMonitor ( m . get ( ) ) ;
}
}
2022-07-01 23:05:58 +02:00
2022-07-02 18:26:59 +02:00
void CHyprpaper : : recheckMonitor ( SMonitor * pMonitor ) {
ensureMonitorHasActiveWallpaper ( pMonitor ) ;
2022-07-01 23:05:58 +02:00
2022-07-02 18:26:59 +02:00
if ( pMonitor - > wantsACK ) {
pMonitor - > wantsACK = false ;
2022-07-02 19:53:12 +02:00
zwlr_layer_surface_v1_ack_configure ( pMonitor - > pCurrentLayerSurface - > pLayerSurface , pMonitor - > configureSerial ) ;
2022-07-02 18:26:59 +02:00
}
if ( pMonitor - > wantsReload ) {
pMonitor - > wantsReload = false ;
renderWallpaperForMonitor ( pMonitor ) ;
}
}
2022-07-17 13:59:43 +02:00
void CHyprpaper : : removeOldHyprpaperImages ( ) {
int cleaned = 0 ;
uint64_t memoryFreed = 0 ;
for ( const auto & entry : std : : filesystem : : directory_iterator ( std : : string ( getenv ( " XDG_RUNTIME_DIR " ) ) ) ) {
if ( entry . is_directory ( ) )
continue ;
const auto FILENAME = entry . path ( ) . filename ( ) . string ( ) ;
if ( FILENAME . contains ( " .hyprpaper_ " ) ) {
// unlink it
memoryFreed + = entry . file_size ( ) ;
2022-07-17 14:12:03 +02:00
if ( ! std : : filesystem : : remove ( entry . path ( ) ) )
Debug : : log ( LOG , " Couldn't remove %s " , entry . path ( ) . string ( ) . c_str ( ) ) ;
2022-07-17 13:59:43 +02:00
cleaned + + ;
}
}
if ( cleaned ! = 0 ) {
Debug : : log ( LOG , " Cleaned old hyprpaper preloads (%i), removing %.1fMB " , cleaned , ( ( float ) memoryFreed ) / 1000000.f ) ;
}
}
2022-07-02 18:26:59 +02:00
SMonitor * CHyprpaper : : getMonitorFromName ( const std : : string & monname ) {
for ( auto & m : m_vMonitors ) {
if ( m - > name = = monname )
return m . get ( ) ;
}
return nullptr ;
}
2022-07-02 19:10:50 +02:00
void CHyprpaper : : ensurePoolBuffersPresent ( ) {
2022-07-03 10:07:43 +02:00
bool anyNewBuffers = false ;
2022-07-02 19:10:50 +02:00
for ( auto & [ file , wt ] : m_mWallpaperTargets ) {
for ( auto & m : m_vMonitors ) {
if ( m - > size = = Vector2D ( ) )
continue ;
auto it = std : : find_if ( m_vBuffers . begin ( ) , m_vBuffers . end ( ) , [ & ] ( const std : : unique_ptr < SPoolBuffer > & el ) {
2022-07-18 12:26:13 +02:00
return el - > target = = wt . m_szPath & & el - > pixelSize = = m - > size * m - > scale ;
2022-07-02 19:10:50 +02:00
} ) ;
if ( it = = m_vBuffers . end ( ) ) {
// create
const auto PBUFFER = m_vBuffers . emplace_back ( std : : make_unique < SPoolBuffer > ( ) ) . get ( ) ;
createBuffer ( PBUFFER , m - > size . x * m - > scale , m - > size . y * m - > scale , WL_SHM_FORMAT_ARGB8888 ) ;
2022-07-18 12:26:13 +02:00
PBUFFER - > target = wt . m_szPath ;
2022-07-03 10:07:43 +02:00
2022-07-18 12:28:56 +02:00
Debug : : log ( LOG , " Buffer created for target %s, Shared Memory usage: %.1fMB " , wt . m_szPath . c_str ( ) , PBUFFER - > size / 1000000.f ) ;
2022-07-03 10:07:43 +02:00
anyNewBuffers = true ;
2022-07-02 19:10:50 +02:00
}
}
}
2022-07-03 10:07:43 +02:00
if ( anyNewBuffers ) {
uint64_t bytesUsed = 0 ;
for ( auto & bf : m_vBuffers ) {
bytesUsed + = bf - > size ;
}
Debug : : log ( LOG , " Total SM usage for all buffers: %.1fMB " , bytesUsed / 1000000.f ) ;
}
2022-07-02 19:10:50 +02:00
}
2022-07-02 18:26:59 +02:00
void CHyprpaper : : clearWallpaperFromMonitor ( const std : : string & monname ) {
const auto PMONITOR = getMonitorFromName ( monname ) ;
if ( ! PMONITOR )
return ;
auto it = m_mMonitorActiveWallpaperTargets . find ( PMONITOR ) ;
if ( it ! = m_mMonitorActiveWallpaperTargets . end ( ) )
m_mMonitorActiveWallpaperTargets . erase ( it ) ;
2022-07-02 19:53:12 +02:00
if ( PMONITOR - > pCurrentLayerSurface ) {
PMONITOR - > pCurrentLayerSurface = nullptr ;
2022-07-02 18:26:59 +02:00
PMONITOR - > wantsACK = false ;
PMONITOR - > wantsReload = false ;
PMONITOR - > initialized = false ;
PMONITOR - > readyForLS = true ;
2022-07-02 19:53:12 +02:00
2022-07-01 23:05:58 +02:00
}
}
void CHyprpaper : : ensureMonitorHasActiveWallpaper ( SMonitor * pMonitor ) {
2022-07-02 15:27:05 +02:00
if ( ! pMonitor - > readyForLS | | ! pMonitor - > hasATarget )
2022-07-01 23:05:58 +02:00
return ;
auto it = m_mMonitorActiveWallpaperTargets . find ( pMonitor ) ;
if ( it = = m_mMonitorActiveWallpaperTargets . end ( ) ) {
m_mMonitorActiveWallpaperTargets [ pMonitor ] = nullptr ;
it = m_mMonitorActiveWallpaperTargets . find ( pMonitor ) ;
}
if ( it - > second )
return ; // has
// get the target
for ( auto & [ mon , path1 ] : m_mMonitorActiveWallpapers ) {
if ( mon = = pMonitor - > name ) {
for ( auto & [ path2 , target ] : m_mWallpaperTargets ) {
if ( path1 = = path2 ) {
it - > second = & target ;
break ;
}
}
break ;
}
}
if ( ! it - > second ) {
2022-07-02 15:27:05 +02:00
pMonitor - > hasATarget = false ;
Debug : : log ( WARN , " Monitor %s does not have a target! A wallpaper will not be created. " , pMonitor - > name . c_str ( ) ) ;
2022-07-01 23:05:58 +02:00
return ;
}
2022-07-02 15:27:05 +02:00
2022-07-02 18:26:59 +02:00
// create it for thy if it doesnt have
2022-07-02 19:53:12 +02:00
if ( ! pMonitor - > pCurrentLayerSurface )
2022-07-02 18:26:59 +02:00
createLSForMonitor ( pMonitor ) ;
else
pMonitor - > wantsReload = true ;
2022-07-01 23:05:58 +02:00
}
void CHyprpaper : : createLSForMonitor ( SMonitor * pMonitor ) {
2022-07-02 19:53:12 +02:00
pMonitor - > pCurrentLayerSurface = pMonitor - > layerSurfaces . emplace_back ( std : : make_unique < CLayerSurface > ( pMonitor ) ) . get ( ) ;
2022-07-01 23:05:58 +02:00
}
bool CHyprpaper : : setCloexec ( const int & FD ) {
long flags = fcntl ( FD , F_GETFD ) ;
if ( flags = = - 1 ) {
return false ;
}
if ( fcntl ( FD , F_SETFD , flags | FD_CLOEXEC ) = = - 1 ) {
return false ;
}
return true ;
}
int CHyprpaper : : createPoolFile ( size_t size , std : : string & name ) {
const auto XDGRUNTIMEDIR = getenv ( " XDG_RUNTIME_DIR " ) ;
if ( ! XDGRUNTIMEDIR ) {
Debug : : log ( CRIT , " XDG_RUNTIME_DIR not set! " ) ;
exit ( 1 ) ;
}
name = std : : string ( XDGRUNTIMEDIR ) + " /.hyprpaper_XXXXXX " ;
const auto FD = mkstemp ( ( char * ) name . c_str ( ) ) ;
if ( FD < 0 ) {
Debug : : log ( CRIT , " createPoolFile: fd < 0 " ) ;
exit ( 1 ) ;
}
if ( ! setCloexec ( FD ) ) {
close ( FD ) ;
Debug : : log ( CRIT , " createPoolFile: !setCloexec " ) ;
exit ( 1 ) ;
}
if ( ftruncate ( FD , size ) < 0 ) {
close ( FD ) ;
Debug : : log ( CRIT , " createPoolFile: ftruncate < 0 " ) ;
exit ( 1 ) ;
}
return FD ;
}
void CHyprpaper : : createBuffer ( SPoolBuffer * pBuffer , int32_t w , int32_t h , uint32_t format ) {
const uint STRIDE = w * 4 ;
const size_t SIZE = STRIDE * h ;
std : : string name ;
const auto FD = createPoolFile ( SIZE , name ) ;
if ( FD = = - 1 ) {
Debug : : log ( CRIT , " Unable to create pool file! " ) ;
exit ( 1 ) ;
}
const auto DATA = mmap ( NULL , SIZE , PROT_READ | PROT_WRITE , MAP_SHARED , FD , 0 ) ;
const auto POOL = wl_shm_create_pool ( g_pHyprpaper - > m_sSHM , FD , SIZE ) ;
pBuffer - > buffer = wl_shm_pool_create_buffer ( POOL , 0 , w , h , STRIDE , format ) ;
wl_shm_pool_destroy ( POOL ) ;
close ( FD ) ;
pBuffer - > size = SIZE ;
pBuffer - > data = DATA ;
pBuffer - > surface = cairo_image_surface_create_for_data ( ( unsigned char * ) DATA , CAIRO_FORMAT_ARGB32 , w , h , STRIDE ) ;
pBuffer - > cairo = cairo_create ( pBuffer - > surface ) ;
2022-07-02 19:10:50 +02:00
pBuffer - > pixelSize = Vector2D ( w , h ) ;
2022-07-17 14:17:54 +02:00
pBuffer - > name = name ;
2022-07-01 23:05:58 +02:00
}
void CHyprpaper : : destroyBuffer ( SPoolBuffer * pBuffer ) {
wl_buffer_destroy ( pBuffer - > buffer ) ;
cairo_destroy ( pBuffer - > cairo ) ;
cairo_surface_destroy ( pBuffer - > surface ) ;
munmap ( pBuffer - > data , pBuffer - > size ) ;
2022-07-02 18:26:59 +02:00
pBuffer - > buffer = nullptr ;
2022-07-01 23:05:58 +02:00
}
2022-07-02 19:10:50 +02:00
SPoolBuffer * CHyprpaper : : getPoolBuffer ( SMonitor * pMonitor , CWallpaperTarget * pWallpaperTarget ) {
return std : : find_if ( m_vBuffers . begin ( ) , m_vBuffers . end ( ) , [ & ] ( const std : : unique_ptr < SPoolBuffer > & el ) {
2022-07-18 12:26:13 +02:00
return el - > target = = pWallpaperTarget - > m_szPath & & el - > pixelSize = = pMonitor - > size * pMonitor - > scale ;
2022-07-02 19:10:50 +02:00
} ) - > get ( ) ;
}
2022-07-01 23:05:58 +02:00
void CHyprpaper : : renderWallpaperForMonitor ( SMonitor * pMonitor ) {
2022-07-02 19:10:50 +02:00
const auto PWALLPAPERTARGET = m_mMonitorActiveWallpaperTargets [ pMonitor ] ;
2022-07-02 18:26:59 +02:00
2022-07-02 19:10:50 +02:00
if ( ! PWALLPAPERTARGET ) {
Debug : : log ( CRIT , " wallpaper target null in render?? " ) ;
exit ( 1 ) ;
2022-07-02 18:26:59 +02:00
}
2022-07-01 23:05:58 +02:00
2022-07-16 22:51:18 +02:00
auto * PBUFFER = getPoolBuffer ( pMonitor , PWALLPAPERTARGET ) ;
if ( ! PBUFFER ) {
Debug : : log ( LOG , " Pool buffer missing for available target?? " ) ;
ensurePoolBuffersPresent ( ) ;
PBUFFER = getPoolBuffer ( pMonitor , PWALLPAPERTARGET ) ;
if ( ! PBUFFER ) {
Debug : : log ( LOG , " Pool buffer failed #2. Ignoring WP. " ) ;
return ;
}
}
2022-07-02 19:10:50 +02:00
2022-07-02 18:26:59 +02:00
const auto PCAIRO = PBUFFER - > cairo ;
2022-07-01 23:05:58 +02:00
cairo_save ( PCAIRO ) ;
cairo_set_operator ( PCAIRO , CAIRO_OPERATOR_CLEAR ) ;
cairo_paint ( PCAIRO ) ;
cairo_restore ( PCAIRO ) ;
2022-07-02 15:09:59 +02:00
// get scale
// we always do cover
float scale ;
Vector2D origin ;
if ( pMonitor - > size . x / pMonitor - > size . y > PWALLPAPERTARGET - > m_vSize . x / PWALLPAPERTARGET - > m_vSize . y ) {
2022-07-16 23:03:29 +02:00
scale = pMonitor - > size . x * pMonitor - > scale / PWALLPAPERTARGET - > m_vSize . x ;
2022-07-02 15:09:59 +02:00
2022-07-16 23:03:29 +02:00
origin . y = - ( PWALLPAPERTARGET - > m_vSize . y * scale - pMonitor - > size . y * pMonitor - > scale ) / 2.f / scale ;
2022-07-02 15:09:59 +02:00
} else {
2022-07-16 23:03:29 +02:00
scale = pMonitor - > size . y * pMonitor - > scale / PWALLPAPERTARGET - > m_vSize . y ;
2022-07-02 15:09:59 +02:00
2022-07-16 23:03:29 +02:00
origin . x = - ( PWALLPAPERTARGET - > m_vSize . x * scale - pMonitor - > size . x * pMonitor - > scale ) / 2.f / scale ;
2022-07-02 15:09:59 +02:00
}
Debug : : log ( LOG , " Image data for %s: %s at [%.2f, %.2f], scale: %.2f (original image size: [%i, %i]) " , pMonitor - > name . c_str ( ) , PWALLPAPERTARGET - > m_szPath . c_str ( ) , origin . x , origin . y , scale , ( int ) PWALLPAPERTARGET - > m_vSize . x , ( int ) PWALLPAPERTARGET - > m_vSize . y ) ;
cairo_scale ( PCAIRO , scale , scale ) ;
cairo_set_source_surface ( PCAIRO , PWALLPAPERTARGET - > m_pCairoSurface , origin . x , origin . y ) ;
2022-07-01 23:05:58 +02:00
cairo_paint ( PCAIRO ) ;
cairo_restore ( PCAIRO ) ;
2022-07-02 19:53:12 +02:00
wl_surface_attach ( pMonitor - > pCurrentLayerSurface - > pSurface , PBUFFER - > buffer , 0 , 0 ) ;
wl_surface_set_buffer_scale ( pMonitor - > pCurrentLayerSurface - > pSurface , pMonitor - > scale ) ;
wl_surface_damage_buffer ( pMonitor - > pCurrentLayerSurface - > pSurface , 0 , 0 , pMonitor - > size . x , pMonitor - > size . y ) ;
wl_surface_commit ( pMonitor - > pCurrentLayerSurface - > pSurface ) ;
// check if we dont need to remove a wallpaper
if ( pMonitor - > layerSurfaces . size ( ) > 1 ) {
for ( auto it = pMonitor - > layerSurfaces . begin ( ) ; it ! = pMonitor - > layerSurfaces . end ( ) ; it + + ) {
if ( pMonitor - > pCurrentLayerSurface ! = it - > get ( ) ) {
pMonitor - > layerSurfaces . erase ( it ) ;
break ;
}
}
}
2022-07-01 23:05:58 +02:00
}