2022-07-01 23:05:58 +02:00
# include "Hyprpaper.hpp"
2023-05-29 18:35:41 +02:00
# include <filesystem>
# include <fstream>
# include <signal.h>
2023-12-31 01:37:50 +01:00
# include <sys/types.h>
2022-07-01 23:05:58 +02:00
2022-09-30 12:18:26 +02:00
CHyprpaper : : CHyprpaper ( ) = default ;
2022-07-01 23:05:58 +02:00
2024-07-17 16:25:07 +02:00
static void handleGlobal ( CCWlRegistry * registry , uint32_t name , const char * interface , uint32_t version ) {
if ( strcmp ( interface , wl_compositor_interface . name ) = = 0 ) {
g_pHyprpaper - > m_pCompositor = makeShared < CCWlCompositor > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) registry - > resource ( ) , name , & wl_compositor_interface , 4 ) ) ;
} else if ( strcmp ( interface , wl_shm_interface . name ) = = 0 ) {
g_pHyprpaper - > m_pSHM = makeShared < CCWlShm > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) registry - > resource ( ) , name , & wl_shm_interface , 1 ) ) ;
} else if ( strcmp ( interface , wl_output_interface . name ) = = 0 ) {
g_pHyprpaper - > m_mtTickMutex . lock ( ) ;
const auto PMONITOR = g_pHyprpaper - > m_vMonitors . emplace_back ( std : : make_unique < SMonitor > ( ) ) . get ( ) ;
PMONITOR - > wayland_name = name ;
PMONITOR - > name = " " ;
PMONITOR - > output = makeShared < CCWlOutput > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) registry - > resource ( ) , name , & wl_output_interface , 4 ) ) ;
PMONITOR - > registerListeners ( ) ;
g_pHyprpaper - > m_mtTickMutex . unlock ( ) ;
} else if ( strcmp ( interface , wl_seat_interface . name ) = = 0 ) {
2024-07-23 00:08:55 +02:00
g_pHyprpaper - > createSeat ( makeShared < CCWlSeat > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) registry - > resource ( ) , name , & wl_seat_interface , 7 ) ) ) ;
2024-07-17 16:25:07 +02:00
} else if ( strcmp ( interface , zwlr_layer_shell_v1_interface . name ) = = 0 ) {
g_pHyprpaper - > m_pLayerShell = makeShared < CCZwlrLayerShellV1 > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) registry - > resource ( ) , name , & zwlr_layer_shell_v1_interface , 1 ) ) ;
} else if ( strcmp ( interface , wp_fractional_scale_manager_v1_interface . name ) = = 0 & & ! g_pHyprpaper - > m_bNoFractionalScale ) {
g_pHyprpaper - > m_pFractionalScale =
makeShared < CCWpFractionalScaleManagerV1 > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) registry - > resource ( ) , name , & wp_fractional_scale_manager_v1_interface , 1 ) ) ;
} else if ( strcmp ( interface , wp_viewporter_interface . name ) = = 0 ) {
g_pHyprpaper - > m_pViewporter = makeShared < CCWpViewporter > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) registry - > resource ( ) , name , & wp_viewporter_interface , 1 ) ) ;
} else if ( strcmp ( interface , wp_cursor_shape_manager_v1_interface . name ) = = 0 ) {
g_pHyprpaper - > m_pCursorShape =
makeShared < CCWpCursorShapeManagerV1 > ( ( wl_proxy * ) wl_registry_bind ( ( wl_registry * ) registry - > resource ( ) , name , & wp_cursor_shape_manager_v1_interface , 1 ) ) ;
}
}
static void handleGlobalRemove ( CCWlRegistry * registry , uint32_t name ) {
for ( auto & m : g_pHyprpaper - > m_vMonitors ) {
if ( m - > wayland_name = = name ) {
Debug : : log ( LOG , " Destroying output %s " , m - > name . c_str ( ) ) ;
g_pHyprpaper - > clearWallpaperFromMonitor ( m - > name ) ;
std : : erase_if ( g_pHyprpaper - > m_vMonitors , [ & ] ( const auto & other ) { return other - > wayland_name = = name ; } ) ;
return ;
}
}
}
2022-07-01 23:05:58 +02:00
void CHyprpaper : : init ( ) {
2022-07-17 13:59:43 +02:00
2023-05-29 18:35:41 +02:00
if ( ! lockSingleInstance ( ) ) {
Debug : : log ( CRIT , " Cannot launch multiple instances of Hyprpaper at once! " ) ;
exit ( 1 ) ;
}
2022-07-17 13:59:43 +02:00
removeOldHyprpaperImages ( ) ;
2022-12-28 23:26:58 +01:00
m_sDisplay = ( wl_display * ) wl_display_connect ( nullptr ) ;
2022-07-01 23:05:58 +02:00
if ( ! m_sDisplay ) {
Debug : : log ( CRIT , " No wayland compositor running! " ) ;
exit ( 1 ) ;
}
2024-01-03 13:47:49 +01:00
// run
2024-07-17 16:25:07 +02:00
auto REGISTRY = makeShared < CCWlRegistry > ( ( wl_proxy * ) wl_display_get_registry ( m_sDisplay ) ) ;
REGISTRY - > setGlobal ( : : handleGlobal ) ;
REGISTRY - > setGlobalRemove ( : : handleGlobalRemove ) ;
2024-01-03 13:47:49 +01:00
wl_display_roundtrip ( m_sDisplay ) ;
while ( m_vMonitors . size ( ) < 1 | | m_vMonitors [ 0 ] - > name . empty ( ) ) {
wl_display_dispatch ( m_sDisplay ) ;
}
g_pConfigManager = std : : make_unique < CConfigManager > ( ) ;
2024-07-17 16:25:07 +02:00
g_pIPCSocket = std : : make_unique < CIPCSocket > ( ) ;
2024-01-03 13:47:49 +01:00
g_pConfigManager - > parse ( ) ;
2022-07-01 23:05:58 +02:00
preloadAllWallpapersFromConfig ( ) ;
2024-01-01 13:34:50 +01:00
if ( std : : any_cast < Hyprlang : : INT > ( g_pConfigManager - > config - > getConfigValue ( " ipc " ) ) )
2022-07-31 17:28:37 +02:00
g_pIPCSocket - > initialize ( ) ;
2022-07-02 18:26:59 +02:00
2024-01-03 13:47:49 +01:00
do {
2023-05-08 20:07:28 +02:00
std : : lock_guard < std : : mutex > lg ( m_mtTickMutex ) ;
2022-11-02 22:41:32 +01:00
tick ( true ) ;
2024-01-03 13:47:49 +01:00
} while ( wl_display_dispatch ( m_sDisplay ) ! = - 1 ) ;
2023-05-29 18:35:41 +02:00
unlockSingleInstance ( ) ;
2022-07-01 23:05:58 +02:00
}
2022-07-31 17:30:14 +02:00
void CHyprpaper : : tick ( bool force ) {
2024-01-03 13:47:49 +01:00
bool reload = g_pIPCSocket & & g_pIPCSocket - > mainThreadParseRequest ( ) ;
2022-07-31 17:28:37 +02:00
2022-07-31 17:30:14 +02:00
if ( ! reload & & ! force )
2022-07-31 17:28:37 +02:00
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 ) {
2022-12-28 23:26:58 +01:00
for ( auto & [ pt , wt ] : m_mWallpaperTargets ) {
2022-07-02 18:26:59 +02:00
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 ;
2022-12-28 23:26:58 +01:00
for ( auto & [ ewp , cls ] : m_mWallpaperTargets ) {
2022-07-07 15:26:38 +02:00
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 ( ) ;
2023-07-22 18:28:08 +02:00
if ( std : : filesystem : : is_symlink ( wp ) ) {
2024-07-17 16:25:07 +02:00
auto real_wp = std : : filesystem : : read_symlink ( wp ) ;
2023-08-19 23:28:55 +02:00
std : : filesystem : : path absolute_path = std : : filesystem : : path ( wp ) . parent_path ( ) / real_wp ;
2024-07-17 16:25:07 +02:00
absolute_path = absolute_path . lexically_normal ( ) ;
2023-08-19 23:28:55 +02:00
m_mWallpaperTargets [ wp ] . create ( absolute_path ) ;
2023-07-22 18:28:08 +02:00
} else {
m_mWallpaperTargets [ wp ] . create ( wp ) ;
}
2022-07-01 23:05:58 +02:00
}
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
2024-07-17 16:25:07 +02:00
void CHyprpaper : : createSeat ( SP < CCWlSeat > pSeat ) {
m_pSeat = pSeat ;
pSeat - > setCapabilities ( [ this ] ( CCWlSeat * r , wl_seat_capability caps ) {
if ( caps & WL_SEAT_CAPABILITY_POINTER ) {
m_pSeatPointer = makeShared < CCWlPointer > ( m_pSeat - > sendGetPointer ( ) ) ;
if ( ! m_pCursorShape )
Debug : : log ( WARN , " No cursor-shape-v1 support from the compositor: cursor will be blank " ) ;
else
m_pSeatCursorShapeDevice = makeShared < CCWpCursorShapeDeviceV1 > ( m_pCursorShape - > sendGetPointer ( m_pSeatPointer - > resource ( ) ) ) ;
m_pSeatPointer - > setEnter ( [ this ] ( CCWlPointer * r , uint32_t serial , wl_resource * surface , wl_fixed_t x , wl_fixed_t y ) {
if ( ! m_pCursorShape ) {
m_pSeatPointer - > sendSetCursor ( serial , nullptr , 0 , 0 ) ;
return ;
}
m_pSeatCursorShapeDevice - > sendSetShape ( serial , wpCursorShapeDeviceV1Shape : : WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT ) ;
} ) ;
} else
Debug : : log ( LOG , " No pointer capability from the compositor " ) ;
} ) ;
2022-11-18 21:35:33 +01: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 ;
2024-07-17 16:25:07 +02:00
pMonitor - > pCurrentLayerSurface - > pLayerSurface - > sendAckConfigure ( 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 ( ) {
2024-07-17 16:25:07 +02:00
int cleaned = 0 ;
2022-07-17 13:59:43 +02:00
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 ) {
2024-07-17 16:25:07 +02:00
bool useDesc = false ;
std : : string desc = " " ;
2023-04-26 22:44:51 +02:00
if ( monname . find ( " desc: " ) = = 0 ) {
useDesc = true ;
2024-07-17 16:25:07 +02:00
desc = monname . substr ( 5 ) ;
2023-04-26 22:44:51 +02:00
}
2022-07-02 18:26:59 +02:00
for ( auto & m : m_vMonitors ) {
2023-04-26 22:44:51 +02:00
if ( useDesc & & m - > description . find ( desc ) = = 0 )
return m . get ( ) ;
2022-07-02 18:26:59 +02:00
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-12-28 23:26:58 +01:00
for ( auto & [ file , wt ] : m_mWallpaperTargets ) {
2022-07-02 19:10:50 +02:00
for ( auto & m : m_vMonitors ) {
if ( m - > size = = Vector2D ( ) )
continue ;
2023-01-15 17:13:29 +01:00
auto it = std : : find_if ( m_vBuffers . begin ( ) , m_vBuffers . end ( ) , [ wt = & wt , & m ] ( const std : : unique_ptr < SPoolBuffer > & el ) {
2023-02-18 01:47:40 +01:00
auto scale = std : : round ( ( m - > pCurrentLayerSurface & & m - > pCurrentLayerSurface - > pFractionalScaleInfo ? m - > pCurrentLayerSurface - > fScale : m - > scale ) * 120.0 ) / 120.0 ;
2023-04-09 19:26:11 +02:00
return el - > target = = wt - > m_szPath & & vectorDeltaLessThan ( el - > pixelSize , m - > size * scale , 1 ) ;
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 ( ) ;
2023-02-18 01:47:40 +01:00
auto scale = std : : round ( ( m - > pCurrentLayerSurface & & m - > pCurrentLayerSurface - > pFractionalScaleInfo ? m - > pCurrentLayerSurface - > fScale : m - > scale ) * 120.0 ) / 120.0 ;
2023-02-14 21:27:04 +01:00
createBuffer ( PBUFFER , m - > size . x * scale , m - > size . y * scale , WL_SHM_FORMAT_ARGB8888 ) ;
2022-07-02 19:10:50 +02:00
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-12-06 07:12:46 +01:00
2023-08-03 13:00:55 +02:00
PMONITOR - > hasATarget = true ;
2022-07-02 19:53:12 +02:00
if ( PMONITOR - > pCurrentLayerSurface ) {
2022-12-06 07:12:46 +01:00
2022-07-02 19:53:12 +02:00
PMONITOR - > pCurrentLayerSurface = nullptr ;
2022-07-02 18:26:59 +02:00
2024-07-17 16:25:07 +02:00
PMONITOR - > wantsACK = false ;
2022-07-02 18:26:59 +02:00
PMONITOR - > wantsReload = false ;
PMONITOR - > initialized = false ;
2024-07-17 16:25:07 +02:00
PMONITOR - > readyForLS = true ;
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 ;
2024-07-17 16:25:07 +02:00
it = m_mMonitorActiveWallpaperTargets . find ( pMonitor ) ;
2022-07-01 23:05:58 +02:00
}
2022-12-06 07:12:46 +01:00
if ( it - > second )
2022-07-01 23:05:58 +02:00
return ; // has
// get the target
2023-04-26 22:44:51 +02:00
for ( auto & [ mon , path1 ] : m_mMonitorActiveWallpapers ) {
if ( mon . find ( " desc: " ) ! = 0 )
continue ;
if ( pMonitor - > description . find ( mon . substr ( 5 ) ) = = 0 ) {
for ( auto & [ path2 , target ] : m_mWallpaperTargets ) {
if ( path1 = = path2 ) {
it - > second = & target ;
break ;
}
}
break ;
}
}
2024-08-12 19:31:02 +02:00
if ( ! it - > second ) {
for ( auto & [ mon , path1 ] : m_mMonitorActiveWallpapers ) {
if ( mon = = pMonitor - > name ) {
for ( auto & [ path2 , target ] : m_mWallpaperTargets ) {
if ( path1 = = path2 ) {
it - > second = & target ;
break ;
}
2022-07-01 23:05:58 +02:00
}
2024-08-12 19:31:02 +02:00
break ;
2022-07-01 23:05:58 +02:00
}
}
}
2022-11-06 23:56:42 +01:00
if ( ! it - > second ) {
// try to find a wildcard
2022-12-28 23:26:58 +01:00
for ( auto & [ mon , path1 ] : m_mMonitorActiveWallpapers ) {
2022-11-06 23:56:42 +01:00
if ( mon . empty ( ) ) {
2022-12-28 23:26:58 +01:00
for ( auto & [ path2 , target ] : m_mWallpaperTargets ) {
2022-11-06 23:56:42 +01:00
if ( path1 = = path2 ) {
it - > second = & target ;
break ;
}
}
break ;
}
}
}
2022-07-01 23:05:58 +02:00
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 ) ;
2022-12-06 07:12:46 +01:00
else
2022-07-02 18:26:59 +02:00
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 ) {
2022-12-13 15:27:19 +01:00
const size_t STRIDE = w * 4 ;
2024-07-17 16:25:07 +02:00
const size_t SIZE = STRIDE * h ;
2022-07-01 23:05:58 +02:00
2024-07-17 16:25:07 +02:00
std : : string name ;
const auto FD = createPoolFile ( SIZE , name ) ;
2022-07-01 23:05:58 +02:00
if ( FD = = - 1 ) {
Debug : : log ( CRIT , " Unable to create pool file! " ) ;
exit ( 1 ) ;
}
2022-09-30 12:18:26 +02:00
const auto DATA = mmap ( nullptr , SIZE , PROT_READ | PROT_WRITE , MAP_SHARED , FD , 0 ) ;
2024-07-17 16:25:07 +02:00
auto POOL = makeShared < CCWlShmPool > ( g_pHyprpaper - > m_pSHM - > sendCreatePool ( FD , SIZE ) ) ;
pBuffer - > buffer = makeShared < CCWlBuffer > ( POOL - > sendCreateBuffer ( 0 , w , h , STRIDE , format ) ) ;
POOL . reset ( ) ;
2022-07-01 23:05:58 +02:00
close ( FD ) ;
2024-07-17 16:25:07 +02:00
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 ) ;
2024-07-17 16:25:07 +02:00
pBuffer - > name = name ;
2022-07-01 23:05:58 +02:00
}
void CHyprpaper : : destroyBuffer ( SPoolBuffer * pBuffer ) {
2024-07-17 16:25:07 +02:00
pBuffer - > buffer . reset ( ) ;
2022-07-01 23:05:58 +02:00
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 ) {
2023-04-09 19:52:43 +02:00
const auto IT = std : : find_if ( m_vBuffers . begin ( ) , m_vBuffers . end ( ) , [ & ] ( const std : : unique_ptr < SPoolBuffer > & el ) {
2024-07-17 16:25:07 +02:00
auto scale =
std : : round ( ( pMonitor - > pCurrentLayerSurface & & pMonitor - > pCurrentLayerSurface - > pFractionalScaleInfo ? pMonitor - > pCurrentLayerSurface - > fScale : pMonitor - > scale ) *
120.0 ) /
120.0 ;
2023-04-09 19:26:11 +02:00
return el - > target = = pWallpaperTarget - > m_szPath & & vectorDeltaLessThan ( el - > pixelSize , pMonitor - > size * scale , 1 ) ;
2023-04-09 19:52:43 +02:00
} ) ;
if ( IT = = m_vBuffers . end ( ) )
return nullptr ;
return IT - > get ( ) ;
2022-07-02 19:10:50 +02:00
}
2022-07-01 23:05:58 +02:00
void CHyprpaper : : renderWallpaperForMonitor ( SMonitor * pMonitor ) {
2024-01-01 13:34:50 +01:00
static auto * const PRENDERSPLASH = reinterpret_cast < Hyprlang : : INT * const * > ( g_pConfigManager - > config - > getConfigValuePtr ( " splash " ) - > getDataStaticPtr ( ) ) ;
static auto * const PSPLASHOFFSET = reinterpret_cast < Hyprlang : : FLOAT * const * > ( g_pConfigManager - > config - > getConfigValuePtr ( " splash_offset " ) - > getDataStaticPtr ( ) ) ;
2024-01-03 13:47:49 +01:00
if ( ! m_mMonitorActiveWallpaperTargets [ pMonitor ] )
recheckMonitor ( pMonitor ) ;
2022-07-02 19:10:50 +02:00
const auto PWALLPAPERTARGET = m_mMonitorActiveWallpaperTargets [ pMonitor ] ;
2024-07-17 16:25:07 +02:00
const auto CONTAIN = m_mMonitorWallpaperRenderData [ pMonitor - > name ] . contain ;
2024-10-25 13:29:16 +02:00
const auto TILE = m_mMonitorWallpaperRenderData [ pMonitor - > name ] . tile ;
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
2024-07-17 16:25:07 +02:00
const double SURFACESCALE = pMonitor - > pCurrentLayerSurface & & pMonitor - > pCurrentLayerSurface - > pFractionalScaleInfo ? pMonitor - > pCurrentLayerSurface - > fScale : pMonitor - > scale ;
const Vector2D DIMENSIONS = Vector2D { std : : round ( pMonitor - > size . x * SURFACESCALE ) , std : : round ( pMonitor - > size . y * SURFACESCALE ) } ;
2023-01-29 15:00:08 +01:00
2024-07-17 16:25:07 +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 ) ;
2023-10-13 22:23:24 +02:00
// always draw a black background behind the wallpaper
cairo_set_source_rgb ( PCAIRO , 0 , 0 , 0 ) ;
cairo_rectangle ( PCAIRO , 0 , 0 , DIMENSIONS . x , DIMENSIONS . y ) ;
cairo_fill ( PCAIRO ) ;
cairo_surface_flush ( PBUFFER - > surface ) ;
2022-08-03 18:06:33 +02:00
2022-07-02 15:09:59 +02:00
// get scale
// we always do cover
2024-07-17 16:25:07 +02:00
double scale ;
Vector2D origin ;
2022-07-02 15:09:59 +02:00
2023-10-13 22:23:24 +02:00
const bool LOWASPECTRATIO = pMonitor - > size . x / pMonitor - > size . y > PWALLPAPERTARGET - > m_vSize . x / PWALLPAPERTARGET - > m_vSize . y ;
if ( ( CONTAIN & & ! LOWASPECTRATIO ) | | ( ! CONTAIN & & LOWASPECTRATIO ) ) {
2024-07-17 16:25:07 +02:00
scale = DIMENSIONS . x / PWALLPAPERTARGET - > m_vSize . x ;
2023-10-13 22:23:24 +02:00
origin . y = - ( PWALLPAPERTARGET - > m_vSize . y * scale - DIMENSIONS . y ) / 2.0 / scale ;
2022-07-02 15:09:59 +02:00
} else {
2024-07-17 16:25:07 +02:00
scale = DIMENSIONS . y / PWALLPAPERTARGET - > m_vSize . y ;
2023-10-13 22:23:24 +02:00
origin . x = - ( PWALLPAPERTARGET - > m_vSize . x * scale - DIMENSIONS . x ) / 2.0 / scale ;
2022-07-02 15:09:59 +02:00
}
2024-07-17 16:25:07 +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 ) ;
2022-07-02 15:09:59 +02:00
2024-10-25 13:29:16 +02:00
if ( TILE ) {
cairo_pattern_t * pattern = cairo_pattern_create_for_surface ( PWALLPAPERTARGET - > m_pCairoSurface ) ;
cairo_pattern_set_extend ( pattern , CAIRO_EXTEND_REPEAT ) ;
cairo_set_source ( PCAIRO , pattern ) ;
} else {
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 ) ;
2023-04-09 19:47:49 +02:00
2024-01-01 13:34:50 +01:00
if ( * * PRENDERSPLASH & & getenv ( " HYPRLAND_INSTANCE_SIGNATURE " ) ) {
2023-04-09 19:47:49 +02:00
auto SPLASH = execAndGet ( " hyprctl splash " ) ;
SPLASH . pop_back ( ) ;
Debug : : log ( LOG , " Rendering splash: %s " , SPLASH . c_str ( ) ) ;
cairo_select_font_face ( PCAIRO , " Sans " , CAIRO_FONT_SLANT_NORMAL , CAIRO_FONT_WEIGHT_NORMAL ) ;
const auto FONTSIZE = ( int ) ( DIMENSIONS . y / 76.0 / scale ) ;
cairo_set_font_size ( PCAIRO , FONTSIZE ) ;
2024-04-04 21:52:48 +02:00
static auto * const PSPLASHCOLOR = reinterpret_cast < Hyprlang : : INT * const * > ( g_pConfigManager - > config - > getConfigValuePtr ( " splash_color " ) - > getDataStaticPtr ( ) ) ;
2024-07-17 16:25:07 +02:00
2024-04-04 21:52:48 +02:00
Debug : : log ( LOG , " Splash color: %x " , * * PSPLASHCOLOR ) ;
2024-07-17 16:25:07 +02:00
cairo_set_source_rgba ( PCAIRO , ( ( * * PSPLASHCOLOR > > 16 ) & 0xFF ) / 255.0 , ( ( * * PSPLASHCOLOR > > 8 ) & 0xFF ) / 255.0 , ( * * PSPLASHCOLOR & 0xFF ) / 255.0 ,
( ( * * PSPLASHCOLOR > > 24 ) & 0xFF ) / 255.0 ) ;
2023-04-09 19:47:49 +02:00
cairo_text_extents_t textExtents ;
cairo_text_extents ( PCAIRO , SPLASH . c_str ( ) , & textExtents ) ;
2024-01-01 13:34:50 +01:00
cairo_move_to ( PCAIRO , ( ( DIMENSIONS . x - textExtents . width * scale ) / 2.0 ) / scale , ( ( DIMENSIONS . y * ( 100 - * * PSPLASHOFFSET ) ) / 100 - textExtents . height * scale ) / scale ) ;
2023-04-09 19:47:49 +02:00
2024-07-17 16:25:07 +02:00
Debug : : log ( LOG , " Splash font size: %d, pos: %.2f, %.2f " , FONTSIZE , ( DIMENSIONS . x - textExtents . width ) / 2.0 / scale ,
( ( DIMENSIONS . y * ( 100 - * * PSPLASHOFFSET ) ) / 100 - textExtents . height * scale ) / scale ) ;
2023-04-09 19:47:49 +02:00
cairo_show_text ( PCAIRO , SPLASH . c_str ( ) ) ;
cairo_surface_flush ( PWALLPAPERTARGET - > m_pCairoSurface ) ;
}
2022-07-01 23:05:58 +02:00
cairo_restore ( PCAIRO ) ;
2023-02-18 01:47:40 +01:00
if ( pMonitor - > pCurrentLayerSurface ) {
2024-07-17 16:25:07 +02:00
pMonitor - > pCurrentLayerSurface - > pSurface - > sendAttach ( PBUFFER - > buffer . get ( ) , 0 , 0 ) ;
pMonitor - > pCurrentLayerSurface - > pSurface - > sendSetBufferScale ( pMonitor - > pCurrentLayerSurface - > pFractionalScaleInfo ? 1 : pMonitor - > scale ) ;
pMonitor - > pCurrentLayerSurface - > pSurface - > sendDamageBuffer ( 0 , 0 , 0xFFFF , 0xFFFF ) ;
2023-11-26 16:25:38 +01:00
// our wps are always opaque
2024-07-17 16:25:07 +02:00
auto opaqueRegion = makeShared < CCWlRegion > ( g_pHyprpaper - > m_pCompositor - > sendCreateRegion ( ) ) ;
opaqueRegion - > sendAdd ( 0 , 0 , PBUFFER - > pixelSize . x , PBUFFER - > pixelSize . y ) ;
pMonitor - > pCurrentLayerSurface - > pSurface - > sendSetOpaqueRegion ( opaqueRegion . get ( ) ) ;
2023-11-26 16:25:38 +01:00
2023-02-14 21:27:04 +01:00
if ( pMonitor - > pCurrentLayerSurface - > pFractionalScaleInfo ) {
2024-07-17 16:25:07 +02:00
Debug : : log ( LOG , " Submitting viewport dest size %ix%i for %x " , static_cast < int > ( std : : round ( pMonitor - > size . x ) ) , static_cast < int > ( std : : round ( pMonitor - > size . y ) ) ,
pMonitor - > pCurrentLayerSurface ) ;
pMonitor - > pCurrentLayerSurface - > pViewport - > sendSetDestination ( static_cast < int > ( std : : round ( pMonitor - > size . x ) ) , static_cast < int > ( std : : round ( pMonitor - > size . y ) ) ) ;
2023-02-14 21:27:04 +01:00
}
2024-07-17 16:25:07 +02:00
pMonitor - > pCurrentLayerSurface - > pSurface - > sendCommit ( ) ;
2023-01-29 15:00:08 +01:00
}
2022-07-02 19:53:12 +02:00
// 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-09-30 12:18:26 +02:00
}
2023-05-29 18:35:41 +02:00
bool CHyprpaper : : lockSingleInstance ( ) {
const std : : string XDG_RUNTIME_DIR = getenv ( " XDG_RUNTIME_DIR " ) ;
2024-07-17 16:25:07 +02:00
const auto LOCKFILE = XDG_RUNTIME_DIR + " /hyprpaper.lock " ;
2023-05-29 18:35:41 +02:00
if ( std : : filesystem : : exists ( LOCKFILE ) ) {
std : : ifstream ifs ( LOCKFILE ) ;
2024-07-17 16:25:07 +02:00
std : : string content ( ( std : : istreambuf_iterator < char > ( ifs ) ) , ( std : : istreambuf_iterator < char > ( ) ) ) ;
2023-05-29 18:35:41 +02:00
try {
kill ( std : : stoull ( content ) , 0 ) ;
if ( errno ! = ESRCH )
return false ;
2024-07-17 16:25:07 +02:00
} catch ( std : : exception & e ) { ; }
2023-05-29 18:35:41 +02:00
}
// create lockfile
std : : ofstream ofs ( LOCKFILE , std : : ios : : trunc ) ;
ofs < < std : : to_string ( getpid ( ) ) ;
ofs . close ( ) ;
return true ;
}
void CHyprpaper : : unlockSingleInstance ( ) {
const std : : string XDG_RUNTIME_DIR = getenv ( " XDG_RUNTIME_DIR " ) ;
2024-07-17 16:25:07 +02:00
const auto LOCKFILE = XDG_RUNTIME_DIR + " /hyprpaper.lock " ;
2023-05-29 18:35:41 +02:00
unlink ( LOCKFILE . c_str ( ) ) ;
2023-07-22 18:28:08 +02:00
}