2022-12-05 17:05:15 +00:00
# include "ToplevelExport.hpp"
# include "../Compositor.hpp"
2024-04-25 00:58:40 +01:00
# include "ForeignToplevelWlr.hpp"
2024-05-05 22:18:10 +01:00
# include "../managers/PointerManager.hpp"
2025-01-12 09:09:02 -08:00
# include "../managers/SeatManager.hpp"
2024-06-08 10:07:59 +02:00
# include "types/WLBuffer.hpp"
# include "types/Buffer.hpp"
# include "../helpers/Format.hpp"
2025-01-17 15:21:35 +00:00
# include "../managers/EventManager.hpp"
# include "../managers/input/InputManager.hpp"
2025-04-08 19:39:53 +02:00
# include "../managers/permissions/DynamicPermissionManager.hpp"
2025-01-17 15:21:35 +00:00
# include "../render/Renderer.hpp"
2022-12-05 17:05:15 +00:00
# include <algorithm>
2025-02-06 13:42:20 +01:00
# include <hyprutils/math/Vector2D.hpp>
2022-12-05 17:05:15 +00:00
2025-05-04 19:21:36 +02:00
CToplevelExportClient : : CToplevelExportClient ( SP < CHyprlandToplevelExportManagerV1 > resource_ ) : m_resource ( resource_ ) {
2025-01-17 18:21:34 +01:00
if UNLIKELY ( ! good ( ) )
2024-07-27 10:02:02 -05:00
return ;
2022-12-05 17:05:15 +00:00
2025-05-04 19:21:36 +02:00
m_resource - > setOnDestroy ( [ this ] ( CHyprlandToplevelExportManagerV1 * pMgr ) { PROTO : : toplevelExport - > destroyResource ( this ) ; } ) ;
m_resource - > setDestroy ( [ this ] ( CHyprlandToplevelExportManagerV1 * pMgr ) { PROTO : : toplevelExport - > destroyResource ( this ) ; } ) ;
m_resource - > setCaptureToplevel ( [ this ] ( CHyprlandToplevelExportManagerV1 * pMgr , uint32_t frame , int32_t overlayCursor , uint32_t handle ) {
2024-07-27 10:02:02 -05:00
this - > captureToplevel ( pMgr , frame , overlayCursor , g_pCompositor - > getWindowFromHandle ( handle ) ) ;
} ) ;
2025-05-04 19:21:36 +02:00
m_resource - > setCaptureToplevelWithWlrToplevelHandle ( [ this ] ( CHyprlandToplevelExportManagerV1 * pMgr , uint32_t frame , int32_t overlayCursor , wl_resource * handle ) {
2024-07-27 10:02:02 -05:00
this - > captureToplevel ( pMgr , frame , overlayCursor , PROTO : : foreignToplevelWlr - > windowFromHandleResource ( handle ) ) ;
} ) ;
2025-05-04 19:21:36 +02:00
m_lastMeasure . reset ( ) ;
m_lastFrame . reset ( ) ;
m_tickCallback = g_pHookSystem - > hookDynamic ( " tick " , [ & ] ( void * self , SCallbackInfo & info , std : : any data ) { onTick ( ) ; } ) ;
2024-07-24 12:07:36 -05:00
}
2024-07-27 10:02:02 -05:00
void CToplevelExportClient : : captureToplevel ( CHyprlandToplevelExportManagerV1 * pMgr , uint32_t frame , int32_t overlayCursor_ , PHLWINDOW handle ) {
// create a frame
2025-05-04 19:21:36 +02:00
const auto FRAME = PROTO : : toplevelExport - > m_frames . emplace_back (
makeShared < CToplevelExportFrame > ( makeShared < CHyprlandToplevelExportFrameV1 > ( m_resource - > client ( ) , m_resource - > version ( ) , frame ) , overlayCursor_ , handle ) ) ;
2022-12-05 17:05:15 +00:00
2025-01-17 18:21:34 +01:00
if UNLIKELY ( ! FRAME - > good ( ) ) {
2024-07-27 10:02:02 -05:00
LOGM ( ERR , " Couldn't alloc frame for sharing! (no memory) " ) ;
2025-05-04 19:21:36 +02:00
m_resource - > noMemory ( ) ;
2024-07-27 10:02:02 -05:00
PROTO : : toplevelExport - > destroyResource ( FRAME . get ( ) ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-04 19:21:36 +02:00
FRAME - > m_self = FRAME ;
FRAME - > m_client = m_self ;
2022-12-05 17:05:15 +00:00
}
2024-07-27 10:02:02 -05:00
void CToplevelExportClient : : onTick ( ) {
2025-05-04 19:21:36 +02:00
if ( m_lastMeasure . getMillis ( ) < 500 )
2024-07-27 10:02:02 -05:00
return ;
2022-12-05 17:05:15 +00:00
2025-05-04 19:21:36 +02:00
m_framesInLastHalfSecond = m_frameCounter ;
m_frameCounter = 0 ;
m_lastMeasure . reset ( ) ;
const auto LASTFRAMEDELTA = m_lastFrame . getMillis ( ) / 1000.0 ;
const bool FRAMEAWAITING = std : : ranges : : any_of ( PROTO : : toplevelExport - > m_frames , [ & ] ( const auto & frame ) { return frame - > m_client . get ( ) = = this ; } ) ;
if ( m_framesInLastHalfSecond > 3 & & ! m_sentScreencast ) {
EMIT_HOOK_EVENT ( " screencast " , ( std : : vector < uint64_t > { 1 , ( uint64_t ) m_framesInLastHalfSecond , ( uint64_t ) m_clientOwner } ) ) ;
g_pEventManager - > postEvent ( SHyprIPCEvent { " screencast " , " 1, " + std : : to_string ( m_clientOwner ) } ) ;
m_sentScreencast = true ;
} else if ( m_framesInLastHalfSecond < 4 & & m_sentScreencast & & LASTFRAMEDELTA > 1.0 & & ! FRAMEAWAITING ) {
EMIT_HOOK_EVENT ( " screencast " , ( std : : vector < uint64_t > { 0 , ( uint64_t ) m_framesInLastHalfSecond , ( uint64_t ) m_clientOwner } ) ) ;
g_pEventManager - > postEvent ( SHyprIPCEvent { " screencast " , " 0, " + std : : to_string ( m_clientOwner ) } ) ;
m_sentScreencast = false ;
2022-12-05 17:05:15 +00:00
}
}
2024-07-27 10:02:02 -05:00
bool CToplevelExportClient : : good ( ) {
2025-05-04 19:21:36 +02:00
return m_resource - > resource ( ) ;
2022-12-05 17:05:15 +00:00
}
2025-05-04 19:21:36 +02:00
CToplevelExportFrame : : CToplevelExportFrame ( SP < CHyprlandToplevelExportFrameV1 > resource_ , int32_t overlayCursor_ , PHLWINDOW pWindow_ ) : m_resource ( resource_ ) , m_window ( pWindow_ ) {
2025-01-17 18:21:34 +01:00
if UNLIKELY ( ! good ( ) )
2022-12-05 17:05:15 +00:00
return ;
2025-05-04 19:21:36 +02:00
m_cursorOverlayRequested = ! ! overlayCursor_ ;
2022-12-05 17:05:15 +00:00
2025-05-04 19:21:36 +02:00
if UNLIKELY ( ! m_window ) {
LOGM ( ERR , " Client requested sharing of window handle {:x} which does not exist! " , m_window ) ;
m_resource - > sendFailed ( ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-04 19:21:36 +02:00
if UNLIKELY ( ! m_window - > m_isMapped ) {
LOGM ( ERR , " Client requested sharing of window handle {:x} which is not shareable! " , m_window ) ;
m_resource - > sendFailed ( ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-04 19:21:36 +02:00
m_resource - > setOnDestroy ( [ this ] ( CHyprlandToplevelExportFrameV1 * pFrame ) { PROTO : : toplevelExport - > destroyResource ( this ) ; } ) ;
m_resource - > setDestroy ( [ this ] ( CHyprlandToplevelExportFrameV1 * pFrame ) { PROTO : : toplevelExport - > destroyResource ( this ) ; } ) ;
m_resource - > setCopy ( [ this ] ( CHyprlandToplevelExportFrameV1 * pFrame , wl_resource * res , int32_t ignoreDamage ) { this - > copy ( pFrame , res , ignoreDamage ) ; } ) ;
2022-12-05 17:05:15 +00:00
2025-05-04 19:21:36 +02:00
const auto PMONITOR = m_window - > m_monitor . lock ( ) ;
2022-12-05 17:05:15 +00:00
2023-12-03 22:04:07 +00:00
g_pHyprRenderer - > makeEGLCurrent ( ) ;
2024-02-15 00:58:58 +00:00
2025-05-04 19:21:36 +02:00
m_shmFormat = g_pHyprOpenGL - > getPreferredReadFormat ( PMONITOR ) ;
if UNLIKELY ( m_shmFormat = = DRM_FORMAT_INVALID ) {
2024-07-27 10:02:02 -05:00
LOGM ( ERR , " No format supported by renderer in capture toplevel " ) ;
2025-05-04 19:21:36 +02:00
m_resource - > sendFailed ( ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-04 19:21:36 +02:00
const auto PSHMINFO = NFormatUtils : : getPixelFormatFromDRM ( m_shmFormat ) ;
2025-01-17 18:21:34 +01:00
if UNLIKELY ( ! PSHMINFO ) {
2024-07-27 10:02:02 -05:00
LOGM ( ERR , " No pixel format supported by renderer in capture toplevel " ) ;
2025-05-04 19:21:36 +02:00
m_resource - > sendFailed ( ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-04 19:21:36 +02:00
m_dmabufFormat = PMONITOR - > m_output - > state - > state ( ) . drmFormat ;
2022-12-05 17:05:15 +00:00
2025-05-04 19:21:36 +02:00
m_box = { 0 , 0 , ( int ) ( m_window - > m_realSize - > value ( ) . x * PMONITOR - > m_scale ) , ( int ) ( m_window - > m_realSize - > value ( ) . y * PMONITOR - > m_scale ) } ;
2024-07-21 13:09:54 +02:00
2025-05-04 19:21:36 +02:00
m_box . transform ( wlTransformToHyprutils ( PMONITOR - > m_transform ) , PMONITOR - > m_transformedSize . x , PMONITOR - > m_transformedSize . y ) . round ( ) ;
2022-12-05 17:05:15 +00:00
2025-05-04 19:21:36 +02:00
m_shmStride = NFormatUtils : : minStride ( PSHMINFO , m_box . w ) ;
2022-12-05 17:05:15 +00:00
2025-05-04 19:21:36 +02:00
m_resource - > sendBuffer ( NFormatUtils : : drmToShm ( m_shmFormat ) , m_box . width , m_box . height , m_shmStride ) ;
2023-07-20 12:42:25 +02:00
2025-05-04 19:21:36 +02:00
if LIKELY ( m_dmabufFormat ! = DRM_FORMAT_INVALID )
m_resource - > sendLinuxDmabuf ( m_dmabufFormat , m_box . width , m_box . height ) ;
2023-07-20 12:42:25 +02:00
2025-05-04 19:21:36 +02:00
m_resource - > sendBufferDone ( ) ;
2022-12-05 17:05:15 +00:00
}
2024-07-27 10:02:02 -05:00
void CToplevelExportFrame : : copy ( CHyprlandToplevelExportFrameV1 * pFrame , wl_resource * buffer_ , int32_t ignoreDamage ) {
2025-01-17 18:21:34 +01:00
if UNLIKELY ( ! good ( ) ) {
2024-07-27 10:02:02 -05:00
LOGM ( ERR , " No frame in copyFrame?? " ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-04 19:21:36 +02:00
if UNLIKELY ( ! validMapped ( m_window ) ) {
LOGM ( ERR , " Client requested sharing of window handle {:x} which is gone! " , m_window ) ;
m_resource - > sendFailed ( ) ;
2024-02-07 00:18:47 +00:00
return ;
}
2025-05-04 19:21:36 +02:00
if UNLIKELY ( ! m_window - > m_isMapped ) {
LOGM ( ERR , " Client requested sharing of window handle {:x} which is not shareable (2)! " , m_window ) ;
m_resource - > sendFailed ( ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2024-07-27 10:02:02 -05:00
const auto PBUFFER = CWLBufferResource : : fromResource ( buffer_ ) ;
2025-01-17 18:21:34 +01:00
if UNLIKELY ( ! PBUFFER ) {
2025-05-04 19:21:36 +02:00
m_resource - > error ( HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER , " invalid buffer " ) ;
2024-07-27 10:02:02 -05:00
PROTO : : toplevelExport - > destroyResource ( this ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-04 19:21:36 +02:00
if UNLIKELY ( PBUFFER - > m_buffer - > size ! = m_box . size ( ) ) {
m_resource - > error ( HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER , " invalid buffer dimensions " ) ;
2024-07-27 10:02:02 -05:00
PROTO : : toplevelExport - > destroyResource ( this ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-04 19:21:36 +02:00
if UNLIKELY ( m_buffer ) {
m_resource - > error ( HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED , " frame already used " ) ;
2024-07-27 10:02:02 -05:00
PROTO : : toplevelExport - > destroyResource ( this ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-03 18:54:50 +02:00
if ( auto attrs = PBUFFER - > m_buffer - > dmabuf ( ) ; attrs . success ) {
2025-05-04 19:21:36 +02:00
m_bufferDMA = true ;
2022-12-05 17:05:15 +00:00
2025-05-04 19:21:36 +02:00
if ( attrs . format ! = m_dmabufFormat ) {
m_resource - > error ( HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER , " invalid buffer format " ) ;
2024-07-27 10:02:02 -05:00
PROTO : : toplevelExport - > destroyResource ( this ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-03 18:54:50 +02:00
} else if ( auto attrs = PBUFFER - > m_buffer - > shm ( ) ; attrs . success ) {
2025-05-04 19:21:36 +02:00
if ( attrs . format ! = m_shmFormat ) {
m_resource - > error ( HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER , " invalid buffer format " ) ;
2024-07-27 10:02:02 -05:00
PROTO : : toplevelExport - > destroyResource ( this ) ;
2022-12-05 17:05:15 +00:00
return ;
2025-05-04 19:21:36 +02:00
} else if ( ( int ) attrs . stride ! = m_shmStride ) {
m_resource - > error ( HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER , " invalid buffer stride " ) ;
2024-07-27 10:02:02 -05:00
PROTO : : toplevelExport - > destroyResource ( this ) ;
2022-12-05 17:05:15 +00:00
return ;
}
} else {
2025-05-04 19:21:36 +02:00
m_resource - > error ( HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER , " invalid buffer type " ) ;
2024-07-27 10:02:02 -05:00
PROTO : : toplevelExport - > destroyResource ( this ) ;
2022-12-05 17:05:15 +00:00
return ;
}
2025-05-04 19:21:36 +02:00
m_buffer = CHLBufferReference ( PBUFFER - > m_buffer . lock ( ) ) ;
2022-12-05 17:05:15 +00:00
2025-01-14 08:44:09 -08:00
m_ignoreDamage = ignoreDamage ;
2025-05-04 19:21:36 +02:00
if ( ignoreDamage & & validMapped ( m_window ) )
2025-01-14 08:44:09 -08:00
share ( ) ;
else
2025-05-04 19:21:36 +02:00
PROTO : : toplevelExport - > m_framesAwaitingWrite . emplace_back ( m_self ) ;
2022-12-05 17:05:15 +00:00
}
2024-07-27 10:02:02 -05:00
void CToplevelExportFrame : : share ( ) {
2025-05-04 19:21:36 +02:00
if ( ! m_buffer | | ! validMapped ( m_window ) )
2022-12-05 17:05:15 +00:00
return ;
2025-05-04 19:21:36 +02:00
if ( m_bufferDMA ) {
2025-04-16 01:37:48 +01:00
if ( ! copyDmabuf ( Time : : steadyNow ( ) ) ) {
2025-05-04 19:21:36 +02:00
m_resource - > sendFailed ( ) ;
2022-12-05 17:05:15 +00:00
return ;
}
} else {
2025-04-16 01:37:48 +01:00
if ( ! copyShm ( Time : : steadyNow ( ) ) ) {
2025-05-04 19:21:36 +02:00
m_resource - > sendFailed ( ) ;
2022-12-05 17:05:15 +00:00
return ;
}
}
2025-05-04 19:21:36 +02:00
m_resource - > sendFlags ( ( hyprlandToplevelExportFrameV1Flags ) 0 ) ;
2024-07-27 10:02:02 -05:00
2025-04-16 01:37:48 +01:00
if ( ! m_ignoreDamage )
2025-05-04 19:21:36 +02:00
m_resource - > sendDamage ( 0 , 0 , m_box . width , m_box . height ) ;
2024-07-27 10:02:02 -05:00
2025-04-16 01:37:48 +01:00
const auto [ sec , nsec ] = Time : : secNsec ( Time : : steadyNow ( ) ) ;
uint32_t tvSecHi = ( sizeof ( sec ) > 4 ) ? sec > > 32 : 0 ;
uint32_t tvSecLo = sec & 0xFFFFFFFF ;
2025-05-04 19:21:36 +02:00
m_resource - > sendReady ( tvSecHi , tvSecLo , nsec ) ;
2022-12-05 17:05:15 +00:00
}
2025-04-16 01:37:48 +01:00
bool CToplevelExportFrame : : copyShm ( const Time : : steady_tp & now ) {
2025-05-04 19:21:36 +02:00
const auto PERM = g_pDynamicPermissionManager - > clientPermissionMode ( m_resource - > client ( ) , PERMISSION_TYPE_SCREENCOPY ) ;
auto shm = m_buffer - > shm ( ) ;
auto [ pixelData , fmt , bufLen ] = m_buffer - > beginDataPtr ( 0 ) ; // no need for end, cuz it's shm
2022-12-05 17:05:15 +00:00
// render the client
2025-05-04 19:21:36 +02:00
const auto PMONITOR = m_window - > m_monitor . lock ( ) ;
2025-04-30 23:45:20 +02:00
CRegion fakeDamage { 0 , 0 , PMONITOR - > m_pixelSize . x * 10 , PMONITOR - > m_pixelSize . y * 10 } ;
2022-12-05 17:05:15 +00:00
2023-11-30 10:14:35 +00:00
g_pHyprRenderer - > makeEGLCurrent ( ) ;
CFramebuffer outFB ;
2025-04-30 23:45:20 +02:00
outFB . alloc ( PMONITOR - > m_pixelSize . x , PMONITOR - > m_pixelSize . y , PMONITOR - > m_output - > state - > state ( ) . drmFormat ) ;
2023-11-30 10:14:35 +00:00
2025-01-12 09:09:02 -08:00
auto overlayCursor = shouldOverlayCursor ( ) ;
2024-07-27 10:02:02 -05:00
if ( overlayCursor ) {
2025-04-30 23:45:20 +02:00
g_pPointerManager - > lockSoftwareForMonitor ( PMONITOR - > m_self . lock ( ) ) ;
g_pPointerManager - > damageCursor ( PMONITOR - > m_self . lock ( ) ) ;
2024-05-05 22:18:10 +01:00
}
2023-11-24 10:54:21 +00:00
2024-06-08 10:07:59 +02:00
if ( ! g_pHyprRenderer - > beginRender ( PMONITOR , fakeDamage , RENDER_MODE_FULL_FAKE , nullptr , & outFB ) )
2024-06-03 21:10:31 +02:00
return false ;
2024-12-03 18:58:24 +00:00
g_pHyprOpenGL - > clear ( CHyprColor ( 0 , 0 , 0 , 1.0 ) ) ;
2022-12-05 17:05:15 +00:00
// render client at 0,0
2025-04-08 19:39:53 +02:00
if ( PERM = = PERMISSION_RULE_ALLOW_MODE_ALLOW ) {
2025-05-27 20:10:22 +05:00
if ( ! m_window - > m_windowData . noScreenShare . valueOrDefault ( ) ) {
g_pHyprRenderer - > m_bBlockSurfaceFeedback = g_pHyprRenderer - > shouldRenderWindow ( m_window ) ; // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer - > renderWindow ( m_window , PMONITOR , now , false , RENDER_PASS_ALL , true , true ) ;
g_pHyprRenderer - > m_bBlockSurfaceFeedback = false ;
}
2025-04-08 19:39:53 +02:00
if ( overlayCursor )
2025-05-04 19:21:36 +02:00
g_pPointerManager - > renderSoftwareCursorsFor ( PMONITOR - > m_self . lock ( ) , now , fakeDamage , g_pInputManager - > getMouseCoordsInternal ( ) - m_window - > m_realPosition - > value ( ) ) ;
2025-04-08 19:39:53 +02:00
} else if ( PERM = = PERMISSION_RULE_ALLOW_MODE_DENY ) {
2025-05-05 23:44:49 +02:00
CBox texbox = CBox { PMONITOR - > m_transformedSize / 2.F , g_pHyprOpenGL - > m_screencopyDeniedTexture - > m_size } . translate ( - g_pHyprOpenGL - > m_screencopyDeniedTexture - > m_size / 2.F ) ;
g_pHyprOpenGL - > renderTexture ( g_pHyprOpenGL - > m_screencopyDeniedTexture , texbox , 1 ) ;
2025-04-08 19:39:53 +02:00
}
2023-04-04 00:58:30 +01:00
2024-12-07 18:51:18 +01:00
const auto PFORMAT = NFormatUtils : : getPixelFormatFromDRM ( shm . format ) ;
2023-12-04 03:52:54 +00:00
if ( ! PFORMAT ) {
g_pHyprRenderer - > endRender ( ) ;
return false ;
2023-12-03 22:04:07 +00:00
}
2022-12-05 17:05:15 +00:00
2025-05-05 23:44:49 +02:00
g_pHyprOpenGL - > m_renderData . blockScreenShader = true ;
2023-12-07 17:58:13 +00:00
g_pHyprRenderer - > endRender ( ) ;
g_pHyprRenderer - > makeEGLCurrent ( ) ;
2025-05-05 23:44:49 +02:00
g_pHyprOpenGL - > m_renderData . pMonitor = PMONITOR ;
2023-12-07 17:58:13 +00:00
outFB . bind ( ) ;
2022-12-05 17:05:15 +00:00
2024-11-02 15:26:25 +00:00
glBindFramebuffer ( GL_READ_FRAMEBUFFER , outFB . getFBID ( ) ) ;
2023-12-03 22:04:07 +00:00
glPixelStorei ( GL_PACK_ALIGNMENT , 1 ) ;
2024-07-13 12:53:53 +02:00
auto glFormat = PFORMAT - > flipRB ? GL_BGRA_EXT : GL_RGBA ;
2025-02-06 13:42:20 +01:00
auto origin = Vector2D ( 0 , 0 ) ;
2025-04-30 23:45:20 +02:00
switch ( PMONITOR - > m_transform ) {
2025-02-06 13:42:20 +01:00
case WL_OUTPUT_TRANSFORM_FLIPPED_180 :
case WL_OUTPUT_TRANSFORM_90 : {
2025-05-04 19:21:36 +02:00
origin . y = PMONITOR - > m_pixelSize . y - m_box . height ;
2025-02-06 13:42:20 +01:00
break ;
}
case WL_OUTPUT_TRANSFORM_FLIPPED_270 :
case WL_OUTPUT_TRANSFORM_180 : {
2025-05-04 19:21:36 +02:00
origin . x = PMONITOR - > m_pixelSize . x - m_box . width ;
origin . y = PMONITOR - > m_pixelSize . y - m_box . height ;
2025-02-06 13:42:20 +01:00
break ;
}
case WL_OUTPUT_TRANSFORM_FLIPPED :
case WL_OUTPUT_TRANSFORM_270 : {
2025-05-04 19:21:36 +02:00
origin . x = PMONITOR - > m_pixelSize . x - m_box . width ;
2025-02-06 13:42:20 +01:00
break ;
}
default : break ;
}
2025-05-04 19:21:36 +02:00
glReadPixels ( origin . x , origin . y , m_box . width , m_box . height , glFormat , PFORMAT - > glType , pixelData ) ;
2022-12-05 17:05:15 +00:00
2024-07-27 10:02:02 -05:00
if ( overlayCursor ) {
2025-04-30 23:45:20 +02:00
g_pPointerManager - > unlockSoftwareForMonitor ( PMONITOR - > m_self . lock ( ) ) ;
g_pPointerManager - > damageCursor ( PMONITOR - > m_self . lock ( ) ) ;
2024-05-05 22:18:10 +01:00
}
2023-04-04 00:58:30 +01:00
2025-02-08 01:46:26 +01:00
outFB . unbind ( ) ;
glBindFramebuffer ( GL_READ_FRAMEBUFFER , 0 ) ;
2022-12-05 17:05:15 +00:00
return true ;
}
2025-04-16 01:37:48 +01:00
bool CToplevelExportFrame : : copyDmabuf ( const Time : : steady_tp & now ) {
2025-05-04 19:21:36 +02:00
const auto PERM = g_pDynamicPermissionManager - > clientPermissionMode ( m_resource - > client ( ) , PERMISSION_TYPE_SCREENCOPY ) ;
const auto PMONITOR = m_window - > m_monitor . lock ( ) ;
2023-07-20 12:42:25 +02:00
2023-08-08 19:10:47 +02:00
CRegion fakeDamage { 0 , 0 , INT16_MAX , INT16_MAX } ;
2023-07-20 12:42:25 +02:00
2025-01-12 09:09:02 -08:00
auto overlayCursor = shouldOverlayCursor ( ) ;
2024-07-27 10:02:02 -05:00
if ( overlayCursor ) {
2025-04-30 23:45:20 +02:00
g_pPointerManager - > lockSoftwareForMonitor ( PMONITOR - > m_self . lock ( ) ) ;
g_pPointerManager - > damageCursor ( PMONITOR - > m_self . lock ( ) ) ;
2024-07-27 10:02:02 -05:00
}
2025-05-04 19:21:36 +02:00
if ( ! g_pHyprRenderer - > beginRender ( PMONITOR , fakeDamage , RENDER_MODE_TO_BUFFER , m_buffer . m_buffer ) )
2023-11-24 10:54:21 +00:00
return false ;
2023-07-20 12:42:25 +02:00
2024-12-03 18:58:24 +00:00
g_pHyprOpenGL - > clear ( CHyprColor ( 0 , 0 , 0 , 1.0 ) ) ;
2025-04-08 19:39:53 +02:00
if ( PERM = = PERMISSION_RULE_ALLOW_MODE_ALLOW ) {
2025-05-27 20:10:22 +05:00
if ( ! m_window - > m_windowData . noScreenShare . valueOrDefault ( ) ) {
g_pHyprRenderer - > m_bBlockSurfaceFeedback = g_pHyprRenderer - > shouldRenderWindow ( m_window ) ; // block the feedback to avoid spamming the surface if it's visible
g_pHyprRenderer - > renderWindow ( m_window , PMONITOR , now , false , RENDER_PASS_ALL , true , true ) ;
g_pHyprRenderer - > m_bBlockSurfaceFeedback = false ;
}
2025-04-08 19:39:53 +02:00
if ( overlayCursor )
2025-05-04 19:21:36 +02:00
g_pPointerManager - > renderSoftwareCursorsFor ( PMONITOR - > m_self . lock ( ) , now , fakeDamage , g_pInputManager - > getMouseCoordsInternal ( ) - m_window - > m_realPosition - > value ( ) ) ;
2025-04-08 19:39:53 +02:00
} else if ( PERM = = PERMISSION_RULE_ALLOW_MODE_DENY ) {
2025-05-05 23:44:49 +02:00
CBox texbox = CBox { PMONITOR - > m_transformedSize / 2.F , g_pHyprOpenGL - > m_screencopyDeniedTexture - > m_size } . translate ( - g_pHyprOpenGL - > m_screencopyDeniedTexture - > m_size / 2.F ) ;
g_pHyprOpenGL - > renderTexture ( g_pHyprOpenGL - > m_screencopyDeniedTexture , texbox , 1 ) ;
2025-04-08 19:39:53 +02:00
}
2023-07-20 12:42:25 +02:00
2025-05-05 23:44:49 +02:00
g_pHyprOpenGL - > m_renderData . blockScreenShader = true ;
2023-11-24 10:54:21 +00:00
g_pHyprRenderer - > endRender ( ) ;
2024-07-27 10:02:02 -05:00
if ( overlayCursor ) {
2025-04-30 23:45:20 +02:00
g_pPointerManager - > unlockSoftwareForMonitor ( PMONITOR - > m_self . lock ( ) ) ;
g_pPointerManager - > damageCursor ( PMONITOR - > m_self . lock ( ) ) ;
2024-07-27 10:02:02 -05:00
}
2023-07-20 12:42:25 +02:00
return true ;
2022-12-05 17:05:15 +00:00
}
2025-01-12 09:09:02 -08:00
bool CToplevelExportFrame : : shouldOverlayCursor ( ) const {
2025-05-04 19:21:36 +02:00
if ( ! m_cursorOverlayRequested )
2025-01-12 09:09:02 -08:00
return false ;
2025-05-02 17:07:20 +02:00
auto pointerSurfaceResource = g_pSeatManager - > m_state . pointerFocus . lock ( ) ;
2025-01-12 09:09:02 -08:00
if ( ! pointerSurfaceResource )
return false ;
auto pointerSurface = CWLSurface : : fromResource ( pointerSurfaceResource ) ;
2025-05-04 19:21:36 +02:00
return pointerSurface & & pointerSurface - > getWindow ( ) = = m_window ;
2025-01-12 09:09:02 -08:00
}
2024-07-27 10:02:02 -05:00
bool CToplevelExportFrame : : good ( ) {
2025-05-04 19:21:36 +02:00
return m_resource - > resource ( ) ;
2024-07-27 10:02:02 -05:00
}
CToplevelExportProtocol : : CToplevelExportProtocol ( const wl_interface * iface , const int & ver , const std : : string & name ) : IWaylandProtocol ( iface , ver , name ) {
;
}
void CToplevelExportProtocol : : bindManager ( wl_client * client , void * data , uint32_t ver , uint32_t id ) {
2025-05-04 19:21:36 +02:00
const auto CLIENT = m_clients . emplace_back ( makeShared < CToplevelExportClient > ( makeShared < CHyprlandToplevelExportManagerV1 > ( client , ver , id ) ) ) ;
2024-07-27 10:02:02 -05:00
if ( ! CLIENT - > good ( ) ) {
LOGM ( LOG , " Failed to bind client! (out of memory) " ) ;
wl_client_post_no_memory ( client ) ;
2025-05-04 19:21:36 +02:00
m_clients . pop_back ( ) ;
2024-07-27 10:02:02 -05:00
return ;
}
2025-05-04 19:21:36 +02:00
CLIENT - > m_self = CLIENT ;
2024-07-27 10:02:02 -05:00
LOGM ( LOG , " Bound client successfully! " ) ;
}
void CToplevelExportProtocol : : destroyResource ( CToplevelExportClient * client ) {
2025-05-04 19:21:36 +02:00
std : : erase_if ( m_clients , [ & ] ( const auto & other ) { return other . get ( ) = = client ; } ) ;
std : : erase_if ( m_frames , [ & ] ( const auto & other ) { return other - > m_client . get ( ) = = client ; } ) ;
std : : erase_if ( m_framesAwaitingWrite , [ & ] ( const auto & other ) { return ! other | | other - > m_client . get ( ) = = client ; } ) ;
2024-07-27 10:02:02 -05:00
}
void CToplevelExportProtocol : : destroyResource ( CToplevelExportFrame * frame ) {
2025-05-04 19:21:36 +02:00
std : : erase_if ( m_frames , [ & ] ( const auto & other ) { return other . get ( ) = = frame ; } ) ;
std : : erase_if ( m_framesAwaitingWrite , [ & ] ( const auto & other ) { return ! other | | other . get ( ) = = frame ; } ) ;
2024-07-27 10:02:02 -05:00
}
2024-10-19 23:03:29 +01:00
void CToplevelExportProtocol : : onOutputCommit ( PHLMONITOR pMonitor ) {
2025-05-04 19:21:36 +02:00
if ( m_framesAwaitingWrite . empty ( ) )
2024-07-27 10:02:02 -05:00
return ; // nothing to share
std : : vector < WP < CToplevelExportFrame > > framesToRemove ;
2025-01-19 10:38:42 +00:00
// reserve number of elements to avoid reallocations
2025-05-04 19:21:36 +02:00
framesToRemove . reserve ( m_framesAwaitingWrite . size ( ) ) ;
2024-07-27 10:02:02 -05:00
// share frame if correct output
2025-05-04 19:21:36 +02:00
for ( auto const & f : m_framesAwaitingWrite ) {
2024-10-07 19:23:48 -05:00
if ( ! f )
continue ;
2025-04-08 19:39:53 +02:00
// check permissions
2025-05-04 19:21:36 +02:00
const auto PERM = g_pDynamicPermissionManager - > clientPermissionMode ( f - > m_resource - > client ( ) , PERMISSION_TYPE_SCREENCOPY ) ;
2025-04-08 19:39:53 +02:00
if ( PERM = = PERMISSION_RULE_ALLOW_MODE_PENDING )
continue ; // pending an answer, don't do anything yet.
2025-05-04 19:21:36 +02:00
if ( ! validMapped ( f - > m_window ) ) {
2024-10-07 19:23:48 -05:00
framesToRemove . emplace_back ( f ) ;
2024-07-27 10:02:02 -05:00
continue ;
}
2025-05-04 19:21:36 +02:00
if ( ! f - > m_window )
2024-10-07 19:23:48 -05:00
continue ;
2025-05-04 19:21:36 +02:00
const auto PWINDOW = f - > m_window ;
2024-07-27 10:02:02 -05:00
2025-04-28 22:25:22 +02:00
if ( pMonitor ! = PWINDOW - > m_monitor . lock ( ) )
2024-07-27 10:02:02 -05:00
continue ;
2025-04-28 22:25:22 +02:00
CBox geometry = { PWINDOW - > m_realPosition - > value ( ) . x , PWINDOW - > m_realPosition - > value ( ) . y , PWINDOW - > m_realSize - > value ( ) . x , PWINDOW - > m_realSize - > value ( ) . y } ;
2024-07-27 10:02:02 -05:00
2025-04-30 23:45:20 +02:00
if ( geometry . intersection ( { pMonitor - > m_position , pMonitor - > m_size } ) . empty ( ) )
2024-07-27 10:02:02 -05:00
continue ;
f - > share ( ) ;
2025-05-04 19:21:36 +02:00
f - > m_client - > m_lastFrame . reset ( ) ;
+ + f - > m_client - > m_frameCounter ;
2024-07-27 10:02:02 -05:00
framesToRemove . push_back ( f ) ;
}
2024-08-26 20:24:30 +02:00
for ( auto const & f : framesToRemove ) {
2025-05-04 19:21:36 +02:00
std : : erase ( m_framesAwaitingWrite , f ) ;
2024-07-27 10:02:02 -05:00
}
}
void CToplevelExportProtocol : : onWindowUnmap ( PHLWINDOW pWindow ) {
2025-05-04 19:21:36 +02:00
for ( auto const & f : m_frames ) {
if ( f - > m_window = = pWindow )
f - > m_window . reset ( ) ;
2022-12-05 17:05:15 +00:00
}
2023-02-16 22:51:34 +00:00
}