2022-07-27 12:32:00 +02:00
# include "Monitor.hpp"
2024-02-15 07:22:20 -07:00
# include "MiscFunctions.hpp"
2024-07-21 13:09:54 +02:00
# include "math/Math.hpp"
2024-08-06 14:52:19 +01:00
# include "sync/SyncReleaser.hpp"
2022-07-27 12:32:00 +02:00
# include "../Compositor.hpp"
2024-03-03 18:39:20 +00:00
# include "../config/ConfigValue.hpp"
2024-04-22 18:21:03 +01:00
# include "../protocols/GammaControl.hpp"
2024-05-03 22:34:10 +01:00
# include "../devices/ITouch.hpp"
2024-05-09 21:47:21 +01:00
# include "../protocols/LayerShell.hpp"
2024-05-10 02:27:54 +01:00
# include "../protocols/PresentationTime.hpp"
2024-07-21 13:09:54 +02:00
# include "../protocols/DRMLease.hpp"
2024-08-06 14:52:19 +01:00
# include "../protocols/DRMSyncobj.hpp"
2024-07-21 13:09:54 +02:00
# include "../protocols/core/Output.hpp"
2024-10-19 16:21:47 +01:00
# include "../protocols/Screencopy.hpp"
# include "../protocols/ToplevelExport.hpp"
2024-06-19 10:24:28 -04:00
# include "../managers/PointerManager.hpp"
2024-10-05 00:44:30 +01:00
# include "../managers/eventLoop/EventLoopManager.hpp"
2024-07-21 13:09:54 +02:00
# include "../protocols/core/Compositor.hpp"
# include "sync/SyncTimeline.hpp"
# include <aquamarine/output/Output.hpp>
2024-10-12 03:29:51 +03:00
# include "debug/Log.hpp"
2024-06-11 17:17:45 +02:00
# include <hyprutils/string/String.hpp>
2024-09-21 00:33:48 +01:00
# include <hyprutils/utils/ScopeGuard.hpp>
2024-06-11 17:17:45 +02:00
using namespace Hyprutils : : String ;
2024-09-21 00:33:48 +01:00
using namespace Hyprutils : : Utils ;
2024-03-03 18:39:20 +00:00
2023-03-24 19:23:16 +00:00
int ratHandler ( void * data ) {
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > renderMonitor ( ( ( CMonitor * ) data ) - > self . lock ( ) ) ;
2023-03-24 19:23:16 +00:00
return 1 ;
}
2024-08-19 18:44:22 +02:00
CMonitor : : CMonitor ( SP < Aquamarine : : IOutput > output_ ) : state ( this ) , output ( output_ ) {
2024-06-19 18:25:20 +02:00
;
2023-04-07 12:18:40 +01:00
}
CMonitor : : ~ CMonitor ( ) {
2024-04-22 18:21:03 +01:00
events . destroy . emit ( ) ;
2023-04-07 12:18:40 +01:00
}
2024-07-21 13:09:54 +02:00
void CMonitor : : onConnect ( bool noRule ) {
2024-11-17 20:34:03 +01:00
EMIT_HOOK_EVENT ( " preMonitorAdded " , self . lock ( ) ) ;
2024-10-05 14:41:44 +01:00
CScopeGuard x = { [ ] ( ) { g_pCompositor - > arrangeMonitors ( ) ; } } ;
2024-05-10 02:27:54 +01:00
2024-07-21 13:09:54 +02:00
if ( output - > supportsExplicit ) {
inTimeline = CSyncTimeline : : create ( output - > getBackend ( ) - > drmFD ( ) ) ;
outTimeline = CSyncTimeline : : create ( output - > getBackend ( ) - > drmFD ( ) ) ;
}
2024-10-19 16:21:47 +01:00
listeners . frame = output - > events . frame . registerListener ( [ this ] ( std : : any d ) { onMonitorFrame ( ) ; } ) ;
listeners . commit = output - > events . commit . registerListener ( [ this ] ( std : : any d ) {
if ( true ) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER
2024-10-19 23:03:29 +01:00
PROTO : : screencopy - > onOutputCommit ( self . lock ( ) ) ;
PROTO : : toplevelExport - > onOutputCommit ( self . lock ( ) ) ;
2024-10-19 16:21:47 +01:00
}
} ) ;
2024-07-21 13:09:54 +02:00
listeners . needsFrame =
2024-10-19 23:03:29 +01:00
output - > events . needsFrame . registerListener ( [ this ] ( std : : any d ) { g_pCompositor - > scheduleFrameForMonitor ( self . lock ( ) , Aquamarine : : IOutput : : AQ_SCHEDULE_NEEDS_FRAME ) ; } ) ;
2024-08-19 18:44:22 +02:00
2024-07-21 13:09:54 +02:00
listeners . presented = output - > events . present . registerListener ( [ this ] ( std : : any d ) {
auto E = std : : any_cast < Aquamarine : : IOutput : : SPresentEvent > ( d ) ;
2024-08-30 15:50:25 +02:00
PROTO : : presentation - > onPresented ( self . lock ( ) , E . when , E . refresh , E . seq , E . flags ) ;
2024-07-21 13:09:54 +02:00
} ) ;
2024-08-19 18:44:22 +02:00
listeners . destroy = output - > events . destroy . registerListener ( [ this ] ( std : : any d ) {
Debug : : log ( LOG , " Destroy called for monitor {} " , szName ) ;
onDisconnect ( true ) ;
output = nullptr ;
m_bRenderingInitPassed = false ;
Debug : : log ( LOG , " Removing monitor {} from realMonitors " , szName ) ;
2024-10-26 02:06:13 +01:00
std : : erase_if ( g_pCompositor - > m_vRealMonitors , [ & ] ( PHLMONITOR & el ) { return el . get ( ) = = this ; } ) ;
2024-08-19 18:44:22 +02:00
} ) ;
2024-07-21 13:09:54 +02:00
listeners . state = output - > events . state . registerListener ( [ this ] ( std : : any d ) {
auto E = std : : any_cast < Aquamarine : : IOutput : : SStateEvent > ( d ) ;
if ( E . size = = Vector2D { } ) {
// an indication to re-set state
// we can't do much for createdByUser displays I think
if ( createdByUser )
return ;
Debug : : log ( LOG , " Reapplying monitor rule for {} from a state request " , szName ) ;
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > applyMonitorRule ( self . lock ( ) , & activeMonitorRule , true ) ;
2024-07-21 13:09:54 +02:00
return ;
}
2024-05-10 02:27:54 +01:00
2024-07-21 13:09:54 +02:00
if ( ! createdByUser )
return ;
const auto SIZE = E . size ;
forceSize = SIZE ;
SMonitorRule rule = activeMonitorRule ;
rule . resolution = SIZE ;
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > applyMonitorRule ( self . lock ( ) , & rule ) ;
2024-07-21 13:09:54 +02:00
} ) ;
tearingState . canTear = output - > getBackend ( ) - > type ( ) = = Aquamarine : : AQ_BACKEND_DRM ;
2023-09-28 21:48:33 +01:00
2022-11-19 13:14:55 +00:00
if ( m_bEnabled ) {
2024-08-06 14:52:19 +01:00
output - > state - > resetExplicitFences ( ) ;
2024-07-21 13:09:54 +02:00
output - > state - > setEnabled ( true ) ;
2024-01-28 01:57:13 +00:00
state . commit ( ) ;
2022-08-03 16:05:25 +02:00
return ;
2022-11-19 13:14:55 +00:00
}
2022-08-03 16:05:25 +02:00
2022-08-10 13:44:04 +02:00
szName = output - > name ;
2024-07-21 13:09:54 +02:00
szDescription = output - > description ;
2023-11-30 02:48:10 +01:00
// remove comma character from description. This allow monitor specific rules to work on monitor with comma on their description
2024-03-05 21:41:51 +08:00
std : : erase ( szDescription , ' , ' ) ;
2023-11-30 02:48:10 +01:00
2024-02-15 07:22:20 -07:00
// field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix
2024-07-21 13:09:54 +02:00
szShortDescription = trim ( std : : format ( " {} {} {} " , output - > make , output - > model , output - > serial ) ) ;
2024-03-05 21:41:51 +08:00
std : : erase ( szShortDescription , ' , ' ) ;
2024-02-15 07:22:20 -07:00
2024-07-21 13:09:54 +02:00
if ( output - > getBackend ( ) - > type ( ) ! = Aquamarine : : AQ_BACKEND_DRM )
createdByUser = true ; // should be true. WL and Headless backends should be addable / removable
2022-11-05 18:04:44 +00:00
2022-07-27 12:32:00 +02:00
// get monitor rule that matches
2024-09-26 11:10:53 +01:00
SMonitorRule monitorRule = g_pConfigManager - > getMonitorRuleFor ( self . lock ( ) ) ;
2022-07-27 12:32:00 +02:00
// if it's disabled, disable and ignore
if ( monitorRule . disabled ) {
2022-08-10 13:31:58 +02:00
2024-08-06 14:52:19 +01:00
output - > state - > resetExplicitFences ( ) ;
2024-07-21 13:09:54 +02:00
output - > state - > setEnabled ( false ) ;
2022-09-25 20:07:48 +02:00
2024-01-28 01:57:13 +00:00
if ( ! state . commit ( ) )
2023-09-06 12:51:36 +02:00
Debug : : log ( ERR , " Couldn't commit disabled state on output {} " , output - > name ) ;
2022-08-10 13:44:04 +02:00
2022-08-10 13:31:58 +02:00
m_bEnabled = false ;
2024-07-21 13:09:54 +02:00
listeners . frame . reset ( ) ;
2022-07-27 12:32:00 +02:00
return ;
}
2024-07-21 13:09:54 +02:00
if ( output - > nonDesktop ) {
2022-08-27 17:10:13 -04:00
Debug : : log ( LOG , " Not configuring non-desktop output " ) ;
2024-07-21 13:09:54 +02:00
if ( PROTO : : lease )
PROTO : : lease - > offer ( self . lock ( ) ) ;
2022-08-27 17:10:13 -04:00
2024-07-21 13:09:54 +02:00
return ;
2022-08-10 13:44:04 +02:00
}
2024-10-26 02:06:13 +01:00
PHLMONITOR * thisWrapper = nullptr ;
2022-08-03 17:42:19 +02:00
2023-11-01 18:53:36 +00:00
// find the wrap
for ( auto & m : g_pCompositor - > m_vRealMonitors ) {
if ( m - > ID = = ID ) {
thisWrapper = & m ;
break ;
2022-08-03 17:42:19 +02:00
}
}
2023-11-01 18:53:36 +00:00
RASSERT ( thisWrapper - > get ( ) , " CMonitor::onConnect: Had no wrapper??? " ) ;
if ( std : : find_if ( g_pCompositor - > m_vMonitors . begin ( ) , g_pCompositor - > m_vMonitors . end ( ) , [ & ] ( auto & other ) { return other . get ( ) = = this ; } ) = = g_pCompositor - > m_vMonitors . end ( ) )
g_pCompositor - > m_vMonitors . push_back ( * thisWrapper ) ;
2022-09-25 20:07:48 +02:00
2022-07-27 12:32:00 +02:00
m_bEnabled = true ;
2024-08-06 14:52:19 +01:00
output - > state - > resetExplicitFences ( ) ;
2024-07-21 13:09:54 +02:00
output - > state - > setEnabled ( true ) ;
2022-07-27 12:32:00 +02:00
// set mode, also applies
if ( ! noRule )
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > applyMonitorRule ( self . lock ( ) , & monitorRule , true ) ;
2022-07-27 12:32:00 +02:00
2024-01-28 01:57:13 +00:00
if ( ! state . commit ( ) )
2024-07-21 13:09:54 +02:00
Debug : : log ( WARN , " state.commit() failed in CMonitor::onCommit " ) ;
2023-11-01 18:53:36 +00:00
2024-06-19 18:25:20 +02:00
damage . setSize ( vecTransformedSize ) ;
2023-04-07 12:18:40 +01:00
2024-07-22 11:06:11 -05:00
Debug : : log ( LOG , " Added new monitor with name {} at {:j0} with size {:j0}, pointer {:x} " , output - > name , vecPosition , vecPixelSize , ( uintptr_t ) output . get ( ) ) ;
2022-07-27 12:32:00 +02:00
2022-09-13 15:25:42 +02:00
setupDefaultWS ( monitorRule ) ;
2022-07-27 12:32:00 +02:00
2024-08-26 20:24:30 +02:00
for ( auto const & ws : g_pCompositor - > m_vWorkspaces ) {
2024-04-05 16:56:46 +01:00
if ( ! valid ( ws ) )
continue ;
2023-05-06 16:49:46 +01:00
if ( ws - > m_szLastMonitor = = szName | | g_pCompositor - > m_vMonitors . size ( ) = = 1 /* avoid lost workspaces on recover */ ) {
2024-10-19 23:03:29 +01:00
g_pCompositor - > moveWorkspaceToMonitor ( ws , self . lock ( ) ) ;
2023-05-03 15:15:56 +01:00
ws - > startAnim ( true , true , true ) ;
ws - > m_szLastMonitor = " " ;
}
}
2022-07-27 12:32:00 +02:00
scale = monitorRule . scale ;
2023-04-04 22:54:35 +01:00
if ( scale < 0.1 )
scale = getDefaultScale ( ) ;
2022-07-27 12:32:00 +02:00
2022-12-16 17:17:31 +00:00
forceFullFrames = 3 ; // force 3 full frames to make sure there is no blinking due to double-buffering.
2022-07-27 12:32:00 +02:00
//
2024-06-09 22:28:51 +02:00
if ( ! activeMonitorRule . mirrorOf . empty ( ) )
setMirror ( activeMonitorRule . mirrorOf ) ;
2022-12-16 17:17:31 +00:00
if ( ! g_pCompositor - > m_pLastMonitor ) // set the last monitor if it isnt set yet
2024-10-19 23:03:29 +01:00
g_pCompositor - > setActiveMonitor ( self . lock ( ) ) ;
2022-07-27 12:32:00 +02:00
2022-08-10 21:54:09 +02:00
g_pHyprRenderer - > arrangeLayersForMonitor ( ID ) ;
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( ID ) ;
2022-10-22 21:45:17 +01:00
// ensure VRR (will enable if necessary)
2024-10-19 23:03:29 +01:00
g_pConfigManager - > ensureVRR ( self . lock ( ) ) ;
2022-12-12 20:51:20 +00:00
// verify last mon valid
bool found = false ;
2024-08-26 17:25:39 +02:00
for ( auto const & m : g_pCompositor - > m_vMonitors ) {
2024-05-05 22:18:10 +01:00
if ( m = = g_pCompositor - > m_pLastMonitor ) {
2022-12-12 20:51:20 +00:00
found = true ;
break ;
}
}
if ( ! found )
2024-10-19 23:03:29 +01:00
g_pCompositor - > setActiveMonitor ( self . lock ( ) ) ;
2023-03-24 19:23:16 +00:00
renderTimer = wl_event_loop_add_timer ( g_pCompositor - > m_sWLEventLoop , ratHandler , this ) ;
2023-11-01 18:53:36 +00:00
2024-10-19 23:03:29 +01:00
g_pCompositor - > scheduleFrameForMonitor ( self . lock ( ) , Aquamarine : : IOutput : : AQ_SCHEDULE_NEW_MONITOR ) ;
2024-04-22 18:21:03 +01:00
2024-10-19 23:03:29 +01:00
PROTO : : gamma - > applyGammaToState ( self . lock ( ) ) ;
2024-04-22 18:21:03 +01:00
events . connect . emit ( ) ;
2024-09-15 22:03:42 +02:00
g_pEventManager - > postEvent ( SHyprIPCEvent { " monitoradded " , szName } ) ;
g_pEventManager - > postEvent ( SHyprIPCEvent { " monitoraddedv2 " , std : : format ( " {},{},{} " , ID , szName , szShortDescription ) } ) ;
2024-10-19 23:03:29 +01:00
EMIT_HOOK_EVENT ( " monitorAdded " , self . lock ( ) ) ;
2022-07-27 12:32:00 +02:00
}
2023-11-12 14:14:05 +01:00
void CMonitor : : onDisconnect ( bool destroy ) {
2024-11-17 20:34:03 +01:00
EMIT_HOOK_EVENT ( " preMonitorRemoved " , self . lock ( ) ) ;
2024-10-05 14:37:12 +01:00
CScopeGuard x = { [ this ] ( ) {
if ( g_pCompositor - > m_bIsShuttingDown )
return ;
g_pEventManager - > postEvent ( SHyprIPCEvent { " monitorremoved " , szName } ) ;
2024-10-19 23:03:29 +01:00
EMIT_HOOK_EVENT ( " monitorRemoved " , self . lock ( ) ) ;
2024-10-05 14:41:44 +01:00
g_pCompositor - > arrangeMonitors ( ) ;
2024-10-05 14:37:12 +01:00
} } ;
2022-08-03 16:05:25 +02:00
2023-03-24 19:23:16 +00:00
if ( renderTimer ) {
wl_event_source_remove ( renderTimer ) ;
renderTimer = nullptr ;
}
2022-10-06 18:43:50 +01:00
if ( ! m_bEnabled | | g_pCompositor - > m_bIsShuttingDown )
2022-08-03 16:05:25 +02:00
return ;
2023-09-06 12:51:36 +02:00
Debug : : log ( LOG , " onDisconnect called for {} " , output - > name ) ;
2022-11-19 13:01:32 +00:00
2024-04-22 18:21:03 +01:00
events . disconnect . emit ( ) ;
2022-07-27 12:32:00 +02:00
// Cleanup everything. Move windows back, snap cursor, shit.
2024-10-19 23:03:29 +01:00
PHLMONITOR BACKUPMON = nullptr ;
2024-08-26 20:24:30 +02:00
for ( auto const & m : g_pCompositor - > m_vMonitors ) {
2022-07-27 12:32:00 +02:00
if ( m . get ( ) ! = this ) {
2024-10-19 23:03:29 +01:00
BACKUPMON = m ;
2022-07-27 12:32:00 +02:00
break ;
}
}
2022-09-13 15:25:42 +02:00
// remove mirror
if ( pMirrorOf ) {
2024-10-19 23:03:29 +01:00
pMirrorOf - > mirrors . erase ( std : : find_if ( pMirrorOf - > mirrors . begin ( ) , pMirrorOf - > mirrors . end ( ) , [ & ] ( const auto & other ) { return other = = self ; } ) ) ;
2024-06-19 10:24:28 -04:00
// unlock software for mirrored monitor
2024-10-19 23:03:29 +01:00
g_pPointerManager - > unlockSoftwareForMonitor ( pMirrorOf . lock ( ) ) ;
pMirrorOf . reset ( ) ;
2022-09-13 15:25:42 +02:00
}
if ( ! mirrors . empty ( ) ) {
2024-08-26 20:24:30 +02:00
for ( auto const & m : mirrors ) {
2022-09-13 15:25:42 +02:00
m - > setMirror ( " " ) ;
}
g_pConfigManager - > m_bWantsMonitorReload = true ;
}
2024-07-21 13:09:54 +02:00
listeners . frame . reset ( ) ;
listeners . presented . reset ( ) ;
listeners . needsFrame . reset ( ) ;
listeners . commit . reset ( ) ;
2022-07-27 12:32:00 +02:00
2023-01-02 16:16:28 +01:00
for ( size_t i = 0 ; i < 4 ; + + i ) {
2024-08-26 17:25:39 +02:00
for ( auto const & ls : m_aLayerSurfaceLayers [ i ] ) {
2023-01-23 18:23:44 +00:00
if ( ls - > layerSurface & & ! ls - > fadingOut )
2024-05-09 21:47:21 +01:00
ls - > layerSurface - > sendClosed ( ) ;
2023-01-02 16:16:28 +01:00
}
2023-01-22 17:03:25 +01:00
m_aLayerSurfaceLayers [ i ] . clear ( ) ;
2023-01-02 16:16:28 +01:00
}
2023-09-06 21:45:37 +02:00
Debug : : log ( LOG , " Removed monitor {}! " , szName ) ;
2023-01-19 16:27:04 +01:00
2022-08-10 21:54:09 +02:00
if ( ! BACKUPMON ) {
Debug : : log ( WARN , " Unplugged last monitor, entering an unsafe state. Good luck my friend. " ) ;
2023-09-24 18:04:38 +01:00
g_pCompositor - > enterUnsafeState ( ) ;
2022-08-10 21:54:09 +02:00
}
2023-11-01 18:53:36 +00:00
m_bEnabled = false ;
m_bRenderingInitPassed = false ;
2023-09-01 22:03:56 +02:00
if ( BACKUPMON ) {
// snap cursor
2024-05-05 22:18:10 +01:00
g_pCompositor - > warpCursorTo ( BACKUPMON - > vecPosition + BACKUPMON - > vecTransformedSize / 2.F , true ) ;
2022-07-27 12:32:00 +02:00
2023-09-01 22:03:56 +02:00
// move workspaces
2024-04-02 20:32:39 +01:00
std : : deque < PHLWORKSPACE > wspToMove ;
2024-08-26 20:24:30 +02:00
for ( auto const & w : g_pCompositor - > m_vWorkspaces ) {
2024-10-27 18:45:38 +00:00
if ( w - > m_pMonitor = = self | | ! w - > m_pMonitor )
2024-04-02 20:32:39 +01:00
wspToMove . push_back ( w ) ;
2022-07-27 12:32:00 +02:00
}
2024-08-26 20:24:30 +02:00
for ( auto const & w : wspToMove ) {
2023-09-01 22:03:56 +02:00
w - > m_szLastMonitor = szName ;
g_pCompositor - > moveWorkspaceToMonitor ( w , BACKUPMON ) ;
w - > startAnim ( true , true , true ) ;
}
} else {
2024-06-08 10:07:59 +02:00
g_pCompositor - > m_pLastFocus . reset ( ) ;
2024-04-27 12:43:12 +01:00
g_pCompositor - > m_pLastWindow . reset ( ) ;
2024-05-05 22:18:10 +01:00
g_pCompositor - > m_pLastMonitor . reset ( ) ;
2022-07-27 12:32:00 +02:00
}
2024-04-10 17:21:39 +01:00
if ( activeWorkspace )
activeWorkspace - > m_bVisible = false ;
2024-04-02 20:32:39 +01:00
activeWorkspace . reset ( ) ;
2022-07-27 12:32:00 +02:00
2024-08-06 14:52:19 +01:00
output - > state - > resetExplicitFences ( ) ;
2024-11-01 07:30:26 -05:00
output - > state - > setAdaptiveSync ( false ) ;
2024-07-21 13:09:54 +02:00
output - > state - > setEnabled ( false ) ;
2022-07-27 12:32:00 +02:00
2024-01-28 01:57:13 +00:00
if ( ! state . commit ( ) )
2024-07-21 13:09:54 +02:00
Debug : : log ( WARN , " state.commit() failed in CMonitor::onDisconnect " ) ;
2022-07-27 12:32:00 +02:00
2024-10-19 23:03:29 +01:00
if ( g_pCompositor - > m_pLastMonitor = = self )
g_pCompositor - > setActiveMonitor ( BACKUPMON ? BACKUPMON : g_pCompositor - > m_pUnsafeOutput . lock ( ) ) ;
2022-12-21 15:17:24 +00:00
2024-10-19 23:03:29 +01:00
if ( g_pHyprRenderer - > m_pMostHzMonitor = = self ) {
int mostHz = 0 ;
PHLMONITOR pMonitorMostHz = nullptr ;
2022-12-22 12:15:00 +00:00
2024-08-26 20:24:30 +02:00
for ( auto const & m : g_pCompositor - > m_vMonitors ) {
2024-10-19 23:03:29 +01:00
if ( m - > refreshRate > mostHz & & m ! = self ) {
pMonitorMostHz = m ;
2022-12-22 12:15:00 +00:00
mostHz = m - > refreshRate ;
}
}
g_pHyprRenderer - > m_pMostHzMonitor = pMonitorMostHz ;
}
2024-10-26 02:06:13 +01:00
std : : erase_if ( g_pCompositor - > m_vMonitors , [ & ] ( PHLMONITOR & el ) { return el . get ( ) = = this ; } ) ;
2022-07-27 12:32:00 +02:00
}
2022-08-23 16:07:47 +02:00
2023-04-07 16:31:51 +01:00
void CMonitor : : addDamage ( const pixman_region32_t * rg ) {
2024-05-09 22:23:01 +01:00
static auto PZOOMFACTOR = CConfigValue < Hyprlang : : FLOAT > ( " cursor:zoom_factor " ) ;
2024-10-19 23:03:29 +01:00
if ( * PZOOMFACTOR ! = 1.f & & g_pCompositor - > getMonitorFromCursor ( ) = = self ) {
2024-06-19 18:25:20 +02:00
damage . damageEntire ( ) ;
2024-10-19 23:03:29 +01:00
g_pCompositor - > scheduleFrameForMonitor ( self . lock ( ) , Aquamarine : : IOutput : : AQ_SCHEDULE_DAMAGE ) ;
2024-06-19 18:25:20 +02:00
} else if ( damage . damage ( rg ) )
2024-10-19 23:03:29 +01:00
g_pCompositor - > scheduleFrameForMonitor ( self . lock ( ) , Aquamarine : : IOutput : : AQ_SCHEDULE_DAMAGE ) ;
2022-08-23 16:07:47 +02:00
}
2023-07-19 20:09:49 +02:00
void CMonitor : : addDamage ( const CRegion * rg ) {
addDamage ( const_cast < CRegion * > ( rg ) - > pixman ( ) ) ;
}
2023-11-04 17:03:05 +00:00
void CMonitor : : addDamage ( const CBox * box ) {
2024-05-09 22:23:01 +01:00
static auto PZOOMFACTOR = CConfigValue < Hyprlang : : FLOAT > ( " cursor:zoom_factor " ) ;
2024-10-19 23:03:29 +01:00
if ( * PZOOMFACTOR ! = 1.f & & g_pCompositor - > getMonitorFromCursor ( ) = = self ) {
2024-06-19 18:25:20 +02:00
damage . damageEntire ( ) ;
2024-10-19 23:03:29 +01:00
g_pCompositor - > scheduleFrameForMonitor ( self . lock ( ) , Aquamarine : : IOutput : : AQ_SCHEDULE_DAMAGE ) ;
2023-04-16 14:48:38 +01:00
}
2024-06-19 18:25:20 +02:00
if ( damage . damage ( * box ) )
2024-10-19 23:03:29 +01:00
g_pCompositor - > scheduleFrameForMonitor ( self . lock ( ) , Aquamarine : : IOutput : : AQ_SCHEDULE_DAMAGE ) ;
2022-08-23 16:07:47 +02:00
}
2022-09-13 15:25:42 +02:00
2024-06-14 14:45:32 +03:00
bool CMonitor : : shouldSkipScheduleFrameOnMouseEvent ( ) {
static auto PNOBREAK = CConfigValue < Hyprlang : : INT > ( " cursor:no_break_fs_vrr " ) ;
static auto PMINRR = CConfigValue < Hyprlang : : INT > ( " cursor:min_refresh_rate " ) ;
// skip scheduling extra frames for fullsreen apps with vrr
2024-07-21 13:09:54 +02:00
bool shouldSkip =
2024-07-31 17:55:52 +00:00
* PNOBREAK & & output - > state - > state ( ) . adaptiveSync & & activeWorkspace & & activeWorkspace - > m_bHasFullscreenWindow & & activeWorkspace - > m_efFullscreenMode = = FSMODE_FULLSCREEN ;
2024-06-14 14:45:32 +03:00
// keep requested minimum refresh rate
2024-10-12 03:29:51 +03:00
if ( shouldSkip & & * PMINRR & & lastPresentationTimer . getMillis ( ) > 1000.0f / * PMINRR ) {
2024-06-14 14:45:32 +03:00
// damage whole screen because some previous cursor box damages were skipped
2024-06-19 18:25:20 +02:00
damage . damageEntire ( ) ;
2024-06-14 14:45:32 +03:00
return false ;
}
return shouldSkip ;
}
2022-09-13 15:25:42 +02:00
bool CMonitor : : isMirror ( ) {
return pMirrorOf ! = nullptr ;
}
2024-02-27 23:11:59 +01:00
bool CMonitor : : matchesStaticSelector ( const std : : string & selector ) const {
if ( selector . starts_with ( " desc: " ) ) {
// match by description
const auto DESCRIPTIONSELECTOR = selector . substr ( 5 ) ;
2024-03-05 21:41:51 +08:00
return DESCRIPTIONSELECTOR = = szShortDescription | | DESCRIPTIONSELECTOR = = szDescription ;
2024-02-27 23:11:59 +01:00
} else {
// match by selector
return szName = = selector ;
}
}
2024-08-08 21:01:50 +02:00
WORKSPACEID CMonitor : : findAvailableDefaultWS ( ) {
for ( WORKSPACEID i = 1 ; i < LONG_MAX ; + + i ) {
2022-12-09 17:17:02 +00:00
if ( g_pCompositor - > getWorkspaceByID ( i ) )
continue ;
if ( const auto BOUND = g_pConfigManager - > getBoundMonitorStringForWS ( std : : to_string ( i ) ) ; ! BOUND . empty ( ) & & BOUND ! = szName )
continue ;
2022-12-16 17:17:31 +00:00
2022-12-09 17:17:02 +00:00
return i ;
}
2024-08-08 21:01:50 +02:00
return LONG_MAX ; // shouldn't be reachable
2022-12-09 17:17:02 +00:00
}
2022-09-13 15:25:42 +02:00
void CMonitor : : setupDefaultWS ( const SMonitorRule & monitorRule ) {
// Workspace
std : : string newDefaultWorkspaceName = " " ;
2024-06-23 00:52:42 +03:00
int64_t wsID = WORKSPACE_INVALID ;
if ( g_pConfigManager - > getDefaultWorkspaceFor ( szName ) . empty ( ) )
wsID = findAvailableDefaultWS ( ) ;
else {
const auto ws = getWorkspaceIDNameFromString ( g_pConfigManager - > getDefaultWorkspaceFor ( szName ) ) ;
wsID = ws . id ;
newDefaultWorkspaceName = ws . name ;
}
2022-09-13 15:25:42 +02:00
2024-06-23 00:52:42 +03:00
if ( wsID = = WORKSPACE_INVALID | | ( wsID > = SPECIAL_WORKSPACE_START & & wsID < = - 2 ) ) {
wsID = g_pCompositor - > m_vWorkspaces . size ( ) + 1 ;
newDefaultWorkspaceName = std : : to_string ( wsID ) ;
2022-09-13 15:25:42 +02:00
2023-09-06 21:45:37 +02:00
Debug : : log ( LOG , " Invalid workspace= directive name in monitor parsing, workspace name \" {} \" is invalid. " , g_pConfigManager - > getDefaultWorkspaceFor ( szName ) ) ;
2022-09-13 15:25:42 +02:00
}
2024-06-23 00:52:42 +03:00
auto PNEWWORKSPACE = g_pCompositor - > getWorkspaceByID ( wsID ) ;
2022-09-13 15:25:42 +02:00
2024-06-23 00:52:42 +03:00
Debug : : log ( LOG , " New monitor: WORKSPACEID {}, exists: {} " , wsID , ( int ) ( PNEWWORKSPACE ! = nullptr ) ) ;
2022-09-13 15:25:42 +02:00
if ( PNEWWORKSPACE ) {
// workspace exists, move it to the newly connected monitor
2024-10-19 23:03:29 +01:00
g_pCompositor - > moveWorkspaceToMonitor ( PNEWWORKSPACE , self . lock ( ) ) ;
2024-04-02 20:32:39 +01:00
activeWorkspace = PNEWWORKSPACE ;
2022-09-13 15:25:42 +02:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( ID ) ;
PNEWWORKSPACE - > startAnim ( true , true , true ) ;
} else {
if ( newDefaultWorkspaceName = = " " )
2024-06-23 00:52:42 +03:00
newDefaultWorkspaceName = std : : to_string ( wsID ) ;
2022-09-13 15:25:42 +02:00
2024-10-27 18:45:38 +00:00
PNEWWORKSPACE = g_pCompositor - > m_vWorkspaces . emplace_back ( CWorkspace : : create ( wsID , self . lock ( ) , newDefaultWorkspaceName ) ) ;
2022-09-13 15:25:42 +02:00
}
2024-04-02 20:32:39 +01:00
activeWorkspace = PNEWWORKSPACE ;
2022-09-13 15:25:42 +02:00
PNEWWORKSPACE - > setActive ( true ) ;
2024-04-03 10:09:42 +01:00
PNEWWORKSPACE - > m_bVisible = true ;
2023-05-03 15:15:56 +01:00
PNEWWORKSPACE - > m_szLastMonitor = " " ;
2022-09-13 15:25:42 +02:00
}
void CMonitor : : setMirror ( const std : : string & mirrorOf ) {
const auto PMIRRORMON = g_pCompositor - > getMonitorFromString ( mirrorOf ) ;
if ( PMIRRORMON = = pMirrorOf )
return ;
if ( PMIRRORMON & & PMIRRORMON - > isMirror ( ) ) {
Debug : : log ( ERR , " Cannot mirror a mirror! " ) ;
return ;
}
2024-10-19 23:03:29 +01:00
if ( PMIRRORMON = = self ) {
2022-09-13 15:25:42 +02:00
Debug : : log ( ERR , " Cannot mirror self! " ) ;
return ;
}
if ( ! PMIRRORMON ) {
// disable mirroring
if ( pMirrorOf ) {
2024-10-19 23:03:29 +01:00
pMirrorOf - > mirrors . erase ( std : : find_if ( pMirrorOf - > mirrors . begin ( ) , pMirrorOf - > mirrors . end ( ) , [ & ] ( const auto & other ) { return other = = self ; } ) ) ;
2024-06-19 10:24:28 -04:00
// unlock software for mirrored monitor
2024-10-19 23:03:29 +01:00
g_pPointerManager - > unlockSoftwareForMonitor ( pMirrorOf . lock ( ) ) ;
2022-09-13 15:25:42 +02:00
}
2024-10-19 23:03:29 +01:00
pMirrorOf . reset ( ) ;
2022-09-13 15:25:42 +02:00
// set rule
2024-09-26 11:10:53 +01:00
const auto RULE = g_pConfigManager - > getMonitorRuleFor ( self . lock ( ) ) ;
2022-09-13 15:25:42 +02:00
vecPosition = RULE . offset ;
// push to mvmonitors
2023-11-01 18:53:36 +00:00
2024-10-26 02:06:13 +01:00
PHLMONITOR * thisWrapper = nullptr ;
2023-11-01 18:53:36 +00:00
// find the wrap
for ( auto & m : g_pCompositor - > m_vRealMonitors ) {
if ( m - > ID = = ID ) {
thisWrapper = & m ;
break ;
2022-09-13 15:25:42 +02:00
}
}
2023-11-01 18:53:36 +00:00
RASSERT ( thisWrapper - > get ( ) , " CMonitor::setMirror: Had no wrapper??? " ) ;
2022-12-16 17:17:31 +00:00
if ( std : : find_if ( g_pCompositor - > m_vMonitors . begin ( ) , g_pCompositor - > m_vMonitors . end ( ) , [ & ] ( auto & other ) { return other . get ( ) = = this ; } ) = =
g_pCompositor - > m_vMonitors . end ( ) ) {
2023-11-01 18:53:36 +00:00
g_pCompositor - > m_vMonitors . push_back ( * thisWrapper ) ;
2022-09-13 15:25:42 +02:00
}
setupDefaultWS ( RULE ) ;
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > applyMonitorRule ( self . lock ( ) , ( SMonitorRule * ) & RULE , true ) ; // will apply the offset and stuff
2022-09-13 15:25:42 +02:00
} else {
2024-10-19 23:03:29 +01:00
PHLMONITOR BACKUPMON = nullptr ;
2024-08-26 20:24:30 +02:00
for ( auto const & m : g_pCompositor - > m_vMonitors ) {
2022-09-13 15:25:42 +02:00
if ( m . get ( ) ! = this ) {
2024-10-19 23:03:29 +01:00
BACKUPMON = m ;
2022-09-13 15:25:42 +02:00
break ;
}
}
// move all the WS
2024-04-02 20:32:39 +01:00
std : : deque < PHLWORKSPACE > wspToMove ;
2024-08-26 20:24:30 +02:00
for ( auto const & w : g_pCompositor - > m_vWorkspaces ) {
2024-10-27 18:45:38 +00:00
if ( w - > m_pMonitor = = self | | ! w - > m_pMonitor )
2024-04-02 20:32:39 +01:00
wspToMove . push_back ( w ) ;
2022-09-13 15:25:42 +02:00
}
2024-08-26 20:24:30 +02:00
for ( auto const & w : wspToMove ) {
2022-09-13 15:25:42 +02:00
g_pCompositor - > moveWorkspaceToMonitor ( w , BACKUPMON ) ;
w - > startAnim ( true , true , true ) ;
}
2024-04-02 20:32:39 +01:00
activeWorkspace . reset ( ) ;
2022-09-13 15:25:42 +02:00
2023-03-04 00:48:02 +00:00
vecPosition = PMIRRORMON - > vecPosition ;
2022-09-13 15:25:42 +02:00
pMirrorOf = PMIRRORMON ;
2024-10-19 23:03:29 +01:00
pMirrorOf - > mirrors . push_back ( self ) ;
2022-09-13 15:25:42 +02:00
// remove from mvmonitors
2024-10-19 23:03:29 +01:00
std : : erase_if ( g_pCompositor - > m_vMonitors , [ & ] ( const auto & other ) { return other = = self ; } ) ;
2022-09-13 15:25:42 +02:00
2023-08-14 14:22:06 +02:00
g_pCompositor - > arrangeMonitors ( ) ;
2024-10-19 23:03:29 +01:00
g_pCompositor - > setActiveMonitor ( g_pCompositor - > m_vMonitors . front ( ) ) ;
2022-12-26 13:26:53 +01:00
g_pCompositor - > sanityCheckWorkspaces ( ) ;
2024-06-19 10:24:28 -04:00
// Software lock mirrored monitor
g_pPointerManager - > lockSoftwareForMonitor ( PMIRRORMON ) ;
2022-09-13 15:25:42 +02:00
}
2024-06-09 22:28:51 +02:00
events . modeChanged . emit ( ) ;
2022-09-19 20:44:33 +02:00
}
2022-12-14 17:57:18 +00:00
float CMonitor : : getDefaultScale ( ) {
if ( ! m_bEnabled )
return 1 ;
static constexpr double MMPERINCH = 25.4 ;
2022-12-16 17:17:31 +00:00
const auto DIAGONALPX = sqrt ( pow ( vecPixelSize . x , 2 ) + pow ( vecPixelSize . y , 2 ) ) ;
2024-07-21 13:09:54 +02:00
const auto DIAGONALIN = sqrt ( pow ( output - > physicalSize . x / MMPERINCH , 2 ) + pow ( output - > physicalSize . y / MMPERINCH , 2 ) ) ;
2022-12-14 17:57:18 +00:00
2022-12-16 17:17:31 +00:00
const auto PPI = DIAGONALPX / DIAGONALIN ;
2022-12-14 17:57:18 +00:00
if ( PPI > 200 /* High PPI, 2x*/ )
return 2 ;
2022-12-20 13:33:29 +00:00
else if ( PPI > 140 /* Medium PPI, 1.5x*/ )
2022-12-14 17:57:18 +00:00
return 1.5 ;
return 1 ;
}
2023-04-14 15:03:53 +01:00
2024-04-02 20:32:39 +01:00
void CMonitor : : changeWorkspace ( const PHLWORKSPACE & pWorkspace , bool internal , bool noMouseMove , bool noFocus ) {
2023-04-14 15:28:22 +01:00
if ( ! pWorkspace )
2023-04-14 15:03:53 +01:00
return ;
2023-04-17 13:32:35 +01:00
if ( pWorkspace - > m_bIsSpecialWorkspace ) {
2024-04-02 20:32:39 +01:00
if ( activeSpecialWorkspace ! = pWorkspace ) {
2023-09-06 12:51:36 +02:00
Debug : : log ( LOG , " changeworkspace on special, togglespecialworkspace to id {} " , pWorkspace - > m_iID ) ;
2024-04-13 06:39:20 -07:00
setSpecialWorkspace ( pWorkspace ) ;
2023-05-05 15:01:28 +01:00
}
2023-04-17 13:32:35 +01:00
return ;
}
2024-04-02 20:32:39 +01:00
if ( pWorkspace = = activeWorkspace )
2023-09-03 17:21:55 +02:00
return ;
2024-04-03 10:09:42 +01:00
const auto POLDWORKSPACE = activeWorkspace ;
POLDWORKSPACE - > m_bVisible = false ;
pWorkspace - > m_bVisible = true ;
2023-04-14 15:03:53 +01:00
2024-04-02 20:32:39 +01:00
activeWorkspace = pWorkspace ;
2023-04-14 15:03:53 +01:00
if ( ! internal ) {
const auto ANIMTOLEFT = pWorkspace - > m_iID > POLDWORKSPACE - > m_iID ;
POLDWORKSPACE - > startAnim ( false , ANIMTOLEFT ) ;
pWorkspace - > startAnim ( true , ANIMTOLEFT ) ;
// move pinned windows
2024-08-26 20:24:30 +02:00
for ( auto const & w : g_pCompositor - > m_vWindows ) {
2024-04-02 20:32:39 +01:00
if ( w - > m_pWorkspace = = POLDWORKSPACE & & w - > m_bPinned )
w - > moveToWorkspace ( pWorkspace ) ;
2023-04-14 15:03:53 +01:00
}
2024-04-07 22:13:56 +00:00
if ( ! noFocus & & ! g_pCompositor - > m_pLastMonitor - > activeSpecialWorkspace & &
2024-10-27 18:45:38 +00:00
! ( g_pCompositor - > m_pLastWindow . lock ( ) & & g_pCompositor - > m_pLastWindow - > m_bPinned & & g_pCompositor - > m_pLastWindow - > m_pMonitor = = self ) ) {
2024-03-03 18:39:20 +00:00
static auto PFOLLOWMOUSE = CConfigValue < Hyprlang : : INT > ( " input:follow_mouse " ) ;
2024-11-22 16:01:02 +00:00
auto pWindow = pWorkspace - > m_bHasFullscreenWindow ? pWorkspace - > getFullscreenWindow ( ) : pWorkspace - > getLastFocusedWindow ( ) ;
2023-08-21 18:54:02 +00:00
2023-12-07 22:12:08 +00:00
if ( ! pWindow ) {
2024-03-03 18:39:20 +00:00
if ( * PFOLLOWMOUSE = = 1 )
2024-02-04 15:40:20 +00:00
pWindow = g_pCompositor - > vectorToWindowUnified ( g_pInputManager - > getMouseCoordsInternal ( ) , RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING ) ;
2023-08-21 18:54:02 +00:00
2023-12-07 22:12:08 +00:00
if ( ! pWindow )
2024-11-22 16:01:02 +00:00
pWindow = pWorkspace - > getTopLeftWindow ( ) ;
2023-08-21 18:54:02 +00:00
2023-12-07 22:12:08 +00:00
if ( ! pWindow )
2024-11-22 16:01:02 +00:00
pWindow = pWorkspace - > getFirstWindow ( ) ;
2023-12-07 22:12:08 +00:00
}
2023-08-21 18:54:02 +00:00
g_pCompositor - > focusWindow ( pWindow ) ;
2023-04-14 15:03:53 +01:00
}
2023-12-07 22:12:08 +00:00
2023-09-08 17:17:04 +00:00
if ( ! noMouseMove )
g_pInputManager - > simulateMouseMovement ( ) ;
2023-08-21 18:54:02 +00:00
2023-04-16 01:11:57 +01:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( ID ) ;
2023-04-14 15:03:53 +01:00
g_pEventManager - > postEvent ( SHyprIPCEvent { " workspace " , pWorkspace - > m_szName } ) ;
2024-03-09 01:39:53 +08:00
g_pEventManager - > postEvent ( SHyprIPCEvent { " workspacev2 " , std : : format ( " {},{} " , pWorkspace - > m_iID , pWorkspace - > m_szName ) } ) ;
2023-04-14 15:03:53 +01:00
EMIT_HOOK_EVENT ( " workspace " , pWorkspace ) ;
}
2023-04-15 16:16:33 +01:00
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > damageMonitor ( self . lock ( ) ) ;
2023-04-30 01:12:20 +01:00
g_pCompositor - > updateFullscreenFadeOnWorkspace ( pWorkspace ) ;
2023-10-12 17:26:31 +01:00
2024-10-19 23:03:29 +01:00
g_pConfigManager - > ensureVRR ( self . lock ( ) ) ;
2023-12-23 22:30:49 +01:00
g_pCompositor - > updateSuspendedStates ( ) ;
2024-03-07 05:27:58 -08:00
2024-04-02 20:32:39 +01:00
if ( activeSpecialWorkspace )
g_pCompositor - > updateFullscreenFadeOnWorkspace ( activeSpecialWorkspace ) ;
2023-04-14 15:03:53 +01:00
}
2024-08-08 21:01:50 +02:00
void CMonitor : : changeWorkspace ( const WORKSPACEID & id , bool internal , bool noMouseMove , bool noFocus ) {
2024-02-10 00:47:00 +01:00
changeWorkspace ( g_pCompositor - > getWorkspaceByID ( id ) , internal , noMouseMove , noFocus ) ;
2023-04-14 15:03:53 +01:00
}
2024-04-02 20:32:39 +01:00
void CMonitor : : setSpecialWorkspace ( const PHLWORKSPACE & pWorkspace ) {
2024-07-07 21:27:18 +02:00
if ( activeSpecialWorkspace = = pWorkspace )
return ;
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > damageMonitor ( self . lock ( ) ) ;
2023-04-15 16:16:33 +01:00
2023-04-14 15:03:53 +01:00
if ( ! pWorkspace ) {
// remove special if exists
2024-04-02 20:32:39 +01:00
if ( activeSpecialWorkspace ) {
2024-04-03 10:09:42 +01:00
activeSpecialWorkspace - > m_bVisible = false ;
2024-04-02 20:32:39 +01:00
activeSpecialWorkspace - > startAnim ( false , false ) ;
2023-09-04 13:11:51 +00:00
g_pEventManager - > postEvent ( SHyprIPCEvent { " activespecial " , " , " + szName } ) ;
}
2024-04-02 20:32:39 +01:00
activeSpecialWorkspace . reset ( ) ;
2023-04-14 15:03:53 +01:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( ID ) ;
2024-10-27 18:45:38 +00:00
if ( ! ( g_pCompositor - > m_pLastWindow . lock ( ) & & g_pCompositor - > m_pLastWindow - > m_bPinned & & g_pCompositor - > m_pLastWindow - > m_pMonitor = = self ) ) {
2024-04-07 22:13:56 +00:00
if ( const auto PLAST = activeWorkspace - > getLastFocusedWindow ( ) ; PLAST )
g_pCompositor - > focusWindow ( PLAST ) ;
else
g_pInputManager - > refocus ( ) ;
}
2023-04-14 15:03:53 +01:00
2024-04-02 20:32:39 +01:00
g_pCompositor - > updateFullscreenFadeOnWorkspace ( activeWorkspace ) ;
2024-03-04 15:29:45 -08:00
2024-10-19 23:03:29 +01:00
g_pConfigManager - > ensureVRR ( self . lock ( ) ) ;
2024-03-04 15:29:45 -08:00
2023-12-23 22:30:49 +01:00
g_pCompositor - > updateSuspendedStates ( ) ;
2023-04-14 15:03:53 +01:00
return ;
}
2024-04-03 10:09:42 +01:00
if ( activeSpecialWorkspace ) {
activeSpecialWorkspace - > m_bVisible = false ;
2024-04-02 20:32:39 +01:00
activeSpecialWorkspace - > startAnim ( false , false ) ;
2024-04-03 10:09:42 +01:00
}
2023-05-06 16:10:51 +01:00
2023-09-04 13:11:51 +00:00
bool animate = true ;
//close if open elsewhere
2024-10-27 18:45:38 +00:00
const auto PMONITORWORKSPACEOWNER = pWorkspace - > m_pMonitor . lock ( ) ;
2024-04-02 20:32:39 +01:00
if ( PMONITORWORKSPACEOWNER - > activeSpecialWorkspace = = pWorkspace ) {
PMONITORWORKSPACEOWNER - > activeSpecialWorkspace . reset ( ) ;
2023-09-04 13:11:51 +00:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( PMONITORWORKSPACEOWNER - > ID ) ;
g_pEventManager - > postEvent ( SHyprIPCEvent { " activespecial " , " , " + PMONITORWORKSPACEOWNER - > szName } ) ;
2024-03-07 05:27:58 -08:00
2024-04-02 20:32:39 +01:00
const auto PACTIVEWORKSPACE = PMONITORWORKSPACEOWNER - > activeWorkspace ;
2024-03-07 05:27:58 -08:00
g_pCompositor - > updateFullscreenFadeOnWorkspace ( PACTIVEWORKSPACE ) ;
2023-09-04 13:11:51 +00:00
animate = false ;
}
2023-04-14 15:03:53 +01:00
// open special
2024-10-27 18:45:38 +00:00
pWorkspace - > m_pMonitor = self ;
2024-04-03 10:09:42 +01:00
activeSpecialWorkspace = pWorkspace ;
activeSpecialWorkspace - > m_bVisible = true ;
2023-09-04 13:11:51 +00:00
if ( animate )
pWorkspace - > startAnim ( true , true ) ;
2023-04-14 15:03:53 +01:00
2024-08-26 20:24:30 +02:00
for ( auto const & w : g_pCompositor - > m_vWindows ) {
2024-04-02 20:32:39 +01:00
if ( w - > m_pWorkspace = = pWorkspace ) {
2024-10-27 18:45:38 +00:00
w - > m_pMonitor = self ;
2024-01-09 18:14:08 +01:00
w - > updateSurfaceScaleTransformDetails ( ) ;
2024-03-23 15:14:50 -07:00
w - > setAnimationsToMove ( ) ;
2023-12-26 19:44:38 +01:00
const auto MIDDLE = w - > middle ( ) ;
2024-08-30 14:12:23 +02:00
if ( w - > m_bIsFloating & & ! VECINRECT ( MIDDLE , vecPosition . x , vecPosition . y , vecPosition . x + vecSize . x , vecPosition . y + vecSize . y ) & & ! w - > isX11OverrideRedirect ( ) ) {
2023-12-26 19:44:38 +01:00
// if it's floating and the middle isnt on the current mon, move it to the center
const auto PMONFROMMIDDLE = g_pCompositor - > getMonitorFromVector ( MIDDLE ) ;
2024-03-02 01:35:17 +01:00
Vector2D pos = w - > m_vRealPosition . goal ( ) ;
2023-12-26 19:44:38 +01:00
if ( ! VECINRECT ( MIDDLE , PMONFROMMIDDLE - > vecPosition . x , PMONFROMMIDDLE - > vecPosition . y , PMONFROMMIDDLE - > vecPosition . x + PMONFROMMIDDLE - > vecSize . x ,
PMONFROMMIDDLE - > vecPosition . y + PMONFROMMIDDLE - > vecSize . y ) ) {
// not on any monitor, center
2024-03-02 01:35:17 +01:00
pos = middle ( ) / 2.f - w - > m_vRealSize . goal ( ) / 2.f ;
2023-12-26 19:44:38 +01:00
} else
pos = pos - PMONFROMMIDDLE - > vecPosition + vecPosition ;
w - > m_vRealPosition = pos ;
w - > m_vPosition = pos ;
}
2023-06-02 20:14:34 +02:00
}
}
2023-04-14 15:03:53 +01:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( ID ) ;
2024-10-27 18:45:38 +00:00
if ( ! ( g_pCompositor - > m_pLastWindow . lock ( ) & & g_pCompositor - > m_pLastWindow - > m_bPinned & & g_pCompositor - > m_pLastWindow - > m_pMonitor = = self ) ) {
2024-04-07 22:13:56 +00:00
if ( const auto PLAST = pWorkspace - > getLastFocusedWindow ( ) ; PLAST )
g_pCompositor - > focusWindow ( PLAST ) ;
else
g_pInputManager - > refocus ( ) ;
}
2023-09-04 13:11:51 +00:00
g_pEventManager - > postEvent ( SHyprIPCEvent { " activespecial " , pWorkspace - > m_szName + " , " + szName } ) ;
2023-09-22 23:36:28 +01:00
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > damageMonitor ( self . lock ( ) ) ;
2023-12-23 22:30:49 +01:00
2024-03-04 15:29:45 -08:00
g_pCompositor - > updateFullscreenFadeOnWorkspace ( pWorkspace ) ;
2024-10-19 23:03:29 +01:00
g_pConfigManager - > ensureVRR ( self . lock ( ) ) ;
2024-03-04 15:29:45 -08:00
2023-12-23 22:30:49 +01:00
g_pCompositor - > updateSuspendedStates ( ) ;
2023-04-14 15:03:53 +01:00
}
2024-08-08 21:01:50 +02:00
void CMonitor : : setSpecialWorkspace ( const WORKSPACEID & id ) {
2023-04-14 15:03:53 +01:00
setSpecialWorkspace ( g_pCompositor - > getWorkspaceByID ( id ) ) ;
}
2023-08-14 14:22:06 +02:00
void CMonitor : : moveTo ( const Vector2D & pos ) {
vecPosition = pos ;
}
2023-09-11 09:09:34 +00:00
Vector2D CMonitor : : middle ( ) {
return vecPosition + vecSize / 2.f ;
2023-11-12 14:14:05 +01:00
}
2023-11-24 10:54:21 +00:00
void CMonitor : : updateMatrix ( ) {
2024-09-25 10:01:13 +01:00
projMatrix = Mat3x3 : : identity ( ) ;
if ( transform ! = WL_OUTPUT_TRANSFORM_NORMAL )
projMatrix . translate ( vecPixelSize / 2.0 ) . transform ( wlTransformToHyprutils ( transform ) ) . translate ( - vecTransformedSize / 2.0 ) ;
2023-11-30 02:48:10 +01:00
}
2024-01-27 19:11:03 +00:00
2024-08-08 21:01:50 +02:00
WORKSPACEID CMonitor : : activeWorkspaceID ( ) {
2024-04-02 20:32:39 +01:00
return activeWorkspace ? activeWorkspace - > m_iID : 0 ;
}
2024-05-05 22:18:10 +01:00
2024-08-08 21:01:50 +02:00
WORKSPACEID CMonitor : : activeSpecialWorkspaceID ( ) {
2024-04-02 20:32:39 +01:00
return activeSpecialWorkspace ? activeSpecialWorkspace - > m_iID : 0 ;
}
2024-05-05 22:18:10 +01:00
CBox CMonitor : : logicalBox ( ) {
return { vecPosition , vecSize } ;
}
2024-10-05 00:44:30 +01:00
void CMonitor : : scheduleDone ( ) {
if ( doneScheduled )
2024-07-21 13:09:54 +02:00
return ;
2024-10-05 00:44:30 +01:00
doneScheduled = true ;
2024-07-21 13:09:54 +02:00
2024-10-05 00:44:30 +01:00
g_pEventLoopManager - > doLater ( [ M = self ] {
if ( ! M ) // if M is gone, we got destroyed, doesn't matter.
return ;
if ( ! PROTO : : outputs . contains ( M - > szName ) )
return ;
2024-07-21 13:09:54 +02:00
2024-10-05 00:44:30 +01:00
PROTO : : outputs . at ( M - > szName ) - > sendDone ( ) ;
M - > doneScheduled = false ;
} ) ;
2024-07-21 13:09:54 +02:00
}
2024-10-08 16:59:15 +01:00
void CMonitor : : setCTM ( const Mat3x3 & ctm_ ) {
ctm = ctm_ ;
ctmUpdated = true ;
2024-10-19 23:03:29 +01:00
g_pCompositor - > scheduleFrameForMonitor ( self . lock ( ) , Aquamarine : : IOutput : : scheduleFrameReason : : AQ_SCHEDULE_NEEDS_FRAME ) ;
2024-10-08 16:59:15 +01:00
}
2024-07-21 13:09:54 +02:00
bool CMonitor : : attemptDirectScanout ( ) {
if ( ! mirrors . empty ( ) | | isMirror ( ) | | g_pHyprRenderer - > m_bDirectScanoutBlocked )
return false ; // do not DS if this monitor is being mirrored. Will break the functionality.
if ( g_pPointerManager - > softwareLockedFor ( self . lock ( ) ) )
return false ;
const auto PCANDIDATE = solitaryClient . lock ( ) ;
if ( ! PCANDIDATE )
return false ;
const auto PSURFACE = g_pXWaylandManager - > getWindowSurface ( PCANDIDATE ) ;
2024-07-31 20:47:26 +01:00
if ( ! PSURFACE | | ! PSURFACE - > current . buffer | | PSURFACE - > current . bufferSize ! = vecPixelSize | | PSURFACE - > current . transform ! = transform )
2024-07-21 13:09:54 +02:00
return false ;
// we can't scanout shm buffers.
2024-10-19 23:48:25 +01:00
if ( ! PSURFACE - > current . buffer | | ! PSURFACE - > current . buffer - > buffer | | ! PSURFACE - > current . texture | | ! PSURFACE - > current . texture - > m_pEglImage /* dmabuf */ )
2024-07-21 13:09:54 +02:00
return false ;
2024-08-06 14:52:19 +01:00
Debug : : log ( TRACE , " attemptDirectScanout: surface {:x} passed, will attempt " , ( uintptr_t ) PSURFACE . get ( ) ) ;
2024-07-21 13:09:54 +02:00
// FIXME: make sure the buffer actually follows the available scanout dmabuf formats
// and comes from the appropriate device. This may implode on multi-gpu!!
2024-08-31 08:07:52 -05:00
const auto params = PSURFACE - > current . buffer - > buffer - > dmabuf ( ) ;
// scanout buffer isn't dmabuf, so no scanout
if ( ! params . success )
return false ;
// entering into scanout, so save monitor format
if ( lastScanout . expired ( ) )
prevDrmFormat = drmFormat ;
if ( drmFormat ! = params . format ) {
output - > state - > setFormat ( params . format ) ;
drmFormat = params . format ;
}
2024-07-31 20:47:26 +01:00
output - > state - > setBuffer ( PSURFACE - > current . buffer - > buffer . lock ( ) ) ;
2024-07-30 15:50:14 +02:00
output - > state - > setPresentationMode ( tearingState . activelyTearing ? Aquamarine : : eOutputPresentationMode : : AQ_OUTPUT_PRESENTATION_IMMEDIATE :
Aquamarine : : eOutputPresentationMode : : AQ_OUTPUT_PRESENTATION_VSYNC ) ;
2024-07-21 13:09:54 +02:00
2024-08-06 14:52:19 +01:00
if ( ! state . test ( ) ) {
Debug : : log ( TRACE , " attemptDirectScanout: failed basic test " ) ;
2024-07-21 13:09:54 +02:00
return false ;
2024-08-06 14:52:19 +01:00
}
auto explicitOptions = g_pHyprRenderer - > getExplicitSyncSettings ( ) ;
// wait for the explicit fence if present, and if kms explicit is allowed
bool DOEXPLICIT = PSURFACE - > syncobj & & PSURFACE - > syncobj - > current . acquireTimeline & & PSURFACE - > syncobj - > current . acquireTimeline - > timeline & & explicitOptions . explicitKMSEnabled ;
int explicitWaitFD = - 1 ;
if ( DOEXPLICIT ) {
explicitWaitFD = PSURFACE - > syncobj - > current . acquireTimeline - > timeline - > exportAsSyncFileFD ( PSURFACE - > syncobj - > current . acquirePoint ) ;
if ( explicitWaitFD < 0 )
Debug : : log ( TRACE , " attemptDirectScanout: failed to acquire an explicit wait fd " ) ;
}
DOEXPLICIT = DOEXPLICIT & & explicitWaitFD > = 0 ;
auto cleanup = CScopeGuard ( [ explicitWaitFD , this ] ( ) {
output - > state - > resetExplicitFences ( ) ;
if ( explicitWaitFD > = 0 )
close ( explicitWaitFD ) ;
} ) ;
2024-07-21 13:09:54 +02:00
timespec now ;
clock_gettime ( CLOCK_MONOTONIC , & now ) ;
2024-08-30 15:50:25 +02:00
PSURFACE - > presentFeedback ( & now , self . lock ( ) ) ;
2024-07-21 13:09:54 +02:00
2024-07-30 15:46:35 +02:00
output - > state - > addDamage ( CBox { { } , vecPixelSize } ) ;
2024-08-06 14:52:19 +01:00
output - > state - > resetExplicitFences ( ) ;
if ( DOEXPLICIT ) {
Debug : : log ( TRACE , " attemptDirectScanout: setting IN_FENCE for aq to {} " , explicitWaitFD ) ;
output - > state - > setExplicitInFence ( explicitWaitFD ) ;
}
bool ok = output - > commit ( ) ;
if ( ! ok & & DOEXPLICIT ) {
Debug : : log ( TRACE , " attemptDirectScanout: EXPLICIT SYNC FAILED: commit() returned false. Resetting fences and retrying, might result in glitches. " ) ;
output - > state - > resetExplicitFences ( ) ;
ok = output - > commit ( ) ;
}
2024-07-30 15:46:35 +02:00
2024-08-06 14:52:19 +01:00
if ( ok ) {
2024-07-21 13:09:54 +02:00
if ( lastScanout . expired ( ) ) {
lastScanout = PCANDIDATE ;
Debug : : log ( LOG , " Entered a direct scanout to {:x}: \" {} \" " , ( uintptr_t ) PCANDIDATE . get ( ) , PCANDIDATE - > m_szTitle ) ;
}
2024-08-06 14:52:19 +01:00
// delay explicit sync feedback until kms release of the buffer
if ( DOEXPLICIT ) {
Debug : : log ( TRACE , " attemptDirectScanout: Delaying explicit sync release feedback until kms release " ) ;
PSURFACE - > current . buffer - > releaser - > drop ( ) ;
PSURFACE - > current . buffer - > buffer - > hlEvents . backendRelease2 = PSURFACE - > current . buffer - > buffer - > events . backendRelease . registerListener ( [ PSURFACE ] ( std : : any d ) {
const bool DOEXPLICIT = PSURFACE - > syncobj & & PSURFACE - > syncobj - > current . releaseTimeline & & PSURFACE - > syncobj - > current . releaseTimeline - > timeline ;
if ( DOEXPLICIT )
PSURFACE - > syncobj - > current . releaseTimeline - > timeline - > signal ( PSURFACE - > syncobj - > current . releasePoint ) ;
} ) ;
}
2024-07-21 13:09:54 +02:00
} else {
2024-08-06 14:52:19 +01:00
Debug : : log ( TRACE , " attemptDirectScanout: failed to scanout surface " ) ;
2024-07-21 13:09:54 +02:00
lastScanout . reset ( ) ;
return false ;
}
return true ;
}
2024-10-12 03:29:51 +03:00
void CMonitor : : debugLastPresentation ( const std : : string & message ) {
Debug : : log ( TRACE , " {} (last presentation {} - {} fps) " , message , lastPresentationTimer . getMillis ( ) ,
lastPresentationTimer . getMillis ( ) > 0 ? 1000.0f / lastPresentationTimer . getMillis ( ) : 0.0f ) ;
}
2024-10-19 16:21:47 +01:00
void CMonitor : : onMonitorFrame ( ) {
if ( ( g_pCompositor - > m_pAqBackend - > hasSession ( ) & & ! g_pCompositor - > m_pAqBackend - > session - > active ) | | ! g_pCompositor - > m_bSessionActive | | g_pCompositor - > m_bUnsafeState ) {
Debug : : log ( WARN , " Attempted to render frame on inactive session! " ) ;
if ( g_pCompositor - > m_bUnsafeState & & std : : ranges : : any_of ( g_pCompositor - > m_vMonitors . begin ( ) , g_pCompositor - > m_vMonitors . end ( ) , [ & ] ( auto & m ) {
return m - > output ! = g_pCompositor - > m_pUnsafeOutput - > output ;
} ) ) {
// restore from unsafe state
g_pCompositor - > leaveUnsafeState ( ) ;
}
return ; // cannot draw on session inactive (different tty)
}
if ( ! m_bEnabled )
return ;
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > recheckSolitaryForMonitor ( self . lock ( ) ) ;
2024-10-19 16:21:47 +01:00
tearingState . busy = false ;
if ( tearingState . activelyTearing & & solitaryClient . lock ( ) /* can be invalidated by a recheck */ ) {
if ( ! tearingState . frameScheduledWhileBusy )
return ; // we did not schedule a frame yet to be displayed, but we are tearing. Why render?
tearingState . nextRenderTorn = true ;
tearingState . frameScheduledWhileBusy = false ;
}
static auto PENABLERAT = CConfigValue < Hyprlang : : INT > ( " misc:render_ahead_of_time " ) ;
static auto PRATSAFE = CConfigValue < Hyprlang : : INT > ( " misc:render_ahead_safezone " ) ;
lastPresentationTimer . reset ( ) ;
if ( * PENABLERAT & & ! tearingState . nextRenderTorn ) {
if ( ! RATScheduled ) {
// render
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > renderMonitor ( self . lock ( ) ) ;
2024-10-19 16:21:47 +01:00
}
RATScheduled = false ;
2024-10-19 23:03:29 +01:00
const auto & [ avg , max , min ] = g_pHyprRenderer - > getRenderTimes ( self . lock ( ) ) ;
2024-10-19 16:21:47 +01:00
if ( max + * PRATSAFE > 1000.0 / refreshRate )
return ;
const auto MSLEFT = 1000.0 / refreshRate - lastPresentationTimer . getMillis ( ) ;
RATScheduled = true ;
const auto ESTRENDERTIME = std : : ceil ( avg + * PRATSAFE ) ;
const auto TIMETOSLEEP = std : : floor ( MSLEFT - ESTRENDERTIME ) ;
if ( MSLEFT < 1 | | MSLEFT < ESTRENDERTIME | | TIMETOSLEEP < 1 )
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > renderMonitor ( self . lock ( ) ) ;
2024-10-19 16:21:47 +01:00
else
wl_event_source_timer_update ( renderTimer , TIMETOSLEEP ) ;
} else
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > renderMonitor ( self . lock ( ) ) ;
2024-10-19 16:21:47 +01:00
}
2024-12-07 18:51:18 +01:00
CMonitorState : : CMonitorState ( CMonitor * owner ) : m_pOwner ( owner ) {
;
2024-01-28 01:57:13 +00:00
}
CMonitorState : : ~ CMonitorState ( ) {
2024-07-21 13:09:54 +02:00
;
2024-01-28 01:57:13 +00:00
}
2024-07-21 13:09:54 +02:00
void CMonitorState : : ensureBufferPresent ( ) {
2024-08-28 08:07:13 -05:00
const auto STATE = m_pOwner - > output - > state - > state ( ) ;
if ( ! STATE . enabled ) {
2024-07-21 13:09:54 +02:00
Debug : : log ( TRACE , " CMonitorState::ensureBufferPresent: Ignoring, monitor is not enabled " ) ;
return ;
}
2024-08-28 08:07:13 -05:00
if ( STATE . buffer ) {
if ( const auto params = STATE . buffer - > dmabuf ( ) ; params . success & & params . format = = m_pOwner - > drmFormat )
return ;
}
2024-01-28 01:57:13 +00:00
2024-07-21 13:09:54 +02:00
// this is required for modesetting being possible and might be missing in case of first tests in the renderer
// where we test modes and buffers
2024-08-28 08:07:13 -05:00
Debug : : log ( LOG , " CMonitorState::ensureBufferPresent: no buffer or mismatched format, attaching one from the swapchain for modeset being possible " ) ;
2024-07-21 13:09:54 +02:00
m_pOwner - > output - > state - > setBuffer ( m_pOwner - > output - > swapchain - > next ( nullptr ) ) ;
m_pOwner - > output - > swapchain - > rollback ( ) ; // restore the counter, don't advance the swapchain
2024-01-28 01:57:13 +00:00
}
bool CMonitorState : : commit ( ) {
2024-07-21 13:09:54 +02:00
if ( ! updateSwapchain ( ) )
return false ;
2024-10-19 23:03:29 +01:00
EMIT_HOOK_EVENT ( " preMonitorCommit " , m_pOwner - > self . lock ( ) ) ;
2024-07-30 15:32:38 +02:00
2024-07-21 13:09:54 +02:00
ensureBufferPresent ( ) ;
bool ret = m_pOwner - > output - > commit ( ) ;
2024-01-28 01:57:13 +00:00
return ret ;
}
2024-01-28 00:41:54 +00:00
2024-01-28 01:57:13 +00:00
bool CMonitorState : : test ( ) {
2024-07-21 13:09:54 +02:00
if ( ! updateSwapchain ( ) )
return false ;
ensureBufferPresent ( ) ;
return m_pOwner - > output - > test ( ) ;
}
bool CMonitorState : : updateSwapchain ( ) {
auto options = m_pOwner - > output - > swapchain - > currentOptions ( ) ;
const auto & STATE = m_pOwner - > output - > state - > state ( ) ;
const auto & MODE = STATE . mode ? STATE . mode : STATE . customMode ;
if ( ! MODE ) {
Debug : : log ( WARN , " updateSwapchain: No mode? " ) ;
return true ;
}
2024-08-17 19:27:11 +02:00
options . format = m_pOwner - > drmFormat ;
2024-07-21 13:09:54 +02:00
options . scanout = true ;
options . length = 2 ;
options . size = MODE - > pixelSize ;
return m_pOwner - > output - > swapchain - > reconfigure ( options ) ;
2024-01-27 19:11:03 +00:00
}