2025-02-02 20:34:26 +03:00
# include <ranges>
2024-12-16 19:21:44 +01:00
# include <re2/re2.h>
2022-03-16 21:37:21 +01:00
# include "Compositor.hpp"
2024-10-12 03:29:51 +03:00
# include "debug/Log.hpp"
2025-01-21 17:17:07 +03:00
# include "desktop/DesktopTypes.hpp"
2022-07-10 15:41:26 +02:00
# include "helpers/Splashes.hpp"
2024-03-03 18:39:20 +00:00
# include "config/ConfigValue.hpp"
2025-01-19 15:39:19 +01:00
# include "config/ConfigWatcher.hpp"
2024-03-09 16:52:59 +00:00
# include "managers/CursorManager.hpp"
2024-04-23 01:27:08 +01:00
# include "managers/TokenManager.hpp"
2024-05-05 22:18:10 +01:00
# include "managers/PointerManager.hpp"
2024-05-10 18:27:57 +01:00
# include "managers/SeatManager.hpp"
2024-12-06 15:45:02 +01:00
# include "managers/VersionKeeperManager.hpp"
2025-01-10 19:09:40 +01:00
# include "managers/DonationNagManager.hpp"
2025-02-18 15:10:40 +00:00
# include "managers/ANRManager.hpp"
2024-04-07 03:31:51 +01:00
# include "managers/eventLoop/EventLoopManager.hpp"
2025-04-08 19:39:53 +02:00
# include "managers/permissions/DynamicPermissionManager.hpp"
2025-01-21 17:17:07 +03:00
# include <algorithm>
2024-07-21 13:09:54 +02:00
# include <aquamarine/output/Output.hpp>
2024-08-03 12:02:10 +00:00
# include <bit>
2024-10-13 14:24:10 +02:00
# include <ctime>
2022-07-10 15:41:26 +02:00
# include <random>
2024-10-13 14:24:10 +02:00
# include <print>
2024-07-21 13:09:54 +02:00
# include <cstring>
# include <filesystem>
2023-07-09 23:08:40 +02:00
# include <unordered_set>
2022-07-31 00:27:32 +02:00
# include "debug/HyprCtl.hpp"
2023-02-19 13:45:56 +00:00
# include "debug/CrashReporter.hpp"
2023-01-05 14:17:55 -05:00
# ifdef USES_SYSTEMD
2024-04-20 22:50:07 +05:00
# include <helpers/SdDaemon.hpp> // for SdNotify
2023-01-05 14:17:55 -05:00
# endif
2025-01-10 19:09:40 +01:00
# include "helpers/fs/FsUtils.hpp"
2024-04-20 14:14:54 +01:00
# include "protocols/FractionalScale.hpp"
2024-04-26 23:55:41 +01:00
# include "protocols/PointerConstraints.hpp"
2024-05-09 21:47:21 +01:00
# include "protocols/LayerShell.hpp"
2024-05-10 23:28:33 +01:00
# include "protocols/XDGShell.hpp"
2024-10-05 00:44:16 +01:00
# include "protocols/XDGOutput.hpp"
2024-10-06 14:07:07 +01:00
# include "protocols/SecurityContext.hpp"
2025-01-07 21:32:50 +03:00
# include "protocols/ColorManagement.hpp"
2024-06-08 10:07:59 +02:00
# include "protocols/core/Compositor.hpp"
# include "protocols/core/Subcompositor.hpp"
2024-04-30 02:41:27 +01:00
# include "desktop/LayerSurface.hpp"
2024-07-21 13:09:54 +02:00
# include "render/Renderer.hpp"
2024-05-25 22:43:51 +02:00
# include "xwayland/XWayland.hpp"
2024-09-30 17:25:39 +01:00
# include "helpers/ByteOperations.hpp"
2024-10-22 23:51:25 +00:00
# include "render/decorations/CHyprGroupBarDecoration.hpp"
2022-03-16 21:37:21 +01:00
2025-01-17 15:21:35 +00:00
# include "managers/KeybindManager.hpp"
# include "managers/SessionLockManager.hpp"
# include "managers/XWaylandManager.hpp"
# include "config/ConfigManager.hpp"
# include "render/OpenGL.hpp"
# include "managers/input/InputManager.hpp"
2025-08-28 11:20:29 +02:00
# include "managers/animation/AnimationManager.hpp"
# include "managers/animation/DesktopAnimationManager.hpp"
2025-01-17 15:21:35 +00:00
# include "managers/EventManager.hpp"
# include "managers/HookSystemManager.hpp"
# include "managers/ProtocolManager.hpp"
# include "managers/LayoutManager.hpp"
2025-09-18 22:10:30 +02:00
# include "render/AsyncResourceGatherer.hpp"
2025-01-17 15:21:35 +00:00
# include "plugins/PluginSystem.hpp"
# include "hyprerror/HyprError.hpp"
# include "debug/HyprNotificationOverlay.hpp"
# include "debug/HyprDebugOverlay.hpp"
2025-07-08 12:41:10 +02:00
# include "helpers/MonitorFrameScheduler.hpp"
2025-01-17 15:21:35 +00:00
2024-06-11 17:17:45 +02:00
# include <hyprutils/string/String.hpp>
2024-07-21 13:09:54 +02:00
# include <aquamarine/input/Input.hpp>
2024-06-11 17:17:45 +02:00
2024-07-20 00:37:20 +02:00
# include <fcntl.h>
2024-04-28 18:58:31 +01:00
# include <sys/types.h>
2024-04-28 22:25:24 +01:00
# include <sys/stat.h>
2024-06-18 21:52:55 +02:00
# include <sys/resource.h>
2025-02-09 17:38:20 +00:00
# include <malloc.h>
# include <unistd.h>
2025-07-23 23:11:07 +02:00
# include <xf86drm.h>
2024-04-28 18:58:31 +01:00
2024-07-21 13:09:54 +02:00
using namespace Hyprutils : : String ;
using namespace Aquamarine ;
2025-02-02 22:25:29 +03:00
using enum NContentType : : eContentType ;
2025-02-26 17:56:37 +03:00
using namespace NColorManagement ;
2024-07-21 13:09:54 +02:00
2024-12-18 19:56:01 -06:00
static int handleCritSignal ( int signo , void * data ) {
2023-09-06 12:51:36 +02:00
Debug : : log ( LOG , " Hyprland received signal {} " , signo ) ;
2022-07-13 18:18:23 +02:00
2023-08-19 19:24:48 +02:00
if ( signo = = SIGTERM | | signo = = SIGINT | | signo = = SIGKILL )
2024-07-24 12:07:36 -05:00
g_pCompositor - > stopCompositor ( ) ;
2022-07-13 18:18:23 +02:00
2023-08-19 19:24:48 +02:00
return 0 ;
2022-07-13 18:18:23 +02:00
}
2024-12-18 19:56:01 -06:00
static void handleUnrecoverableSignal ( int sig ) {
2023-02-27 12:32:38 +00:00
2023-03-01 21:55:30 +00:00
// remove our handlers
signal ( SIGABRT , SIG_DFL ) ;
signal ( SIGSEGV , SIG_DFL ) ;
2025-05-02 17:07:20 +02:00
if ( g_pHookSystem & & g_pHookSystem - > m_currentEventPlugin ) {
longjmp ( g_pHookSystem - > m_hookFaultJumpBuf , 1 ) ;
2023-02-27 12:32:38 +00:00
return ;
}
2024-04-28 00:38:48 +08:00
// Kill the program if the crash-reporter is caught in a deadlock.
signal ( SIGALRM , [ ] ( int _ ) {
char const * msg = " \n CrashReporter exceeded timeout, forcefully exiting \n " ;
write ( 2 , msg , strlen ( msg ) ) ;
abort ( ) ;
} ) ;
alarm ( 15 ) ;
2024-12-07 18:51:18 +01:00
NCrashReporter : : createAndSaveCrash ( sig ) ;
2023-03-01 21:55:30 +00:00
2023-02-20 11:02:44 +00:00
abort ( ) ;
2023-02-19 13:45:56 +00:00
}
2024-12-18 19:56:01 -06:00
static void handleUserSignal ( int sig ) {
2023-09-29 16:38:13 +01:00
if ( sig = = SIGUSR1 ) {
// means we have to unwind a timed out event
throw std : : exception ( ) ;
}
}
2024-12-07 18:51:18 +01:00
static eLogLevel aqLevelToHl ( Aquamarine : : eBackendLogLevel level ) {
2024-07-21 13:09:54 +02:00
switch ( level ) {
case Aquamarine : : eBackendLogLevel : : AQ_LOG_TRACE : return TRACE ;
case Aquamarine : : eBackendLogLevel : : AQ_LOG_DEBUG : return LOG ;
case Aquamarine : : eBackendLogLevel : : AQ_LOG_ERROR : return ERR ;
case Aquamarine : : eBackendLogLevel : : AQ_LOG_WARNING : return WARN ;
case Aquamarine : : eBackendLogLevel : : AQ_LOG_CRITICAL : return CRIT ;
default : break ;
}
return NONE ;
}
2024-12-18 19:56:01 -06:00
static void aqLog ( Aquamarine : : eBackendLogLevel level , std : : string msg ) {
2024-07-21 13:09:54 +02:00
Debug : : log ( aqLevelToHl ( level ) , " [AQ] {} " , msg ) ;
}
2024-06-19 18:36:40 +02:00
void CCompositor : : bumpNofile ( ) {
2025-05-05 23:44:49 +02:00
if ( ! getrlimit ( RLIMIT_NOFILE , & m_originalNofile ) )
Debug : : log ( LOG , " Old rlimit: soft -> {}, hard -> {} " , m_originalNofile . rlim_cur , m_originalNofile . rlim_max ) ;
2024-06-19 18:36:40 +02:00
else {
Debug : : log ( ERR , " Failed to get NOFILE rlimits " ) ;
2025-05-05 23:44:49 +02:00
m_originalNofile . rlim_max = 0 ;
2024-06-19 18:36:40 +02:00
return ;
}
2024-06-18 21:52:55 +02:00
2025-05-05 23:44:49 +02:00
rlimit newLimit = m_originalNofile ;
2024-06-18 21:52:55 +02:00
2024-06-19 18:36:40 +02:00
newLimit . rlim_cur = newLimit . rlim_max ;
2024-06-18 21:52:55 +02:00
2024-06-19 18:36:40 +02:00
if ( setrlimit ( RLIMIT_NOFILE , & newLimit ) < 0 ) {
Debug : : log ( ERR , " Failed bumping NOFILE limits higher " ) ;
2025-05-05 23:44:49 +02:00
m_originalNofile . rlim_max = 0 ;
2024-06-19 18:36:40 +02:00
return ;
}
2024-06-18 21:52:55 +02:00
2024-06-19 18:36:40 +02:00
if ( ! getrlimit ( RLIMIT_NOFILE , & newLimit ) )
Debug : : log ( LOG , " New rlimit: soft -> {}, hard -> {} " , newLimit . rlim_cur , newLimit . rlim_max ) ;
}
2024-06-18 21:52:55 +02:00
2024-06-19 18:36:40 +02:00
void CCompositor : : restoreNofile ( ) {
2025-05-05 23:44:49 +02:00
if ( m_originalNofile . rlim_max < = 0 )
2024-06-19 18:36:40 +02:00
return ;
2024-06-18 21:52:55 +02:00
2025-05-05 23:44:49 +02:00
if ( setrlimit ( RLIMIT_NOFILE , & m_originalNofile ) < 0 )
2024-06-19 18:36:40 +02:00
Debug : : log ( ERR , " Failed restoring NOFILE limits " ) ;
2024-06-18 21:52:55 +02:00
}
2025-07-23 23:11:07 +02:00
bool CCompositor : : supportsDrmSyncobjTimeline ( ) const {
2025-08-24 22:32:13 +02:00
return m_drm . syncobjSupport | | m_drmRenderNode . syncObjSupport ;
2025-07-23 23:11:07 +02:00
}
2025-02-09 17:38:20 +00:00
void CCompositor : : setMallocThreshold ( ) {
# ifdef M_TRIM_THRESHOLD
// The default is 128 pages,
// which is very large and can lead to a lot of memory used for no reason
// because trimming hasn't happened
static const int PAGESIZE = sysconf ( _SC_PAGESIZE ) ;
mallopt ( M_TRIM_THRESHOLD , 6 * PAGESIZE ) ;
# endif
}
2025-05-05 23:44:49 +02:00
CCompositor : : CCompositor ( bool onlyConfig ) : m_onlyConfigVerification ( onlyConfig ) , m_hyprlandPID ( getpid ( ) ) {
2025-01-24 20:30:12 +00:00
if ( onlyConfig )
return ;
2025-02-09 17:38:20 +00:00
setMallocThreshold ( ) ;
2025-04-22 15:23:29 +02:00
m_hyprTempDataRoot = std : : string { getenv ( " XDG_RUNTIME_DIR " ) } + " /hypr " ;
2024-04-28 22:25:24 +01:00
2025-04-22 15:23:29 +02:00
if ( m_hyprTempDataRoot . starts_with ( " /hypr " ) ) {
2024-10-13 14:24:10 +02:00
std : : println ( " Bailing out, $XDG_RUNTIME_DIR is invalid " ) ;
2024-04-28 22:25:24 +01:00
throw std : : runtime_error ( " CCompositor() failed " ) ;
}
2025-04-22 15:23:29 +02:00
if ( ! m_hyprTempDataRoot . starts_with ( " /run/user " ) )
2024-10-13 14:24:10 +02:00
std : : println ( " [!!WARNING!!] XDG_RUNTIME_DIR looks non-standard. Proceeding anyways... " ) ;
2024-04-28 22:25:24 +01:00
2024-04-28 20:06:40 +01:00
std : : random_device dev ;
std : : mt19937 engine ( dev ( ) ) ;
std : : uniform_int_distribution < > distribution ( 0 , INT32_MAX ) ;
2025-04-22 15:23:29 +02:00
m_instanceSignature = std : : format ( " {}_{}_{} " , GIT_COMMIT_HASH , std : : time ( nullptr ) , distribution ( engine ) ) ;
2022-03-18 23:52:36 +01:00
2025-04-22 15:23:29 +02:00
setenv ( " HYPRLAND_INSTANCE_SIGNATURE " , m_instanceSignature . c_str ( ) , true ) ;
2022-06-29 12:58:49 +02:00
2025-04-22 15:23:29 +02:00
if ( ! std : : filesystem : : exists ( m_hyprTempDataRoot ) )
mkdir ( m_hyprTempDataRoot . c_str ( ) , S_IRWXU ) ;
else if ( ! std : : filesystem : : is_directory ( m_hyprTempDataRoot ) ) {
std : : println ( " Bailing out, {} is not a directory " , m_hyprTempDataRoot ) ;
2024-04-28 22:25:24 +01:00
throw std : : runtime_error ( " CCompositor() failed " ) ;
2024-04-28 20:06:40 +01:00
}
2022-12-21 15:41:02 +00:00
2025-04-22 15:23:29 +02:00
m_instancePath = m_hyprTempDataRoot + " / " + m_instanceSignature ;
2024-04-28 20:06:40 +01:00
2025-04-22 15:23:29 +02:00
if ( std : : filesystem : : exists ( m_instancePath ) ) {
std : : println ( " Bailing out, {} exists?? " , m_instancePath ) ;
2024-04-28 22:25:24 +01:00
throw std : : runtime_error ( " CCompositor() failed " ) ;
2024-04-28 20:06:40 +01:00
}
2025-04-22 15:23:29 +02:00
if ( mkdir ( m_instancePath . c_str ( ) , S_IRWXU ) < 0 ) {
std : : println ( " Bailing out, couldn't create {} " , m_instancePath ) ;
2024-04-28 22:25:24 +01:00
throw std : : runtime_error ( " CCompositor() failed " ) ;
2024-04-28 20:06:40 +01:00
}
2022-06-29 12:58:49 +02:00
2025-04-22 15:23:29 +02:00
Debug : : init ( m_instancePath ) ;
2022-06-03 17:48:07 +02:00
2025-04-22 15:23:29 +02:00
Debug : : log ( LOG , " Instance Signature: {} " , m_instanceSignature ) ;
2022-06-03 17:41:57 +02:00
2025-04-22 15:23:29 +02:00
Debug : : log ( LOG , " Runtime directory: {} " , m_instancePath ) ;
2024-04-28 22:25:24 +01:00
2025-05-05 23:44:49 +02:00
Debug : : log ( LOG , " Hyprland PID: {} " , m_hyprlandPID ) ;
2022-08-28 11:19:08 +02:00
2022-06-25 20:49:06 +02:00
Debug : : log ( LOG , " ===== SYSTEM INFO: ===== " ) ;
logSystemInfo ( ) ;
Debug : : log ( LOG , " ======================== " ) ;
2022-06-25 21:16:52 +02:00
Debug : : log ( NONE , " \n \n " ) ; // pad
2025-06-20 01:45:06 +03:00
Debug : : log ( INFO , " If you are crashing, or encounter any bugs, please consult https://wiki.hypr.land/Crashes-and-Bugs/ \n \n " ) ;
2022-06-25 21:16:52 +02:00
2022-07-10 15:41:26 +02:00
setRandomSplash ( ) ;
2025-04-22 15:23:29 +02:00
Debug : : log ( LOG , " \n Current splash: {} \n \n " , m_currentSplash ) ;
2024-06-18 21:52:55 +02:00
bumpNofile ( ) ;
2023-03-05 13:37:21 +00:00
}
CCompositor : : ~ CCompositor ( ) {
2025-04-22 15:23:29 +02:00
if ( ! m_isShuttingDown & & ! m_onlyConfigVerification )
2024-07-24 12:07:36 -05:00
cleanup ( ) ;
2023-03-05 13:37:21 +00:00
}
2022-07-10 15:41:26 +02:00
2023-03-05 13:37:21 +00:00
void CCompositor : : setRandomSplash ( ) {
2024-12-05 01:59:29 +00:00
auto tt = std : : chrono : : system_clock : : to_time_t ( std : : chrono : : system_clock : : now ( ) ) ;
auto local = * localtime ( & tt ) ;
const auto * SPLASHES = & NSplashes : : SPLASHES ;
if ( local . tm_mon + 1 = = 12 & & local . tm_mday > = 23 & & local . tm_mday < = 27 ) // dec 23-27
SPLASHES = & NSplashes : : SPLASHES_CHRISTMAS ;
if ( ( local . tm_mon + 1 = = 12 & & local . tm_mday > = 29 ) | | ( local . tm_mon + 1 = = 1 & & local . tm_mday < = 3 ) )
SPLASHES = & NSplashes : : SPLASHES_NEWYEAR ;
2023-03-05 13:37:21 +00:00
std : : random_device dev ;
std : : mt19937 engine ( dev ( ) ) ;
2024-12-05 01:59:29 +00:00
std : : uniform_int_distribution < > distribution ( 0 , SPLASHES - > size ( ) - 1 ) ;
2023-03-05 13:37:21 +00:00
2025-04-22 15:23:29 +02:00
m_currentSplash = SPLASHES - > at ( distribution ( engine ) ) ;
2023-03-05 13:37:21 +00:00
}
2024-07-21 13:09:54 +02:00
static std : : vector < SP < Aquamarine : : IOutput > > pendingOutputs ;
2024-10-06 14:07:07 +01:00
//
static bool filterGlobals ( const wl_client * client , const wl_global * global , void * data ) {
if ( ! PROTO : : securityContext - > isClientSandboxed ( client ) )
return true ;
return ! g_pProtocolManager | | ! g_pProtocolManager - > isGlobalPrivileged ( global ) ;
}
2024-07-21 13:09:54 +02:00
//
2024-07-22 11:16:25 +00:00
void CCompositor : : initServer ( std : : string socketName , int socketFd ) {
2025-04-22 15:23:29 +02:00
if ( m_onlyConfigVerification ) {
2025-01-24 20:30:12 +00:00
g_pHookSystem = makeUnique < CHookSystemManager > ( ) ;
g_pKeybindManager = makeUnique < CKeybindManager > ( ) ;
g_pAnimationManager = makeUnique < CHyprAnimationManager > ( ) ;
g_pConfigManager = makeUnique < CConfigManager > ( ) ;
std : : println ( " \n \n ======== Config parsing result: \n \n {} " , g_pConfigManager - > verify ( ) ) ;
return ;
}
2025-04-22 15:23:29 +02:00
m_wlDisplay = wl_display_create ( ) ;
2022-03-16 21:37:21 +01:00
2025-04-22 15:23:29 +02:00
wl_display_set_global_filter ( m_wlDisplay , : : filterGlobals , nullptr ) ;
2024-10-06 14:07:07 +01:00
2025-04-22 15:23:29 +02:00
m_wlEventLoop = wl_display_get_event_loop ( m_wlDisplay ) ;
2022-07-13 18:18:23 +02:00
// register crit signal handler
2025-04-22 15:23:29 +02:00
m_critSigSource = wl_event_loop_add_signal ( m_wlEventLoop , SIGTERM , handleCritSignal , nullptr ) ;
2024-02-22 23:10:59 +00:00
if ( ! envEnabled ( " HYPRLAND_NO_CRASHREPORTER " ) ) {
signal ( SIGSEGV , handleUnrecoverableSignal ) ;
signal ( SIGABRT , handleUnrecoverableSignal ) ;
}
2023-09-29 16:38:13 +01:00
signal ( SIGUSR1 , handleUserSignal ) ;
2022-07-13 18:18:23 +02:00
2023-03-05 13:37:21 +00:00
initManagers ( STAGE_PRIORITY ) ;
2023-12-21 22:27:12 +01:00
if ( envEnabled ( " HYPRLAND_TRACE " ) )
2025-04-21 20:42:02 +02:00
Debug : : m_trace = true ;
2023-08-21 19:36:09 +02:00
2024-09-30 17:25:39 +01:00
// set the buffer size to 1MB to avoid disconnects due to an app hanging for a short while
2025-04-22 15:23:29 +02:00
wl_display_set_default_max_buffer_size ( m_wlDisplay , 1 _MB ) ;
2024-09-30 17:25:39 +01:00
2024-12-07 18:51:18 +01:00
Aquamarine : : SBackendOptions options { } ;
2024-07-21 13:09:54 +02:00
options . logFunction = aqLog ;
2023-03-05 13:37:21 +00:00
2024-07-21 13:09:54 +02:00
std : : vector < Aquamarine : : SBackendImplementationOptions > implementations ;
Aquamarine : : SBackendImplementationOptions option ;
option . backendType = Aquamarine : : eBackendType : : AQ_BACKEND_HEADLESS ;
option . backendRequestMode = Aquamarine : : eBackendRequestMode : : AQ_BACKEND_REQUEST_MANDATORY ;
implementations . emplace_back ( option ) ;
option . backendType = Aquamarine : : eBackendType : : AQ_BACKEND_DRM ;
option . backendRequestMode = Aquamarine : : eBackendRequestMode : : AQ_BACKEND_REQUEST_IF_AVAILABLE ;
implementations . emplace_back ( option ) ;
option . backendType = Aquamarine : : eBackendType : : AQ_BACKEND_WAYLAND ;
option . backendRequestMode = Aquamarine : : eBackendRequestMode : : AQ_BACKEND_REQUEST_FALLBACK ;
implementations . emplace_back ( option ) ;
2023-03-05 13:37:21 +00:00
2025-04-22 15:23:29 +02:00
m_aqBackend = CBackend : : create ( implementations , options ) ;
2022-03-16 21:37:21 +01:00
2025-04-22 15:23:29 +02:00
if ( ! m_aqBackend ) {
2024-07-21 13:09:54 +02:00
Debug : : log ( CRIT ,
2024-07-30 14:50:13 +03:00
" m_pAqBackend was null! This usually means aquamarine could not find a GPU or encountered some issues. Make sure you're running either on a tty or on a Wayland "
2024-07-21 13:09:54 +02:00
" session, NOT an X11 one. " ) ;
throwError ( " CBackend::create() failed! " ) ;
2022-03-16 21:37:21 +01:00
}
2024-07-21 13:09:54 +02:00
// TODO: headless only
2024-03-15 07:28:14 -07:00
2024-07-21 13:09:54 +02:00
initAllSignals ( ) ;
2024-03-15 07:28:14 -07:00
2025-04-22 15:23:29 +02:00
if ( ! m_aqBackend - > start ( ) ) {
2024-07-21 13:09:54 +02:00
Debug : : log ( CRIT ,
2024-07-30 14:50:13 +03:00
" m_pAqBackend couldn't start! This usually means aquamarine could not find a GPU or encountered some issues. Make sure you're running either on a tty or on a "
2024-07-21 13:09:54 +02:00
" Wayland session, NOT an X11 one. " ) ;
throwError ( " CBackend::create() failed! " ) ;
2022-03-24 15:57:46 +01:00
}
2025-04-22 15:23:29 +02:00
m_initialized = true ;
2022-03-16 21:37:21 +01:00
2025-08-24 22:32:13 +02:00
m_drm . fd = m_aqBackend - > drmFD ( ) ;
Debug : : log ( LOG , " Running on DRMFD: {} " , m_drm . fd ) ;
m_drmRenderNode . fd = m_aqBackend - > drmRenderNodeFD ( ) ;
Debug : : log ( LOG , " Using RENDERNODEFD: {} " , m_drmRenderNode . fd ) ;
2022-03-16 21:37:21 +01:00
2025-07-29 17:55:56 +02:00
# if defined(__linux__)
2025-08-24 22:32:13 +02:00
auto syncObjSupport = [ ] ( auto fd ) {
if ( fd < 0 )
return false ;
uint64_t cap = 0 ;
int ret = drmGetCap ( fd , DRM_CAP_SYNCOBJ_TIMELINE , & cap ) ;
return ret = = 0 & & cap ! = 0 ;
} ;
if ( ( m_drm . syncobjSupport = syncObjSupport ( m_drm . fd ) ) )
Debug : : log ( LOG , " DRM DisplayNode syncobj timeline support: {} " , m_drm . syncobjSupport ? " yes " : " no " ) ;
if ( ( m_drmRenderNode . syncObjSupport = syncObjSupport ( m_drmRenderNode . fd ) ) )
Debug : : log ( LOG , " DRM RenderNode syncobj timeline support: {} " , m_drmRenderNode . syncObjSupport ? " yes " : " no " ) ;
if ( ! m_drm . syncobjSupport & & ! m_drmRenderNode . syncObjSupport )
Debug : : log ( LOG , " DRM no syncobj support, disabling explicit sync " ) ;
2025-07-29 17:55:56 +02:00
# else
Debug : : log ( LOG , " DRM syncobj timeline support: no (not linux) " ) ;
# endif
2025-07-23 23:11:07 +02:00
2024-07-22 11:16:25 +00:00
if ( ! socketName . empty ( ) & & socketFd ! = - 1 ) {
fcntl ( socketFd , F_SETFD , FD_CLOEXEC ) ;
2025-04-22 15:23:29 +02:00
const auto RETVAL = wl_display_add_socket_fd ( m_wlDisplay , socketFd ) ;
2024-07-21 13:09:54 +02:00
if ( RETVAL > = 0 ) {
2025-04-22 15:23:29 +02:00
m_wlDisplaySocket = socketName ;
2024-07-22 11:16:25 +00:00
Debug : : log ( LOG , " wl_display_add_socket_fd for {} succeeded with {} " , socketName , RETVAL ) ;
} else
Debug : : log ( WARN , " wl_display_add_socket_fd for {} returned {}: skipping " , socketName , RETVAL ) ;
} else {
// get socket, avoid using 0
for ( int candidate = 1 ; candidate < = 32 ; candidate + + ) {
const auto CANDIDATESTR = ( " wayland- " + std : : to_string ( candidate ) ) ;
2025-04-22 15:23:29 +02:00
const auto RETVAL = wl_display_add_socket ( m_wlDisplay , CANDIDATESTR . c_str ( ) ) ;
2024-07-22 11:16:25 +00:00
if ( RETVAL > = 0 ) {
2025-04-22 15:23:29 +02:00
m_wlDisplaySocket = CANDIDATESTR ;
2024-07-22 11:16:25 +00:00
Debug : : log ( LOG , " wl_display_add_socket for {} succeeded with {} " , CANDIDATESTR , RETVAL ) ;
break ;
} else
Debug : : log ( WARN , " wl_display_add_socket for {} returned {}: skipping candidate {} " , CANDIDATESTR , RETVAL , candidate ) ;
2024-07-21 13:09:54 +02:00
}
2022-03-16 21:37:21 +01:00
}
2025-04-22 15:23:29 +02:00
if ( m_wlDisplaySocket . empty ( ) ) {
2024-07-21 13:09:54 +02:00
Debug : : log ( WARN , " All candidates failed, trying wl_display_add_socket_auto " ) ;
2025-04-22 15:23:29 +02:00
const auto SOCKETSTR = wl_display_add_socket_auto ( m_wlDisplay ) ;
2024-07-21 13:09:54 +02:00
if ( SOCKETSTR )
2025-04-22 15:23:29 +02:00
m_wlDisplaySocket = SOCKETSTR ;
2024-07-21 13:09:54 +02:00
}
2022-03-24 17:17:08 +01:00
2025-04-22 15:23:29 +02:00
if ( m_wlDisplaySocket . empty ( ) ) {
2024-07-21 13:09:54 +02:00
Debug : : log ( CRIT , " m_szWLDisplaySocket NULL! " ) ;
throwError ( " m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed) " ) ;
2022-03-24 17:17:08 +01:00
}
2022-03-16 21:37:21 +01:00
2025-04-22 15:23:29 +02:00
setenv ( " WAYLAND_DISPLAY " , m_wlDisplaySocket . c_str ( ) , 1 ) ;
2024-08-15 19:14:48 +03:00
if ( ! getenv ( " XDG_CURRENT_DESKTOP " ) ) {
setenv ( " XDG_CURRENT_DESKTOP " , " Hyprland " , 1 ) ;
2025-04-22 15:23:29 +02:00
m_desktopEnvSet = true ;
2024-08-15 19:14:48 +03:00
}
2024-07-21 13:09:54 +02:00
2024-06-08 10:07:59 +02:00
initManagers ( STAGE_BASICINIT ) ;
2022-03-19 14:09:11 +01:00
2024-07-21 13:09:54 +02:00
initManagers ( STAGE_LATE ) ;
2024-08-26 17:25:39 +02:00
for ( auto const & o : pendingOutputs ) {
2024-07-21 13:09:54 +02:00
onNewMonitor ( o ) ;
2022-08-27 17:24:36 -04:00
}
2024-07-21 13:09:54 +02:00
pendingOutputs . clear ( ) ;
}
2022-08-27 17:10:13 -04:00
2024-07-21 13:09:54 +02:00
void CCompositor : : initAllSignals ( ) {
2025-07-08 09:56:40 -07:00
m_aqBackend - > events . newOutput . listenStatic ( [ this ] ( const SP < Aquamarine : : IOutput > & output ) {
Debug : : log ( LOG , " New aquamarine output with name {} " , output - > name ) ;
if ( m_initialized )
onNewMonitor ( output ) ;
else
pendingOutputs . emplace_back ( output ) ;
} ) ;
2022-11-05 18:04:44 +00:00
2025-07-08 09:56:40 -07:00
m_aqBackend - > events . newPointer . listenStatic ( [ ] ( const SP < Aquamarine : : IPointer > & dev ) {
Debug : : log ( LOG , " New aquamarine pointer with name {} " , dev - > getName ( ) ) ;
g_pInputManager - > newMouse ( dev ) ;
g_pInputManager - > updateCapabilities ( ) ;
} ) ;
2022-11-05 18:04:44 +00:00
2025-07-08 09:56:40 -07:00
m_aqBackend - > events . newKeyboard . listenStatic ( [ ] ( const SP < Aquamarine : : IKeyboard > & dev ) {
Debug : : log ( LOG , " New aquamarine keyboard with name {} " , dev - > getName ( ) ) ;
g_pInputManager - > newKeyboard ( dev ) ;
g_pInputManager - > updateCapabilities ( ) ;
} ) ;
2022-07-10 15:41:26 +02:00
2025-07-08 09:56:40 -07:00
m_aqBackend - > events . newTouch . listenStatic ( [ ] ( const SP < Aquamarine : : ITouch > & dev ) {
Debug : : log ( LOG , " New aquamarine touch with name {} " , dev - > getName ( ) ) ;
g_pInputManager - > newTouchDevice ( dev ) ;
g_pInputManager - > updateCapabilities ( ) ;
} ) ;
2022-07-10 15:41:26 +02:00
2025-07-08 09:56:40 -07:00
m_aqBackend - > events . newSwitch . listenStatic ( [ ] ( const SP < Aquamarine : : ISwitch > & dev ) {
Debug : : log ( LOG , " New aquamarine switch with name {} " , dev - > getName ( ) ) ;
g_pInputManager - > newSwitch ( dev ) ;
} ) ;
2022-08-05 13:03:37 +02:00
2025-07-08 09:56:40 -07:00
m_aqBackend - > events . newTablet . listenStatic ( [ ] ( const SP < Aquamarine : : ITablet > & dev ) {
Debug : : log ( LOG , " New aquamarine tablet with name {} " , dev - > getName ( ) ) ;
g_pInputManager - > newTablet ( dev ) ;
} ) ;
2022-08-27 17:10:13 -04:00
2025-07-08 09:56:40 -07:00
m_aqBackend - > events . newTabletPad . listenStatic ( [ ] ( const SP < Aquamarine : : ITabletPad > & dev ) {
Debug : : log ( LOG , " New aquamarine tablet pad with name {} " , dev - > getName ( ) ) ;
g_pInputManager - > newTabletPad ( dev ) ;
} ) ;
2022-04-21 15:59:28 +02:00
2025-07-08 09:56:40 -07:00
if ( m_aqBackend - > hasSession ( ) ) {
m_aqBackend - > session - > events . changeActive . listenStatic ( [ this ] {
if ( m_aqBackend - > session - > active ) {
Debug : : log ( LOG , " Session got activated! " ) ;
m_sessionActive = true ;
2024-01-19 16:20:22 +01:00
2025-07-08 09:56:40 -07:00
for ( auto const & m : m_monitors ) {
scheduleFrameForMonitor ( m ) ;
m - > applyMonitorRule ( & m - > m_activeMonitorRule , true ) ;
2024-07-21 13:09:54 +02:00
}
2025-07-08 09:56:40 -07:00
g_pConfigManager - > m_wantsMonitorReload = true ;
g_pCursorManager - > syncGsettings ( ) ;
} else {
Debug : : log ( LOG , " Session got deactivated! " ) ;
m_sessionActive = false ;
}
} ) ;
2024-07-21 13:09:54 +02:00
}
}
2024-01-19 16:20:22 +01:00
2024-07-21 13:09:54 +02:00
void CCompositor : : removeAllSignals ( ) {
;
2024-01-19 16:20:22 +01:00
}
2024-05-08 12:11:08 -05:00
void CCompositor : : cleanEnvironment ( ) {
// in compositor constructor
unsetenv ( " WAYLAND_DISPLAY " ) ;
// in startCompositor
unsetenv ( " HYPRLAND_INSTANCE_SIGNATURE " ) ;
// in main
unsetenv ( " HYPRLAND_CMD " ) ;
unsetenv ( " XDG_BACKEND " ) ;
2025-04-22 15:23:29 +02:00
if ( m_desktopEnvSet )
2024-08-15 19:14:48 +03:00
unsetenv ( " XDG_CURRENT_DESKTOP " ) ;
2024-05-08 12:11:08 -05:00
2025-04-22 15:23:29 +02:00
if ( m_aqBackend - > hasSession ( ) & & ! envEnabled ( " HYPRLAND_NO_SD_VARS " ) ) {
2024-05-08 12:11:08 -05:00
const auto CMD =
# ifdef USES_SYSTEMD
2024-06-23 12:43:17 +05:30
" systemctl --user unset-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash "
2024-05-08 12:11:08 -05:00
" dbus-update-activation-environment 2>/dev/null && "
# endif
2024-06-23 12:43:17 +05:30
" dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS " ;
2025-02-02 20:34:26 +03:00
CKeybindManager : : spawn ( CMD ) ;
2024-05-08 12:11:08 -05:00
}
}
2024-07-24 12:07:36 -05:00
void CCompositor : : stopCompositor ( ) {
Debug : : log ( LOG , " Hyprland is stopping! " ) ;
// this stops the wayland loop, wl_display_run
2025-04-22 15:23:29 +02:00
wl_display_terminate ( m_wlDisplay ) ;
m_isShuttingDown = true ;
2024-07-24 12:07:36 -05:00
}
2022-07-13 18:18:23 +02:00
void CCompositor : : cleanup ( ) {
2025-04-22 15:23:29 +02:00
if ( ! m_wlDisplay )
2022-04-21 15:59:28 +02:00
return ;
2024-02-23 08:48:27 -08:00
signal ( SIGABRT , SIG_DFL ) ;
signal ( SIGSEGV , SIG_DFL ) ;
2023-08-08 16:16:34 +02:00
removeLockFile ( ) ;
2025-04-22 15:23:29 +02:00
m_isShuttingDown = true ;
2025-04-21 20:42:02 +02:00
Debug : : m_shuttingDown = true ;
2022-10-24 00:14:42 +01:00
2023-07-06 16:24:49 +03:00
# ifdef USES_SYSTEMD
2024-12-07 18:51:18 +01:00
if ( NSystemd : : sdBooted ( ) > 0 & & ! envEnabled ( " HYPRLAND_NO_SD_NOTIFY " ) )
NSystemd : : sdNotify ( 0 , " STOPPING=1 " ) ;
2023-07-06 16:24:49 +03:00
# endif
2024-05-08 12:11:08 -05:00
cleanEnvironment ( ) ;
2023-03-01 00:11:49 +01:00
// unload all remaining plugins while the compositor is
// still in a normal working state.
g_pPluginSystem - > unloadAllPlugins ( ) ;
2025-04-22 15:23:29 +02:00
m_lastFocus . reset ( ) ;
m_lastWindow . reset ( ) ;
2022-05-29 00:07:31 +02:00
2025-04-22 15:23:29 +02:00
m_workspaces . clear ( ) ;
m_windows . clear ( ) ;
2022-05-29 00:00:47 +02:00
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2024-10-19 23:03:29 +01:00
g_pHyprOpenGL - > destroyMonitorResources ( m ) ;
2022-08-22 14:17:25 +02:00
2025-04-30 23:45:20 +02:00
m - > m_output - > state - > setEnabled ( false ) ;
m - > m_state . commit ( ) ;
2022-08-25 21:35:47 +02:00
}
2024-05-25 22:43:51 +02:00
g_pXWayland . reset ( ) ;
2022-10-06 18:43:50 +01:00
2025-04-22 15:23:29 +02:00
m_monitors . clear ( ) ;
2022-04-21 15:59:28 +02:00
2025-04-22 15:23:29 +02:00
wl_display_destroy_clients ( g_pCompositor - > m_wlDisplay ) ;
2024-01-19 16:20:22 +01:00
removeAllSignals ( ) ;
2024-02-23 18:50:54 -08:00
g_pInputManager . reset ( ) ;
2025-04-08 19:39:53 +02:00
g_pDynamicPermissionManager . reset ( ) ;
2024-01-19 16:20:22 +01:00
g_pDecorationPositioner . reset ( ) ;
2024-03-09 16:52:59 +00:00
g_pCursorManager . reset ( ) ;
2024-01-19 16:20:22 +01:00
g_pPluginSystem . reset ( ) ;
g_pHyprNotificationOverlay . reset ( ) ;
g_pDebugOverlay . reset ( ) ;
g_pEventManager . reset ( ) ;
g_pSessionLockManager . reset ( ) ;
g_pProtocolManager . reset ( ) ;
g_pHyprRenderer . reset ( ) ;
g_pHyprOpenGL . reset ( ) ;
g_pConfigManager . reset ( ) ;
g_pLayoutManager . reset ( ) ;
g_pHyprError . reset ( ) ;
g_pConfigManager . reset ( ) ;
g_pKeybindManager . reset ( ) ;
g_pHookSystem . reset ( ) ;
g_pXWaylandManager . reset ( ) ;
2024-05-10 18:27:57 +01:00
g_pPointerManager . reset ( ) ;
g_pSeatManager . reset ( ) ;
2024-06-13 12:08:02 +02:00
g_pHyprCtl . reset ( ) ;
g_pEventLoopManager . reset ( ) ;
2025-01-10 19:09:40 +01:00
g_pVersionKeeperMgr . reset ( ) ;
g_pDonationNagManager . reset ( ) ;
2025-02-18 15:10:40 +00:00
g_pANRManager . reset ( ) ;
2025-01-19 15:39:19 +01:00
g_pConfigWatcher . reset ( ) ;
2025-09-18 22:10:30 +02:00
g_pAsyncResourceGatherer . reset ( ) ;
2024-05-03 15:42:08 +02:00
2025-04-22 15:23:29 +02:00
if ( m_aqBackend )
m_aqBackend . reset ( ) ;
2024-05-03 15:42:08 +02:00
2024-06-06 20:27:09 +02:00
if ( m_critSigSource )
wl_event_source_remove ( m_critSigSource ) ;
2024-07-24 12:07:36 -05:00
// this frees all wayland resources, including sockets
2025-04-22 15:23:29 +02:00
wl_display_destroy ( m_wlDisplay ) ;
2024-07-31 21:00:14 +02:00
Debug : : close ( ) ;
2022-03-18 23:52:36 +01:00
}
2022-03-17 17:08:54 +01:00
2023-03-05 13:37:21 +00:00
void CCompositor : : initManagers ( eManagersInitStage stage ) {
switch ( stage ) {
case STAGE_PRIORITY : {
2024-04-07 03:31:51 +01:00
Debug : : log ( LOG , " Creating the EventLoopManager! " ) ;
2025-04-22 15:23:29 +02:00
g_pEventLoopManager = makeUnique < CEventLoopManager > ( m_wlDisplay , m_wlEventLoop ) ;
2024-04-07 03:31:51 +01:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the HookSystem! " ) ;
2025-01-23 21:55:41 +01:00
g_pHookSystem = makeUnique < CHookSystemManager > ( ) ;
2022-03-19 17:48:18 +01:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the KeybindManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pKeybindManager = makeUnique < CKeybindManager > ( ) ;
2022-04-23 21:47:16 +02:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the AnimationManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pAnimationManager = makeUnique < CHyprAnimationManager > ( ) ;
2022-07-16 15:57:31 +02:00
2025-04-08 19:39:53 +02:00
Debug : : log ( LOG , " Creating the DynamicPermissionManager! " ) ;
g_pDynamicPermissionManager = makeUnique < CDynamicPermissionManager > ( ) ;
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the ConfigManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pConfigManager = makeUnique < CConfigManager > ( ) ;
2022-03-17 17:08:54 +01:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the CHyprError! " ) ;
2025-01-23 21:55:41 +01:00
g_pHyprError = makeUnique < CHyprError > ( ) ;
2023-01-20 20:48:07 +01:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the LayoutManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pLayoutManager = makeUnique < CLayoutManager > ( ) ;
2022-03-17 17:08:54 +01:00
2024-04-23 01:27:08 +01:00
Debug : : log ( LOG , " Creating the TokenManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pTokenManager = makeUnique < CTokenManager > ( ) ;
2024-04-23 01:27:08 +01:00
2023-03-05 13:37:21 +00:00
g_pConfigManager - > init ( ) ;
2024-05-05 22:18:10 +01:00
Debug : : log ( LOG , " Creating the PointerManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pPointerManager = makeUnique < CPointerManager > ( ) ;
2024-07-21 13:09:54 +02:00
Debug : : log ( LOG , " Creating the EventManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pEventManager = makeUnique < CEventManager > ( ) ;
2025-09-18 22:10:30 +02:00
Debug : : log ( LOG , " Creating the AsyncResourceGatherer! " ) ;
g_pAsyncResourceGatherer = makeUnique < Hyprgraphics : : CAsyncResourceGatherer > ( ) ;
2023-03-05 13:37:21 +00:00
} break ;
2024-06-08 10:07:59 +02:00
case STAGE_BASICINIT : {
2024-06-09 21:10:46 +02:00
Debug : : log ( LOG , " Creating the CHyprOpenGLImpl! " ) ;
2025-01-23 21:55:41 +01:00
g_pHyprOpenGL = makeUnique < CHyprOpenGLImpl > ( ) ;
2024-06-09 21:10:46 +02:00
2024-06-08 10:07:59 +02:00
Debug : : log ( LOG , " Creating the ProtocolManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pProtocolManager = makeUnique < CProtocolManager > ( ) ;
2024-06-08 10:07:59 +02:00
Debug : : log ( LOG , " Creating the SeatManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pSeatManager = makeUnique < CSeatManager > ( ) ;
2024-06-08 10:07:59 +02:00
} break ;
2023-03-05 13:37:21 +00:00
case STAGE_LATE : {
2024-02-05 01:43:45 +00:00
Debug : : log ( LOG , " Creating CHyprCtl " ) ;
2025-01-23 21:55:41 +01:00
g_pHyprCtl = makeUnique < CHyprCtl > ( ) ;
2024-02-05 01:43:45 +00:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the InputManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pInputManager = makeUnique < CInputManager > ( ) ;
2022-04-04 19:44:25 +02:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the HyprRenderer! " ) ;
2025-01-23 21:55:41 +01:00
g_pHyprRenderer = makeUnique < CHyprRenderer > ( ) ;
2022-03-19 15:59:53 +01:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the XWaylandManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pXWaylandManager = makeUnique < CHyprXWaylandManager > ( ) ;
2022-12-05 17:05:15 +00:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the SessionLockManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pSessionLockManager = makeUnique < CSessionLockManager > ( ) ;
2022-05-28 17:32:19 +02:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the HyprDebugOverlay! " ) ;
2025-01-23 21:55:41 +01:00
g_pDebugOverlay = makeUnique < CHyprDebugOverlay > ( ) ;
2023-02-27 12:32:38 +00:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the HyprNotificationOverlay! " ) ;
2025-01-23 21:55:41 +01:00
g_pHyprNotificationOverlay = makeUnique < CHyprNotificationOverlay > ( ) ;
2022-03-17 17:08:54 +01:00
2023-03-05 13:37:21 +00:00
Debug : : log ( LOG , " Creating the PluginSystem! " ) ;
2025-01-23 21:55:41 +01:00
g_pPluginSystem = makeUnique < CPluginSystem > ( ) ;
2023-05-01 07:10:53 -07:00
g_pConfigManager - > handlePluginLoads ( ) ;
2023-11-11 14:37:17 +00:00
Debug : : log ( LOG , " Creating the DecorationPositioner! " ) ;
2025-01-23 21:55:41 +01:00
g_pDecorationPositioner = makeUnique < CDecorationPositioner > ( ) ;
2024-03-09 16:52:59 +00:00
Debug : : log ( LOG , " Creating the CursorManager! " ) ;
2025-01-23 21:55:41 +01:00
g_pCursorManager = makeUnique < CCursorManager > ( ) ;
2024-05-25 22:43:51 +02:00
2024-12-06 15:45:02 +01:00
Debug : : log ( LOG , " Creating the VersionKeeper! " ) ;
2025-01-23 21:55:41 +01:00
g_pVersionKeeperMgr = makeUnique < CVersionKeeperManager > ( ) ;
2024-12-06 15:45:02 +01:00
2025-01-10 19:09:40 +01:00
Debug : : log ( LOG , " Creating the DonationNag! " ) ;
2025-01-23 21:55:41 +01:00
g_pDonationNagManager = makeUnique < CDonationNagManager > ( ) ;
2025-01-10 19:09:40 +01:00
2025-02-18 15:10:40 +00:00
Debug : : log ( LOG , " Creating the ANRManager! " ) ;
g_pANRManager = makeUnique < CANRManager > ( ) ;
2024-05-25 22:43:51 +02:00
Debug : : log ( LOG , " Starting XWayland " ) ;
2025-04-22 15:23:29 +02:00
g_pXWayland = makeUnique < CXWayland > ( g_pCompositor - > m_wantsXwayland ) ;
2023-03-05 13:37:21 +00:00
} break ;
default : UNREACHABLE ( ) ;
}
}
2022-08-01 23:31:25 +02:00
2023-08-08 16:16:34 +02:00
void CCompositor : : createLockFile ( ) {
2025-04-22 15:23:29 +02:00
const auto PATH = m_instancePath + " /hyprland.lock " ;
2023-08-08 16:16:34 +02:00
std : : ofstream ofs ( PATH , std : : ios : : trunc ) ;
2025-05-05 23:44:49 +02:00
ofs < < m_hyprlandPID < < " \n " < < m_wlDisplaySocket < < " \n " ;
2023-08-08 16:16:34 +02:00
ofs . close ( ) ;
}
void CCompositor : : removeLockFile ( ) {
2025-04-22 15:23:29 +02:00
const auto PATH = m_instancePath + " /hyprland.lock " ;
2023-08-08 16:16:34 +02:00
if ( std : : filesystem : : exists ( PATH ) )
std : : filesystem : : remove ( PATH ) ;
}
2023-11-01 18:53:36 +00:00
void CCompositor : : prepareFallbackOutput ( ) {
// create a backup monitor
2024-07-21 13:09:54 +02:00
SP < Aquamarine : : IBackendImplementation > headless ;
2025-04-22 15:23:29 +02:00
for ( auto const & impl : m_aqBackend - > getImplementations ( ) ) {
2024-07-21 13:09:54 +02:00
if ( impl - > type ( ) = = Aquamarine : : AQ_BACKEND_HEADLESS ) {
headless = impl ;
break ;
}
}
2023-11-01 18:53:36 +00:00
if ( ! headless ) {
2024-07-21 13:09:54 +02:00
Debug : : log ( WARN , " No headless in prepareFallbackOutput?! " ) ;
2023-11-01 18:53:36 +00:00
return ;
}
2024-07-21 13:09:54 +02:00
headless - > createOutput ( ) ;
2025-07-22 11:14:04 +02:00
if ( m_monitors . empty ( ) )
enterUnsafeState ( ) ;
2023-11-01 18:53:36 +00:00
}
2024-07-22 11:16:25 +00:00
void CCompositor : : startCompositor ( ) {
2022-03-16 21:37:21 +01:00
signal ( SIGPIPE , SIG_IGN ) ;
2024-08-16 10:19:08 +03:00
if (
/* Session-less Hyprland usually means a nest, don't update the env in that case */
2025-04-22 15:23:29 +02:00
m_aqBackend - > hasSession ( ) & &
2024-08-16 10:19:08 +03:00
/* Activation environment management is not disabled */
! envEnabled ( " HYPRLAND_NO_SD_VARS " ) ) {
2023-12-26 17:16:59 +00:00
const auto CMD =
2023-04-15 19:03:09 +00:00
# ifdef USES_SYSTEMD
2024-06-23 12:43:17 +05:30
" systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash "
2023-12-26 17:16:59 +00:00
" dbus-update-activation-environment 2>/dev/null && "
2023-04-15 19:03:09 +00:00
# endif
2024-06-23 12:43:17 +05:30
" dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS " ;
2025-02-02 20:34:26 +03:00
CKeybindManager : : spawn ( CMD ) ;
2023-12-26 17:16:59 +00:00
}
2022-11-04 10:37:14 +00:00
2025-04-22 15:23:29 +02:00
Debug : : log ( LOG , " Running on WAYLAND_DISPLAY: {} " , m_wlDisplaySocket ) ;
2022-03-16 21:37:21 +01:00
2023-11-01 18:53:36 +00:00
prepareFallbackOutput ( ) ;
2023-09-29 17:51:07 +01:00
g_pHyprRenderer - > setCursorFromName ( " left_ptr " ) ;
2022-03-16 21:37:21 +01:00
2023-01-05 14:17:55 -05:00
# ifdef USES_SYSTEMD
2024-12-07 18:51:18 +01:00
if ( NSystemd : : sdBooted ( ) > 0 ) {
2023-01-05 14:17:55 -05:00
// tell systemd that we are ready so it can start other bond, following, related units
2023-12-21 22:27:12 +01:00
if ( ! envEnabled ( " HYPRLAND_NO_SD_NOTIFY " ) )
2024-12-07 18:51:18 +01:00
NSystemd : : sdNotify ( 0 , " READY=1 " ) ;
2023-12-21 22:27:12 +01:00
} else
2023-01-05 14:17:55 -05:00
Debug : : log ( LOG , " systemd integration is baked in but system itself is not booted à la systemd! " ) ;
# endif
2023-08-08 16:16:34 +02:00
createLockFile ( ) ;
2024-06-08 10:07:59 +02:00
EMIT_HOOK_EVENT ( " ready " , nullptr ) ;
2022-03-16 21:37:21 +01:00
// This blocks until we are done.
Debug : : log ( LOG , " Hyprland is ready, running the event loop! " ) ;
2024-07-05 23:05:03 +02:00
g_pEventLoopManager - > enterLoop ( ) ;
2022-03-17 20:22:29 +01:00
}
2024-10-19 23:03:29 +01:00
PHLMONITOR CCompositor : : getMonitorFromID ( const MONITORID & id ) {
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2025-04-30 23:45:20 +02:00
if ( m - > m_id = = id ) {
2024-10-19 23:03:29 +01:00
return m ;
2022-03-17 20:22:29 +01:00
}
}
return nullptr ;
}
2024-10-19 23:03:29 +01:00
PHLMONITOR CCompositor : : getMonitorFromName ( const std : : string & name ) {
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2025-04-30 23:45:20 +02:00
if ( m - > m_name = = name ) {
2024-10-19 23:03:29 +01:00
return m ;
2023-07-09 21:10:35 +00:00
}
}
return nullptr ;
}
2024-10-19 23:03:29 +01:00
PHLMONITOR CCompositor : : getMonitorFromDesc ( const std : : string & desc ) {
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2025-04-30 23:45:20 +02:00
if ( m - > m_description . starts_with ( desc ) )
2024-10-19 23:03:29 +01:00
return m ;
2022-05-29 20:15:34 +02:00
}
return nullptr ;
}
2024-10-19 23:03:29 +01:00
PHLMONITOR CCompositor : : getMonitorFromCursor ( ) {
2024-05-05 22:18:10 +01:00
return getMonitorFromVector ( g_pPointerManager - > position ( ) ) ;
2022-03-18 22:35:51 +01:00
}
2024-10-19 23:03:29 +01:00
PHLMONITOR CCompositor : : getMonitorFromVector ( const Vector2D & point ) {
2025-04-22 15:23:29 +02:00
if ( m_monitors . empty ( ) ) {
2025-03-08 14:14:03 -05:00
Debug : : log ( WARN , " getMonitorFromVector called with empty monitor list " ) ;
return nullptr ;
}
2024-10-26 02:06:13 +01:00
PHLMONITOR mon ;
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2025-04-30 23:45:20 +02:00
if ( CBox { m - > m_position , m - > m_size } . containsPoint ( point ) ) {
2024-05-05 22:18:10 +01:00
mon = m ;
break ;
}
}
2022-03-20 11:22:55 +01:00
2024-05-05 22:18:10 +01:00
if ( ! mon ) {
2024-10-26 02:06:13 +01:00
float bestDistance = 0.f ;
PHLMONITOR pBestMon ;
2022-05-25 18:40:03 +02:00
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2025-04-30 23:45:20 +02:00
float dist = vecToRectDistanceSquared ( point , m - > m_position , m - > m_position + m - > m_size ) ;
2022-05-25 18:40:03 +02:00
if ( dist < bestDistance | | ! pBestMon ) {
bestDistance = dist ;
2024-05-05 22:18:10 +01:00
pBestMon = m ;
2022-05-25 18:40:03 +02:00
}
}
if ( ! pBestMon ) { // ?????
Debug : : log ( WARN , " getMonitorFromVector no close mon??? " ) ;
2025-04-22 15:23:29 +02:00
return m_monitors . front ( ) ;
2022-05-25 18:40:03 +02:00
}
2024-10-19 23:03:29 +01:00
return pBestMon ;
2022-03-20 11:22:55 +01:00
}
2024-10-19 23:03:29 +01:00
return mon ;
2022-03-20 11:22:55 +01:00
}
2024-04-27 12:43:12 +01:00
void CCompositor : : removeWindowFromVectorSafe ( PHLWINDOW pWindow ) {
2025-04-28 22:25:22 +02:00
if ( ! pWindow - > m_fadingOut ) {
2024-04-19 22:16:35 +01:00
EMIT_HOOK_EVENT ( " destroyWindow " , pWindow ) ;
2025-04-22 15:23:29 +02:00
std : : erase_if ( m_windows , [ & ] ( SP < CWindow > & el ) { return el = = pWindow ; } ) ;
std : : erase_if ( m_windowsFadingOut , [ & ] ( PHLWINDOWREF el ) { return el . lock ( ) = = pWindow ; } ) ;
2024-04-06 14:51:35 +01:00
}
2022-03-18 22:53:27 +01:00
}
2024-10-19 23:03:29 +01:00
bool CCompositor : : monitorExists ( PHLMONITOR pMonitor ) {
2025-04-22 15:23:29 +02:00
return std : : ranges : : any_of ( m_realMonitors , [ & ] ( const PHLMONITOR & m ) { return m = = pMonitor ; } ) ;
2024-02-07 00:18:47 +00:00
}
2024-04-27 12:43:12 +01:00
PHLWINDOW CCompositor : : vectorToWindowUnified ( const Vector2D & pos , uint8_t properties , PHLWINDOW pIgnoreWindow ) {
2025-11-05 21:06:31 -03:00
const auto PMONITOR = getMonitorFromVector ( pos ) ;
static auto PRESIZEONBORDER = CConfigValue < Hyprlang : : INT > ( " general:resize_on_border " ) ;
static auto PBORDERSIZE = CConfigValue < Hyprlang : : INT > ( " general:border_size " ) ;
static auto PBORDERGRABEXTEND = CConfigValue < Hyprlang : : INT > ( " general:extend_border_grab_area " ) ;
static auto PSPECIALFALLTHRU = CConfigValue < Hyprlang : : INT > ( " input:special_fallthrough " ) ;
static auto PMODALPARENTBLOCKING = CConfigValue < Hyprlang : : INT > ( " general:modal_parent_blocking " ) ;
const auto BORDER_GRAB_AREA = * PRESIZEONBORDER ? * PBORDERSIZE + * PBORDERGRABEXTEND : 0 ;
const bool ONLY_PRIORITY = properties & FOCUS_PRIORITY ;
const auto isShadowedByModal = [ ] ( PHLWINDOW w ) - > bool {
return * PMODALPARENTBLOCKING & & w - > m_xdgSurface & & w - > m_xdgSurface - > m_toplevel & & w - > m_xdgSurface - > m_toplevel - > anyChildModal ( ) ;
} ;
2022-05-31 14:01:00 +02:00
2022-09-10 13:11:02 +02:00
// pinned windows on top of floating regardless
2024-02-04 15:40:20 +00:00
if ( properties & ALLOW_FLOATING ) {
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows | std : : views : : reverse ) {
2025-05-18 19:34:14 +02:00
if ( ONLY_PRIORITY & & ! w - > priorityFocus ( ) )
continue ;
2025-11-05 21:06:31 -03:00
if ( w - > m_isFloating & & w - > m_isMapped & & ! w - > isHidden ( ) & & ! w - > m_X11ShouldntFocus & & w - > m_pinned & & ! w - > m_windowData . noFocus . valueOrDefault ( ) & & w ! = pIgnoreWindow & &
! isShadowedByModal ( w ) ) {
2024-10-31 00:20:32 +01:00
const auto BB = w - > getWindowBoxUnified ( properties ) ;
CBox box = BB . copy ( ) . expand ( ! w - > isX11OverrideRedirect ( ) ? BORDER_GRAB_AREA : 0 ) ;
2024-05-05 22:18:10 +01:00
if ( box . containsPoint ( g_pPointerManager - > position ( ) ) )
2024-04-27 12:43:12 +01:00
return w ;
2024-02-04 15:40:20 +00:00
2025-04-28 22:25:22 +02:00
if ( ! w - > m_isX11 ) {
2024-02-04 15:40:20 +00:00
if ( w - > hasPopupAt ( pos ) )
2024-04-27 12:43:12 +01:00
return w ;
2024-02-04 15:40:20 +00:00
}
2022-09-10 13:11:02 +02:00
}
2022-09-25 20:07:48 +02:00
}
2022-09-10 13:11:02 +02:00
}
2024-04-27 12:43:12 +01:00
auto windowForWorkspace = [ & ] ( bool special ) - > PHLWINDOW {
auto floating = [ & ] ( bool aboveFullscreen ) - > PHLWINDOW {
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows | std : : views : : reverse ) {
2023-11-18 17:00:24 +00:00
2024-04-02 20:32:39 +01:00
if ( special & & ! w - > onSpecialWorkspace ( ) ) // because special floating may creep up into regular
2023-11-18 17:00:24 +00:00
continue ;
2025-04-28 22:25:22 +02:00
if ( ! w - > m_workspace )
2024-11-22 16:01:02 +00:00
continue ;
2025-05-18 19:34:14 +02:00
if ( ONLY_PRIORITY & & ! w - > priorityFocus ( ) )
continue ;
2025-04-28 22:25:22 +02:00
const auto PWINDOWMONITOR = w - > m_monitor . lock ( ) ;
2024-03-03 17:04:39 +00:00
// to avoid focusing windows behind special workspaces from other monitors
2025-04-30 23:45:20 +02:00
if ( ! * PSPECIALFALLTHRU & & PWINDOWMONITOR & & PWINDOWMONITOR - > m_activeSpecialWorkspace & & w - > m_workspace ! = PWINDOWMONITOR - > m_activeSpecialWorkspace ) {
2024-10-31 00:20:32 +01:00
const auto BB = w - > getWindowBoxUnified ( properties ) ;
2025-04-30 23:45:20 +02:00
if ( BB . x > = PWINDOWMONITOR - > m_position . x & & BB . y > = PWINDOWMONITOR - > m_position . y & &
BB . x + BB . width < = PWINDOWMONITOR - > m_position . x + PWINDOWMONITOR - > m_size . x & & BB . y + BB . height < = PWINDOWMONITOR - > m_position . y + PWINDOWMONITOR - > m_size . y )
2024-10-31 00:20:32 +01:00
continue ;
}
2024-03-03 17:04:39 +00:00
2025-04-28 22:25:22 +02:00
if ( w - > m_isFloating & & w - > m_isMapped & & w - > m_workspace - > isVisible ( ) & & ! w - > isHidden ( ) & & ! w - > m_pinned & & ! w - > m_windowData . noFocus . valueOrDefault ( ) & &
2025-11-05 21:06:31 -03:00
w ! = pIgnoreWindow & & ( ! aboveFullscreen | | w - > m_createdOverFullscreen ) & & ! isShadowedByModal ( w ) ) {
2024-02-04 15:40:20 +00:00
// OR windows should add focus to parent
2025-04-28 22:25:22 +02:00
if ( w - > m_X11ShouldntFocus & & ! w - > isX11OverrideRedirect ( ) )
2024-02-04 15:40:20 +00:00
continue ;
2022-10-01 18:25:02 +01:00
2024-10-31 00:20:32 +01:00
const auto BB = w - > getWindowBoxUnified ( properties ) ;
CBox box = BB . copy ( ) . expand ( ! w - > isX11OverrideRedirect ( ) ? BORDER_GRAB_AREA : 0 ) ;
2024-05-05 22:18:10 +01:00
if ( box . containsPoint ( g_pPointerManager - > position ( ) ) ) {
2023-11-18 17:00:24 +00:00
2025-04-28 22:25:22 +02:00
if ( w - > m_isX11 & & w - > isX11OverrideRedirect ( ) & & ! w - > m_xwaylandSurface - > wantsFocus ( ) ) {
2024-02-04 15:40:20 +00:00
// Override Redirect
2025-04-22 15:23:29 +02:00
return g_pCompositor - > m_lastWindow . lock ( ) ; // we kinda trick everything here.
2025-04-14 02:07:53 -07:00
// TODO: this is wrong, we should focus the parent, but idk how to get it considering it's nullptr in most cases.
2024-02-04 15:40:20 +00:00
}
2022-10-01 18:25:02 +01:00
2024-04-27 12:43:12 +01:00
return w ;
2024-02-04 15:40:20 +00:00
}
2025-04-28 22:25:22 +02:00
if ( ! w - > m_isX11 ) {
2024-02-04 15:40:20 +00:00
if ( w - > hasPopupAt ( pos ) )
2024-04-27 12:43:12 +01:00
return w ;
2024-02-04 15:40:20 +00:00
}
2023-11-18 17:00:24 +00:00
}
2022-10-01 18:25:02 +01:00
}
2024-02-22 15:42:17 +00:00
return nullptr ;
} ;
if ( properties & ALLOW_FLOATING ) {
// first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter.
auto found = floating ( true ) ;
if ( found )
return found ;
2023-11-18 17:00:24 +00:00
}
2022-07-12 09:49:56 +02:00
2024-02-04 15:40:20 +00:00
if ( properties & FLOATING_ONLY )
2024-02-22 15:42:17 +00:00
return floating ( false ) ;
2024-02-04 15:40:20 +00:00
2024-08-08 21:01:50 +02:00
const WORKSPACEID WSPID = special ? PMONITOR - > activeSpecialWorkspaceID ( ) : PMONITOR - > activeWorkspaceID ( ) ;
const auto PWORKSPACE = getWorkspaceByID ( WSPID ) ;
2023-12-03 12:53:12 +00:00
2025-05-18 19:34:14 +02:00
if ( PWORKSPACE - > m_hasFullscreenWindow & & ! ( properties & SKIP_FULLSCREEN_PRIORITY ) & & ! ONLY_PRIORITY )
2024-11-22 16:01:02 +00:00
return PWORKSPACE - > getFullscreenWindow ( ) ;
2023-12-03 12:53:12 +00:00
2024-02-22 15:42:17 +00:00
auto found = floating ( false ) ;
if ( found )
return found ;
2023-11-18 17:00:24 +00:00
// for windows, we need to check their extensions too, first.
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-05-18 19:34:14 +02:00
if ( ONLY_PRIORITY & & ! w - > priorityFocus ( ) )
continue ;
2024-04-02 20:32:39 +01:00
if ( special ! = w - > onSpecialWorkspace ( ) )
2023-11-18 17:00:24 +00:00
continue ;
2025-04-28 22:25:22 +02:00
if ( ! w - > m_workspace )
2024-11-22 16:01:02 +00:00
continue ;
2025-04-28 22:25:22 +02:00
if ( ! w - > m_isX11 & & ! w - > m_isFloating & & w - > m_isMapped & & w - > workspaceID ( ) = = WSPID & & ! w - > isHidden ( ) & & ! w - > m_X11ShouldntFocus & &
2025-11-05 21:06:31 -03:00
! w - > m_windowData . noFocus . valueOrDefault ( ) & & w ! = pIgnoreWindow & & ! isShadowedByModal ( w ) ) {
2023-12-03 12:53:12 +00:00
if ( w - > hasPopupAt ( pos ) )
2024-04-27 12:43:12 +01:00
return w ;
2022-07-12 09:49:56 +02:00
}
2022-09-25 20:07:48 +02:00
}
2023-12-03 12:53:12 +00:00
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-05-18 19:34:14 +02:00
if ( ONLY_PRIORITY & & ! w - > priorityFocus ( ) )
continue ;
2024-04-02 20:32:39 +01:00
if ( special ! = w - > onSpecialWorkspace ( ) )
2023-11-18 17:00:24 +00:00
continue ;
2022-03-20 11:14:24 +01:00
2025-04-28 22:25:22 +02:00
if ( ! w - > m_workspace )
2024-11-22 16:01:02 +00:00
continue ;
2025-04-28 22:25:22 +02:00
if ( ! w - > m_isFloating & & w - > m_isMapped & & w - > workspaceID ( ) = = WSPID & & ! w - > isHidden ( ) & & ! w - > m_X11ShouldntFocus & & ! w - > m_windowData . noFocus . valueOrDefault ( ) & &
2025-11-05 21:06:31 -03:00
w ! = pIgnoreWindow & & ! isShadowedByModal ( w ) ) {
2025-04-28 22:25:22 +02:00
CBox box = ( properties & USE_PROP_TILED ) ? w - > getWindowBoxUnified ( properties ) : CBox { w - > m_position , w - > m_size } ;
2024-10-31 00:20:32 +01:00
if ( box . containsPoint ( pos ) )
return w ;
}
2022-07-12 09:49:56 +02:00
}
2022-03-19 20:30:21 +01:00
2023-11-18 17:00:24 +00:00
return nullptr ;
} ;
// special workspace
2025-04-30 23:45:20 +02:00
if ( PMONITOR - > m_activeSpecialWorkspace & & ! * PSPECIALFALLTHRU )
2023-11-18 17:00:24 +00:00
return windowForWorkspace ( true ) ;
2025-04-30 23:45:20 +02:00
if ( PMONITOR - > m_activeSpecialWorkspace ) {
2024-01-09 13:17:55 +01:00
const auto PWINDOW = windowForWorkspace ( true ) ;
if ( PWINDOW )
return PWINDOW ;
}
2023-11-18 17:00:24 +00:00
return windowForWorkspace ( false ) ;
2022-03-19 20:30:21 +01:00
}
2024-06-08 10:07:59 +02:00
SP < CWLSurfaceResource > CCompositor : : vectorWindowToSurface ( const Vector2D & pos , PHLWINDOW pWindow , Vector2D & sl ) {
2022-04-02 13:02:16 +02:00
2024-04-27 12:43:12 +01:00
if ( ! validMapped ( pWindow ) )
2022-04-02 13:02:16 +02:00
return nullptr ;
2025-04-28 22:25:22 +02:00
RASSERT ( ! pWindow - > m_isX11 , " Cannot call vectorWindowToSurface on an X11 window! " ) ;
2022-04-02 13:02:16 +02:00
2024-05-10 23:28:33 +01:00
// try popups first
2025-04-28 22:25:22 +02:00
const auto PPOPUP = pWindow - > m_popupHead - > at ( pos ) ;
2022-04-02 13:02:16 +02:00
2024-06-08 10:07:59 +02:00
if ( PPOPUP ) {
2024-05-10 23:28:33 +01:00
const auto OFF = PPOPUP - > coordsRelativeToParent ( ) ;
2025-04-28 22:25:22 +02:00
sl = pos - pWindow - > m_realPosition - > goal ( ) - OFF ;
2025-04-24 20:49:49 +02:00
return PPOPUP - > m_wlSurface - > resource ( ) ;
2024-05-10 23:28:33 +01:00
}
2022-04-02 13:02:16 +02:00
2025-04-28 22:25:22 +02:00
auto [ surf , local ] = pWindow - > m_wlSurface - > resource ( ) - > at ( pos - pWindow - > m_realPosition - > goal ( ) , true ) ;
2024-06-08 10:07:59 +02:00
if ( surf ) {
sl = local ;
return surf ;
2022-04-02 13:02:16 +02:00
}
2024-06-08 10:07:59 +02:00
return nullptr ;
2022-04-02 13:02:16 +02:00
}
2024-06-08 10:07:59 +02:00
Vector2D CCompositor : : vectorToSurfaceLocal ( const Vector2D & vec , PHLWINDOW pWindow , SP < CWLSurfaceResource > pSurface ) {
2024-04-27 12:43:12 +01:00
if ( ! validMapped ( pWindow ) )
2023-09-23 01:21:59 +01:00
return { } ;
2025-04-28 22:25:22 +02:00
if ( pWindow - > m_isX11 )
return vec - pWindow - > m_realPosition - > goal ( ) ;
2023-09-23 01:21:59 +01:00
2025-04-28 22:25:22 +02:00
const auto PPOPUP = pWindow - > m_popupHead - > at ( vec ) ;
2024-05-10 23:28:33 +01:00
if ( PPOPUP )
return vec - PPOPUP - > coordsGlobal ( ) ;
2023-09-23 01:21:59 +01:00
2024-06-08 10:07:59 +02:00
std : : tuple < SP < CWLSurfaceResource > , Vector2D > iterData = { pSurface , { - 1337 , - 1337 } } ;
2023-09-23 01:21:59 +01:00
2025-04-28 22:25:22 +02:00
pWindow - > m_wlSurface - > resource ( ) - > breadthfirst (
2024-06-08 10:07:59 +02:00
[ ] ( SP < CWLSurfaceResource > surf , const Vector2D & offset , void * data ) {
2025-08-14 19:44:56 +05:00
const auto PDATA = sc < std : : tuple < SP < CWLSurfaceResource > , Vector2D > * > ( data ) ;
2024-06-08 10:07:59 +02:00
if ( surf = = std : : get < 0 > ( * PDATA ) )
std : : get < 1 > ( * PDATA ) = offset ;
2023-09-23 01:21:59 +01:00
} ,
& iterData ) ;
2025-05-04 23:39:00 +02:00
CBox geom = pWindow - > m_xdgSurface - > m_current . geometry ;
2023-10-30 19:36:34 +00:00
2024-06-08 10:07:59 +02:00
if ( std : : get < 1 > ( iterData ) = = Vector2D { - 1337 , - 1337 } )
2025-04-28 22:25:22 +02:00
return vec - pWindow - > m_realPosition - > goal ( ) ;
2023-09-23 01:21:59 +01:00
2025-04-28 22:25:22 +02:00
return vec - pWindow - > m_realPosition - > goal ( ) - std : : get < 1 > ( iterData ) + Vector2D { geom . x , geom . y } ;
2023-09-23 01:21:59 +01:00
}
2024-10-19 23:03:29 +01:00
PHLMONITOR CCompositor : : getMonitorFromOutput ( SP < Aquamarine : : IOutput > out ) {
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2025-04-30 23:45:20 +02:00
if ( m - > m_output = = out ) {
2024-10-19 23:03:29 +01:00
return m ;
2022-03-19 20:56:19 +01:00
}
}
return nullptr ;
}
2024-10-19 23:03:29 +01:00
PHLMONITOR CCompositor : : getRealMonitorFromOutput ( SP < Aquamarine : : IOutput > out ) {
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_realMonitors ) {
2025-04-30 23:45:20 +02:00
if ( m - > m_output = = out ) {
2024-10-19 23:03:29 +01:00
return m ;
2024-02-18 03:24:01 +01:00
}
}
return nullptr ;
}
2025-02-03 04:34:30 +03:00
void CCompositor : : focusWindow ( PHLWINDOW pWindow , SP < CWLSurfaceResource > pSurface , bool preserveFocusHistory ) {
2022-03-18 22:53:27 +01:00
2025-11-05 21:06:31 -03:00
static auto PFOLLOWMOUSE = CConfigValue < Hyprlang : : INT > ( " input:follow_mouse " ) ;
static auto PSPECIALFALLTHROUGH = CConfigValue < Hyprlang : : INT > ( " input:special_fallthrough " ) ;
static auto PMODALPARENTBLOCKING = CConfigValue < Hyprlang : : INT > ( " general:modal_parent_blocking " ) ;
if ( * PMODALPARENTBLOCKING & & pWindow & & pWindow - > m_xdgSurface & & pWindow - > m_xdgSurface - > m_toplevel & & pWindow - > m_xdgSurface - > m_toplevel - > anyChildModal ( ) ) {
Debug : : log ( LOG , " Refusing focus to window shadowed by modal dialog " ) ;
return ;
}
2023-10-26 22:17:49 +01:00
2025-05-18 19:34:14 +02:00
if ( ! pWindow | | ! pWindow - > priorityFocus ( ) ) {
if ( g_pSessionLockManager - > isSessionLocked ( ) ) {
Debug : : log ( LOG , " Refusing a keyboard focus to a window because of a sessionlock " ) ;
return ;
}
2022-04-18 17:16:01 +02:00
2025-05-18 19:34:14 +02:00
if ( ! g_pInputManager - > m_exclusiveLSes . empty ( ) ) {
Debug : : log ( LOG , " Refusing a keyboard focus to a window because of an exclusive ls " ) ;
return ;
}
2024-01-19 16:45:34 +01:00
}
2025-04-28 22:25:22 +02:00
if ( pWindow & & pWindow - > m_isX11 & & pWindow - > isX11OverrideRedirect ( ) & & ! pWindow - > m_xwaylandSurface - > wantsFocus ( ) )
2024-03-09 22:39:23 +00:00
return ;
2023-09-12 14:37:08 -07:00
g_pLayoutManager - > getCurrentLayout ( ) - > bringWindowToTop ( pWindow ) ;
2023-08-11 19:27:09 +04:00
2024-04-27 12:43:12 +01:00
if ( ! pWindow | | ! validMapped ( pWindow ) ) {
2023-10-06 01:11:47 +01:00
2025-04-22 15:23:29 +02:00
if ( m_lastWindow . expired ( ) & & ! pWindow )
2023-10-06 01:11:47 +01:00
return ;
2025-04-22 15:23:29 +02:00
const auto PLASTWINDOW = m_lastWindow . lock ( ) ;
m_lastWindow . reset ( ) ;
2022-08-08 20:42:14 +02:00
2025-04-28 22:25:22 +02:00
if ( PLASTWINDOW & & PLASTWINDOW - > m_isMapped ) {
2022-08-08 20:42:14 +02:00
updateWindowAnimatedDecorationValues ( PLASTWINDOW ) ;
2022-08-08 20:21:11 +02:00
2022-10-28 19:18:10 +01:00
g_pXWaylandManager - > activateWindow ( PLASTWINDOW , false ) ;
2022-08-08 20:21:11 +02:00
}
2024-05-10 18:27:57 +01:00
g_pSeatManager - > setKeyboardFocus ( nullptr ) ;
2022-08-08 20:21:11 +02:00
2022-08-21 18:34:38 +02:00
g_pEventManager - > postEvent ( SHyprIPCEvent { " activewindow " , " , " } ) ;
2024-05-01 13:57:27 +01:00
g_pEventManager - > postEvent ( SHyprIPCEvent { " activewindowv2 " , " " } ) ;
2022-08-21 18:34:38 +02:00
2025-08-14 19:44:56 +05:00
EMIT_HOOK_EVENT ( " activeWindow " , PHLWINDOW { nullptr } ) ;
2023-02-19 20:54:53 +00:00
2022-10-24 12:25:36 +01:00
g_pLayoutManager - > getCurrentLayout ( ) - > onWindowFocusChange ( nullptr ) ;
2025-04-22 15:23:29 +02:00
m_lastFocus . reset ( ) ;
2022-10-31 12:26:07 +00:00
g_pInputManager - > recheckIdleInhibitorStatus ( ) ;
2022-05-14 14:37:57 +02:00
return ;
}
2025-04-28 22:25:22 +02:00
if ( pWindow - > m_windowData . noFocus . valueOrDefault ( ) ) {
2022-05-14 20:56:21 +02:00
Debug : : log ( LOG , " Ignoring focus to nofocus window! " ) ;
2022-03-18 22:53:27 +01:00
return ;
}
2025-05-02 17:07:20 +02:00
if ( m_lastWindow . lock ( ) = = pWindow & & g_pSeatManager - > m_state . keyboardFocus = = pSurface & & g_pSeatManager - > m_state . keyboardFocus )
2022-04-05 18:29:58 +02:00
return ;
2025-04-28 22:25:22 +02:00
if ( pWindow - > m_pinned )
2025-04-30 23:45:20 +02:00
pWindow - > m_workspace = m_lastMonitor - > m_activeWorkspace ;
2022-09-10 13:11:02 +02:00
2025-04-28 22:25:22 +02:00
const auto PMONITOR = pWindow - > m_monitor . lock ( ) ;
2024-01-07 06:06:33 -05:00
2025-04-28 22:25:22 +02:00
if ( ! pWindow - > m_workspace | | ! pWindow - > m_workspace - > isVisible ( ) ) {
const auto PWORKSPACE = pWindow - > m_workspace ;
2023-01-20 20:57:35 +01:00
// This is to fix incorrect feedback on the focus history.
2025-04-25 02:37:12 +02:00
PWORKSPACE - > m_lastFocusedWindow = pWindow ;
2025-04-30 23:45:20 +02:00
if ( m_lastMonitor - > m_activeWorkspace )
PWORKSPACE - > rememberPrevWorkspace ( m_lastMonitor - > m_activeWorkspace ) ;
2025-04-25 02:37:12 +02:00
if ( PWORKSPACE - > m_isSpecialWorkspace )
2025-04-22 15:23:29 +02:00
m_lastMonitor - > changeWorkspace ( PWORKSPACE , false , true ) ; // if special ws, open on current monitor
2024-11-16 23:04:57 +00:00
else if ( PMONITOR )
2024-04-13 06:39:20 -07:00
PMONITOR - > changeWorkspace ( PWORKSPACE , false , true ) ;
2023-01-20 20:57:35 +01:00
// changeworkspace already calls focusWindow
return ;
}
2022-08-06 21:05:19 +02:00
2025-04-22 15:23:29 +02:00
const auto PLASTWINDOW = m_lastWindow . lock ( ) ;
m_lastWindow = pWindow ;
2022-04-23 14:16:02 +02:00
2024-01-09 13:17:55 +01:00
/* If special fallthrough is enabled, this behavior will be disabled, as I have no better idea of nicely tracking which
window focuses are " via keybinds " and which ones aren ' t . */
2025-04-30 23:45:20 +02:00
if ( PMONITOR & & PMONITOR - > m_activeSpecialWorkspace & & PMONITOR - > m_activeSpecialWorkspace ! = pWindow - > m_workspace & & ! pWindow - > m_pinned & & ! * PSPECIALFALLTHROUGH )
2024-01-07 06:06:33 -05:00
PMONITOR - > setSpecialWorkspace ( nullptr ) ;
2022-04-23 14:16:02 +02:00
// we need to make the PLASTWINDOW not equal to m_pLastWindow so that RENDERDATA is correct for an unfocused window
2025-04-28 22:25:22 +02:00
if ( PLASTWINDOW & & PLASTWINDOW - > m_isMapped ) {
2023-12-08 16:02:16 +00:00
PLASTWINDOW - > updateDynamicRules ( ) ;
2022-07-12 13:40:55 +02:00
updateWindowAnimatedDecorationValues ( PLASTWINDOW ) ;
2022-04-23 14:16:02 +02:00
2025-04-28 22:25:22 +02:00
if ( ! pWindow - > m_isX11 | | ! pWindow - > isX11OverrideRedirect ( ) )
2022-10-28 20:12:17 +01:00
g_pXWaylandManager - > activateWindow ( PLASTWINDOW , false ) ;
2022-04-05 18:29:58 +02:00
}
2025-04-22 15:23:29 +02:00
m_lastWindow = PLASTWINDOW ;
2022-04-23 14:16:02 +02:00
2025-04-28 22:25:22 +02:00
const auto PWINDOWSURFACE = pSurface ? pSurface : pWindow - > m_wlSurface - > resource ( ) ;
2022-03-18 22:53:27 +01:00
2022-04-02 18:57:09 +02:00
focusSurface ( PWINDOWSURFACE , pWindow ) ;
2022-04-23 14:16:02 +02:00
g_pXWaylandManager - > activateWindow ( pWindow , true ) ; // sets the m_pLastWindow
2022-04-02 18:57:09 +02:00
2023-12-08 16:02:16 +00:00
pWindow - > updateDynamicRules ( ) ;
2025-01-24 18:22:05 +00:00
pWindow - > onFocusAnimUpdate ( ) ;
2023-12-08 16:02:16 +00:00
2022-07-12 13:40:55 +02:00
updateWindowAnimatedDecorationValues ( pWindow ) ;
2022-05-24 22:21:31 +02:00
2025-04-28 22:25:22 +02:00
if ( pWindow - > m_isUrgent )
pWindow - > m_isUrgent = false ;
2023-01-14 20:31:11 +01:00
2022-05-24 22:21:31 +02:00
// Send an event
2025-04-28 22:25:22 +02:00
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " activewindow " , . data = pWindow - > m_class + " , " + pWindow - > m_title } ) ;
2025-08-14 19:44:56 +05:00
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " activewindowv2 " , . data = std : : format ( " {:x} " , rc < uintptr_t > ( pWindow . get ( ) ) ) } ) ;
2022-05-29 11:24:42 +02:00
2023-02-19 20:54:53 +00:00
EMIT_HOOK_EVENT ( " activeWindow " , pWindow ) ;
2022-10-24 12:25:36 +01:00
g_pLayoutManager - > getCurrentLayout ( ) - > onWindowFocusChange ( pWindow ) ;
2022-10-31 12:26:07 +00:00
g_pInputManager - > recheckIdleInhibitorStatus ( ) ;
2023-01-20 19:15:15 +01:00
2025-02-03 04:34:30 +03:00
if ( ! preserveFocusHistory ) {
// move to front of the window history
2025-04-22 15:23:29 +02:00
const auto HISTORYPIVOT = std : : ranges : : find_if ( m_windowFocusHistory , [ & ] ( const auto & other ) { return other . lock ( ) = = pWindow ; } ) ;
if ( HISTORYPIVOT = = m_windowFocusHistory . end ( ) )
2025-02-03 04:34:30 +03:00
Debug : : log ( ERR , " BUG THIS: {} has no pivot in history " , pWindow ) ;
else
2025-04-22 15:23:29 +02:00
std : : rotate ( m_windowFocusHistory . begin ( ) , HISTORYPIVOT , HISTORYPIVOT + 1 ) ;
2023-01-20 19:15:15 +01:00
}
2023-10-26 22:17:49 +01:00
2024-03-03 18:39:20 +00:00
if ( * PFOLLOWMOUSE = = 0 )
2023-10-26 22:17:49 +01:00
g_pInputManager - > sendMotionEventsToFocused ( ) ;
2025-02-08 09:05:44 -05:00
2025-04-28 22:25:22 +02:00
if ( pWindow - > m_groupData . pNextWindow )
2025-02-08 09:05:44 -05:00
pWindow - > deactivateGroupMembers ( ) ;
2022-03-20 14:36:55 +01:00
}
2024-06-08 10:07:59 +02:00
void CCompositor : : focusSurface ( SP < CWLSurfaceResource > pSurface , PHLWINDOW pWindowOwner ) {
2022-04-18 17:16:01 +02:00
2025-05-02 17:07:20 +02:00
if ( g_pSeatManager - > m_state . keyboardFocus = = pSurface | | ( pWindowOwner & & g_pSeatManager - > m_state . keyboardFocus = = pWindowOwner - > m_wlSurface - > resource ( ) ) )
2022-12-16 17:17:31 +00:00
return ; // Don't focus when already focused on this.
2022-03-20 14:36:55 +01:00
2024-09-04 15:59:00 +00:00
if ( g_pSessionLockManager - > isSessionLocked ( ) & & pSurface & & ! g_pSessionLockManager - > isSurfaceSessionLock ( pSurface ) )
2024-01-07 14:04:25 +01:00
return ;
2023-02-12 19:20:13 +00:00
2025-05-02 17:07:20 +02:00
if ( g_pSeatManager - > m_seatGrab & & ! g_pSeatManager - > m_seatGrab - > accepts ( pSurface ) ) {
2025-08-14 19:44:56 +05:00
Debug : : log ( LOG , " surface {:x} won't receive kb focus because grab rejected it " , rc < uintptr_t > ( pSurface . get ( ) ) ) ;
2024-05-11 01:02:57 +01:00
return ;
}
2025-04-22 15:23:29 +02:00
const auto PLASTSURF = m_lastFocus . lock ( ) ;
2024-03-02 21:04:55 +00:00
2022-07-07 21:47:59 +02:00
// Unfocus last surface if should
2025-04-22 15:23:29 +02:00
if ( m_lastFocus & & ! pWindowOwner )
g_pXWaylandManager - > activateSurface ( m_lastFocus . lock ( ) , false ) ;
2022-07-07 21:47:59 +02:00
2022-07-11 12:29:50 +02:00
if ( ! pSurface ) {
2024-05-10 18:27:57 +01:00
g_pSeatManager - > setKeyboardFocus ( nullptr ) ;
2025-02-02 20:34:26 +03:00
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " activewindow " , . data = " , " } ) ;
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " activewindowv2 " , . data = " " } ) ;
2025-08-14 19:44:56 +05:00
EMIT_HOOK_EVENT ( " keyboardFocus " , SP < CWLSurfaceResource > { nullptr } ) ;
2025-04-22 15:23:29 +02:00
m_lastFocus . reset ( ) ;
2022-03-27 19:16:33 +02:00
return ;
2022-07-11 12:29:50 +02:00
}
2022-09-25 20:07:48 +02:00
2025-05-02 17:07:20 +02:00
if ( g_pSeatManager - > m_keyboard )
2024-05-10 18:27:57 +01:00
g_pSeatManager - > setKeyboardFocus ( pSurface ) ;
2022-04-02 19:09:27 +02:00
2022-04-10 11:17:06 +02:00
if ( pWindowOwner )
2025-08-14 19:44:56 +05:00
Debug : : log ( LOG , " Set keyboard focus to surface {:x}, with {} " , rc < uintptr_t > ( pSurface . get ( ) ) , pWindowOwner ) ;
2022-03-31 19:41:55 +02:00
else
2025-08-14 19:44:56 +05:00
Debug : : log ( LOG , " Set keyboard focus to surface {:x} " , rc < uintptr_t > ( pSurface . get ( ) ) ) ;
2022-07-08 13:19:57 +02:00
2022-11-02 18:54:41 +00:00
g_pXWaylandManager - > activateSurface ( pSurface , true ) ;
2025-04-22 15:23:29 +02:00
m_lastFocus = pSurface ;
2023-02-19 20:54:53 +00:00
EMIT_HOOK_EVENT ( " keyboardFocus " , pSurface ) ;
2024-03-02 21:04:55 +00:00
2024-06-08 10:07:59 +02:00
const auto SURF = CWLSurface : : fromResource ( pSurface ) ;
const auto OLDSURF = CWLSurface : : fromResource ( PLASTSURF ) ;
2024-03-02 21:04:55 +00:00
if ( OLDSURF & & OLDSURF - > constraint ( ) )
OLDSURF - > constraint ( ) - > deactivate ( ) ;
if ( SURF & & SURF - > constraint ( ) )
SURF - > constraint ( ) - > activate ( ) ;
2022-03-18 23:16:15 +01:00
}
2024-10-19 23:03:29 +01:00
SP < CWLSurfaceResource > CCompositor : : vectorToLayerPopupSurface ( const Vector2D & pos , PHLMONITOR monitor , Vector2D * sCoords , PHLLS * ppLayerSurfaceFound ) {
2025-04-30 23:45:20 +02:00
for ( auto const & lsl : monitor - > m_layerSurfaceLayers | std : : views : : reverse ) {
2024-08-26 17:25:39 +02:00
for ( auto const & ls : lsl | std : : views : : reverse ) {
2025-05-04 00:13:29 +02:00
if ( ! ls - > m_mapped | | ls - > m_fadingOut | | ! ls - > m_layerSurface | | ( ls - > m_layerSurface & & ! ls - > m_layerSurface - > m_mapped ) | | ls - > m_alpha - > value ( ) = = 0.f )
2024-03-25 16:20:30 +00:00
continue ;
2025-04-24 20:49:49 +02:00
auto SURFACEAT = ls - > m_popupHead - > at ( pos , true ) ;
2024-03-25 16:20:30 +00:00
if ( SURFACEAT ) {
2024-05-09 21:47:21 +01:00
* ppLayerSurfaceFound = ls . lock ( ) ;
* sCoords = pos - SURFACEAT - > coordsGlobal ( ) ;
2025-04-24 20:49:49 +02:00
return SURFACEAT - > m_wlSurface - > resource ( ) ;
2024-03-25 16:20:30 +00:00
}
}
}
return nullptr ;
}
2025-04-25 16:38:31 +02:00
SP < CWLSurfaceResource > CCompositor : : vectorToLayerSurface ( const Vector2D & pos , std : : vector < PHLLSREF > * layerSurfaces , Vector2D * sCoords , PHLLS * ppLayerSurfaceFound ,
bool aboveLockscreen ) {
2024-08-26 17:25:39 +02:00
for ( auto const & ls : * layerSurfaces | std : : views : : reverse ) {
2025-05-04 00:13:29 +02:00
if ( ! ls - > m_mapped | | ls - > m_fadingOut | | ! ls - > m_layerSurface | | ( ls - > m_layerSurface & & ! ls - > m_layerSurface - > m_surface - > m_mapped ) | | ls - > m_alpha - > value ( ) = = 0.f | |
2025-04-25 16:38:31 +02:00
( aboveLockscreen & & ( ! ls - > m_aboveLockscreen | | ! ls - > m_aboveLockscreenInteractable ) ) )
2022-03-20 14:36:55 +01:00
continue ;
2025-05-04 00:13:29 +02:00
auto [ surf , local ] = ls - > m_layerSurface - > m_surface - > at ( pos - ls - > m_geometry . pos ( ) , true ) ;
2022-11-18 20:35:15 +00:00
2024-06-08 10:07:59 +02:00
if ( surf ) {
2025-05-03 16:02:49 +02:00
if ( surf - > m_current . input . empty ( ) )
2022-12-22 15:03:32 +00:00
continue ;
2024-05-09 21:47:21 +01:00
* ppLayerSurfaceFound = ls . lock ( ) ;
2024-06-08 10:07:59 +02:00
* sCoords = local ;
return surf ;
2022-07-01 20:14:33 +02:00
}
2022-03-20 14:36:55 +01:00
}
return nullptr ;
}
2024-06-08 10:07:59 +02:00
PHLWINDOW CCompositor : : getWindowFromSurface ( SP < CWLSurfaceResource > pSurface ) {
2025-05-03 16:02:49 +02:00
if ( ! pSurface | | ! pSurface - > m_hlSurface )
2024-07-24 11:07:22 +02:00
return nullptr ;
2022-03-20 14:36:55 +01:00
2025-05-03 16:02:49 +02:00
return pSurface - > m_hlSurface - > getWindow ( ) ;
2022-03-20 15:55:47 +01:00
}
2024-04-27 12:43:12 +01:00
PHLWINDOW CCompositor : : getWindowFromHandle ( uint32_t handle ) {
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-08-14 19:44:56 +05:00
if ( sc < uint32_t > ( rc < uint64_t > ( w . get ( ) ) & 0xFFFFFFFF ) = = handle ) {
2024-04-27 12:43:12 +01:00
return w ;
2022-12-05 17:57:59 +00:00
}
2022-12-05 17:05:15 +00:00
}
return nullptr ;
}
2024-08-08 21:01:50 +02:00
PHLWORKSPACE CCompositor : : getWorkspaceByID ( const WORKSPACEID & id ) {
2025-07-24 00:36:29 +02:00
for ( auto const & w : getWorkspaces ( ) ) {
2025-04-25 02:37:12 +02:00
if ( w - > m_id = = id & & ! w - > inert ( ) )
2025-07-24 00:36:29 +02:00
return w . lock ( ) ;
2022-03-20 15:55:47 +01:00
}
return nullptr ;
}
2024-04-27 12:43:12 +01:00
PHLWINDOW CCompositor : : getUrgentWindow ( ) {
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( w - > m_isMapped & & w - > m_isUrgent )
2024-04-27 12:43:12 +01:00
return w ;
2023-01-21 11:18:55 +01:00
}
return nullptr ;
}
2024-04-27 12:43:12 +01:00
bool CCompositor : : isWindowActive ( PHLWINDOW pWindow ) {
2025-04-22 15:23:29 +02:00
if ( m_lastWindow . expired ( ) & & ! m_lastFocus )
2022-05-29 00:07:31 +02:00
return false ;
2025-04-28 22:25:22 +02:00
if ( ! pWindow - > m_isMapped )
2022-04-02 18:57:09 +02:00
return false ;
2025-04-28 22:25:22 +02:00
const auto PSURFACE = pWindow - > m_wlSurface - > resource ( ) ;
2022-04-02 13:02:16 +02:00
2025-04-22 15:23:29 +02:00
return PSURFACE = = m_lastFocus | | pWindow = = m_lastWindow . lock ( ) ;
2022-04-04 16:25:30 +02:00
}
2024-04-27 12:43:12 +01:00
void CCompositor : : changeWindowZOrder ( PHLWINDOW pWindow , bool top ) {
if ( ! validMapped ( pWindow ) )
2022-04-04 16:25:30 +02:00
return ;
2024-11-18 15:44:15 +01:00
if ( top )
2025-04-28 22:25:22 +02:00
pWindow - > m_createdOverFullscreen = true ;
2024-11-18 15:44:15 +01:00
2025-04-22 15:23:29 +02:00
if ( pWindow = = ( top ? m_windows . back ( ) : m_windows . front ( ) ) )
2024-10-12 03:29:51 +03:00
return ;
2024-04-27 12:43:12 +01:00
auto moveToZ = [ & ] ( PHLWINDOW pw , bool top ) - > void {
2023-09-21 17:18:26 -04:00
if ( top ) {
2025-04-22 15:23:29 +02:00
for ( auto it = m_windows . begin ( ) ; it ! = m_windows . end ( ) ; + + it ) {
2024-04-27 12:43:12 +01:00
if ( * it = = pw ) {
2025-04-22 15:23:29 +02:00
std : : rotate ( it , it + 1 , m_windows . end ( ) ) ;
2023-09-21 17:18:26 -04:00
break ;
}
}
} else {
2025-04-22 15:23:29 +02:00
for ( auto it = m_windows . rbegin ( ) ; it ! = m_windows . rend ( ) ; + + it ) {
2024-04-27 12:43:12 +01:00
if ( * it = = pw ) {
2025-04-22 15:23:29 +02:00
std : : rotate ( it , it + 1 , m_windows . rend ( ) ) ;
2023-09-21 17:18:26 -04:00
break ;
}
2022-08-28 19:47:06 +02:00
}
}
2023-01-11 17:57:54 +01:00
2025-04-28 22:25:22 +02:00
if ( pw - > m_isMapped )
g_pHyprRenderer - > damageMonitor ( pw - > m_monitor . lock ( ) ) ;
2022-08-28 19:47:06 +02:00
} ;
2025-04-28 22:25:22 +02:00
if ( ! pWindow - > m_isX11 )
2023-09-21 17:18:26 -04:00
moveToZ ( pWindow , top ) ;
2024-05-25 22:43:51 +02:00
else {
2023-09-21 17:18:26 -04:00
// move X11 window stack
2022-08-28 19:47:06 +02:00
2024-12-16 15:58:19 +00:00
std : : vector < PHLWINDOW > toMove ;
2022-08-28 19:47:06 +02:00
2024-12-16 15:58:19 +00:00
auto x11Stack = [ & ] ( PHLWINDOW pw , bool top , auto & & x11Stack ) - > void {
2023-09-21 17:18:26 -04:00
if ( top )
toMove . emplace_back ( pw ) ;
else
2024-12-16 15:58:19 +00:00
toMove . insert ( toMove . begin ( ) , pw ) ;
2022-08-28 19:47:06 +02:00
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( w - > m_isMapped & & ! w - > isHidden ( ) & & w - > m_isX11 & & w - > x11TransientFor ( ) = = pw & & w ! = pw & & std : : ranges : : find ( toMove , w ) = = toMove . end ( ) ) {
2024-04-27 12:43:12 +01:00
x11Stack ( w , top , x11Stack ) ;
2023-09-21 17:18:26 -04:00
}
}
} ;
2022-08-28 19:47:06 +02:00
2023-09-21 17:18:26 -04:00
x11Stack ( pWindow , top , x11Stack ) ;
2024-12-07 18:51:18 +01:00
for ( const auto & it : toMove ) {
2023-09-21 17:18:26 -04:00
moveToZ ( it , top ) ;
}
2022-08-28 19:47:06 +02:00
}
2022-04-05 19:28:10 +02:00
}
2024-08-08 21:01:50 +02:00
void CCompositor : : cleanupFadingOut ( const MONITORID & monid ) {
2025-04-22 15:23:29 +02:00
for ( auto const & ww : m_windowsFadingOut ) {
2024-04-27 12:43:12 +01:00
2024-05-09 21:47:21 +01:00
auto w = ww . lock ( ) ;
2022-04-26 17:51:00 +02:00
2025-04-28 22:25:22 +02:00
if ( w - > monitorID ( ) ! = monid & & w - > m_monitor )
2022-07-12 23:11:34 +02:00
continue ;
2025-04-28 22:25:22 +02:00
if ( ! w - > m_fadingOut | | w - > m_alpha - > value ( ) = = 0.f ) {
2022-06-30 15:44:26 +02:00
2025-04-28 22:25:22 +02:00
w - > m_fadingOut = false ;
2024-03-14 18:25:28 +00:00
2025-04-28 22:25:22 +02:00
if ( ! w - > m_readyToDelete )
2024-04-06 14:59:30 +01:00
continue ;
2023-03-01 13:15:51 +00:00
2024-04-06 14:59:30 +01:00
removeWindowFromVectorSafe ( w ) ;
2022-04-10 11:17:06 +02:00
2024-05-09 21:47:21 +01:00
w . reset ( ) ;
2022-04-10 11:17:06 +02:00
Debug : : log ( LOG , " Cleanup: destroyed a window " ) ;
2022-04-05 19:28:10 +02:00
return ;
}
}
2022-05-14 17:23:46 +02:00
2024-08-07 13:35:02 +02:00
bool layersDirty = false ;
2025-04-22 15:23:29 +02:00
for ( auto const & lsr : m_surfacesFadingOut ) {
2022-07-25 22:40:34 +02:00
2024-04-30 02:41:27 +01:00
auto ls = lsr . lock ( ) ;
2022-07-25 22:40:34 +02:00
2024-08-07 13:35:02 +02:00
if ( ! ls ) {
layersDirty = true ;
2024-04-30 02:41:27 +01:00
continue ;
2024-08-07 13:35:02 +02:00
}
2022-07-25 22:40:34 +02:00
2025-04-24 20:49:49 +02:00
if ( ls - > monitorID ( ) ! = monid & & ls - > m_monitor )
2022-07-12 23:11:34 +02:00
continue ;
2022-08-01 12:16:33 +02:00
// mark blur for recalc
2025-04-24 20:49:49 +02:00
if ( ls - > m_layer = = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND | | ls - > m_layer = = ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM )
2022-08-01 12:16:33 +02:00
g_pHyprOpenGL - > markBlurDirtyForMonitor ( getMonitorFromID ( monid ) ) ;
2025-04-24 20:49:49 +02:00
if ( ls - > m_fadingOut & & ls - > m_readyToDelete & & ls - > isFadedOut ( ) ) {
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2025-04-30 23:45:20 +02:00
for ( auto & lsl : m - > m_layerSurfaceLayers ) {
2025-02-02 20:34:26 +03:00
if ( ! lsl . empty ( ) & & std : : ranges : : find_if ( lsl , [ & ] ( auto & other ) { return other = = ls ; } ) ! = lsl . end ( ) ) {
2024-08-07 13:35:02 +02:00
std : : erase_if ( lsl , [ & ] ( auto & other ) { return other = = ls | | ! other ; } ) ;
2022-07-25 21:08:54 +02:00
}
2022-07-23 15:48:08 +02:00
}
}
2025-04-22 15:23:29 +02:00
std : : erase_if ( m_surfacesFadingOut , [ ls ] ( const auto & el ) { return el . lock ( ) = = ls ; } ) ;
std : : erase_if ( m_layers , [ ls ] ( const auto & el ) { return el = = ls ; } ) ;
2024-05-09 21:47:21 +01:00
ls . reset ( ) ;
2022-07-25 22:00:46 +02:00
2022-05-14 17:28:55 +02:00
Debug : : log ( LOG , " Cleanup: destroyed a layersurface " ) ;
2022-05-14 17:23:46 +02:00
return ;
}
}
2024-08-07 13:35:02 +02:00
if ( layersDirty )
2025-04-22 15:23:29 +02:00
std : : erase_if ( m_surfacesFadingOut , [ ] ( const auto & el ) { return el . expired ( ) ; } ) ;
2022-04-09 13:26:55 +02:00
}
2024-04-30 02:41:27 +01:00
void CCompositor : : addToFadingOutSafe ( PHLLS pLS ) {
2025-04-22 15:23:29 +02:00
const auto FOUND = std : : ranges : : find_if ( m_surfacesFadingOut , [ & ] ( auto & other ) { return other . lock ( ) = = pLS ; } ) ;
2022-07-25 21:08:54 +02:00
2025-04-22 15:23:29 +02:00
if ( FOUND ! = m_surfacesFadingOut . end ( ) )
2022-12-16 17:17:31 +00:00
return ; // if it's already added, don't add it.
2022-07-25 21:08:54 +02:00
2025-04-22 15:23:29 +02:00
m_surfacesFadingOut . emplace_back ( pLS ) ;
2022-07-25 21:08:54 +02:00
}
2024-06-12 22:57:06 +02:00
void CCompositor : : removeFromFadingOutSafe ( PHLLS ls ) {
2025-04-22 15:23:29 +02:00
std : : erase ( m_surfacesFadingOut , ls ) ;
2024-06-12 22:57:06 +02:00
}
2024-04-27 12:43:12 +01:00
void CCompositor : : addToFadingOutSafe ( PHLWINDOW pWindow ) {
2025-04-22 15:23:29 +02:00
const auto FOUND = std : : ranges : : find_if ( m_windowsFadingOut , [ & ] ( PHLWINDOWREF & other ) { return other . lock ( ) = = pWindow ; } ) ;
2022-07-25 21:08:54 +02:00
2025-04-22 15:23:29 +02:00
if ( FOUND ! = m_windowsFadingOut . end ( ) )
2022-12-16 17:17:31 +00:00
return ; // if it's already added, don't add it.
2022-07-25 21:08:54 +02:00
2025-04-22 15:23:29 +02:00
m_windowsFadingOut . emplace_back ( pWindow ) ;
2022-07-25 21:08:54 +02:00
}
2024-04-27 12:43:12 +01:00
PHLWINDOW CCompositor : : getWindowInDirection ( PHLWINDOW pWindow , char dir ) {
2023-11-05 16:18:41 +00:00
if ( ! isDirection ( dir ) )
return nullptr ;
2025-04-28 22:25:22 +02:00
const auto PMONITOR = pWindow - > m_monitor . lock ( ) ;
2022-04-09 13:26:55 +02:00
2023-11-04 21:03:08 +00:00
if ( ! PMONITOR )
return nullptr ; // ??
2023-01-26 14:36:22 +00:00
2025-04-30 23:45:20 +02:00
const auto WINDOWIDEALBB = pWindow - > isFullscreen ( ) ? CBox { PMONITOR - > m_position , PMONITOR - > m_size } : pWindow - > getWindowIdealBoundingBoxIgnoreReserved ( ) ;
2025-04-28 22:25:22 +02:00
const auto PWORKSPACE = pWindow - > m_workspace ;
2023-11-04 21:03:08 +00:00
2025-04-28 22:25:22 +02:00
return getWindowInDirection ( WINDOWIDEALBB , PWORKSPACE , dir , pWindow , pWindow - > m_isFloating ) ;
2025-01-02 17:36:02 +01:00
}
2023-11-04 21:03:08 +00:00
2025-01-02 17:36:02 +01:00
PHLWINDOW CCompositor : : getWindowInDirection ( const CBox & box , PHLWORKSPACE pWorkspace , char dir , PHLWINDOW ignoreWindow , bool useVectorAngles ) {
if ( ! isDirection ( dir ) )
return nullptr ;
2022-04-09 13:26:55 +02:00
2025-01-02 17:36:02 +01:00
// 0 -> history, 1 -> shared length
static auto PMETHOD = CConfigValue < Hyprlang : : INT > ( " binds:focus_preferred_method " ) ;
static auto PMONITORFALLBACK = CConfigValue < Hyprlang : : INT > ( " binds:window_direction_monitor_fallback " ) ;
2022-04-09 13:26:55 +02:00
2025-01-02 17:36:02 +01:00
const auto POSA = box . pos ( ) ;
const auto SIZEA = box . size ( ) ;
auto leaderValue = - 1 ;
PHLWINDOW leaderWindow = nullptr ;
if ( ! useVectorAngles ) {
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( w = = ignoreWindow | | ! w - > m_workspace | | ! w - > m_isMapped | | w - > isHidden ( ) | | ( ! w - > isFullscreen ( ) & & w - > m_isFloating ) | | ! w - > m_workspace - > isVisible ( ) )
2023-11-05 16:18:41 +00:00
continue ;
2023-10-20 02:53:37 -07:00
2025-04-28 22:25:22 +02:00
if ( pWorkspace - > m_monitor = = w - > m_monitor & & pWorkspace ! = w - > m_workspace )
2023-11-05 16:18:41 +00:00
continue ;
2022-11-23 23:40:05 +00:00
2025-04-28 22:25:22 +02:00
if ( pWorkspace - > m_hasFullscreenWindow & & ! w - > isFullscreen ( ) & & ! w - > m_createdOverFullscreen )
2023-11-05 16:18:41 +00:00
continue ;
2022-06-23 20:39:48 +02:00
2025-04-28 22:25:22 +02:00
if ( ! * PMONITORFALLBACK & & pWorkspace - > m_monitor ! = w - > m_monitor )
2024-05-22 13:51:46 -06:00
continue ;
2023-11-05 16:18:41 +00:00
const auto BWINDOWIDEALBB = w - > getWindowIdealBoundingBoxIgnoreReserved ( ) ;
2022-06-23 20:39:48 +02:00
2023-11-05 16:18:41 +00:00
const auto POSB = Vector2D ( BWINDOWIDEALBB . x , BWINDOWIDEALBB . y ) ;
const auto SIZEB = Vector2D ( BWINDOWIDEALBB . width , BWINDOWIDEALBB . height ) ;
2023-01-26 14:36:22 +00:00
2023-11-05 16:18:41 +00:00
double intersectLength = - 1 ;
2023-01-26 14:36:22 +00:00
2023-11-05 16:18:41 +00:00
switch ( dir ) {
case ' l ' :
if ( STICKS ( POSA . x , POSB . x + SIZEB . x ) ) {
intersectLength = std : : max ( 0.0 , std : : min ( POSA . y + SIZEA . y , POSB . y + SIZEB . y ) - std : : max ( POSA . y , POSB . y ) ) ;
}
break ;
case ' r ' :
if ( STICKS ( POSA . x + SIZEA . x , POSB . x ) ) {
intersectLength = std : : max ( 0.0 , std : : min ( POSA . y + SIZEA . y , POSB . y + SIZEB . y ) - std : : max ( POSA . y , POSB . y ) ) ;
}
break ;
case ' t ' :
case ' u ' :
if ( STICKS ( POSA . y , POSB . y + SIZEB . y ) ) {
intersectLength = std : : max ( 0.0 , std : : min ( POSA . x + SIZEA . x , POSB . x + SIZEB . x ) - std : : max ( POSA . x , POSB . x ) ) ;
}
break ;
case ' b ' :
case ' d ' :
if ( STICKS ( POSA . y + SIZEA . y , POSB . y ) ) {
intersectLength = std : : max ( 0.0 , std : : min ( POSA . x + SIZEA . x , POSB . x + SIZEB . x ) - std : : max ( POSA . x , POSB . x ) ) ;
}
break ;
}
2023-01-26 14:36:22 +00:00
2024-03-03 18:39:20 +00:00
if ( * PMETHOD = = 0 /* history */ ) {
2023-11-05 16:18:41 +00:00
if ( intersectLength > 0 ) {
// get idx
int windowIDX = - 1 ;
2025-04-22 15:23:29 +02:00
for ( size_t i = 0 ; i < g_pCompositor - > m_windowFocusHistory . size ( ) ; + + i ) {
if ( g_pCompositor - > m_windowFocusHistory [ i ] . lock ( ) = = w ) {
2023-11-05 16:18:41 +00:00
windowIDX = i ;
break ;
}
2023-01-26 14:36:22 +00:00
}
2025-04-22 15:23:29 +02:00
windowIDX = g_pCompositor - > m_windowFocusHistory . size ( ) - windowIDX ;
2023-01-26 14:36:22 +00:00
2023-11-05 16:18:41 +00:00
if ( windowIDX > leaderValue ) {
leaderValue = windowIDX ;
2024-04-27 12:43:12 +01:00
leaderWindow = w ;
2023-11-05 16:18:41 +00:00
}
}
} else /* length */ {
if ( intersectLength > leaderValue ) {
leaderValue = intersectLength ;
2024-04-27 12:43:12 +01:00
leaderWindow = w ;
2023-01-26 14:36:22 +00:00
}
}
2023-11-05 16:18:41 +00:00
}
} else {
if ( dir = = ' u ' )
dir = ' t ' ;
if ( dir = = ' d ' )
dir = ' b ' ;
static const std : : unordered_map < char , Vector2D > VECTORS = { { ' r ' , { 1 , 0 } } , { ' t ' , { 0 , - 1 } } , { ' b ' , { 0 , 1 } } , { ' l ' , { - 1 , 0 } } } ;
//
2024-12-07 18:51:18 +01:00
auto vectorAngles = [ ] ( const Vector2D & a , const Vector2D & b ) - > double {
2025-02-02 20:34:26 +03:00
double dot = ( a . x * b . x ) + ( a . y * b . y ) ;
2023-11-05 16:18:41 +00:00
double ang = std : : acos ( dot / ( a . size ( ) * b . size ( ) ) ) ;
return ang ;
} ;
float bestAngleAbs = 2.0 * M_PI ;
constexpr float THRESHOLD = 0.3 * M_PI ;
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( w = = ignoreWindow | | ! w - > m_isMapped | | ! w - > m_workspace | | w - > isHidden ( ) | | ( ! w - > isFullscreen ( ) & & ! w - > m_isFloating ) | | ! w - > m_workspace - > isVisible ( ) )
2023-11-05 16:18:41 +00:00
continue ;
2025-04-28 22:25:22 +02:00
if ( pWorkspace - > m_monitor = = w - > m_monitor & & pWorkspace ! = w - > m_workspace )
2023-11-05 16:18:41 +00:00
continue ;
2025-04-28 22:25:22 +02:00
if ( pWorkspace - > m_hasFullscreenWindow & & ! w - > isFullscreen ( ) & & ! w - > m_createdOverFullscreen )
2023-11-05 16:18:41 +00:00
continue ;
2025-04-28 22:25:22 +02:00
if ( ! * PMONITORFALLBACK & & pWorkspace - > m_monitor ! = w - > m_monitor )
2024-05-22 13:51:46 -06:00
continue ;
2025-01-02 17:36:02 +01:00
const auto DIST = w - > middle ( ) . distance ( box . middle ( ) ) ;
const auto ANGLE = vectorAngles ( Vector2D { w - > middle ( ) - box . middle ( ) } , VECTORS . at ( dir ) ) ;
2023-11-05 16:18:41 +00:00
if ( ANGLE > M_PI_2 )
continue ; // if the angle is over 90 degrees, ignore. Wrong direction entirely.
if ( ( bestAngleAbs < THRESHOLD & & DIST < leaderValue & & ANGLE < THRESHOLD ) | | ( ANGLE < bestAngleAbs & & bestAngleAbs > THRESHOLD ) | | leaderValue = = - 1 ) {
leaderValue = DIST ;
bestAngleAbs = ANGLE ;
2024-04-27 12:43:12 +01:00
leaderWindow = w ;
2023-01-26 14:36:22 +00:00
}
}
2023-11-05 16:18:41 +00:00
2025-04-25 02:37:12 +02:00
if ( ! leaderWindow & & pWorkspace - > m_hasFullscreenWindow )
2025-01-02 17:36:02 +01:00
leaderWindow = pWorkspace - > getFullscreenWindow ( ) ;
2022-04-09 13:26:55 +02:00
}
2023-01-26 14:36:22 +00:00
if ( leaderValue ! = - 1 )
return leaderWindow ;
2022-04-09 13:26:55 +02:00
return nullptr ;
2022-04-11 19:51:37 +02:00
}
2025-02-03 04:34:30 +03:00
template < typename WINDOWPTR >
static bool isWorkspaceMatches ( WINDOWPTR pWindow , const WINDOWPTR w , bool anyWorkspace ) {
2025-04-28 22:25:22 +02:00
return anyWorkspace ? w - > m_workspace & & w - > m_workspace - > isVisible ( ) : w - > m_workspace = = pWindow - > m_workspace ;
2022-04-21 16:38:48 +02:00
}
2025-02-03 04:34:30 +03:00
template < typename WINDOWPTR >
static bool isFloatingMatches ( WINDOWPTR w , std : : optional < bool > floating ) {
2025-04-28 22:25:22 +02:00
return ! floating . has_value ( ) | | w - > m_isFloating = = floating . value ( ) ;
2025-01-21 17:17:07 +03:00
}
2022-07-09 18:39:41 +02:00
2025-02-03 04:34:30 +03:00
template < typename WINDOWPTR >
static bool isWindowAvailableForCycle ( WINDOWPTR pWindow , WINDOWPTR w , bool focusableOnly , std : : optional < bool > floating , bool anyWorkspace = false ) {
return isFloatingMatches ( w , floating ) & &
2025-04-28 22:25:22 +02:00
( w ! = pWindow & & isWorkspaceMatches ( pWindow , w , anyWorkspace ) & & w - > m_isMapped & & ! w - > isHidden ( ) & & ( ! focusableOnly | | ! w - > m_windowData . noFocus . valueOrDefault ( ) ) ) ;
2025-01-21 17:17:07 +03:00
}
2023-12-24 15:08:48 +01:00
2025-02-03 04:34:30 +03:00
template < typename Iterator >
static PHLWINDOW getWindowPred ( Iterator cur , Iterator end , Iterator begin , const std : : function < bool ( const PHLWINDOW & ) > PRED ) {
const auto IN_ONE_SIDE = std : : find_if ( cur , end , PRED ) ;
if ( IN_ONE_SIDE ! = end )
return * IN_ONE_SIDE ;
const auto IN_OTHER_SIDE = std : : find_if ( begin , cur , PRED ) ;
return * IN_OTHER_SIDE ;
}
template < typename Iterator >
static PHLWINDOW getWeakWindowPred ( Iterator cur , Iterator end , Iterator begin , const std : : function < bool ( const PHLWINDOWREF & ) > PRED ) {
const auto IN_ONE_SIDE = std : : find_if ( cur , end , PRED ) ;
if ( IN_ONE_SIDE ! = end )
return IN_ONE_SIDE - > lock ( ) ;
const auto IN_OTHER_SIDE = std : : find_if ( begin , cur , PRED ) ;
return IN_OTHER_SIDE - > lock ( ) ;
}
PHLWINDOW CCompositor : : getWindowCycleHist ( PHLWINDOWREF cur , bool focusableOnly , std : : optional < bool > floating , bool visible , bool next ) {
const auto FINDER = [ & ] ( const PHLWINDOWREF & w ) { return isWindowAvailableForCycle ( cur , w , focusableOnly , floating , visible ) ; } ;
// also m_vWindowFocusHistory has reverse order, so when it is next - we need to reverse again
2025-05-30 18:25:59 +05:00
return next ? getWeakWindowPred ( std : : ranges : : find ( m_windowFocusHistory | std : : views : : reverse , cur ) , m_windowFocusHistory . rend ( ) , m_windowFocusHistory . rbegin ( ) , FINDER ) :
2025-04-22 15:23:29 +02:00
getWeakWindowPred ( std : : ranges : : find ( m_windowFocusHistory , cur ) , m_windowFocusHistory . end ( ) , m_windowFocusHistory . begin ( ) , FINDER ) ;
2025-02-03 04:34:30 +03:00
}
2022-07-09 18:39:41 +02:00
2025-02-03 04:34:30 +03:00
PHLWINDOW CCompositor : : getWindowCycle ( PHLWINDOW cur , bool focusableOnly , std : : optional < bool > floating , bool visible , bool prev ) {
const auto FINDER = [ & ] ( const PHLWINDOW & w ) { return isWindowAvailableForCycle ( cur , w , focusableOnly , floating , visible ) ; } ;
2025-05-30 18:25:59 +05:00
return prev ? getWindowPred ( std : : ranges : : find ( m_windows | std : : views : : reverse , cur ) , m_windows . rend ( ) , m_windows . rbegin ( ) , FINDER ) :
2025-04-22 15:23:29 +02:00
getWindowPred ( std : : ranges : : find ( m_windows , cur ) , m_windows . end ( ) , m_windows . begin ( ) , FINDER ) ;
2022-07-09 18:39:41 +02:00
}
2024-08-08 21:01:50 +02:00
WORKSPACEID CCompositor : : getNextAvailableNamedWorkspace ( ) {
WORKSPACEID lowest = - 1337 + 1 ;
2025-07-24 00:36:29 +02:00
for ( auto const & w : getWorkspaces ( ) ) {
2025-04-25 02:37:12 +02:00
if ( w - > m_id < - 1 & & w - > m_id < lowest )
lowest = w - > m_id ;
2022-04-21 16:38:48 +02:00
}
2025-08-13 09:45:34 +02:00
// Give priority to persistent workspaces to avoid any conflicts between them.
for ( auto const & rule : g_pConfigManager - > getAllWorkspaceRules ( ) ) {
if ( ! rule . isPersistent )
continue ;
if ( rule . workspaceId < - 1 & & rule . workspaceId < lowest )
lowest = rule . workspaceId ;
}
2022-04-21 21:35:08 +02:00
return lowest - 1 ;
2022-04-21 16:38:48 +02:00
}
2024-04-02 20:32:39 +01:00
PHLWORKSPACE CCompositor : : getWorkspaceByName ( const std : : string & name ) {
2025-07-24 00:36:29 +02:00
for ( auto const & w : getWorkspaces ( ) ) {
2025-04-25 02:37:12 +02:00
if ( w - > m_name = = name & & ! w - > inert ( ) )
2025-07-24 00:36:29 +02:00
return w . lock ( ) ;
2022-04-21 16:38:48 +02:00
}
return nullptr ;
}
2024-04-02 20:32:39 +01:00
PHLWORKSPACE CCompositor : : getWorkspaceByString ( const std : : string & str ) {
2023-10-15 20:07:23 +02:00
if ( str . starts_with ( " name: " ) ) {
2022-04-21 16:38:48 +02:00
return getWorkspaceByName ( str . substr ( str . find_first_of ( ' : ' ) + 1 ) ) ;
}
try {
2024-06-23 00:52:42 +03:00
return getWorkspaceByID ( getWorkspaceIDNameFromString ( str ) . id ) ;
2022-12-16 17:17:31 +00:00
} catch ( std : : exception & e ) { Debug : : log ( ERR , " Error in getWorkspaceByString, invalid id " ) ; }
2022-04-21 16:38:48 +02:00
2022-04-13 20:45:06 +02:00
return nullptr ;
2022-04-23 14:16:02 +02:00
}
2022-04-24 17:53:50 +02:00
bool CCompositor : : isPointOnAnyMonitor ( const Vector2D & point ) {
2025-01-21 17:17:07 +03:00
return std : : ranges : : any_of (
2025-04-30 23:45:20 +02:00
m_monitors , [ & ] ( const PHLMONITOR & m ) { return VECINRECT ( point , m - > m_position . x , m - > m_position . y , m - > m_size . x + m - > m_position . x , m - > m_size . y + m - > m_position . y ) ; } ) ;
2022-04-25 13:40:46 +02:00
}
2024-10-19 23:03:29 +01:00
bool CCompositor : : isPointOnReservedArea ( const Vector2D & point , const PHLMONITOR pMonitor ) {
2023-12-26 16:24:31 +00:00
const auto PMONITOR = pMonitor ? pMonitor : getMonitorFromVector ( point ) ;
2025-04-30 23:45:20 +02:00
const auto XY1 = PMONITOR - > m_position + PMONITOR - > m_reservedTopLeft ;
const auto XY2 = PMONITOR - > m_position + PMONITOR - > m_size - PMONITOR - > m_reservedBottomRight ;
2023-12-26 16:24:31 +00:00
2025-02-02 20:34:26 +03:00
return VECNOTINRECT ( point , XY1 . x , XY1 . y , XY2 . x , XY2 . y ) ;
2023-12-26 16:24:31 +00:00
}
2025-10-29 06:24:34 -05:00
CBox CCompositor : : calculateX11WorkArea ( ) {
static auto PXWLFORCESCALEZERO = CConfigValue < Hyprlang : : INT > ( " xwayland:force_zero_scaling " ) ;
CBox workbox = { 0 , 0 , 0 , 0 } ;
bool firstMonitor = true ;
for ( const auto & monitor : m_monitors ) {
// we ignore monitor->m_position on purpose
auto x = monitor - > m_reservedTopLeft . x ;
auto y = monitor - > m_reservedTopLeft . y ;
auto w = monitor - > m_size . x - monitor - > m_reservedBottomRight . x - x ;
auto h = monitor - > m_size . y - monitor - > m_reservedBottomRight . y - y ;
CBox box = { x , y , w , h } ;
if ( ( * PXWLFORCESCALEZERO ) )
box . scale ( monitor - > m_scale ) ;
if ( firstMonitor ) {
firstMonitor = false ;
workbox = box ;
} else {
// if this monitor creates a different workbox than previous monitor, we remove the _NET_WORKAREA property all together
if ( ( std : : abs ( box . x - workbox . x ) > 3 ) | | ( std : : abs ( box . y - workbox . y ) > 3 ) | | ( std : : abs ( box . w - workbox . w ) > 3 ) | | ( std : : abs ( box . h - workbox . h ) > 3 ) ) {
workbox = { 0 , 0 , 0 , 0 } ;
break ;
}
}
}
// returning 0, 0 will remove the _NET_WORKAREA property
return workbox ;
}
2024-10-19 23:03:29 +01:00
PHLMONITOR CCompositor : : getMonitorInDirection ( const char & dir ) {
2025-04-22 15:23:29 +02:00
return getMonitorInDirection ( m_lastMonitor . lock ( ) , dir ) ;
2024-02-25 14:03:00 +00:00
}
2024-10-19 23:03:29 +01:00
PHLMONITOR CCompositor : : getMonitorInDirection ( PHLMONITOR pSourceMonitor , const char & dir ) {
2024-02-25 17:05:20 -07:00
if ( ! pSourceMonitor )
return nullptr ;
2024-02-25 14:03:00 +00:00
2025-04-30 23:45:20 +02:00
const auto POSA = pSourceMonitor - > m_position ;
const auto SIZEA = pSourceMonitor - > m_size ;
2022-05-05 12:50:25 +02:00
2022-12-16 17:17:31 +00:00
auto longestIntersect = - 1 ;
2024-10-19 23:03:29 +01:00
PHLMONITOR longestIntersectMonitor = nullptr ;
2022-05-05 12:50:25 +02:00
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2025-04-20 11:22:21 +09:00
if ( m = = pSourceMonitor )
2022-05-05 12:50:25 +02:00
continue ;
2025-04-30 23:45:20 +02:00
const auto POSB = m - > m_position ;
const auto SIZEB = m - > m_size ;
2022-05-05 12:50:25 +02:00
switch ( dir ) {
case ' l ' :
if ( STICKS ( POSA . x , POSB . x + SIZEB . x ) ) {
2022-09-28 19:43:35 +02:00
const auto INTERSECTLEN = std : : max ( 0.0 , std : : min ( POSA . y + SIZEA . y , POSB . y + SIZEB . y ) - std : : max ( POSA . y , POSB . y ) ) ;
2022-05-05 12:50:25 +02:00
if ( INTERSECTLEN > longestIntersect ) {
2022-12-16 17:17:31 +00:00
longestIntersect = INTERSECTLEN ;
2024-10-19 23:03:29 +01:00
longestIntersectMonitor = m ;
2022-05-05 12:50:25 +02:00
}
}
break ;
case ' r ' :
if ( STICKS ( POSA . x + SIZEA . x , POSB . x ) ) {
2022-09-28 19:43:35 +02:00
const auto INTERSECTLEN = std : : max ( 0.0 , std : : min ( POSA . y + SIZEA . y , POSB . y + SIZEB . y ) - std : : max ( POSA . y , POSB . y ) ) ;
2022-05-05 12:50:25 +02:00
if ( INTERSECTLEN > longestIntersect ) {
2022-12-16 17:17:31 +00:00
longestIntersect = INTERSECTLEN ;
2024-10-19 23:03:29 +01:00
longestIntersectMonitor = m ;
2022-05-05 12:50:25 +02:00
}
}
break ;
case ' t ' :
case ' u ' :
if ( STICKS ( POSA . y , POSB . y + SIZEB . y ) ) {
2022-09-28 19:43:35 +02:00
const auto INTERSECTLEN = std : : max ( 0.0 , std : : min ( POSA . x + SIZEA . x , POSB . x + SIZEB . x ) - std : : max ( POSA . x , POSB . x ) ) ;
2022-05-05 12:50:25 +02:00
if ( INTERSECTLEN > longestIntersect ) {
2022-12-16 17:17:31 +00:00
longestIntersect = INTERSECTLEN ;
2024-10-19 23:03:29 +01:00
longestIntersectMonitor = m ;
2022-05-05 12:50:25 +02:00
}
}
break ;
case ' b ' :
case ' d ' :
if ( STICKS ( POSA . y + SIZEA . y , POSB . y ) ) {
2022-09-28 19:43:35 +02:00
const auto INTERSECTLEN = std : : max ( 0.0 , std : : min ( POSA . x + SIZEA . x , POSB . x + SIZEB . x ) - std : : max ( POSA . x , POSB . x ) ) ;
2022-05-05 12:50:25 +02:00
if ( INTERSECTLEN > longestIntersect ) {
2022-12-16 17:17:31 +00:00
longestIntersect = INTERSECTLEN ;
2024-10-19 23:03:29 +01:00
longestIntersectMonitor = m ;
2022-05-05 12:50:25 +02:00
}
}
break ;
}
}
if ( longestIntersect ! = - 1 )
return longestIntersectMonitor ;
2022-04-25 13:40:46 +02:00
return nullptr ;
2022-05-14 17:23:46 +02:00
}
2022-05-26 21:23:13 +02:00
2022-07-12 13:40:55 +02:00
void CCompositor : : updateAllWindowsAnimatedDecorationValues ( ) {
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( ! w - > m_isMapped )
2022-05-26 21:23:13 +02:00
continue ;
2024-04-27 12:43:12 +01:00
updateWindowAnimatedDecorationValues ( w ) ;
2022-05-26 21:23:13 +02:00
}
}
2024-04-27 12:43:12 +01:00
void CCompositor : : updateWindowAnimatedDecorationValues ( PHLWINDOW pWindow ) {
2022-05-26 21:23:13 +02:00
// optimization
2024-03-03 18:39:20 +00:00
static auto PACTIVECOL = CConfigValue < Hyprlang : : CUSTOMTYPE > ( " general:col.active_border " ) ;
static auto PINACTIVECOL = CConfigValue < Hyprlang : : CUSTOMTYPE > ( " general:col.inactive_border " ) ;
static auto PNOGROUPACTIVECOL = CConfigValue < Hyprlang : : CUSTOMTYPE > ( " general:col.nogroup_border_active " ) ;
static auto PNOGROUPINACTIVECOL = CConfigValue < Hyprlang : : CUSTOMTYPE > ( " general:col.nogroup_border " ) ;
static auto PGROUPACTIVECOL = CConfigValue < Hyprlang : : CUSTOMTYPE > ( " group:col.border_active " ) ;
static auto PGROUPINACTIVECOL = CConfigValue < Hyprlang : : CUSTOMTYPE > ( " group:col.border_inactive " ) ;
static auto PGROUPACTIVELOCKEDCOL = CConfigValue < Hyprlang : : CUSTOMTYPE > ( " group:col.border_locked_active " ) ;
static auto PGROUPINACTIVELOCKEDCOL = CConfigValue < Hyprlang : : CUSTOMTYPE > ( " group:col.border_locked_inactive " ) ;
static auto PINACTIVEALPHA = CConfigValue < Hyprlang : : FLOAT > ( " decoration:inactive_opacity " ) ;
static auto PACTIVEALPHA = CConfigValue < Hyprlang : : FLOAT > ( " decoration:active_opacity " ) ;
static auto PFULLSCREENALPHA = CConfigValue < Hyprlang : : FLOAT > ( " decoration:fullscreen_opacity " ) ;
2024-11-05 15:44:40 +00:00
static auto PSHADOWCOL = CConfigValue < Hyprlang : : INT > ( " decoration:shadow:color " ) ;
static auto PSHADOWCOLINACTIVE = CConfigValue < Hyprlang : : INT > ( " decoration:shadow:color_inactive " ) ;
2024-03-03 18:39:20 +00:00
static auto PDIMSTRENGTH = CConfigValue < Hyprlang : : FLOAT > ( " decoration:dim_strength " ) ;
static auto PDIMENABLED = CConfigValue < Hyprlang : : INT > ( " decoration:dim_inactive " ) ;
2025-08-21 14:59:20 +02:00
static auto PDIMMODAL = CConfigValue < Hyprlang : : INT > ( " decoration:dim_modal " ) ;
2024-03-03 18:39:20 +00:00
2025-08-14 19:44:56 +05:00
auto * const ACTIVECOL = sc < CGradientValueData * > ( ( PACTIVECOL . ptr ( ) ) - > getData ( ) ) ;
auto * const INACTIVECOL = sc < CGradientValueData * > ( ( PINACTIVECOL . ptr ( ) ) - > getData ( ) ) ;
auto * const NOGROUPACTIVECOL = sc < CGradientValueData * > ( ( PNOGROUPACTIVECOL . ptr ( ) ) - > getData ( ) ) ;
auto * const NOGROUPINACTIVECOL = sc < CGradientValueData * > ( ( PNOGROUPINACTIVECOL . ptr ( ) ) - > getData ( ) ) ;
auto * const GROUPACTIVECOL = sc < CGradientValueData * > ( ( PGROUPACTIVECOL . ptr ( ) ) - > getData ( ) ) ;
auto * const GROUPINACTIVECOL = sc < CGradientValueData * > ( ( PGROUPINACTIVECOL . ptr ( ) ) - > getData ( ) ) ;
auto * const GROUPACTIVELOCKEDCOL = sc < CGradientValueData * > ( ( PGROUPACTIVELOCKEDCOL . ptr ( ) ) - > getData ( ) ) ;
auto * const GROUPINACTIVELOCKEDCOL = sc < CGradientValueData * > ( ( PGROUPINACTIVELOCKEDCOL . ptr ( ) ) - > getData ( ) ) ;
2024-03-03 18:39:20 +00:00
auto setBorderColor = [ & ] ( CGradientValueData grad ) - > void {
2025-04-28 22:25:22 +02:00
if ( grad = = pWindow - > m_realBorderColor )
2022-11-26 17:56:43 +00:00
return ;
2025-04-28 22:25:22 +02:00
pWindow - > m_realBorderColorPrevious = pWindow - > m_realBorderColor ;
pWindow - > m_realBorderColor = grad ;
pWindow - > m_borderFadeAnimationProgress - > setValueAndWarp ( 0.f ) ;
* pWindow - > m_borderFadeAnimationProgress = 1.f ;
2022-11-26 17:56:43 +00:00
} ;
2025-05-04 23:39:00 +02:00
const bool IS_SHADOWED_BY_MODAL = pWindow - > m_xdgSurface & & pWindow - > m_xdgSurface - > m_toplevel & & pWindow - > m_xdgSurface - > m_toplevel - > anyChildModal ( ) ;
2024-08-30 15:18:12 +02:00
2022-07-12 13:40:55 +02:00
// border
2022-05-26 21:23:13 +02:00
const auto RENDERDATA = g_pLayoutManager - > getCurrentLayout ( ) - > requestRenderHints ( pWindow ) ;
2022-12-31 16:23:56 +01:00
if ( RENDERDATA . isBorderGradient )
setBorderColor ( * RENDERDATA . borderGradient ) ;
2023-02-19 21:07:32 +00:00
else {
2025-04-28 22:25:22 +02:00
const bool GROUPLOCKED = pWindow - > m_groupData . pNextWindow . lock ( ) ? pWindow - > getGroupHead ( ) - > m_groupData . locked : false ;
2025-04-22 15:23:29 +02:00
if ( pWindow = = m_lastWindow ) {
2023-09-21 23:42:00 +00:00
const auto * const ACTIVECOLOR =
2025-04-28 22:25:22 +02:00
! pWindow - > m_groupData . pNextWindow . lock ( ) ? ( ! pWindow - > m_groupData . deny ? ACTIVECOL : NOGROUPACTIVECOL ) : ( GROUPLOCKED ? GROUPACTIVELOCKEDCOL : GROUPACTIVECOL ) ;
setBorderColor ( pWindow - > m_windowData . activeBorderColor . valueOr ( * ACTIVECOLOR ) ) ;
2023-02-19 21:07:32 +00:00
} else {
2025-04-28 22:25:22 +02:00
const auto * const INACTIVECOLOR = ! pWindow - > m_groupData . pNextWindow . lock ( ) ? ( ! pWindow - > m_groupData . deny ? INACTIVECOL : NOGROUPINACTIVECOL ) :
( GROUPLOCKED ? GROUPINACTIVELOCKEDCOL : GROUPINACTIVECOL ) ;
setBorderColor ( pWindow - > m_windowData . inactiveBorderColor . valueOr ( * INACTIVECOLOR ) ) ;
2023-02-19 21:07:32 +00:00
}
}
2022-07-12 13:40:55 +02:00
// opacity
2025-04-28 22:25:22 +02:00
const auto PWORKSPACE = pWindow - > m_workspace ;
2024-07-31 17:55:52 +00:00
if ( pWindow - > isEffectiveInternalFSMode ( FSMODE_FULLSCREEN ) ) {
2025-04-28 22:25:22 +02:00
* pWindow - > m_activeInactiveAlpha = pWindow - > m_windowData . alphaFullscreen . valueOrDefault ( ) . applyAlpha ( * PFULLSCREENALPHA ) ;
2022-07-12 13:40:55 +02:00
} else {
2025-04-22 15:23:29 +02:00
if ( pWindow = = m_lastWindow )
2025-04-28 22:25:22 +02:00
* pWindow - > m_activeInactiveAlpha = pWindow - > m_windowData . alpha . valueOrDefault ( ) . applyAlpha ( * PACTIVEALPHA ) ;
2022-07-12 13:40:55 +02:00
else
2025-04-28 22:25:22 +02:00
* pWindow - > m_activeInactiveAlpha = pWindow - > m_windowData . alphaInactive . valueOrDefault ( ) . applyAlpha ( * PINACTIVEALPHA ) ;
2022-07-12 13:40:55 +02:00
}
2022-07-16 12:44:45 +02:00
2022-08-30 12:46:17 +02:00
// dim
2024-08-30 15:18:12 +02:00
float goalDim = 1.F ;
2025-04-28 22:25:22 +02:00
if ( pWindow = = m_lastWindow . lock ( ) | | pWindow - > m_windowData . noDim . valueOrDefault ( ) | | ! * PDIMENABLED )
2024-08-30 15:18:12 +02:00
goalDim = 0 ;
else
goalDim = * PDIMSTRENGTH ;
2025-08-21 14:59:20 +02:00
if ( IS_SHADOWED_BY_MODAL & & * PDIMMODAL )
2024-08-30 15:18:12 +02:00
goalDim + = ( 1.F - goalDim ) / 2.F ;
2025-04-28 22:25:22 +02:00
* pWindow - > m_dimPercent = goalDim ;
2022-08-30 12:46:17 +02:00
2022-07-16 12:44:45 +02:00
// shadow
2025-04-28 22:25:22 +02:00
if ( ! pWindow - > isX11OverrideRedirect ( ) & & ! pWindow - > m_X11DoesntWantBorders ) {
2025-04-22 15:23:29 +02:00
if ( pWindow = = m_lastWindow )
2025-04-28 22:25:22 +02:00
* pWindow - > m_realShadowColor = CHyprColor ( * PSHADOWCOL ) ;
2024-11-05 15:44:40 +00:00
else
2025-06-16 16:40:38 -04:00
* pWindow - > m_realShadowColor = CHyprColor ( * PSHADOWCOLINACTIVE ! = - 1 ? * PSHADOWCOLINACTIVE : * PSHADOWCOL ) ;
2022-07-16 12:44:45 +02:00
} else {
2025-04-28 22:25:22 +02:00
pWindow - > m_realShadowColor - > setValueAndWarp ( CHyprColor ( 0 , 0 , 0 , 0 ) ) ; // no shadow
2022-07-16 12:44:45 +02:00
}
2023-02-27 23:34:41 +00:00
2023-11-11 14:37:17 +00:00
pWindow - > updateWindowDecos ( ) ;
2022-05-30 17:11:35 +02:00
}
2024-08-08 21:01:50 +02:00
MONITORID CCompositor : : getNextAvailableMonitorID ( std : : string const & name ) {
2023-07-18 12:12:05 +02:00
// reuse ID if it's already in the map, and the monitor with that ID is not being used by another monitor
2025-04-30 23:45:20 +02:00
if ( m_monitorIDMap . contains ( name ) & & ! std : : ranges : : any_of ( m_realMonitors , [ & ] ( auto m ) { return m - > m_id = = m_monitorIDMap [ name ] ; } ) )
2025-04-22 15:23:29 +02:00
return m_monitorIDMap [ name ] ;
2023-07-09 23:08:40 +02:00
// otherwise, find minimum available ID that is not in the map
2024-08-08 21:01:50 +02:00
std : : unordered_set < MONITORID > usedIDs ;
2025-04-22 15:23:29 +02:00
for ( auto const & monitor : m_realMonitors ) {
2025-04-30 23:45:20 +02:00
usedIDs . insert ( monitor - > m_id ) ;
2022-05-30 17:11:35 +02:00
}
2024-08-08 21:01:50 +02:00
MONITORID nextID = 0 ;
2025-02-02 20:34:26 +03:00
while ( usedIDs . contains ( nextID ) ) {
2023-07-09 23:08:40 +02:00
nextID + + ;
}
2025-04-22 15:23:29 +02:00
m_monitorIDMap [ name ] = nextID ;
2023-07-09 23:08:40 +02:00
return nextID ;
2022-05-30 17:11:35 +02:00
}
2022-05-30 20:05:38 +02:00
2024-10-19 23:03:29 +01:00
void CCompositor : : swapActiveWorkspaces ( PHLMONITOR pMonitorA , PHLMONITOR pMonitorB ) {
2025-04-30 23:45:20 +02:00
const auto PWORKSPACEA = pMonitorA - > m_activeWorkspace ;
const auto PWORKSPACEB = pMonitorB - > m_activeWorkspace ;
2022-08-25 21:25:28 +02:00
2025-04-25 02:37:12 +02:00
PWORKSPACEA - > m_monitor = pMonitorB ;
2025-07-08 09:56:40 -07:00
PWORKSPACEA - > m_events . monitorChanged . emit ( ) ;
2022-08-25 21:25:28 +02:00
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( w - > m_workspace = = PWORKSPACEA ) {
if ( w - > m_pinned ) {
w - > m_workspace = PWORKSPACEB ;
2023-03-20 01:50:46 +00:00
continue ;
}
2025-04-28 22:25:22 +02:00
w - > m_monitor = pMonitorB ;
2022-08-25 21:25:28 +02:00
2022-08-27 19:11:21 +02:00
// additionally, move floating and fs windows manually
2025-04-28 22:25:22 +02:00
if ( w - > m_isFloating )
2025-04-30 23:45:20 +02:00
* w - > m_realPosition = w - > m_realPosition - > goal ( ) - pMonitorA - > m_position + pMonitorB - > m_position ;
2022-08-27 19:11:21 +02:00
2024-07-31 17:55:52 +00:00
if ( w - > isFullscreen ( ) ) {
2025-04-30 23:45:20 +02:00
* w - > m_realPosition = pMonitorB - > m_position ;
* w - > m_realSize = pMonitorB - > m_size ;
2022-08-25 21:25:28 +02:00
}
w - > updateToplevel ( ) ;
}
}
2025-04-25 02:37:12 +02:00
PWORKSPACEB - > m_monitor = pMonitorA ;
2025-07-08 09:56:40 -07:00
PWORKSPACEB - > m_events . monitorChanged . emit ( ) ;
2022-08-25 21:25:28 +02:00
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( w - > m_workspace = = PWORKSPACEB ) {
if ( w - > m_pinned ) {
w - > m_workspace = PWORKSPACEA ;
2023-03-20 01:50:46 +00:00
continue ;
}
2025-04-28 22:25:22 +02:00
w - > m_monitor = pMonitorA ;
2022-08-25 21:25:28 +02:00
2022-08-27 19:11:21 +02:00
// additionally, move floating and fs windows manually
2025-04-28 22:25:22 +02:00
if ( w - > m_isFloating )
2025-04-30 23:45:20 +02:00
* w - > m_realPosition = w - > m_realPosition - > goal ( ) - pMonitorB - > m_position + pMonitorA - > m_position ;
2022-08-27 19:11:21 +02:00
2024-07-31 17:55:52 +00:00
if ( w - > isFullscreen ( ) ) {
2025-04-30 23:45:20 +02:00
* w - > m_realPosition = pMonitorA - > m_position ;
* w - > m_realSize = pMonitorA - > m_size ;
2022-08-25 21:25:28 +02:00
}
w - > updateToplevel ( ) ;
}
}
2025-04-30 23:45:20 +02:00
pMonitorA - > m_activeWorkspace = PWORKSPACEB ;
pMonitorB - > m_activeWorkspace = PWORKSPACEA ;
2022-08-25 21:25:28 +02:00
2024-03-04 18:05:20 +01:00
PWORKSPACEA - > rememberPrevWorkspace ( PWORKSPACEB ) ;
PWORKSPACEB - > rememberPrevWorkspace ( PWORKSPACEA ) ;
2025-04-30 23:45:20 +02:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( pMonitorA - > m_id ) ;
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( pMonitorB - > m_id ) ;
2022-08-25 21:25:28 +02:00
2025-08-28 11:20:29 +02:00
g_pDesktopAnimationManager - > setFullscreenFadeAnimation (
PWORKSPACEB , PWORKSPACEB - > m_hasFullscreenWindow ? CDesktopAnimationManager : : ANIMATION_TYPE_IN : CDesktopAnimationManager : : ANIMATION_TYPE_OUT ) ;
g_pDesktopAnimationManager - > setFullscreenFadeAnimation (
PWORKSPACEA , PWORKSPACEA - > m_hasFullscreenWindow ? CDesktopAnimationManager : : ANIMATION_TYPE_IN : CDesktopAnimationManager : : ANIMATION_TYPE_OUT ) ;
2023-04-24 23:21:51 +01:00
2025-04-30 23:45:20 +02:00
if ( pMonitorA - > m_id = = g_pCompositor - > m_lastMonitor - > m_id | | pMonitorB - > m_id = = g_pCompositor - > m_lastMonitor - > m_id ) {
const auto LASTWIN = pMonitorA - > m_id = = g_pCompositor - > m_lastMonitor - > m_id ? PWORKSPACEB - > getLastFocusedWindow ( ) : PWORKSPACEA - > getLastFocusedWindow ( ) ;
2024-02-13 17:53:50 +00:00
g_pCompositor - > focusWindow ( LASTWIN ? LASTWIN :
( g_pCompositor - > vectorToWindowUnified ( g_pInputManager - > getMouseCoordsInternal ( ) , RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING ) ) ) ;
2024-03-24 18:50:41 -07:00
2025-04-30 23:45:20 +02:00
const auto PNEWWORKSPACE = pMonitorA - > m_id = = g_pCompositor - > m_lastMonitor - > m_id ? PWORKSPACEB : PWORKSPACEA ;
2025-04-25 02:37:12 +02:00
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " workspace " , . data = PNEWWORKSPACE - > m_name } ) ;
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " workspacev2 " , . data = std : : format ( " {},{} " , PNEWWORKSPACE - > m_id , PNEWWORKSPACE - > m_name ) } ) ;
2024-03-24 18:50:41 -07:00
EMIT_HOOK_EVENT ( " workspace " , PNEWWORKSPACE ) ;
2024-02-13 17:53:50 +00:00
}
2022-08-26 16:05:02 +02:00
// event
2025-04-30 23:45:20 +02:00
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " moveworkspace " , . data = PWORKSPACEA - > m_name + " , " + pMonitorB - > m_name } ) ;
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " moveworkspacev2 " , . data = std : : format ( " {},{},{} " , PWORKSPACEA - > m_id , PWORKSPACEA - > m_name , pMonitorB - > m_name ) } ) ;
2024-04-02 20:32:39 +01:00
EMIT_HOOK_EVENT ( " moveWorkspace " , ( std : : vector < std : : any > { PWORKSPACEA , pMonitorB } ) ) ;
2025-04-30 23:45:20 +02:00
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " moveworkspace " , . data = PWORKSPACEB - > m_name + " , " + pMonitorA - > m_name } ) ;
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " moveworkspacev2 " , . data = std : : format ( " {},{},{} " , PWORKSPACEB - > m_id , PWORKSPACEB - > m_name , pMonitorA - > m_name ) } ) ;
2025-02-02 20:34:26 +03:00
2024-04-02 20:32:39 +01:00
EMIT_HOOK_EVENT ( " moveWorkspace " , ( std : : vector < std : : any > { PWORKSPACEB , pMonitorA } ) ) ;
2022-08-25 21:25:28 +02:00
}
2024-10-19 23:03:29 +01:00
PHLMONITOR CCompositor : : getMonitorFromString ( const std : : string & name ) {
2024-02-27 23:11:59 +01:00
if ( name = = " current " )
2025-04-22 15:23:29 +02:00
return g_pCompositor - > m_lastMonitor . lock ( ) ;
2024-02-27 23:11:59 +01:00
else if ( isDirection ( name ) )
return getMonitorInDirection ( name [ 0 ] ) ;
else if ( name [ 0 ] = = ' + ' | | name [ 0 ] = = ' - ' ) {
2022-11-10 12:22:19 +00:00
// relative
2023-01-14 20:45:24 +01:00
2025-04-22 15:23:29 +02:00
if ( m_monitors . size ( ) = = 1 )
return * m_monitors . begin ( ) ;
2023-01-14 20:45:24 +01:00
2022-11-10 12:22:19 +00:00
const auto OFFSET = name [ 0 ] = = ' - ' ? name : name . substr ( 1 ) ;
if ( ! isNumber ( OFFSET ) ) {
Debug : : log ( ERR , " Error in getMonitorFromString: Not a number in relative. " ) ;
return nullptr ;
}
2022-12-08 12:09:48 +00:00
int offsetLeft = std : : stoi ( OFFSET ) ;
2025-04-22 15:23:29 +02:00
offsetLeft = offsetLeft < 0 ? - ( ( - offsetLeft ) % m_monitors . size ( ) ) : offsetLeft % m_monitors . size ( ) ;
2022-11-10 12:22:19 +00:00
int currentPlace = 0 ;
2025-08-14 19:44:56 +05:00
for ( int i = 0 ; i < sc < int > ( m_monitors . size ( ) ) ; i + + ) {
2025-04-22 15:23:29 +02:00
if ( m_monitors [ i ] = = m_lastMonitor ) {
2022-11-10 12:22:19 +00:00
currentPlace = i ;
break ;
}
}
currentPlace + = offsetLeft ;
if ( currentPlace < 0 ) {
2025-04-22 15:23:29 +02:00
currentPlace = m_monitors . size ( ) + currentPlace ;
2022-11-10 12:22:19 +00:00
} else {
2025-04-22 15:23:29 +02:00
currentPlace = currentPlace % m_monitors . size ( ) ;
2022-11-10 12:22:19 +00:00
}
2025-08-14 19:44:56 +05:00
if ( currentPlace ! = std : : clamp ( currentPlace , 0 , sc < int > ( m_monitors . size ( ) ) - 1 ) ) {
2022-11-10 12:22:19 +00:00
Debug : : log ( WARN , " Error in getMonitorFromString: Vaxry's code sucks. " ) ;
2025-08-14 19:44:56 +05:00
currentPlace = std : : clamp ( currentPlace , 0 , sc < int > ( m_monitors . size ( ) ) - 1 ) ;
2022-11-10 12:22:19 +00:00
}
2025-04-22 15:23:29 +02:00
return m_monitors [ currentPlace ] ;
2022-11-10 12:22:19 +00:00
} else if ( isNumber ( name ) ) {
2022-08-25 21:25:28 +02:00
// change by ID
2024-08-08 21:01:50 +02:00
MONITORID monID = MONITOR_INVALID ;
2022-08-25 21:25:28 +02:00
try {
monID = std : : stoi ( name ) ;
} catch ( std : : exception & e ) {
// shouldn't happen but jic
Debug : : log ( ERR , " Error in getMonitorFromString: invalid num " ) ;
return nullptr ;
}
2025-08-14 19:44:56 +05:00
if ( monID > - 1 & & monID < sc < MONITORID > ( m_monitors . size ( ) ) ) {
2023-03-02 12:04:41 +00:00
return getMonitorFromID ( monID ) ;
2022-08-25 21:25:28 +02:00
} else {
Debug : : log ( ERR , " Error in getMonitorFromString: invalid arg 1 " ) ;
return nullptr ;
}
2024-02-27 23:11:59 +01:00
} else {
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2025-04-30 23:45:20 +02:00
if ( ! m - > m_output )
2023-03-16 14:03:40 +00:00
continue ;
2024-02-27 23:11:59 +01:00
if ( m - > matchesStaticSelector ( name ) ) {
2024-10-19 23:03:29 +01:00
return m ;
2023-03-02 12:04:41 +00:00
}
}
2022-08-25 21:25:28 +02:00
}
return nullptr ;
}
2024-10-19 23:03:29 +01:00
void CCompositor : : moveWorkspaceToMonitor ( PHLWORKSPACE pWorkspace , PHLMONITOR pMonitor , bool noWarpCursor ) {
2025-03-30 03:11:39 +02:00
static auto PHIDESPECIALONWORKSPACECHANGE = CConfigValue < Hyprlang : : INT > ( " binds:hide_special_on_workspace_change " ) ;
2025-01-04 00:10:10 +01:00
if ( ! pWorkspace | | ! pMonitor )
return ;
2022-05-30 20:05:38 +02:00
2025-04-25 02:37:12 +02:00
if ( pWorkspace - > m_monitor = = pMonitor )
2022-05-30 20:05:38 +02:00
return ;
2025-04-30 23:45:20 +02:00
Debug : : log ( LOG , " moveWorkspaceToMonitor: Moving {} to monitor {} " , pWorkspace - > m_id , pMonitor - > m_id ) ;
2022-05-30 20:05:38 +02:00
2025-04-25 02:37:12 +02:00
const auto POLDMON = pWorkspace - > m_monitor . lock ( ) ;
2022-05-30 20:05:38 +02:00
2025-04-30 23:45:20 +02:00
const bool SWITCHINGISACTIVE = POLDMON ? POLDMON - > m_activeWorkspace = = pWorkspace : false ;
2022-05-30 20:05:38 +02:00
// fix old mon
2024-08-08 21:01:50 +02:00
WORKSPACEID nextWorkspaceOnMonitorID = WORKSPACE_INVALID ;
2024-03-04 18:05:20 +01:00
if ( ! SWITCHINGISACTIVE )
2025-04-25 02:37:12 +02:00
nextWorkspaceOnMonitorID = pWorkspace - > m_id ;
2023-04-28 21:40:40 +01:00
else {
2025-08-17 20:29:01 +02:00
PHLWORKSPACE newWorkspace ; // for holding a ref to the new workspace that might be created
2025-07-24 00:36:29 +02:00
for ( auto const & w : getWorkspaces ( ) ) {
2025-04-25 02:37:12 +02:00
if ( w - > m_monitor = = POLDMON & & w - > m_id ! = pWorkspace - > m_id & & ! w - > m_isSpecialWorkspace ) {
nextWorkspaceOnMonitorID = w - > m_id ;
2023-04-28 21:40:40 +01:00
break ;
}
2022-05-30 20:05:38 +02:00
}
2024-08-08 21:01:50 +02:00
if ( nextWorkspaceOnMonitorID = = WORKSPACE_INVALID ) {
2023-04-28 21:40:40 +01:00
nextWorkspaceOnMonitorID = 1 ;
2022-05-30 20:05:38 +02:00
2023-04-28 21:40:40 +01:00
while ( getWorkspaceByID ( nextWorkspaceOnMonitorID ) | | [ & ] ( ) - > bool {
const auto B = g_pConfigManager - > getBoundMonitorForWS ( std : : to_string ( nextWorkspaceOnMonitorID ) ) ;
return B & & B ! = POLDMON ;
} ( ) )
nextWorkspaceOnMonitorID + + ;
2022-05-30 20:05:38 +02:00
2023-09-06 12:51:36 +02:00
Debug : : log ( LOG , " moveWorkspaceToMonitor: Plugging gap with new {} " , nextWorkspaceOnMonitorID ) ;
2023-04-14 15:03:53 +01:00
2025-03-30 17:18:04 -04:00
if ( POLDMON )
2025-08-17 20:29:01 +02:00
newWorkspace = g_pCompositor - > createNewWorkspace ( nextWorkspaceOnMonitorID , POLDMON - > m_id ) ;
2023-04-28 21:40:40 +01:00
}
2022-05-30 20:05:38 +02:00
2023-09-06 12:51:36 +02:00
Debug : : log ( LOG , " moveWorkspaceToMonitor: Plugging gap with existing {} " , nextWorkspaceOnMonitorID ) ;
2025-03-30 17:18:04 -04:00
if ( POLDMON )
POLDMON - > changeWorkspace ( nextWorkspaceOnMonitorID , false , true , true ) ;
2023-04-28 21:40:40 +01:00
}
2023-04-13 21:09:50 +01:00
2022-05-30 20:05:38 +02:00
// move the workspace
2025-04-25 02:37:12 +02:00
pWorkspace - > m_monitor = pMonitor ;
2025-07-08 09:56:40 -07:00
pWorkspace - > m_events . monitorChanged . emit ( ) ;
2022-05-30 20:05:38 +02:00
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( w - > m_workspace = = pWorkspace ) {
if ( w - > m_pinned ) {
w - > m_workspace = g_pCompositor - > getWorkspaceByID ( nextWorkspaceOnMonitorID ) ;
2023-03-20 01:50:46 +00:00
continue ;
}
2025-04-28 22:25:22 +02:00
w - > m_monitor = pMonitor ;
2022-07-25 14:22:32 +02:00
2022-08-27 19:11:21 +02:00
// additionally, move floating and fs windows manually
2025-04-28 22:25:22 +02:00
if ( w - > m_isMapped & & ! w - > isHidden ( ) ) {
2023-05-06 16:49:46 +01:00
if ( POLDMON ) {
2025-04-28 22:25:22 +02:00
if ( w - > m_isFloating )
2025-04-30 23:45:20 +02:00
* w - > m_realPosition = w - > m_realPosition - > goal ( ) - POLDMON - > m_position + pMonitor - > m_position ;
2022-08-27 19:11:21 +02:00
2024-07-31 17:55:52 +00:00
if ( w - > isFullscreen ( ) ) {
2025-04-30 23:45:20 +02:00
* w - > m_realPosition = pMonitor - > m_position ;
* w - > m_realSize = pMonitor - > m_size ;
2023-05-06 16:49:46 +01:00
}
2025-04-06 00:54:29 +02:00
} else
2025-04-28 22:25:22 +02:00
* w - > m_realPosition = Vector2D {
2025-08-14 19:44:56 +05:00
( pMonitor - > m_size . x ! = 0 ) ? sc < int > ( w - > m_realPosition - > goal ( ) . x ) % sc < int > ( pMonitor - > m_size . x ) : 0 ,
( pMonitor - > m_size . y ! = 0 ) ? sc < int > ( w - > m_realPosition - > goal ( ) . y ) % sc < int > ( pMonitor - > m_size . y ) : 0 ,
2025-04-06 00:54:29 +02:00
} ;
2022-07-25 14:22:32 +02:00
}
2022-08-06 20:57:38 +02:00
w - > updateToplevel ( ) ;
2022-07-25 14:22:32 +02:00
}
2022-05-30 20:05:38 +02:00
}
2025-04-22 15:23:29 +02:00
if ( SWITCHINGISACTIVE & & POLDMON = = g_pCompositor - > m_lastMonitor ) { // if it was active, preserve its' status. If it wasn't, don't.
2025-04-25 02:37:12 +02:00
Debug : : log ( LOG , " moveWorkspaceToMonitor: SWITCHINGISACTIVE, active {} -> {} " , pMonitor - > activeWorkspaceID ( ) , pWorkspace - > m_id ) ;
2022-05-31 17:17:44 +02:00
2025-04-30 23:45:20 +02:00
if ( valid ( pMonitor - > m_activeWorkspace ) ) {
pMonitor - > m_activeWorkspace - > m_visible = false ;
2025-08-28 11:20:29 +02:00
g_pDesktopAnimationManager - > startAnimation ( pWorkspace , CDesktopAnimationManager : : ANIMATION_TYPE_OUT , false ) ;
2024-04-04 18:30:32 +01:00
}
2022-05-30 20:05:38 +02:00
2025-03-30 03:11:39 +02:00
if ( * PHIDESPECIALONWORKSPACECHANGE )
pMonitor - > setSpecialWorkspace ( nullptr ) ;
2024-02-10 00:47:00 +01:00
setActiveMonitor ( pMonitor ) ;
2025-06-26 09:32:44 -07:00
auto oldWorkspace = pMonitor - > m_activeWorkspace ;
2025-04-30 23:45:20 +02:00
pMonitor - > m_activeWorkspace = pWorkspace ;
2025-06-26 09:32:44 -07:00
if ( oldWorkspace )
2025-07-08 09:56:40 -07:00
oldWorkspace - > m_events . activeChanged . emit ( ) ;
2025-06-26 09:32:44 -07:00
2025-07-08 09:56:40 -07:00
pWorkspace - > m_events . activeChanged . emit ( ) ;
2025-06-26 09:32:44 -07:00
2025-04-30 23:45:20 +02:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( pMonitor - > m_id ) ;
2022-05-30 20:05:38 +02:00
2025-08-28 11:20:29 +02:00
g_pDesktopAnimationManager - > startAnimation ( pWorkspace , CDesktopAnimationManager : : ANIMATION_TYPE_IN , true , true ) ;
2025-04-25 02:37:12 +02:00
pWorkspace - > m_visible = true ;
2022-05-30 20:05:38 +02:00
2024-01-15 23:30:46 +08:00
if ( ! noWarpCursor )
2025-04-30 23:45:20 +02:00
g_pPointerManager - > warpTo ( pMonitor - > m_position + pMonitor - > m_transformedSize / 2.F ) ;
2023-05-06 17:13:26 +02:00
2023-12-12 14:58:43 +00:00
g_pInputManager - > sendMotionEventsToFocused ( ) ;
2022-05-30 20:05:38 +02:00
}
// finalize
2023-05-06 16:49:46 +01:00
if ( POLDMON ) {
2025-04-30 23:45:20 +02:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( POLDMON - > m_id ) ;
if ( valid ( POLDMON - > m_activeWorkspace ) )
2025-08-28 11:20:29 +02:00
g_pDesktopAnimationManager - > setFullscreenFadeAnimation ( POLDMON - > m_activeWorkspace ,
POLDMON - > m_activeWorkspace - > m_hasFullscreenWindow ? CDesktopAnimationManager : : ANIMATION_TYPE_IN :
CDesktopAnimationManager : : ANIMATION_TYPE_OUT ) ;
2024-06-12 16:02:19 +02:00
updateSuspendedStates ( ) ;
2023-05-06 16:49:46 +01:00
}
2022-05-30 20:05:38 +02:00
2025-08-28 11:20:29 +02:00
g_pDesktopAnimationManager - > setFullscreenFadeAnimation (
pWorkspace , pWorkspace - > m_hasFullscreenWindow ? CDesktopAnimationManager : : ANIMATION_TYPE_IN : CDesktopAnimationManager : : ANIMATION_TYPE_OUT ) ;
2024-06-12 16:02:19 +02:00
updateSuspendedStates ( ) ;
2023-04-24 23:23:12 +01:00
2022-08-26 16:05:02 +02:00
// event
2025-04-30 23:45:20 +02:00
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " moveworkspace " , . data = pWorkspace - > m_name + " , " + pMonitor - > m_name } ) ;
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " moveworkspacev2 " , . data = std : : format ( " {},{},{} " , pWorkspace - > m_id , pWorkspace - > m_name , pMonitor - > m_name ) } ) ;
2024-04-02 20:32:39 +01:00
EMIT_HOOK_EVENT ( " moveWorkspace " , ( std : : vector < std : : any > { pWorkspace , pMonitor } ) ) ;
2022-05-31 12:33:08 +02:00
}
2024-08-08 21:01:50 +02:00
bool CCompositor : : workspaceIDOutOfBounds ( const WORKSPACEID & id ) {
WORKSPACEID lowestID = INT64_MAX ;
WORKSPACEID highestID = INT64_MIN ;
2022-05-31 12:33:08 +02:00
2025-07-24 00:36:29 +02:00
for ( auto const & w : getWorkspaces ( ) ) {
2025-04-25 02:37:12 +02:00
if ( w - > m_isSpecialWorkspace )
2022-09-17 15:05:12 +01:00
continue ;
2025-04-25 02:37:12 +02:00
lowestID = std : : min ( w - > m_id , lowestID ) ;
highestID = std : : max ( w - > m_id , highestID ) ;
2022-05-31 12:33:08 +02:00
}
return std : : clamp ( id , lowestID , highestID ) ! = id ;
2022-06-26 12:12:29 +02:00
}
2024-07-31 17:55:52 +00:00
void CCompositor : : changeWindowFullscreenModeClient ( const PHLWINDOW PWINDOW , const eFullscreenMode MODE , const bool ON ) {
2025-08-14 19:44:56 +05:00
setWindowFullscreenClient (
PWINDOW ,
sc < eFullscreenMode > ( ON ? sc < uint8_t > ( PWINDOW - > m_fullscreenState . client ) | sc < uint8_t > ( MODE ) : ( sc < uint8_t > ( PWINDOW - > m_fullscreenState . client ) & sc < uint8_t > ( ~ MODE ) ) ) ) ;
2024-07-31 17:55:52 +00:00
}
void CCompositor : : setWindowFullscreenInternal ( const PHLWINDOW PWINDOW , const eFullscreenMode MODE ) {
2025-04-28 22:25:22 +02:00
if ( PWINDOW - > m_windowData . syncFullscreen . valueOrDefault ( ) )
2024-12-07 18:51:18 +01:00
setWindowFullscreenState ( PWINDOW , SFullscreenState { . internal = MODE , . client = MODE } ) ;
2024-07-31 17:55:52 +00:00
else
2025-04-28 22:25:22 +02:00
setWindowFullscreenState ( PWINDOW , SFullscreenState { . internal = MODE , . client = PWINDOW - > m_fullscreenState . client } ) ;
2024-07-31 17:55:52 +00:00
}
void CCompositor : : setWindowFullscreenClient ( const PHLWINDOW PWINDOW , const eFullscreenMode MODE ) {
2025-04-28 22:25:22 +02:00
if ( PWINDOW - > m_windowData . syncFullscreen . valueOrDefault ( ) )
2024-12-07 18:51:18 +01:00
setWindowFullscreenState ( PWINDOW , SFullscreenState { . internal = MODE , . client = MODE } ) ;
2024-07-31 17:55:52 +00:00
else
2025-04-28 22:25:22 +02:00
setWindowFullscreenState ( PWINDOW , SFullscreenState { . internal = PWINDOW - > m_fullscreenState . internal , . client = MODE } ) ;
2024-07-31 17:55:52 +00:00
}
2024-12-07 18:51:18 +01:00
void CCompositor : : setWindowFullscreenState ( const PHLWINDOW PWINDOW , SFullscreenState state ) {
2024-11-23 22:32:13 +08:00
static auto PDIRECTSCANOUT = CConfigValue < Hyprlang : : INT > ( " render:direct_scanout " ) ;
static auto PALLOWPINFULLSCREEN = CConfigValue < Hyprlang : : INT > ( " binds:allow_pin_fullscreen " ) ;
2024-07-21 23:25:20 +02:00
2025-04-22 15:23:29 +02:00
if ( ! validMapped ( PWINDOW ) | | g_pCompositor - > m_unsafeState )
2022-06-26 12:12:29 +02:00
return ;
2025-08-14 19:44:56 +05:00
state . internal = std : : clamp ( state . internal , sc < eFullscreenMode > ( 0 ) , FSMODE_MAX ) ;
state . client = std : : clamp ( state . client , sc < eFullscreenMode > ( 0 ) , FSMODE_MAX ) ;
2022-09-21 14:09:26 +01:00
2025-04-28 22:25:22 +02:00
const auto PMONITOR = PWINDOW - > m_monitor . lock ( ) ;
const auto PWORKSPACE = PWINDOW - > m_workspace ;
2024-03-12 19:09:20 -07:00
2025-08-14 19:44:56 +05:00
const eFullscreenMode CURRENT_EFFECTIVE_MODE = sc < eFullscreenMode > ( std : : bit_floor ( sc < uint8_t > ( PWINDOW - > m_fullscreenState . internal ) ) ) ;
const eFullscreenMode EFFECTIVE_MODE = sc < eFullscreenMode > ( std : : bit_floor ( sc < uint8_t > ( state . internal ) ) ) ;
2022-09-29 10:20:12 +01:00
2025-04-28 22:25:22 +02:00
if ( PWINDOW - > m_isFloating & & CURRENT_EFFECTIVE_MODE = = FSMODE_NONE & & EFFECTIVE_MODE ! = FSMODE_NONE )
2025-03-30 17:28:12 -04:00
g_pHyprRenderer - > damageWindow ( PWINDOW ) ;
2025-04-28 22:25:22 +02:00
if ( * PALLOWPINFULLSCREEN & & ! PWINDOW - > m_pinFullscreened & & ! PWINDOW - > isFullscreen ( ) & & PWINDOW - > m_pinned ) {
PWINDOW - > m_pinned = false ;
PWINDOW - > m_pinFullscreened = true ;
2024-11-23 22:32:13 +08:00
}
2025-04-25 02:37:12 +02:00
if ( PWORKSPACE - > m_hasFullscreenWindow & & ! PWINDOW - > isFullscreen ( ) )
2024-12-11 04:09:47 +08:00
setWindowFullscreenInternal ( PWORKSPACE - > getFullscreenWindow ( ) , FSMODE_NONE ) ;
2025-04-28 22:25:22 +02:00
const bool CHANGEINTERNAL = ! PWINDOW - > m_pinned & & CURRENT_EFFECTIVE_MODE ! = EFFECTIVE_MODE ;
2024-11-23 22:32:13 +08:00
2025-04-28 22:25:22 +02:00
if ( * PALLOWPINFULLSCREEN & & PWINDOW - > m_pinFullscreened & & PWINDOW - > isFullscreen ( ) & & ! PWINDOW - > m_pinned & & state . internal = = FSMODE_NONE ) {
PWINDOW - > m_pinned = true ;
PWINDOW - > m_pinFullscreened = false ;
2024-11-23 22:32:13 +08:00
}
2022-09-29 10:20:12 +01:00
2024-07-31 17:55:52 +00:00
// TODO: update the state on syncFullscreen changes
2025-04-28 22:25:22 +02:00
if ( ! CHANGEINTERNAL & & PWINDOW - > m_windowData . syncFullscreen . valueOrDefault ( ) )
2024-07-31 17:55:52 +00:00
return ;
2023-09-28 17:49:33 +01:00
2025-04-28 22:25:22 +02:00
PWINDOW - > m_fullscreenState . client = state . client ;
2024-07-31 17:55:52 +00:00
g_pXWaylandManager - > setWindowFullscreen ( PWINDOW , state . client & FSMODE_FULLSCREEN ) ;
2024-08-24 04:42:14 +09:00
if ( ! CHANGEINTERNAL ) {
PWINDOW - > updateDynamicRules ( ) ;
updateWindowAnimatedDecorationValues ( PWINDOW ) ;
2024-10-27 18:45:38 +00:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( PWINDOW - > monitorID ( ) ) ;
2022-09-29 10:20:12 +01:00
return ;
2024-08-24 04:42:14 +09:00
}
2022-09-29 10:20:12 +01:00
2024-07-31 17:55:52 +00:00
g_pLayoutManager - > getCurrentLayout ( ) - > fullscreenRequestForWindow ( PWINDOW , CURRENT_EFFECTIVE_MODE , EFFECTIVE_MODE ) ;
2022-06-26 12:12:29 +02:00
2025-04-28 22:25:22 +02:00
PWINDOW - > m_fullscreenState . internal = state . internal ;
PWORKSPACE - > m_fullscreenMode = EFFECTIVE_MODE ;
PWORKSPACE - > m_hasFullscreenWindow = EFFECTIVE_MODE ! = FSMODE_NONE ;
2022-09-25 20:07:48 +02:00
2025-08-14 19:44:56 +05:00
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " fullscreen " , . data = std : : to_string ( sc < int > ( EFFECTIVE_MODE ) ! = FSMODE_NONE ) } ) ;
2024-07-31 17:55:52 +00:00
EMIT_HOOK_EVENT ( " fullscreen " , PWINDOW ) ;
2024-08-01 18:43:02 +09:00
PWINDOW - > updateDynamicRules ( ) ;
2024-07-31 17:55:52 +00:00
updateWindowAnimatedDecorationValues ( PWINDOW ) ;
2024-10-27 18:45:38 +00:00
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateMonitor ( PWINDOW - > monitorID ( ) ) ;
2022-11-15 11:21:26 +01:00
2022-06-26 12:12:29 +02:00
// make all windows on the same workspace under the fullscreen window
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( w - > m_workspace = = PWORKSPACE & & ! w - > isFullscreen ( ) & & ! w - > m_fadingOut & & ! w - > m_pinned )
w - > m_createdOverFullscreen = false ;
2022-09-19 10:23:13 +01:00
}
2024-07-31 17:55:52 +00:00
2025-08-28 11:20:29 +02:00
g_pDesktopAnimationManager - > setFullscreenFadeAnimation (
PWORKSPACE , PWORKSPACE - > m_hasFullscreenWindow ? CDesktopAnimationManager : : ANIMATION_TYPE_IN : CDesktopAnimationManager : : ANIMATION_TYPE_OUT ) ;
2022-09-25 20:07:48 +02:00
2025-02-06 11:21:04 +00:00
PWINDOW - > sendWindowSize ( true ) ;
2022-08-22 14:22:21 +02:00
2024-11-22 16:01:02 +00:00
PWORKSPACE - > forceReportSizesToWindows ( ) ;
2022-10-31 12:26:07 +00:00
g_pInputManager - > recheckIdleInhibitorStatus ( ) ;
2022-11-05 12:50:47 +00:00
2024-07-21 13:09:54 +02:00
// further updates require a monitor
if ( ! PMONITOR )
return ;
// send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
2024-07-21 23:25:20 +02:00
// ignore if DS is disabled.
2025-08-22 20:24:25 +03:00
if ( * PDIRECTSCANOUT = = 1 | | ( * PDIRECTSCANOUT = = 2 & & PWINDOW - > getContentType ( ) = = CONTENT_TYPE_GAME ) ) {
auto surf = PWINDOW - > getSolitaryResource ( ) ;
if ( surf )
g_pHyprRenderer - > setSurfaceScanoutMode ( surf , EFFECTIVE_MODE ! = FSMODE_NONE ? PMONITOR - > m_self . lock ( ) : nullptr ) ;
}
2023-02-14 17:08:42 +00:00
g_pConfigManager - > ensureVRR ( PMONITOR ) ;
2022-06-30 15:44:26 +02:00
}
2024-04-27 12:43:12 +01:00
PHLWINDOW CCompositor : : getX11Parent ( PHLWINDOW pWindow ) {
2025-04-28 22:25:22 +02:00
if ( ! pWindow - > m_isX11 )
2022-06-30 15:44:26 +02:00
return nullptr ;
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( ! w - > m_isX11 )
2022-06-30 15:44:26 +02:00
continue ;
2025-05-07 15:21:44 +02:00
if ( w - > m_xwaylandSurface = = pWindow - > m_xwaylandSurface - > m_parent )
2024-04-27 12:43:12 +01:00
return w ;
2022-06-30 15:44:26 +02:00
}
2022-09-25 20:07:48 +02:00
2022-06-30 15:44:26 +02:00
return nullptr ;
2022-07-07 11:52:12 +02:00
}
2024-10-19 23:03:29 +01:00
void CCompositor : : scheduleFrameForMonitor ( PHLMONITOR pMonitor , IOutput : : scheduleFrameReason reason ) {
2025-04-22 15:23:29 +02:00
if ( ( m_aqBackend - > hasSession ( ) & & ! m_aqBackend - > session - > active ) | | ! m_sessionActive )
2022-07-13 18:18:23 +02:00
return ;
2025-04-30 23:45:20 +02:00
if ( ! pMonitor - > m_enabled )
2022-11-19 13:01:32 +00:00
return ;
2025-04-30 23:45:20 +02:00
if ( pMonitor - > m_renderingActive )
pMonitor - > m_pendingFrame = true ;
2023-02-27 12:32:38 +00:00
2025-04-30 23:45:20 +02:00
pMonitor - > m_output - > scheduleFrame ( reason ) ;
2022-07-26 17:30:30 +02:00
}
2024-10-08 17:50:06 +01:00
PHLWINDOW CCompositor : : getWindowByRegex ( const std : : string & regexp_ ) {
auto regexp = trim ( regexp_ ) ;
2024-04-21 17:19:59 +03:00
if ( regexp . starts_with ( " active " ) )
2025-04-22 15:23:29 +02:00
return m_lastWindow . lock ( ) ;
2024-10-08 17:50:06 +01:00
else if ( regexp . starts_with ( " floating " ) | | regexp . starts_with ( " tiled " ) ) {
// first floating on the current ws
2025-04-22 15:23:29 +02:00
if ( ! valid ( m_lastWindow ) )
2024-10-08 17:50:06 +01:00
return nullptr ;
const bool FLOAT = regexp . starts_with ( " floating " ) ;
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( ! w - > m_isMapped | | w - > m_isFloating ! = FLOAT | | w - > m_workspace ! = m_lastWindow - > m_workspace | | w - > isHidden ( ) )
2024-10-08 17:50:06 +01:00
continue ;
return w ;
}
return nullptr ;
}
2024-04-21 17:19:59 +03:00
2022-07-26 17:30:30 +02:00
eFocusWindowMode mode = MODE_CLASS_REGEX ;
2024-12-16 19:21:44 +01:00
std : : string regexCheck ;
2022-12-16 17:17:31 +00:00
std : : string matchCheck ;
2024-04-21 17:19:59 +03:00
if ( regexp . starts_with ( " class: " ) ) {
2024-12-16 19:21:44 +01:00
regexCheck = regexp . substr ( 6 ) ;
2024-04-21 17:19:59 +03:00
} else if ( regexp . starts_with ( " initialclass: " ) ) {
mode = MODE_INITIAL_CLASS_REGEX ;
2024-12-16 19:21:44 +01:00
regexCheck = regexp . substr ( 13 ) ;
2024-04-21 17:19:59 +03:00
} else if ( regexp . starts_with ( " title: " ) ) {
2022-12-16 17:17:31 +00:00
mode = MODE_TITLE_REGEX ;
2024-12-16 19:21:44 +01:00
regexCheck = regexp . substr ( 6 ) ;
2024-04-21 17:19:59 +03:00
} else if ( regexp . starts_with ( " initialtitle: " ) ) {
mode = MODE_INITIAL_TITLE_REGEX ;
2024-12-16 19:21:44 +01:00
regexCheck = regexp . substr ( 13 ) ;
2025-01-09 14:52:26 -06:00
} else if ( regexp . starts_with ( " tag: " ) ) {
mode = MODE_TAG_REGEX ;
regexCheck = regexp . substr ( 4 ) ;
2023-10-15 20:07:23 +02:00
} else if ( regexp . starts_with ( " address: " ) ) {
2022-12-16 17:17:31 +00:00
mode = MODE_ADDRESS ;
2022-07-26 17:30:30 +02:00
matchCheck = regexp . substr ( 8 ) ;
2023-10-15 20:07:23 +02:00
} else if ( regexp . starts_with ( " pid: " ) ) {
2022-12-16 17:17:31 +00:00
mode = MODE_PID ;
2022-07-26 17:30:30 +02:00
matchCheck = regexp . substr ( 4 ) ;
}
2025-04-22 15:23:29 +02:00
for ( auto const & w : g_pCompositor - > m_windows ) {
2025-04-28 22:25:22 +02:00
if ( ! w - > m_isMapped | | ( w - > isHidden ( ) & & ! g_pLayoutManager - > getCurrentLayout ( ) - > isWindowReachable ( w ) ) )
2022-07-26 17:30:30 +02:00
continue ;
switch ( mode ) {
case MODE_CLASS_REGEX : {
2025-04-28 22:25:22 +02:00
const auto windowClass = w - > m_class ;
2024-12-16 19:21:44 +01:00
if ( ! RE2 : : FullMatch ( windowClass , regexCheck ) )
2024-04-21 17:19:59 +03:00
continue ;
break ;
}
case MODE_INITIAL_CLASS_REGEX : {
2025-04-28 22:25:22 +02:00
const auto initialWindowClass = w - > m_initialClass ;
2024-12-16 19:21:44 +01:00
if ( ! RE2 : : FullMatch ( initialWindowClass , regexCheck ) )
2022-07-26 17:30:30 +02:00
continue ;
break ;
}
case MODE_TITLE_REGEX : {
2025-04-28 22:25:22 +02:00
const auto windowTitle = w - > m_title ;
2024-12-16 19:21:44 +01:00
if ( ! RE2 : : FullMatch ( windowTitle , regexCheck ) )
2022-07-26 17:30:30 +02:00
continue ;
break ;
}
2024-04-21 17:19:59 +03:00
case MODE_INITIAL_TITLE_REGEX : {
2025-04-28 22:25:22 +02:00
const auto initialWindowTitle = w - > m_initialTitle ;
2024-12-16 19:21:44 +01:00
if ( ! RE2 : : FullMatch ( initialWindowTitle , regexCheck ) )
2024-04-21 17:19:59 +03:00
continue ;
break ;
}
2025-01-09 14:52:26 -06:00
case MODE_TAG_REGEX : {
bool tagMatched = false ;
for ( auto const & t : w - > m_tags . getTags ( ) ) {
if ( RE2 : : FullMatch ( t , regexCheck ) ) {
tagMatched = true ;
break ;
}
}
if ( ! tagMatched )
continue ;
break ;
}
2022-07-26 17:30:30 +02:00
case MODE_ADDRESS : {
2025-08-14 19:44:56 +05:00
std : : string addr = std : : format ( " 0x{:x} " , rc < uintptr_t > ( w . get ( ) ) ) ;
2022-07-26 17:30:30 +02:00
if ( matchCheck ! = addr )
continue ;
break ;
}
case MODE_PID : {
2023-09-20 07:26:20 +00:00
std : : string pid = std : : format ( " {} " , w - > getPID ( ) ) ;
2022-07-26 17:30:30 +02:00
if ( matchCheck ! = pid )
continue ;
break ;
}
2022-12-16 17:17:31 +00:00
default : break ;
2022-07-26 17:30:30 +02:00
}
2024-04-27 12:43:12 +01:00
return w ;
2022-07-26 17:30:30 +02:00
}
return nullptr ;
2022-07-31 00:27:32 +02:00
}
2023-01-28 12:26:33 +00:00
void CCompositor : : warpCursorTo ( const Vector2D & pos , bool force ) {
2022-08-01 18:50:16 +02:00
// warpCursorTo should only be used for warps that
2024-05-09 22:23:01 +01:00
// should be disabled with no_warps
2022-08-01 18:50:16 +02:00
2024-05-09 22:23:01 +01:00
static auto PNOWARPS = CConfigValue < Hyprlang : : INT > ( " cursor:no_warps " ) ;
2022-08-01 18:50:16 +02:00
2024-05-27 20:45:14 +00:00
if ( * PNOWARPS & & ! force ) {
const auto PMONITORNEW = getMonitorFromVector ( pos ) ;
2025-04-22 15:23:29 +02:00
if ( PMONITORNEW ! = m_lastMonitor )
2024-05-27 20:45:14 +00:00
setActiveMonitor ( PMONITORNEW ) ;
2022-08-01 18:50:16 +02:00
return ;
2024-05-27 20:45:14 +00:00
}
2022-08-01 18:50:16 +02:00
2024-05-05 22:18:10 +01:00
g_pPointerManager - > warpTo ( pos ) ;
2022-09-03 11:55:14 +02:00
const auto PMONITORNEW = getMonitorFromVector ( pos ) ;
2025-04-22 15:23:29 +02:00
if ( PMONITORNEW ! = m_lastMonitor )
2022-11-19 16:41:36 +00:00
setActiveMonitor ( PMONITORNEW ) ;
2022-08-01 18:50:16 +02:00
}
2024-04-27 12:43:12 +01:00
void CCompositor : : closeWindow ( PHLWINDOW pWindow ) {
2025-04-29 18:14:02 +01:00
if ( pWindow & & validMapped ( pWindow ) )
2022-08-06 20:57:38 +02:00
g_pXWaylandManager - > sendCloseWindow ( pWindow ) ;
2022-08-10 17:46:01 +02:00
}
2024-06-08 10:07:59 +02:00
PHLLS CCompositor : : getLayerSurfaceFromSurface ( SP < CWLSurfaceResource > pSurface ) {
std : : pair < SP < CWLSurfaceResource > , bool > result = { pSurface , false } ;
2023-04-02 13:30:45 +01:00
2025-04-22 15:23:29 +02:00
for ( auto const & ls : m_layers ) {
2025-05-04 00:13:29 +02:00
if ( ls - > m_layerSurface & & ls - > m_layerSurface - > m_surface = = pSurface )
2024-05-09 21:47:21 +01:00
return ls ;
2023-04-02 13:42:57 +01:00
2025-04-24 20:49:49 +02:00
if ( ! ls - > m_layerSurface | | ! ls - > m_mapped )
2024-05-09 21:47:21 +01:00
continue ;
2023-04-02 13:30:45 +01:00
2025-05-04 00:13:29 +02:00
ls - > m_layerSurface - > m_surface - > breadthfirst (
2025-01-25 03:26:46 +04:00
[ & result ] ( SP < CWLSurfaceResource > surf , const Vector2D & offset , void * data ) {
if ( surf = = result . first ) {
result . second = true ;
2024-06-08 10:07:59 +02:00
return ;
}
} ,
2025-01-25 03:26:46 +04:00
nullptr ) ;
2024-05-09 21:47:21 +01:00
if ( result . second )
return ls ;
2022-08-10 17:46:01 +02:00
}
return nullptr ;
2022-08-15 15:59:07 +02:00
}
// returns a delta
Vector2D CCompositor : : parseWindowVectorArgsRelative ( const std : : string & args , const Vector2D & relativeTo ) {
2024-03-31 01:48:39 +01:00
if ( ! args . contains ( ' ' ) & & ! args . contains ( ' \t ' ) )
2022-08-15 15:59:07 +02:00
return relativeTo ;
2025-04-22 15:23:29 +02:00
const auto PMONITOR = m_lastMonitor ;
2023-09-10 18:26:14 -04:00
bool xIsPercent = false ;
bool yIsPercent = false ;
bool isExact = false ;
2024-03-31 01:48:39 +01:00
CVarList varList ( args , 0 , ' s ' , true ) ;
std : : string x = varList [ 0 ] ;
std : : string y = varList [ 1 ] ;
2022-08-15 15:59:07 +02:00
if ( x = = " exact " ) {
2024-03-31 01:48:39 +01:00
x = varList [ 1 ] ;
y = varList [ 2 ] ;
2023-09-10 18:26:14 -04:00
isExact = true ;
}
2022-08-15 15:59:07 +02:00
2023-09-10 18:26:14 -04:00
if ( x . contains ( ' % ' ) ) {
xIsPercent = true ;
x = x . substr ( 0 , x . length ( ) - 1 ) ;
}
2022-08-15 15:59:07 +02:00
2023-09-10 18:26:14 -04:00
if ( y . contains ( ' % ' ) ) {
yIsPercent = true ;
y = y . substr ( 0 , y . length ( ) - 1 ) ;
2022-08-15 15:59:07 +02:00
}
if ( ! isNumber ( x ) | | ! isNumber ( y ) ) {
Debug : : log ( ERR , " parseWindowVectorArgsRelative: args not numbers " ) ;
return relativeTo ;
}
2023-09-10 18:26:14 -04:00
int X = 0 ;
int Y = 0 ;
if ( isExact ) {
2025-04-30 23:45:20 +02:00
X = xIsPercent ? std : : stof ( x ) * 0.01 * PMONITOR - > m_size . x : std : : stoi ( x ) ;
Y = yIsPercent ? std : : stof ( y ) * 0.01 * PMONITOR - > m_size . y : std : : stoi ( y ) ;
2023-09-10 18:26:14 -04:00
} else {
2025-02-02 20:34:26 +03:00
X = xIsPercent ? ( std : : stof ( x ) * 0.01 * relativeTo . x ) + relativeTo . x : std : : stoi ( x ) + relativeTo . x ;
Y = yIsPercent ? ( std : : stof ( y ) * 0.01 * relativeTo . y ) + relativeTo . y : std : : stoi ( y ) + relativeTo . y ;
2023-09-10 18:26:14 -04:00
}
2022-08-15 15:59:07 +02:00
2023-09-10 18:26:14 -04:00
return Vector2D ( X , Y ) ;
2022-08-22 14:22:21 +02:00
}
2024-08-26 05:25:52 -05:00
PHLWORKSPACE CCompositor : : createNewWorkspace ( const WORKSPACEID & id , const MONITORID & monid , const std : : string & name , bool isEmpty ) {
2024-12-07 18:51:18 +01:00
const auto NAME = name . empty ( ) ? std : : to_string ( id ) : name ;
2022-12-16 17:17:31 +00:00
auto monID = monid ;
2022-10-25 10:30:25 +01:00
// check if bound
2024-10-27 18:45:38 +00:00
if ( const auto PMONITOR = g_pConfigManager - > getBoundMonitorForWS ( NAME ) ; PMONITOR )
2025-04-30 23:45:20 +02:00
monID = PMONITOR - > m_id ;
2022-10-25 10:30:25 +01:00
2022-11-27 22:42:22 +00:00
const bool SPECIAL = id > = SPECIAL_WORKSPACE_START & & id < = - 2 ;
2025-03-27 13:59:45 +00:00
const auto PMONITOR = getMonitorFromID ( monID ) ;
if ( ! PMONITOR ) {
Debug : : log ( ERR , " BUG THIS: No pMonitor for new workspace in createNewWorkspace " ) ;
return nullptr ;
}
2025-07-24 00:36:29 +02:00
const auto PWORKSPACE = CWorkspace : : create ( id , PMONITOR , NAME , SPECIAL , isEmpty ) ;
2022-10-24 18:36:31 +01:00
2025-04-25 02:37:12 +02:00
PWORKSPACE - > m_alpha - > setValueAndWarp ( 0 ) ;
2023-11-18 17:00:24 +00:00
2022-10-24 18:36:31 +01:00
return PWORKSPACE ;
}
2022-11-19 16:41:36 +00:00
2024-10-19 23:03:29 +01:00
void CCompositor : : setActiveMonitor ( PHLMONITOR pMonitor ) {
2025-04-22 15:23:29 +02:00
if ( m_lastMonitor = = pMonitor )
2022-11-19 16:41:36 +00:00
return ;
2022-11-22 23:17:10 +00:00
if ( ! pMonitor ) {
2025-04-22 15:23:29 +02:00
m_lastMonitor . reset ( ) ;
2022-11-22 23:17:10 +00:00
return ;
}
2025-04-30 23:45:20 +02:00
const auto PWORKSPACE = pMonitor - > m_activeWorkspace ;
2022-11-19 16:41:36 +00:00
2025-04-25 02:37:12 +02:00
const auto WORKSPACE_ID = PWORKSPACE ? std : : to_string ( PWORKSPACE - > m_id ) : std : : to_string ( WORKSPACE_INVALID ) ;
const auto WORKSPACE_NAME = PWORKSPACE ? PWORKSPACE - > m_name : " ? " ;
2025-01-03 23:43:48 +01:00
2025-04-30 23:45:20 +02:00
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " focusedmon " , . data = pMonitor - > m_name + " , " + WORKSPACE_NAME } ) ;
g_pEventManager - > postEvent ( SHyprIPCEvent { . event = " focusedmonv2 " , . data = pMonitor - > m_name + " , " + WORKSPACE_ID } ) ;
2025-01-03 23:43:48 +01:00
2023-02-19 20:54:53 +00:00
EMIT_HOOK_EVENT ( " focusedMon " , pMonitor ) ;
2025-04-30 23:45:20 +02:00
m_lastMonitor = pMonitor - > m_self ;
2022-11-19 16:41:36 +00:00
}
2022-11-27 22:42:22 +00:00
2024-08-08 21:01:50 +02:00
bool CCompositor : : isWorkspaceSpecial ( const WORKSPACEID & id ) {
2022-11-27 22:42:22 +00:00
return id > = SPECIAL_WORKSPACE_START & & id < = - 2 ;
}
2024-08-08 21:01:50 +02:00
WORKSPACEID CCompositor : : getNewSpecialID ( ) {
WORKSPACEID highest = SPECIAL_WORKSPACE_START ;
2025-07-24 00:36:29 +02:00
for ( auto const & ws : getWorkspaces ( ) ) {
if ( ws - > m_isSpecialWorkspace & & ws - > m_id > highest )
2025-04-25 02:37:12 +02:00
highest = ws - > m_id ;
2022-11-27 22:42:22 +00:00
}
return highest + 1 ;
}
2023-03-20 15:02:47 +00:00
2025-07-24 00:36:29 +02:00
void CCompositor : : registerWorkspace ( PHLWORKSPACE w ) {
m_workspaces . emplace_back ( w ) ;
w - > m_events . destroy . listenStatic ( [ this , weak = PHLWORKSPACEREF { w } ] { std : : erase ( m_workspaces , weak ) ; } ) ;
}
2025-07-28 22:08:05 +02:00
std : : vector < PHLWORKSPACE > CCompositor : : getWorkspacesCopy ( ) {
std : : vector < PHLWORKSPACE > wsp ;
auto range = getWorkspaces ( ) ;
wsp . reserve ( std : : ranges : : distance ( range ) ) ;
for ( auto & r : range ) {
wsp . emplace_back ( r . lock ( ) ) ;
}
return wsp ;
}
2023-03-20 15:02:47 +00:00
void CCompositor : : performUserChecks ( ) {
2025-11-07 15:48:13 +00:00
static auto PNOCHECKXDG = CConfigValue < Hyprlang : : INT > ( " misc:disable_xdg_env_checks " ) ;
static auto PNOCHECKGUIUTILS = CConfigValue < Hyprlang : : INT > ( " misc:disable_hyprland_guiutils_check " ) ;
2024-09-18 11:22:07 +01:00
if ( ! * PNOCHECKXDG ) {
const auto CURRENT_DESKTOP_ENV = getenv ( " XDG_CURRENT_DESKTOP " ) ;
if ( ! CURRENT_DESKTOP_ENV | | std : : string { CURRENT_DESKTOP_ENV } ! = " Hyprland " ) {
g_pHyprNotificationOverlay - > addNotification (
std : : format ( " Your XDG_CURRENT_DESKTOP environment seems to be managed externally, and the current value is {}. \n This might cause issues unless it's intentional. " ,
CURRENT_DESKTOP_ENV ? CURRENT_DESKTOP_ENV : " unset " ) ,
2024-12-03 18:58:24 +00:00
CHyprColor { } , 15000 , ICON_WARNING ) ;
2024-09-18 11:22:07 +01:00
}
}
2024-11-01 15:52:03 +00:00
2025-11-07 15:48:13 +00:00
if ( ! * PNOCHECKGUIUTILS ) {
2025-01-10 19:09:40 +01:00
if ( ! NFsUtils : : executableExistsInPath ( " hyprland-dialog " ) ) {
2024-12-06 15:45:02 +01:00
g_pHyprNotificationOverlay - > addNotification (
2025-11-07 15:48:13 +00:00
" Your system does not have hyprland-guiutils installed. This is a runtime dependency for some dialogs. Consider installing it. " , CHyprColor { } , 15000 , ICON_WARNING ) ;
2024-12-06 15:45:02 +01:00
}
}
2025-05-05 23:44:49 +02:00
if ( g_pHyprOpenGL - > m_failedAssetsNo > 0 ) {
2024-11-01 15:52:03 +00:00
g_pHyprNotificationOverlay - > addNotification ( std : : format ( " Hyprland failed to load {} essential asset{}, blame your distro's packager for doing a bad job at packaging! " ,
2025-05-05 23:44:49 +02:00
g_pHyprOpenGL - > m_failedAssetsNo , g_pHyprOpenGL - > m_failedAssetsNo > 1 ? " s " : " " ) ,
2024-12-03 18:58:24 +00:00
CHyprColor { 1.0 , 0.1 , 0.1 , 1.0 } , 15000 , ICON_ERROR ) ;
2024-11-01 15:52:03 +00:00
}
2023-03-20 15:02:47 +00:00
}
2023-04-14 15:03:53 +01:00
2024-04-27 12:43:12 +01:00
void CCompositor : : moveWindowToWorkspaceSafe ( PHLWINDOW pWindow , PHLWORKSPACE pWorkspace ) {
2023-04-14 15:03:53 +01:00
if ( ! pWindow | | ! pWorkspace )
return ;
2025-04-28 22:25:22 +02:00
if ( pWindow - > m_pinned & & pWorkspace - > m_isSpecialWorkspace )
2025-03-01 13:34:38 -05:00
return ;
2025-04-28 22:25:22 +02:00
if ( pWindow - > m_workspace = = pWorkspace )
2023-05-29 17:46:06 +02:00
return ;
2024-07-31 17:55:52 +00:00
const bool FULLSCREEN = pWindow - > isFullscreen ( ) ;
2025-04-28 22:25:22 +02:00
const auto FULLSCREENMODE = pWindow - > m_fullscreenState . internal ;
const bool WASVISIBLE = pWindow - > m_workspace & & pWindow - > m_workspace - > isVisible ( ) ;
2023-04-14 15:03:53 +01:00
if ( FULLSCREEN )
2024-07-31 17:55:52 +00:00
setWindowFullscreenInternal ( pWindow , FSMODE_NONE ) ;
2023-04-14 15:03:53 +01:00
2024-11-22 16:01:02 +00:00
const PHLWINDOW pFirstWindowOnWorkspace = pWorkspace - > getFirstWindow ( ) ;
const int visibleWindowsOnWorkspace = pWorkspace - > getWindows ( std : : nullopt , true ) ;
2025-04-30 23:45:20 +02:00
const auto POSTOMON = pWindow - > m_realPosition - > goal ( ) - ( pWindow - > m_monitor ? pWindow - > m_monitor - > m_position : Vector2D { } ) ;
2025-04-25 02:37:12 +02:00
const auto PWORKSPACEMONITOR = pWorkspace - > m_monitor . lock ( ) ;
2024-10-22 23:51:25 +00:00
2025-04-28 22:25:22 +02:00
if ( ! pWindow - > m_isFloating )
2023-04-14 15:03:53 +01:00
g_pLayoutManager - > getCurrentLayout ( ) - > onWindowRemovedTiling ( pWindow ) ;
2024-10-22 23:51:25 +00:00
pWindow - > moveToWorkspace ( pWorkspace ) ;
2025-04-28 22:25:22 +02:00
pWindow - > m_monitor = pWorkspace - > m_monitor ;
2023-04-14 15:03:53 +01:00
2024-10-22 23:51:25 +00:00
static auto PGROUPONMOVETOWORKSPACE = CConfigValue < Hyprlang : : INT > ( " group:group_on_movetoworkspace " ) ;
if ( * PGROUPONMOVETOWORKSPACE & & visibleWindowsOnWorkspace = = 1 & & pFirstWindowOnWorkspace & & pFirstWindowOnWorkspace ! = pWindow & &
2025-04-28 22:25:22 +02:00
pFirstWindowOnWorkspace - > m_groupData . pNextWindow . lock ( ) & & pWindow - > canBeGroupedInto ( pFirstWindowOnWorkspace ) ) {
2024-10-22 23:51:25 +00:00
2025-04-28 22:25:22 +02:00
pWindow - > m_isFloating = pFirstWindowOnWorkspace - > m_isFloating ; // match the floating state. Needed to group tiled into floated and vice versa.
if ( ! pWindow - > m_groupData . pNextWindow . expired ( ) ) {
PHLWINDOW next = pWindow - > m_groupData . pNextWindow . lock ( ) ;
2024-10-22 23:51:25 +00:00
while ( next ! = pWindow ) {
2025-04-28 22:25:22 +02:00
next - > m_isFloating = pFirstWindowOnWorkspace - > m_isFloating ; // match the floating state of group members
next = next - > m_groupData . pNextWindow . lock ( ) ;
2024-10-22 23:51:25 +00:00
}
}
static auto USECURRPOS = CConfigValue < Hyprlang : : INT > ( " group:insert_after_current " ) ;
( * USECURRPOS ? pFirstWindowOnWorkspace : pFirstWindowOnWorkspace - > getGroupTail ( ) ) - > insertWindowToGroup ( pWindow ) ;
pFirstWindowOnWorkspace - > setGroupCurrent ( pWindow ) ;
pWindow - > updateWindowDecos ( ) ;
g_pLayoutManager - > getCurrentLayout ( ) - > recalculateWindow ( pWindow ) ;
if ( ! pWindow - > getDecorationByType ( DECORATION_GROUPBAR ) )
2025-01-23 21:55:41 +01:00
pWindow - > addWindowDeco ( makeUnique < CHyprGroupBarDecoration > ( pWindow ) ) ;
2024-10-22 23:51:25 +00:00
} else {
2025-04-28 22:25:22 +02:00
if ( ! pWindow - > m_isFloating )
2024-10-22 23:51:25 +00:00
g_pLayoutManager - > getCurrentLayout ( ) - > onWindowCreatedTiling ( pWindow ) ;
2023-04-14 15:03:53 +01:00
2025-04-28 22:25:22 +02:00
if ( pWindow - > m_isFloating )
2025-04-30 23:45:20 +02:00
* pWindow - > m_realPosition = POSTOMON + PWORKSPACEMONITOR - > m_position ;
2023-04-14 15:03:53 +01:00
}
2024-03-06 21:33:50 +00:00
pWindow - > updateToplevel ( ) ;
pWindow - > updateDynamicRules ( ) ;
pWindow - > uncacheWindowDecos ( ) ;
2024-10-26 01:22:37 +00:00
pWindow - > updateGroupOutputs ( ) ;
2024-03-06 21:33:50 +00:00
2025-04-28 22:25:22 +02:00
if ( ! pWindow - > m_groupData . pNextWindow . expired ( ) ) {
PHLWINDOW next = pWindow - > m_groupData . pNextWindow . lock ( ) ;
2023-06-21 20:51:18 +02:00
while ( next ! = pWindow ) {
next - > updateToplevel ( ) ;
2025-04-28 22:25:22 +02:00
next = next - > m_groupData . pNextWindow . lock ( ) ;
2023-06-21 20:51:18 +02:00
}
}
2023-04-14 15:03:53 +01:00
if ( FULLSCREEN )
2024-07-31 17:55:52 +00:00
setWindowFullscreenInternal ( pWindow , FULLSCREENMODE ) ;
2023-12-20 23:52:18 +01:00
2024-11-22 16:01:02 +00:00
pWorkspace - > updateWindows ( ) ;
2025-04-28 22:25:22 +02:00
if ( pWindow - > m_workspace )
pWindow - > m_workspace - > updateWindows ( ) ;
2024-08-18 21:02:46 +02:00
g_pCompositor - > updateSuspendedStates ( ) ;
2024-12-22 16:04:10 +00:00
2025-04-28 22:25:22 +02:00
if ( ! WASVISIBLE & & pWindow - > m_workspace & & pWindow - > m_workspace - > isVisible ( ) ) {
pWindow - > m_movingFromWorkspaceAlpha - > setValueAndWarp ( 0.F ) ;
* pWindow - > m_movingFromWorkspaceAlpha = 1.F ;
2024-12-22 16:04:10 +00:00
}
2023-04-14 15:03:53 +01:00
}
2023-07-04 12:05:25 +02:00
2024-04-27 12:43:12 +01:00
PHLWINDOW CCompositor : : getForceFocus ( ) {
2025-04-22 15:23:29 +02:00
for ( auto const & w : m_windows ) {
2025-04-28 22:25:22 +02:00
if ( ! w - > m_isMapped | | w - > isHidden ( ) | | ! w - > m_workspace | | ! w - > m_workspace - > isVisible ( ) )
2023-07-04 12:05:25 +02:00
continue ;
2025-04-28 22:25:22 +02:00
if ( ! w - > m_stayFocused )
2023-07-04 12:05:25 +02:00
continue ;
2024-04-27 12:43:12 +01:00
return w ;
2023-07-04 12:05:25 +02:00
}
return nullptr ;
}
2023-07-13 18:05:34 +02:00
2025-10-26 13:52:09 +00:00
void CCompositor : : scheduleMonitorStateRecheck ( ) {
static bool scheduled = false ;
if ( ! scheduled ) {
scheduled = true ;
g_pEventLoopManager - > doLater ( [ this ] {
arrangeMonitors ( ) ;
checkMonitorOverlaps ( ) ;
scheduled = false ;
} ) ;
}
}
void CCompositor : : checkMonitorOverlaps ( ) {
CRegion monitorRegion ;
for ( const auto & m : m_monitors ) {
if ( ! monitorRegion . copy ( ) . intersect ( m - > logicalBox ( ) ) . empty ( ) ) {
Debug : : log ( ERR , " Monitor {}: detected overlap with layout " , m - > m_name ) ;
g_pHyprNotificationOverlay - > addNotification ( std : : format ( " Your monitor layout is set up incorrectly. Monitor {} overlaps with other monitor(s) in the "
" layout. \n Please see the wiki (Monitors page) for more. This will cause issues. " ,
m - > m_name ) ,
CHyprColor { } , 15000 , ICON_WARNING ) ;
break ;
}
monitorRegion . add ( m - > logicalBox ( ) ) ;
}
}
2023-08-14 14:22:06 +02:00
void CCompositor : : arrangeMonitors ( ) {
2025-08-14 19:44:56 +05:00
static auto * const PXWLFORCESCALEZERO = rc < Hyprlang : : INT * const * > ( g_pConfigManager - > getConfigValuePtr ( " xwayland:force_zero_scaling " ) ) ;
2023-08-15 19:15:37 +02:00
2025-04-22 15:23:29 +02:00
std : : vector < PHLMONITOR > toArrange ( m_monitors . begin ( ) , m_monitors . end ( ) ) ;
2024-10-19 23:03:29 +01:00
std : : vector < PHLMONITOR > arranged ;
2025-01-19 10:38:42 +00:00
arranged . reserve ( toArrange . size ( ) ) ;
2023-08-14 14:22:06 +02:00
2023-09-06 12:51:36 +02:00
Debug : : log ( LOG , " arrangeMonitors: {} to arrange " , toArrange . size ( ) ) ;
2023-08-21 19:52:30 +02:00
2023-08-14 19:27:33 +02:00
for ( auto it = toArrange . begin ( ) ; it ! = toArrange . end ( ) ; ) {
2023-08-14 14:22:06 +02:00
auto m = * it ;
2025-04-30 23:45:20 +02:00
if ( m - > m_activeMonitorRule . offset ! = Vector2D { - INT32_MAX , - INT32_MAX } ) {
2023-08-14 14:22:06 +02:00
// explicit.
2025-04-30 23:45:20 +02:00
Debug : : log ( LOG , " arrangeMonitors: {} explicit {:j} " , m - > m_name , m - > m_activeMonitorRule . offset ) ;
2023-08-21 19:52:30 +02:00
2025-04-30 23:45:20 +02:00
m - > moveTo ( m - > m_activeMonitorRule . offset ) ;
2023-08-14 14:22:06 +02:00
arranged . push_back ( m ) ;
it = toArrange . erase ( it ) ;
if ( it = = toArrange . end ( ) )
break ;
2023-08-14 19:27:33 +02:00
continue ;
2023-08-14 14:22:06 +02:00
}
2023-08-14 19:27:33 +02:00
+ + it ;
2023-08-14 14:22:06 +02:00
}
2024-04-23 00:40:03 +00:00
// Variables to store the max and min values of monitors on each axis.
2024-12-13 22:31:30 +00:00
int maxXOffsetRight = 0 ;
int maxXOffsetLeft = 0 ;
int maxYOffsetUp = 0 ;
int maxYOffsetDown = 0 ;
auto recalcMaxOffsets = [ & ] ( ) {
maxXOffsetRight = 0 ;
maxXOffsetLeft = 0 ;
maxYOffsetUp = 0 ;
maxYOffsetDown = 0 ;
2025-07-25 15:19:23 +00:00
// Finds the max and min values of explicitly placed monitors.
2024-12-13 22:31:30 +00:00
for ( auto const & m : arranged ) {
2025-04-30 23:45:20 +02:00
maxXOffsetRight = std : : max < double > ( m - > m_position . x + m - > m_size . x , maxXOffsetRight ) ;
maxXOffsetLeft = std : : min < double > ( m - > m_position . x , maxXOffsetLeft ) ;
maxYOffsetDown = std : : max < double > ( m - > m_position . y + m - > m_size . y , maxYOffsetDown ) ;
maxYOffsetUp = std : : min < double > ( m - > m_position . y , maxYOffsetUp ) ;
2024-12-13 22:31:30 +00:00
}
} ;
2023-08-14 14:22:06 +02:00
2024-04-23 00:40:03 +00:00
// Iterates through all non-explicitly placed monitors.
2024-08-26 20:24:30 +02:00
for ( auto const & m : toArrange ) {
2024-12-13 22:31:30 +00:00
recalcMaxOffsets ( ) ;
2024-04-23 00:40:03 +00:00
// Moves the monitor to their appropriate position on the x/y axis and
// increments/decrements the corresponding max offset.
2024-04-25 14:07:50 -07:00
Vector2D newPosition = { 0 , 0 } ;
2025-04-30 23:45:20 +02:00
switch ( m - > m_activeMonitorRule . autoDir ) {
case eAutoDirs : : DIR_AUTO_UP : newPosition . y = maxYOffsetUp - m - > m_size . y ; break ;
2024-12-13 22:31:30 +00:00
case eAutoDirs : : DIR_AUTO_DOWN : newPosition . y = maxYOffsetDown ; break ;
2025-04-30 23:45:20 +02:00
case eAutoDirs : : DIR_AUTO_LEFT : newPosition . x = maxXOffsetLeft - m - > m_size . x ; break ;
2024-04-25 14:07:50 -07:00
case eAutoDirs : : DIR_AUTO_RIGHT :
2024-12-13 22:31:30 +00:00
case eAutoDirs : : DIR_AUTO_NONE : newPosition . x = maxXOffsetRight ; break ;
2025-05-27 15:51:59 +01:00
case eAutoDirs : : DIR_AUTO_CENTER_UP : {
int width = maxXOffsetRight - maxXOffsetLeft ;
newPosition . y = maxYOffsetUp - m - > m_size . y ;
newPosition . x = maxXOffsetLeft + ( width - m - > m_size . x ) / 2 ;
break ;
}
case eAutoDirs : : DIR_AUTO_CENTER_DOWN : {
int width = maxXOffsetRight - maxXOffsetLeft ;
newPosition . y = maxYOffsetDown ;
newPosition . x = maxXOffsetLeft + ( width - m - > m_size . x ) / 2 ;
break ;
}
case eAutoDirs : : DIR_AUTO_CENTER_LEFT : {
int height = maxYOffsetDown - maxYOffsetUp ;
newPosition . x = maxXOffsetLeft - m - > m_size . x ;
newPosition . y = maxYOffsetUp + ( height - m - > m_size . y ) / 2 ;
break ;
}
case eAutoDirs : : DIR_AUTO_CENTER_RIGHT : {
int height = maxYOffsetDown - maxYOffsetUp ;
newPosition . x = maxXOffsetRight ;
newPosition . y = maxYOffsetUp + ( height - m - > m_size . y ) / 2 ;
break ;
}
2024-04-25 14:07:50 -07:00
default : UNREACHABLE ( ) ;
2024-04-23 00:40:03 +00:00
}
2025-04-30 23:45:20 +02:00
Debug : : log ( LOG , " arrangeMonitors: {} auto {:j} " , m - > m_name , m - > m_position ) ;
2024-04-25 14:07:50 -07:00
m - > moveTo ( newPosition ) ;
2024-12-13 22:31:30 +00:00
arranged . emplace_back ( m ) ;
2023-08-14 14:22:06 +02:00
}
2023-08-15 19:15:37 +02:00
2024-04-23 00:40:03 +00:00
// reset maxXOffsetRight (reuse)
2023-08-15 19:15:37 +02:00
// and set xwayland positions aka auto for all
2024-04-23 00:40:03 +00:00
maxXOffsetRight = 0 ;
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2025-04-30 23:45:20 +02:00
Debug : : log ( LOG , " arrangeMonitors: {} xwayland [{}, {}] " , m - > m_name , maxXOffsetRight , 0 ) ;
m - > m_xwaylandPosition = { maxXOffsetRight , 0 } ;
maxXOffsetRight + = ( * PXWLFORCESCALEZERO ? m - > m_transformedSize . x : m - > m_size . x ) ;
2023-09-20 16:47:05 +01:00
2024-03-03 18:39:20 +00:00
if ( * PXWLFORCESCALEZERO )
2025-04-30 23:45:20 +02:00
m - > m_xwaylandScale = m - > m_scale ;
2023-09-20 16:47:05 +01:00
else
2025-04-30 23:45:20 +02:00
m - > m_xwaylandScale = 1.f ;
2023-08-15 19:15:37 +02:00
}
2024-10-05 00:44:16 +01:00
PROTO : : xdgOutput - > updateAllOutputs ( ) ;
2025-10-29 06:24:34 -05:00
# ifndef NO_XWAYLAND
CBox box = g_pCompositor - > calculateX11WorkArea ( ) ;
g_pXWayland - > m_wm - > updateWorkArea ( box . x , box . y , box . w , box . h ) ;
# endif
2023-08-14 14:22:06 +02:00
}
2023-09-24 18:04:38 +01:00
void CCompositor : : enterUnsafeState ( ) {
2025-04-22 15:23:29 +02:00
if ( m_unsafeState )
2023-09-24 18:04:38 +01:00
return ;
Debug : : log ( LOG , " Entering unsafe state " ) ;
2025-04-30 23:45:20 +02:00
if ( ! m_unsafeOutput - > m_enabled )
2025-04-22 15:23:29 +02:00
m_unsafeOutput - > onConnect ( false ) ;
2023-09-24 18:04:38 +01:00
2025-04-22 15:23:29 +02:00
m_unsafeState = true ;
2024-03-26 02:26:09 +00:00
2025-04-22 15:23:29 +02:00
setActiveMonitor ( m_unsafeOutput . lock ( ) ) ;
2023-09-24 18:04:38 +01:00
}
void CCompositor : : leaveUnsafeState ( ) {
2025-04-22 15:23:29 +02:00
if ( ! m_unsafeState )
2023-09-24 18:04:38 +01:00
return ;
Debug : : log ( LOG , " Leaving unsafe state " ) ;
2025-04-22 15:23:29 +02:00
m_unsafeState = false ;
2023-09-24 18:04:38 +01:00
2024-10-19 23:03:29 +01:00
PHLMONITOR pNewMonitor = nullptr ;
2025-04-22 15:23:29 +02:00
for ( auto const & pMonitor : m_monitors ) {
2025-04-30 23:45:20 +02:00
if ( pMonitor - > m_output ! = m_unsafeOutput - > m_output ) {
2024-10-19 23:03:29 +01:00
pNewMonitor = pMonitor ;
2023-11-01 18:53:36 +00:00
break ;
}
}
RASSERT ( pNewMonitor , " Tried to leave unsafe without a monitor " ) ;
2025-04-30 23:45:20 +02:00
if ( m_unsafeOutput - > m_enabled )
2025-04-22 15:23:29 +02:00
m_unsafeOutput - > onDisconnect ( ) ;
2023-09-24 18:04:38 +01:00
2025-04-22 15:23:29 +02:00
for ( auto const & m : m_monitors ) {
2024-10-19 23:03:29 +01:00
scheduleFrameForMonitor ( m ) ;
2023-11-01 18:53:36 +00:00
}
2023-09-24 18:04:38 +01:00
}
2023-10-22 16:58:06 +01:00
2024-06-08 10:07:59 +02:00
void CCompositor : : setPreferredScaleForSurface ( SP < CWLSurfaceResource > pSurface , double scale ) {
2024-04-20 14:14:54 +01:00
PROTO : : fractional - > sendScale ( pSurface , scale ) ;
2024-06-08 10:07:59 +02:00
pSurface - > sendPreferredScale ( std : : ceil ( scale ) ) ;
2024-01-11 13:15:20 +01:00
2024-06-08 10:07:59 +02:00
const auto PSURFACE = CWLSurface : : fromResource ( pSurface ) ;
2024-01-11 13:15:20 +01:00
if ( ! PSURFACE ) {
2025-08-14 19:44:56 +05:00
Debug : : log ( WARN , " Orphaned CWLSurfaceResource {:x} in setPreferredScaleForSurface " , rc < uintptr_t > ( pSurface . get ( ) ) ) ;
2024-01-11 13:15:20 +01:00
return ;
}
2025-04-25 02:37:12 +02:00
PSURFACE - > m_lastScaleFloat = scale ;
2025-08-14 19:44:56 +05:00
PSURFACE - > m_lastScaleInt = sc < int32_t > ( std : : ceil ( scale ) ) ;
2023-10-22 16:58:06 +01:00
}
2024-06-08 10:07:59 +02:00
void CCompositor : : setPreferredTransformForSurface ( SP < CWLSurfaceResource > pSurface , wl_output_transform transform ) {
pSurface - > sendPreferredTransform ( transform ) ;
2024-01-11 13:15:20 +01:00
2024-06-08 10:07:59 +02:00
const auto PSURFACE = CWLSurface : : fromResource ( pSurface ) ;
2024-01-11 13:15:20 +01:00
if ( ! PSURFACE ) {
2025-08-14 19:44:56 +05:00
Debug : : log ( WARN , " Orphaned CWLSurfaceResource {:x} in setPreferredTransformForSurface " , rc < uintptr_t > ( pSurface . get ( ) ) ) ;
2024-01-11 13:15:20 +01:00
return ;
}
2025-04-25 02:37:12 +02:00
PSURFACE - > m_lastTransform = transform ;
2023-12-13 17:25:19 +00:00
}
2023-12-23 22:30:49 +01:00
void CCompositor : : updateSuspendedStates ( ) {
2025-04-22 15:23:29 +02:00
for ( auto const & w : g_pCompositor - > m_windows ) {
2025-04-28 22:25:22 +02:00
if ( ! w - > m_isMapped )
2023-12-23 22:30:49 +01:00
continue ;
2025-04-28 22:25:22 +02:00
w - > setSuspended ( w - > isHidden ( ) | | ! w - > m_workspace | | ! w - > m_workspace - > isVisible ( ) ) ;
2023-12-23 22:30:49 +01:00
}
}
2024-04-27 12:43:12 +01:00
2024-10-26 02:06:13 +01:00
static void checkDefaultCursorWarp ( PHLMONITOR monitor ) {
2024-09-14 17:37:18 -05:00
static auto PCURSORMONITOR = CConfigValue < std : : string > ( " cursor:default_monitor " ) ;
static bool cursorDefaultDone = false ;
static bool firstLaunch = true ;
const auto POS = monitor - > middle ( ) ;
// by default, cursor should be set to first monitor detected
// this is needed as a default if the monitor given in config above doesn't exist
if ( firstLaunch ) {
firstLaunch = false ;
g_pCompositor - > warpCursorTo ( POS , true ) ;
g_pInputManager - > refocus ( ) ;
return ;
}
if ( ! cursorDefaultDone & & * PCURSORMONITOR ! = STRVAL_EMPTY ) {
2025-04-30 23:45:20 +02:00
if ( * PCURSORMONITOR = = monitor - > m_name ) {
2024-09-14 17:37:18 -05:00
cursorDefaultDone = true ;
g_pCompositor - > warpCursorTo ( POS , true ) ;
g_pInputManager - > refocus ( ) ;
return ;
}
}
2025-07-25 15:19:23 +00:00
// modechange happened check if cursor is on that monitor and warp it to middle to not place it out of bounds if resolution changed.
2024-10-19 23:03:29 +01:00
if ( g_pCompositor - > getMonitorFromCursor ( ) = = monitor ) {
2024-09-14 17:37:18 -05:00
g_pCompositor - > warpCursorTo ( POS , true ) ;
g_pInputManager - > refocus ( ) ;
}
}
2024-07-21 13:09:54 +02:00
void CCompositor : : onNewMonitor ( SP < Aquamarine : : IOutput > output ) {
// add it to real
2025-04-22 15:23:29 +02:00
auto PNEWMONITOR = g_pCompositor - > m_realMonitors . emplace_back ( makeShared < CMonitor > ( output ) ) ;
2024-07-21 13:09:54 +02:00
if ( std : : string ( " HEADLESS-1 " ) = = output - > name ) {
2025-04-22 15:23:29 +02:00
g_pCompositor - > m_unsafeOutput = PNEWMONITOR ;
output - > name = " FALLBACK " ; // we are allowed to do this :)
2024-07-21 13:09:54 +02:00
}
Debug : : log ( LOG , " New output with name {} " , output - > name ) ;
2025-04-30 23:45:20 +02:00
PNEWMONITOR - > m_name = output - > name ;
PNEWMONITOR - > m_self = PNEWMONITOR ;
const bool FALLBACK = g_pCompositor - > m_unsafeOutput ? output = = g_pCompositor - > m_unsafeOutput - > m_output : false ;
PNEWMONITOR - > m_id = FALLBACK ? MONITOR_INVALID : g_pCompositor - > getNextAvailableMonitorID ( output - > name ) ;
PNEWMONITOR - > m_isUnsafeFallback = FALLBACK ;
2024-07-21 13:09:54 +02:00
EMIT_HOOK_EVENT ( " newMonitor " , PNEWMONITOR ) ;
if ( ! FALLBACK )
PNEWMONITOR - > onConnect ( false ) ;
2025-04-30 23:45:20 +02:00
if ( ! PNEWMONITOR - > m_enabled | | FALLBACK )
2024-07-21 13:09:54 +02:00
return ;
// ready to process if we have a real monitor
2025-05-05 23:44:49 +02:00
if ( ( ! g_pHyprRenderer - > m_mostHzMonitor | | PNEWMONITOR - > m_refreshRate > g_pHyprRenderer - > m_mostHzMonitor - > m_refreshRate ) & & PNEWMONITOR - > m_enabled )
g_pHyprRenderer - > m_mostHzMonitor = PNEWMONITOR ;
2024-07-21 13:09:54 +02:00
2025-04-22 15:23:29 +02:00
g_pCompositor - > m_readyToProcess = true ;
2024-07-21 13:09:54 +02:00
2025-04-20 20:39:33 +02:00
g_pConfigManager - > m_wantsMonitorReload = true ;
2024-10-19 23:03:29 +01:00
g_pCompositor - > scheduleFrameForMonitor ( PNEWMONITOR , IOutput : : AQ_SCHEDULE_NEW_MONITOR ) ;
2024-07-21 13:09:54 +02:00
2024-09-14 17:37:18 -05:00
checkDefaultCursorWarp ( PNEWMONITOR ) ;
2025-04-22 15:23:29 +02:00
for ( auto const & w : g_pCompositor - > m_windows ) {
2025-04-28 22:25:22 +02:00
if ( w - > m_monitor = = PNEWMONITOR ) {
w - > m_lastSurfaceMonitorID = MONITOR_INVALID ;
2024-07-21 13:09:54 +02:00
w - > updateSurfaceScaleTransformDetails ( ) ;
}
}
2024-10-19 23:03:29 +01:00
g_pHyprRenderer - > damageMonitor ( PNEWMONITOR ) ;
2025-07-08 12:41:10 +02:00
PNEWMONITOR - > m_frameScheduler - > onFrame ( ) ;
2025-01-07 21:32:50 +03:00
2025-02-26 17:56:37 +03:00
if ( PROTO : : colorManagement & & shouldChangePreferredImageDescription ( ) ) {
Debug : : log ( ERR , " FIXME: color management protocol is enabled, need a preferred image description id " ) ;
PROTO : : colorManagement - > onImagePreferredChanged ( 0 ) ;
}
2025-01-07 21:32:50 +03:00
}
SImageDescription CCompositor : : getPreferredImageDescription ( ) {
if ( ! PROTO : : colorManagement ) {
Debug : : log ( ERR , " FIXME: color management protocol is not enabled, returning empty image description " ) ;
return SImageDescription { } ;
}
Debug : : log ( WARN , " FIXME: color management protocol is enabled, determine correct preferred image description " ) ;
// should determine some common settings to avoid unnecessary transformations while keeping maximum displayable precision
2025-07-15 20:33:14 +03:00
return m_monitors . size ( ) = = 1 ? m_monitors [ 0 ] - > m_imageDescription : SImageDescription { . primaries = NColorPrimaries : : BT709 } ;
2025-01-07 21:32:50 +03:00
}
bool CCompositor : : shouldChangePreferredImageDescription ( ) {
Debug : : log ( WARN , " FIXME: color management protocol is enabled and outputs changed, check preferred image description changes " ) ;
return false ;
2024-07-21 13:09:54 +02:00
}
2025-01-26 14:40:42 +00:00
2025-02-15 15:01:52 +01:00
void CCompositor : : ensurePersistentWorkspacesPresent ( const std : : vector < SWorkspaceRule > & rules , PHLWORKSPACE pWorkspace ) {
2025-04-22 15:23:29 +02:00
if ( ! m_lastMonitor )
2025-03-27 13:59:45 +00:00
return ;
2025-02-15 15:01:52 +01:00
2025-07-27 18:46:23 +02:00
std : : vector < PHLWORKSPACE > persistentFound ;
2025-01-26 14:40:42 +00:00
for ( const auto & rule : rules ) {
if ( ! rule . isPersistent )
continue ;
2025-02-15 15:01:52 +01:00
PHLWORKSPACE PWORKSPACE = nullptr ;
if ( pWorkspace ) {
if ( pWorkspace - > matchesStaticSelector ( rule . workspaceString ) )
PWORKSPACE = pWorkspace ;
else
continue ;
}
2025-09-23 21:08:30 +02:00
auto PMONITOR = getMonitorFromString ( rule . monitor ) ;
2025-01-26 14:40:42 +00:00
2025-03-27 13:59:45 +00:00
if ( ! rule . monitor . empty ( ) & & ! PMONITOR )
continue ; // don't do anything yet, as the monitor is not yet present.
2025-02-15 15:01:52 +01:00
if ( ! PWORKSPACE ) {
WORKSPACEID id = rule . workspaceId ;
std : : string wsname = rule . workspaceName ;
2025-01-26 14:40:42 +00:00
2025-02-15 15:01:52 +01:00
if ( id = = WORKSPACE_INVALID ) {
const auto R = getWorkspaceIDNameFromString ( rule . workspaceString ) ;
id = R . id ;
wsname = R . name ;
}
if ( id = = WORKSPACE_INVALID ) {
Debug : : log ( ERR , " ensurePersistentWorkspacesPresent: couldn't resolve id for workspace {} " , rule . workspaceString ) ;
continue ;
}
PWORKSPACE = getWorkspaceByID ( id ) ;
2025-09-23 21:08:30 +02:00
if ( ! PMONITOR )
PMONITOR = m_lastMonitor . lock ( ) ;
2025-02-15 15:01:52 +01:00
if ( ! PWORKSPACE )
2025-09-23 21:08:30 +02:00
PWORKSPACE = createNewWorkspace ( id , PMONITOR - > m_id , wsname , false ) ;
2025-01-26 14:40:42 +00:00
}
2025-02-15 15:01:52 +01:00
if ( ! PMONITOR ) {
Debug : : log ( ERR , " ensurePersistentWorkspacesPresent: couldn't resolve monitor for {}, skipping " , rule . monitor ) ;
2025-01-26 14:40:42 +00:00
continue ;
}
2025-07-27 18:46:23 +02:00
if ( PWORKSPACE )
PWORKSPACE - > setPersistent ( true ) ;
if ( ! pWorkspace )
persistentFound . emplace_back ( PWORKSPACE ) ;
2025-02-15 15:01:52 +01:00
if ( PWORKSPACE ) {
2025-04-25 02:37:12 +02:00
if ( PWORKSPACE - > m_monitor = = PMONITOR ) {
2025-04-30 23:45:20 +02:00
Debug : : log ( LOG , " ensurePersistentWorkspacesPresent: workspace persistent {} already on {} " , rule . workspaceString , PMONITOR - > m_name ) ;
2025-02-15 15:01:52 +01:00
2025-01-26 14:40:42 +00:00
continue ;
}
2025-04-30 23:45:20 +02:00
Debug : : log ( LOG , " ensurePersistentWorkspacesPresent: workspace persistent {} not on {}, moving " , rule . workspaceString , PMONITOR - > m_name ) ;
2025-01-26 14:40:42 +00:00
moveWorkspaceToMonitor ( PWORKSPACE , PMONITOR ) ;
continue ;
}
}
2025-07-27 18:46:23 +02:00
if ( ! pWorkspace ) {
// check non-persistent and downgrade if workspace is no longer persistent
std : : vector < PHLWORKSPACEREF > toDowngrade ;
for ( auto & w : getWorkspaces ( ) ) {
if ( ! w - > isPersistent ( ) )
continue ;
if ( std : : ranges : : contains ( persistentFound , w . lock ( ) ) )
continue ;
toDowngrade . emplace_back ( w ) ;
}
for ( auto & ws : toDowngrade ) {
ws - > setPersistent ( false ) ;
}
}
2025-01-26 14:40:42 +00:00
}
2025-08-14 17:13:15 +02:00
std : : optional < unsigned int > CCompositor : : getVTNr ( ) {
if ( ! m_aqBackend - > hasSession ( ) )
return std : : nullopt ;
unsigned int ttynum = 0 ;
Hyprutils : : OS : : CFileDescriptor fd { open ( " /dev/tty " , O_RDONLY | O_NOCTTY ) } ;
if ( fd . isValid ( ) ) {
# if defined(VT_GETSTATE)
struct vt_stat st ;
if ( ! ioctl ( fd . get ( ) , VT_GETSTATE , & st ) )
ttynum = st . v_active ;
# elif defined(VT_GETACTIVE)
int vt ;
if ( ! ioctl ( fd . get ( ) , VT_GETACTIVE , & vt ) )
ttynum = vt ;
# endif
}
return ttynum ;
}