2022-07-01 23:05:58 +02:00
# include "Hyprpaper.hpp"
CHyprpaper : : CHyprpaper ( ) { }
void CHyprpaper : : init ( ) {
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-02 18:26:59 +02:00
g_pIPCSocket - > initialize ( ) ;
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-02 18:26:59 +02:00
std : : thread ( [ & ] ( ) { // we dispatch wl events cuz we have to
while ( wl_display_dispatch ( m_sDisplay ) ! = - 1 ) {
tick ( ) ;
}
m_bShouldExit = true ;
} ) . detach ( ) ;
while ( 1 ) { // we also tick every 1ms for socket and other shit's updates
tick ( ) ;
2022-07-01 23:05:58 +02:00
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 1 ) ) ;
2022-07-02 18:26:59 +02:00
if ( m_bShouldExit )
break ;
2022-07-01 23:05:58 +02:00
}
}
2022-07-02 18:26:59 +02:00
void CHyprpaper : : tick ( ) {
m_mtTickMutex . lock ( ) ;
preloadAllWallpapersFromConfig ( ) ;
2022-07-02 19:10:50 +02:00
ensurePoolBuffersPresent ( ) ;
recheckAllMonitors ( ) ;
2022-07-02 18:26:59 +02:00
g_pIPCSocket - > mainThreadParseRequest ( ) ;
m_mtTickMutex . unlock ( ) ;
}
bool CHyprpaper : : isPreloaded ( const std : : string & path ) {
for ( auto & [ pt , wt ] : m_mWallpaperTargets ) {
if ( pt = = path )
return true ;
}
return false ;
}
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 ) {
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 ;
zwlr_layer_surface_v1_ack_configure ( pMonitor - > pLayerSurface , pMonitor - > configureSerial ) ;
}
if ( pMonitor - > wantsReload ) {
pMonitor - > wantsReload = false ;
renderWallpaperForMonitor ( pMonitor ) ;
}
}
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 ( ) {
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 ) {
return el - > pTarget = = & wt & & el - > pixelSize = = m - > size ;
} ) ;
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 ) ;
PBUFFER - > pTarget = & wt ;
}
}
}
}
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 ) ;
if ( PMONITOR - > pSurface ) {
wl_surface_destroy ( PMONITOR - > pSurface ) ;
zwlr_layer_surface_v1_destroy ( PMONITOR - > pLayerSurface ) ;
PMONITOR - > pSurface = nullptr ;
PMONITOR - > pLayerSurface = nullptr ;
PMONITOR - > wantsACK = false ;
PMONITOR - > wantsReload = false ;
PMONITOR - > initialized = false ;
PMONITOR - > readyForLS = true ;
wl_display_flush ( m_sDisplay ) ;
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
if ( ! pMonitor - > pLayerSurface )
createLSForMonitor ( pMonitor ) ;
else
pMonitor - > wantsReload = true ;
2022-07-01 23:05:58 +02:00
}
void CHyprpaper : : createLSForMonitor ( SMonitor * pMonitor ) {
pMonitor - > pSurface = wl_compositor_create_surface ( m_sCompositor ) ;
if ( ! pMonitor - > pSurface ) {
Debug : : log ( CRIT , " The compositor did not allow hyprpaper a surface! " ) ;
exit ( 1 ) ;
return ;
}
const auto PINPUTREGION = wl_compositor_create_region ( m_sCompositor ) ;
if ( ! PINPUTREGION ) {
Debug : : log ( CRIT , " The compositor did not allow hyprpaper a region! " ) ;
exit ( 1 ) ;
return ;
}
wl_surface_set_input_region ( pMonitor - > pSurface , PINPUTREGION ) ;
pMonitor - > pLayerSurface = zwlr_layer_shell_v1_get_layer_surface ( g_pHyprpaper - > m_sLayerShell , pMonitor - > pSurface , pMonitor - > output , ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND , " hyprpaper " ) ;
if ( ! pMonitor - > pLayerSurface ) {
Debug : : log ( CRIT , " The compositor did not allow hyprpaper a layersurface! " ) ;
exit ( 1 ) ;
return ;
}
zwlr_layer_surface_v1_set_size ( pMonitor - > pLayerSurface , 0 , 0 ) ;
zwlr_layer_surface_v1_set_anchor ( pMonitor - > pLayerSurface , ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT ) ;
zwlr_layer_surface_v1_set_exclusive_zone ( pMonitor - > pLayerSurface , - 1 ) ;
zwlr_layer_surface_v1_add_listener ( pMonitor - > pLayerSurface , & Events : : layersurfaceListener , pMonitor ) ;
wl_surface_commit ( pMonitor - > pSurface ) ;
wl_region_destroy ( PINPUTREGION ) ;
2022-07-02 18:26:59 +02:00
wl_display_flush ( m_sDisplay ) ;
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-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 ) {
return el - > pTarget = = pWallpaperTarget & & el - > pixelSize = = pMonitor - > size ;
} ) - > 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-02 19:10:50 +02:00
auto * const PBUFFER = getPoolBuffer ( pMonitor , PWALLPAPERTARGET ) ;
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 ) {
scale = pMonitor - > size . x / PWALLPAPERTARGET - > m_vSize . x ;
origin . y = - ( PWALLPAPERTARGET - > m_vSize . y * scale - pMonitor - > size . y ) / 2.f / scale ;
} else {
scale = pMonitor - > size . y / PWALLPAPERTARGET - > m_vSize . y ;
origin . x = - ( PWALLPAPERTARGET - > m_vSize . x * scale - pMonitor - > size . x ) / 2.f / scale ;
}
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 18:26:59 +02:00
wl_surface_attach ( pMonitor - > pSurface , PBUFFER - > buffer , 0 , 0 ) ;
2022-07-01 23:05:58 +02:00
wl_surface_set_buffer_scale ( pMonitor - > pSurface , pMonitor - > scale ) ;
2022-07-02 18:26:59 +02:00
wl_surface_damage_buffer ( pMonitor - > pSurface , 0 , 0 , pMonitor - > size . x , pMonitor - > size . y ) ;
2022-07-01 23:05:58 +02:00
wl_surface_commit ( pMonitor - > pSurface ) ;
}