#include <ranges>
#include <re2/re2.h>

#include "Compositor.hpp"
#include "debug/log/Logger.hpp"
#include "desktop/DesktopTypes.hpp"
#include "desktop/state/FocusState.hpp"
#include "desktop/history/WindowHistoryTracker.hpp"
#include "desktop/history/WorkspaceHistoryTracker.hpp"
#include "desktop/view/Group.hpp"
#include "helpers/Splashes.hpp"
#include "config/ConfigValue.hpp"
#include "config/ConfigWatcher.hpp"
#include "managers/CursorManager.hpp"
#include "managers/TokenManager.hpp"
#include "managers/PointerManager.hpp"
#include "managers/SeatManager.hpp"
#include "managers/VersionKeeperManager.hpp"
#include "managers/DonationNagManager.hpp"
#include "managers/ANRManager.hpp"
#include "managers/eventLoop/EventLoopManager.hpp"
#include "managers/permissions/DynamicPermissionManager.hpp"
#include "managers/screenshare/ScreenshareManager.hpp"
#include <algorithm>
#include <aquamarine/output/Output.hpp>
#include <bit>
#include <ctime>
#include <random>
#include <print>
#include <cstring>
#include <filesystem>
#include <unordered_set>
#include "debug/HyprCtl.hpp"
#include "debug/crash/CrashReporter.hpp"
#ifdef USES_SYSTEMD
#include <helpers/SdDaemon.hpp> // for SdNotify
#endif
#include "helpers/fs/FsUtils.hpp"
#include "helpers/env/Env.hpp"
#include "protocols/FractionalScale.hpp"
#include "protocols/PointerConstraints.hpp"
#include "protocols/LayerShell.hpp"
#include "protocols/XDGShell.hpp"
#include "protocols/XDGOutput.hpp"
#include "protocols/SecurityContext.hpp"
#include "protocols/ColorManagement.hpp"
#include "protocols/core/Compositor.hpp"
#include "protocols/core/Subcompositor.hpp"
#include "desktop/view/LayerSurface.hpp"
#include "layout/space/Space.hpp"
#include "render/Renderer.hpp"
#include "xwayland/XWayland.hpp"
#include "helpers/ByteOperations.hpp"
#include "render/decorations/CHyprGroupBarDecoration.hpp"

#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"
#include "managers/animation/AnimationManager.hpp"
#include "managers/animation/DesktopAnimationManager.hpp"
#include "managers/EventManager.hpp"
#include "managers/ProtocolManager.hpp"
#include "managers/WelcomeManager.hpp"
#include "render/AsyncResourceGatherer.hpp"
#include "plugins/PluginSystem.hpp"
#include "hyprerror/HyprError.hpp"
#include "debug/HyprNotificationOverlay.hpp"
#include "debug/HyprDebugOverlay.hpp"
#include "helpers/MonitorFrameScheduler.hpp"
#include "i18n/Engine.hpp"
#include "layout/LayoutManager.hpp"
#include "layout/target/WindowTarget.hpp"
#include "event/EventBus.hpp"

#include <hyprutils/string/String.hpp>
#include <aquamarine/input/Input.hpp>

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <malloc.h>
#include <unistd.h>
#include <xf86drm.h>

using namespace Hyprutils::String;
using namespace Aquamarine;
using enum NContentType::eContentType;
using namespace NColorManagement;

static int handleCritSignal(int signo, void* data) {
    Log::logger->log(Log::DEBUG, "Hyprland received signal {}", signo);

    if (signo == SIGTERM || signo == SIGINT || signo == SIGKILL)
        g_pCompositor->stopCompositor();

    return 0;
}

static void handleUnrecoverableSignal(int sig) {

    // remove our handlers
    signal(SIGABRT, SIG_DFL);
    signal(SIGSEGV, SIG_DFL);

    // Kill the program if the crash-reporter is caught in a deadlock.
    signal(SIGALRM, [](int _) {
        char const* msg = "\nCrashReporter exceeded timeout, forcefully exiting\n";
        write(2, msg, strlen(msg));
        abort();
    });
    alarm(15);

    CrashReporter::createAndSaveCrash(sig);

    abort();
}

static void handleUserSignal(int sig) {
    if (sig == SIGUSR1) {
        // means we have to unwind a timed out event
        throw std::exception();
    }
}

bool CCompositor::setWatchdogFd(int fd) {
    m_watchdogWriteFd = Hyprutils::OS::CFileDescriptor{fd};
    return m_watchdogWriteFd.isValid() && !m_watchdogWriteFd.isClosed();
}

void CCompositor::bumpNofile() {
    if (!getrlimit(RLIMIT_NOFILE, &m_originalNofile))
        Log::logger->log(Log::DEBUG, "Old rlimit: soft -> {}, hard -> {}", m_originalNofile.rlim_cur, m_originalNofile.rlim_max);
    else {
        Log::logger->log(Log::ERR, "Failed to get NOFILE rlimits");
        m_originalNofile.rlim_max = 0;
        return;
    }

    rlimit newLimit = m_originalNofile;

    newLimit.rlim_cur = newLimit.rlim_max;

    if (setrlimit(RLIMIT_NOFILE, &newLimit) < 0) {
        Log::logger->log(Log::ERR, "Failed bumping NOFILE limits higher");
        m_originalNofile.rlim_max = 0;
        return;
    }

    if (!getrlimit(RLIMIT_NOFILE, &newLimit))
        Log::logger->log(Log::DEBUG, "New rlimit: soft -> {}, hard -> {}", newLimit.rlim_cur, newLimit.rlim_max);
}

void CCompositor::restoreNofile() {
    if (m_originalNofile.rlim_max <= 0)
        return;

    if (setrlimit(RLIMIT_NOFILE, &m_originalNofile) < 0)
        Log::logger->log(Log::ERR, "Failed restoring NOFILE limits");
}

bool CCompositor::supportsDrmSyncobjTimeline() const {
    return m_drm.syncobjSupport || m_drmRenderNode.syncObjSupport;
}

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
}

CCompositor::CCompositor(bool onlyConfig) : m_onlyConfigVerification(onlyConfig), m_hyprlandPID(getpid()) {
    if (onlyConfig)
        return;

    setMallocThreshold();

    m_hyprTempDataRoot = std::string{getenv("XDG_RUNTIME_DIR")} + "/hypr";

    if (m_hyprTempDataRoot.starts_with("/hypr")) {
        std::println("Bailing out, $XDG_RUNTIME_DIR is invalid");
        throw std::runtime_error("CCompositor() failed");
    }

    if (!m_hyprTempDataRoot.starts_with("/run/user"))
        std::println("[!!WARNING!!] XDG_RUNTIME_DIR looks non-standard. Proceeding anyways...");

    std::random_device              dev;
    std::mt19937                    engine(dev());
    std::uniform_int_distribution<> distribution(0, INT32_MAX);

    m_instanceSignature = std::format("{}_{}_{}", GIT_COMMIT_HASH, std::time(nullptr), distribution(engine));

    setenv("HYPRLAND_INSTANCE_SIGNATURE", m_instanceSignature.c_str(), true);

    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);
        throw std::runtime_error("CCompositor() failed");
    }

    m_instancePath = m_hyprTempDataRoot + "/" + m_instanceSignature;

    if (std::filesystem::exists(m_instancePath)) {
        std::println("Bailing out, {} exists??", m_instancePath);
        throw std::runtime_error("CCompositor() failed");
    }

    if (mkdir(m_instancePath.c_str(), S_IRWXU) < 0) {
        std::println("Bailing out, couldn't create {}", m_instancePath);
        throw std::runtime_error("CCompositor() failed");
    }

    Log::logger->initIS(m_instancePath);

    Log::logger->log(Log::DEBUG, "Instance Signature: {}", m_instanceSignature);

    Log::logger->log(Log::DEBUG, "Runtime directory: {}", m_instancePath);

    Log::logger->log(Log::DEBUG, "Hyprland PID: {}", m_hyprlandPID);

    Log::logger->log(Log::DEBUG, "===== SYSTEM INFO: =====");

    logSystemInfo();

    Log::logger->log(Log::DEBUG, "========================");

    Log::logger->log(Log::DEBUG, "\n\n"); // pad

    Log::logger->log(Log::INFO, "If you are crashing, or encounter any bugs, please consult https://wiki.hypr.land/Crashes-and-Bugs/\n\n");

    setRandomSplash();

    Log::logger->log(Log::DEBUG, "\nCurrent splash: {}\n\n", m_currentSplash);

    bumpNofile();
}

CCompositor::~CCompositor() {
    if (!m_isShuttingDown && !m_onlyConfigVerification)
        cleanup();
}

void CCompositor::setRandomSplash() {
    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;

    std::random_device              dev;
    std::mt19937                    engine(dev());
    std::uniform_int_distribution<> distribution(0, SPLASHES->size() - 1);

    m_currentSplash = SPLASHES->at(distribution(engine));
}

static std::vector<SP<Aquamarine::IOutput>> pendingOutputs;

//

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);
}

//
void CCompositor::initServer(std::string socketName, int socketFd) {
    if (m_onlyConfigVerification) {
        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;
    }

    m_wlDisplay = wl_display_create();

    wl_display_set_global_filter(m_wlDisplay, ::filterGlobals, nullptr);

    m_wlEventLoop = wl_display_get_event_loop(m_wlDisplay);

    // register crit signal handler
    m_critSigSource = wl_event_loop_add_signal(m_wlEventLoop, SIGTERM, handleCritSignal, nullptr);

    if (!Env::envEnabled("HYPRLAND_NO_CRASHREPORTER")) {
        signal(SIGSEGV, handleUnrecoverableSignal);
        signal(SIGABRT, handleUnrecoverableSignal);
    }
    signal(SIGUSR1, handleUserSignal);

    initManagers(STAGE_PRIORITY);

    Log::logger->initCallbacks();

    // set the buffer size to 1MB to avoid disconnects due to an app hanging for a short while
    wl_display_set_default_max_buffer_size(m_wlDisplay, 1_MB);

    Aquamarine::SBackendOptions           options{};
    SP<Hyprutils::CLI::CLoggerConnection> conn = makeShared<Hyprutils::CLI::CLoggerConnection>(Log::logger->hu());
    conn->setLogLevel(Log::DEBUG);
    conn->setName("aquamarine");
    options.logConnection = std::move(conn);

    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);

    m_aqBackend = CBackend::create(implementations, options);

    if (!m_aqBackend) {
        Log::logger->log(
            Log::CRIT,
            "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 "
            "session, NOT an X11 one.");
        throwError("CBackend::create() failed!");
    }

    // TODO: headless only

    initAllSignals();

    if (!m_aqBackend->start()) {
        Log::logger->log(
            Log::CRIT,
            "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 "
            "Wayland session, NOT an X11 one.");
        throwError("CBackend::create() failed!");
    }

    m_initialized = true;

    m_drm.fd = m_aqBackend->drmFD();
    Log::logger->log(Log::DEBUG, "Running on DRMFD: {}", m_drm.fd);

    m_drmRenderNode.fd = m_aqBackend->drmRenderNodeFD();
    Log::logger->log(Log::DEBUG, "Using RENDERNODEFD: {}", m_drmRenderNode.fd);

#if defined(__linux__)
    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;
    };

    m_drm.syncobjSupport = syncObjSupport(m_drm.fd);
    Log::logger->log(Log::DEBUG, "DRM DisplayNode syncobj timeline support: {}", m_drm.syncobjSupport ? "yes" : "no");

    m_drmRenderNode.syncObjSupport = syncObjSupport(m_drmRenderNode.fd);
    Log::logger->log(Log::DEBUG, "DRM RenderNode syncobj timeline support: {}", m_drmRenderNode.syncObjSupport ? "yes" : "no");

    if (!m_drm.syncobjSupport && !m_drmRenderNode.syncObjSupport)
        Log::logger->log(Log::DEBUG, "DRM no syncobj support, disabling explicit sync");
#else
    Log::logger->log(Log::DEBUG, "DRM syncobj timeline support: no (not linux)");
#endif

    if (!socketName.empty() && socketFd != -1) {
        fcntl(socketFd, F_SETFD, FD_CLOEXEC);
        const auto RETVAL = wl_display_add_socket_fd(m_wlDisplay, socketFd);
        if (RETVAL >= 0) {
            m_wlDisplaySocket = socketName;
            Log::logger->log(Log::DEBUG, "wl_display_add_socket_fd for {} succeeded with {}", socketName, RETVAL);
        } else
            Log::logger->log(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));
            const auto RETVAL       = wl_display_add_socket(m_wlDisplay, CANDIDATESTR.c_str());
            if (RETVAL >= 0) {
                m_wlDisplaySocket = CANDIDATESTR;
                Log::logger->log(Log::DEBUG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
                break;
            } else
                Log::logger->log(Log::WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
        }
    }

    if (m_wlDisplaySocket.empty()) {
        Log::logger->log(Log::WARN, "All candidates failed, trying wl_display_add_socket_auto");
        const auto SOCKETSTR = wl_display_add_socket_auto(m_wlDisplay);
        if (SOCKETSTR)
            m_wlDisplaySocket = SOCKETSTR;
    }

    if (m_wlDisplaySocket.empty()) {
        Log::logger->log(Log::CRIT, "m_szWLDisplaySocket NULL!");
        throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
    }

    setenv("WAYLAND_DISPLAY", m_wlDisplaySocket.c_str(), 1);
    if (!getenv("XDG_CURRENT_DESKTOP")) {
        setenv("XDG_CURRENT_DESKTOP", "Hyprland", 1);
        m_desktopEnvSet = true;
    }

    initManagers(STAGE_BASICINIT);

    initManagers(STAGE_LATE);

    for (auto const& o : pendingOutputs) {
        onNewMonitor(o);
    }
    pendingOutputs.clear();
}

void CCompositor::initAllSignals() {
    m_aqBackend->events.newOutput.listenStatic([this](const SP<Aquamarine::IOutput>& output) {
        Log::logger->log(Log::DEBUG, "New aquamarine output with name {}", output->name);
        if (m_initialized)
            onNewMonitor(output);
        else
            pendingOutputs.emplace_back(output);
    });

    m_aqBackend->events.newPointer.listenStatic([](const SP<Aquamarine::IPointer>& dev) {
        Log::logger->log(Log::DEBUG, "New aquamarine pointer with name {}", dev->getName());
        g_pInputManager->newMouse(dev);
        g_pInputManager->updateCapabilities();
    });

    m_aqBackend->events.newKeyboard.listenStatic([](const SP<Aquamarine::IKeyboard>& dev) {
        Log::logger->log(Log::DEBUG, "New aquamarine keyboard with name {}", dev->getName());
        g_pInputManager->newKeyboard(dev);
        g_pInputManager->updateCapabilities();
    });

    m_aqBackend->events.newTouch.listenStatic([](const SP<Aquamarine::ITouch>& dev) {
        Log::logger->log(Log::DEBUG, "New aquamarine touch with name {}", dev->getName());
        g_pInputManager->newTouchDevice(dev);
        g_pInputManager->updateCapabilities();
    });

    m_aqBackend->events.newSwitch.listenStatic([](const SP<Aquamarine::ISwitch>& dev) {
        Log::logger->log(Log::DEBUG, "New aquamarine switch with name {}", dev->getName());
        g_pInputManager->newSwitch(dev);
    });

    m_aqBackend->events.newTablet.listenStatic([](const SP<Aquamarine::ITablet>& dev) {
        Log::logger->log(Log::DEBUG, "New aquamarine tablet with name {}", dev->getName());
        g_pInputManager->newTablet(dev);
    });

    m_aqBackend->events.newTabletPad.listenStatic([](const SP<Aquamarine::ITabletPad>& dev) {
        Log::logger->log(Log::DEBUG, "New aquamarine tablet pad with name {}", dev->getName());
        g_pInputManager->newTabletPad(dev);
    });

    if (m_aqBackend->hasSession()) {
        m_aqBackend->session->events.changeActive.listenStatic([this] {
            if (m_aqBackend->session->active) {
                Log::logger->log(Log::DEBUG, "Session got activated!");

                m_sessionActive = true;

                // Reset animation tick state to avoid stale timer issues after suspend/wake
                if (g_pAnimationManager)
                    g_pAnimationManager->resetTickState();

                for (auto const& m : m_monitors) {
                    scheduleFrameForMonitor(m);
                    m->applyMonitorRule(&m->m_activeMonitorRule, true);
                }

                g_pConfigManager->m_wantsMonitorReload = true;
                g_pCursorManager->syncGsettings();
            } else {
                Log::logger->log(Log::DEBUG, "Session got deactivated!");

                m_sessionActive = false;
            }
        });
    }
}

void CCompositor::removeAllSignals() {
    ;
}

void CCompositor::cleanEnvironment() {
    // in compositor constructor
    unsetenv("WAYLAND_DISPLAY");
    // in startCompositor
    unsetenv("HYPRLAND_INSTANCE_SIGNATURE");

    // in main
    unsetenv("HYPRLAND_CMD");
    unsetenv("XDG_BACKEND");
    if (m_desktopEnvSet)
        unsetenv("XDG_CURRENT_DESKTOP");

    if (m_aqBackend->hasSession() && !Env::envEnabled("HYPRLAND_NO_SD_VARS")) {
        const auto CMD =
#ifdef USES_SYSTEMD
            "systemctl --user unset-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash "
            "dbus-update-activation-environment 2>/dev/null && "
#endif
            "dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS";
        CKeybindManager::spawn(CMD);
    }
}

void CCompositor::stopCompositor() {
    Log::logger->log(Log::DEBUG, "Hyprland is stopping!");

    // this stops the wayland loop, wl_display_run
    wl_display_terminate(m_wlDisplay);
    m_isShuttingDown = true;
}

void CCompositor::cleanup() {
    if (!m_wlDisplay)
        return;

    if (m_watchdogWriteFd.isValid())
        write(m_watchdogWriteFd.get(), "end", 3);

    signal(SIGABRT, SIG_DFL);
    signal(SIGSEGV, SIG_DFL);

    removeLockFile();

    m_isShuttingDown = true;

#ifdef USES_SYSTEMD
    if (NSystemd::sdBooted() > 0 && !Env::envEnabled("HYPRLAND_NO_SD_NOTIFY"))
        NSystemd::sdNotify(0, "STOPPING=1");
#endif

    cleanEnvironment();

    // unload all remaining plugins while the compositor is
    // still in a normal working state.
    g_pPluginSystem->unloadAllPlugins();

    m_workspaces.clear();
    m_windows.clear();

    for (auto const& m : m_monitors) {
        g_pHyprOpenGL->destroyMonitorResources(m);
    }

    g_pXWayland.reset();

    m_monitors.clear();

    wl_display_destroy_clients(g_pCompositor->m_wlDisplay);
    removeAllSignals();

    g_pInputManager.reset();
    g_pDynamicPermissionManager.reset();
    g_pDecorationPositioner.reset();
    g_pCursorManager.reset();
    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_layoutManager.reset();
    g_pHyprError.reset();
    g_pConfigManager.reset();
    g_pKeybindManager.reset();
    g_pXWaylandManager.reset();
    g_pPointerManager.reset();
    g_pSeatManager.reset();
    g_pHyprCtl.reset();
    g_pEventLoopManager.reset();
    g_pVersionKeeperMgr.reset();
    g_pDonationNagManager.reset();
    g_pWelcomeManager.reset();
    g_pANRManager.reset();
    g_pConfigWatcher.reset();
    g_pAsyncResourceGatherer.reset();

    if (m_aqBackend)
        m_aqBackend.reset();

    if (m_critSigSource)
        wl_event_source_remove(m_critSigSource);

    // this frees all wayland resources, including sockets
    wl_display_destroy(m_wlDisplay);
}

void CCompositor::initManagers(eManagersInitStage stage) {
    switch (stage) {
        case STAGE_PRIORITY: {
            Log::logger->log(Log::DEBUG, "Creating the EventLoopManager!");
            g_pEventLoopManager = makeUnique<CEventLoopManager>(m_wlDisplay, m_wlEventLoop);

            Log::logger->log(Log::DEBUG, "Creating the KeybindManager!");
            g_pKeybindManager = makeUnique<CKeybindManager>();

            Log::logger->log(Log::DEBUG, "Creating the AnimationManager!");
            g_pAnimationManager = makeUnique<CHyprAnimationManager>();

            Log::logger->log(Log::DEBUG, "Creating the DynamicPermissionManager!");
            g_pDynamicPermissionManager = makeUnique<CDynamicPermissionManager>();

            Log::logger->log(Log::DEBUG, "Creating the ConfigManager!");
            g_pConfigManager = makeUnique<CConfigManager>();

            Log::logger->log(Log::DEBUG, "Creating the CHyprError!");
            g_pHyprError = makeUnique<CHyprError>();

            Log::logger->log(Log::DEBUG, "Creating the LayoutManager!");
            g_layoutManager = makeUnique<Layout::CLayoutManager>();

            Log::logger->log(Log::DEBUG, "Creating the TokenManager!");
            g_pTokenManager = makeUnique<CTokenManager>();

            g_pConfigManager->init();

            Log::logger->log(Log::DEBUG, "Creating the PointerManager!");
            g_pPointerManager = makeUnique<CPointerManager>();

            Log::logger->log(Log::DEBUG, "Creating the EventManager!");
            g_pEventManager = makeUnique<CEventManager>();

            Log::logger->log(Log::DEBUG, "Creating the AsyncResourceGatherer!");
            g_pAsyncResourceGatherer = makeUnique<Hyprgraphics::CAsyncResourceGatherer>();
        } break;
        case STAGE_BASICINIT: {
            Log::logger->log(Log::DEBUG, "Creating the CHyprOpenGLImpl!");
            g_pHyprOpenGL = makeUnique<CHyprOpenGLImpl>();

            Log::logger->log(Log::DEBUG, "Creating the ProtocolManager!");
            g_pProtocolManager = makeUnique<CProtocolManager>();

            Log::logger->log(Log::DEBUG, "Creating the SeatManager!");
            g_pSeatManager = makeUnique<CSeatManager>();

            // init focus state els
            Desktop::History::windowTracker();
            Desktop::History::workspaceTracker();

        } break;
        case STAGE_LATE: {
            Log::logger->log(Log::DEBUG, "Creating CHyprCtl");
            g_pHyprCtl = makeUnique<CHyprCtl>();

            Log::logger->log(Log::DEBUG, "Creating the InputManager!");
            g_pInputManager = makeUnique<CInputManager>();

            Log::logger->log(Log::DEBUG, "Creating the HyprRenderer!");
            g_pHyprRenderer = makeUnique<CHyprRenderer>();

            Log::logger->log(Log::DEBUG, "Creating the XWaylandManager!");
            g_pXWaylandManager = makeUnique<CHyprXWaylandManager>();

            Log::logger->log(Log::DEBUG, "Creating the SessionLockManager!");
            g_pSessionLockManager = makeUnique<CSessionLockManager>();

            Log::logger->log(Log::DEBUG, "Creating the HyprDebugOverlay!");
            g_pDebugOverlay = makeUnique<CHyprDebugOverlay>();

            Log::logger->log(Log::DEBUG, "Creating the HyprNotificationOverlay!");
            g_pHyprNotificationOverlay = makeUnique<CHyprNotificationOverlay>();

            Log::logger->log(Log::DEBUG, "Creating the PluginSystem!");
            g_pPluginSystem = makeUnique<CPluginSystem>();
            g_pConfigManager->handlePluginLoads();

            Log::logger->log(Log::DEBUG, "Creating the DecorationPositioner!");
            g_pDecorationPositioner = makeUnique<CDecorationPositioner>();

            Log::logger->log(Log::DEBUG, "Creating the CursorManager!");
            g_pCursorManager = makeUnique<CCursorManager>();

            Log::logger->log(Log::DEBUG, "Creating the VersionKeeper!");
            g_pVersionKeeperMgr = makeUnique<CVersionKeeperManager>();

            Log::logger->log(Log::DEBUG, "Creating the DonationNag!");
            g_pDonationNagManager = makeUnique<CDonationNagManager>();

            Log::logger->log(Log::DEBUG, "Creating the WelcomeManager!");
            g_pWelcomeManager = makeUnique<CWelcomeManager>();

            Log::logger->log(Log::DEBUG, "Creating the ANRManager!");
            g_pANRManager = makeUnique<CANRManager>();

            Log::logger->log(Log::DEBUG, "Starting XWayland");
            g_pXWayland = makeUnique<CXWayland>(g_pCompositor->m_wantsXwayland);
        } break;
        default: UNREACHABLE();
    }
}

void CCompositor::createLockFile() {
    const auto    PATH = m_instancePath + "/hyprland.lock";

    std::ofstream ofs(PATH, std::ios::trunc);

    ofs << m_hyprlandPID << "\n" << m_wlDisplaySocket << "\n";

    ofs.close();
}

void CCompositor::removeLockFile() {
    const auto PATH = m_instancePath + "/hyprland.lock";

    if (std::filesystem::exists(PATH))
        std::filesystem::remove(PATH);
}

void CCompositor::prepareFallbackOutput() {
    // create a backup monitor
    SP<Aquamarine::IBackendImplementation> headless;
    for (auto const& impl : m_aqBackend->getImplementations()) {
        if (impl->type() == Aquamarine::AQ_BACKEND_HEADLESS) {
            headless = impl;
            break;
        }
    }

    if (!headless) {
        Log::logger->log(Log::WARN, "No headless in prepareFallbackOutput?!");
        return;
    }

    headless->createOutput();

    if (m_monitors.empty())
        enterUnsafeState();
}

void CCompositor::startCompositor() {
    signal(SIGPIPE, SIG_IGN);

    if (
        /* Session-less Hyprland usually means a nest, don't update the env in that case */
        m_aqBackend->hasSession() &&
        /* Activation environment management is not disabled */
        !Env::envEnabled("HYPRLAND_NO_SD_VARS")) {
        const auto CMD =
#ifdef USES_SYSTEMD
            "systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash "
            "dbus-update-activation-environment 2>/dev/null && "
#endif
            "dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS";
        CKeybindManager::spawn(CMD);
    }

    Log::logger->log(Log::DEBUG, "Running on WAYLAND_DISPLAY: {}", m_wlDisplaySocket);

    prepareFallbackOutput();

    g_pHyprRenderer->setCursorFromName("left_ptr");

#ifdef USES_SYSTEMD
    if (NSystemd::sdBooted() > 0) {
        // tell systemd that we are ready so it can start other bond, following, related units
        if (!Env::envEnabled("HYPRLAND_NO_SD_NOTIFY"))
            NSystemd::sdNotify(0, "READY=1");
    } else
        Log::logger->log(Log::DEBUG, "systemd integration is baked in but system itself is not booted à la systemd!");
#endif

    createLockFile();

    Event::bus()->m_events.ready.emit();

    if (m_watchdogWriteFd.isValid())
        write(m_watchdogWriteFd.get(), "vax", 3);

    // This blocks until we are done.
    Log::logger->log(Log::DEBUG, "Hyprland is ready, running the event loop!");
    g_pEventLoopManager->enterLoop();
}

PHLMONITOR CCompositor::getMonitorFromID(const MONITORID& id) {
    for (auto const& m : m_monitors) {
        if (m->m_id == id) {
            return m;
        }
    }

    return nullptr;
}

PHLMONITOR CCompositor::getMonitorFromName(const std::string& name) {
    for (auto const& m : m_monitors) {
        if (m->m_name == name) {
            return m;
        }
    }
    return nullptr;
}

PHLMONITOR CCompositor::getMonitorFromDesc(const std::string& desc) {
    for (auto const& m : m_monitors) {
        if (m->m_description.starts_with(desc))
            return m;
    }
    return nullptr;
}

PHLMONITOR CCompositor::getMonitorFromCursor() {
    return getMonitorFromVector(g_pPointerManager->position());
}

PHLMONITOR CCompositor::getMonitorFromVector(const Vector2D& point) {
    if (m_monitors.empty()) {
        Log::logger->log(Log::WARN, "getMonitorFromVector called with empty monitor list");
        return nullptr;
    }

    PHLMONITOR mon;
    for (auto const& m : m_monitors) {
        if (CBox{m->m_position, m->m_size}.containsPoint(point)) {
            mon = m;
            break;
        }
    }

    if (!mon) {
        float      bestDistance = 0.f;
        PHLMONITOR pBestMon;

        for (auto const& m : m_monitors) {
            float dist = vecToRectDistanceSquared(point, m->m_position, m->m_position + m->m_size);

            if (dist < bestDistance || !pBestMon) {
                bestDistance = dist;
                pBestMon     = m;
            }
        }

        if (!pBestMon) { // ?????
            Log::logger->log(Log::WARN, "getMonitorFromVector no close mon???");
            return m_monitors.front();
        }

        return pBestMon;
    }

    return mon;
}

void CCompositor::removeWindowFromVectorSafe(PHLWINDOW pWindow) {
    if (!pWindow->m_fadingOut) {
        Event::bus()->m_events.window.destroy.emit(pWindow);

        std::erase_if(m_windows, [&](SP<Desktop::View::CWindow>& el) { return el == pWindow; });
        std::erase_if(m_windowsFadingOut, [&](PHLWINDOWREF el) { return el.lock() == pWindow; });
    }
}

bool CCompositor::monitorExists(PHLMONITOR pMonitor) {
    return std::ranges::any_of(m_realMonitors, [&](const PHLMONITOR& m) { return m == pMonitor; });
}

PHLWINDOW CCompositor::vectorToWindowUnified(const Vector2D& pos, uint8_t properties, PHLWINDOW pIgnoreWindow) {
    const auto PMONITOR = getMonitorFromVector(pos);
    if (!PMONITOR)
        return nullptr;

    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 & Desktop::View::FOCUS_PRIORITY;

    const auto  isShadowedByModal = [](PHLWINDOW w) -> bool {
        return *PMODALPARENTBLOCKING && w->m_xdgSurface && w->m_xdgSurface->m_toplevel && w->m_xdgSurface->m_toplevel->anyChildModal();
    };

    // pinned windows on top of floating regardless
    if (properties & Desktop::View::ALLOW_FLOATING) {
        for (auto const& w : m_windows | std::views::reverse) {
            if (ONLY_PRIORITY && !w->priorityFocus())
                continue;

            if (w->m_isFloating && w->m_isMapped && !w->isHidden() && !w->m_X11ShouldntFocus && w->m_pinned && !w->m_ruleApplicator->noFocus().valueOrDefault() &&
                w != pIgnoreWindow && !isShadowedByModal(w)) {
                const auto BB  = w->getWindowBoxUnified(properties);
                CBox       box = BB.copy().expand(!w->isX11OverrideRedirect() ? BORDER_GRAB_AREA : 0);
                if (box.containsPoint(g_pPointerManager->position()))
                    return w;

                if (!w->m_isX11) {
                    if (w->hasPopupAt(pos))
                        return w;
                }
            }
        }
    }

    auto windowForWorkspace = [&](bool special) -> PHLWINDOW {
        auto floating = [&](bool aboveFullscreen) -> PHLWINDOW {
            for (auto const& w : m_windows | std::views::reverse) {

                if (special && !w->onSpecialWorkspace()) // because special floating may creep up into regular
                    continue;

                if (!w->m_workspace)
                    continue;

                if (ONLY_PRIORITY && !w->priorityFocus())
                    continue;

                const auto PWINDOWMONITOR = w->m_monitor.lock();

                // to avoid focusing windows behind special workspaces from other monitors
                if (!*PSPECIALFALLTHRU && PWINDOWMONITOR && PWINDOWMONITOR->m_activeSpecialWorkspace && w->m_workspace != PWINDOWMONITOR->m_activeSpecialWorkspace) {
                    const auto BB = w->getWindowBoxUnified(properties);
                    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)
                        continue;
                }

                if (w->m_isFloating && w->m_isMapped && w->m_workspace->isVisible() && !w->isHidden() && !w->m_pinned && !w->m_ruleApplicator->noFocus().valueOrDefault() &&
                    w != pIgnoreWindow && (!aboveFullscreen || w->m_createdOverFullscreen) && !isShadowedByModal(w)) {
                    // OR windows should add focus to parent
                    if (w->m_X11ShouldntFocus && !w->isX11OverrideRedirect())
                        continue;

                    const auto BB  = w->getWindowBoxUnified(properties);
                    CBox       box = BB.copy().expand(!w->isX11OverrideRedirect() ? BORDER_GRAB_AREA : 0);
                    if (box.containsPoint(g_pPointerManager->position())) {

                        if (w->m_isX11 && w->isX11OverrideRedirect() && !w->m_xwaylandSurface->wantsFocus()) {
                            // Override Redirect
                            return Desktop::focusState()->window(); // we kinda trick everything here.
                            // TODO: this is wrong, we should focus the parent, but idk how to get it considering it's nullptr in most cases.
                        }

                        return w;
                    }

                    if (!w->m_isX11) {
                        if (w->hasPopupAt(pos))
                            return w;
                    }
                }
            }

            return nullptr;
        };

        if (properties & Desktop::View::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;
        }

        if (properties & Desktop::View::FLOATING_ONLY)
            return floating(false);

        const WORKSPACEID WSPID      = special ? PMONITOR->activeSpecialWorkspaceID() : PMONITOR->activeWorkspaceID();
        const auto        PWORKSPACE = getWorkspaceByID(WSPID);

        if (PWORKSPACE->m_hasFullscreenWindow && !(properties & Desktop::View::SKIP_FULLSCREEN_PRIORITY) && !ONLY_PRIORITY) {
            const auto FS_WINDOW = PWORKSPACE->getFullscreenWindow();

            if (!FS_WINDOW)
                return nullptr;

            // for maximized windows, don't return a window if we are not directly on it.
            if (FS_WINDOW->m_fullscreenState.internal != FSMODE_MAXIMIZED || FS_WINDOW->getWindowBoxUnified(properties).containsPoint(pos))
                return PWORKSPACE->getFullscreenWindow();
            else
                return nullptr;
        }

        auto found = floating(false);
        if (found)
            return found;

        // for windows, we need to check their extensions too, first.
        for (auto const& w : m_windows) {
            if (ONLY_PRIORITY && !w->priorityFocus())
                continue;

            if (special != w->onSpecialWorkspace())
                continue;

            if (!w->m_workspace)
                continue;

            if (!w->m_isX11 && !w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus &&
                !w->m_ruleApplicator->noFocus().valueOrDefault() && w != pIgnoreWindow && !isShadowedByModal(w)) {
                if (w->hasPopupAt(pos))
                    return w;
            }
        }

        for (auto const& w : m_windows) {
            if (ONLY_PRIORITY && !w->priorityFocus())
                continue;

            if (special != w->onSpecialWorkspace())
                continue;

            if (!w->m_workspace)
                continue;

            if (!w->m_isFloating && w->m_isMapped && w->workspaceID() == WSPID && !w->isHidden() && !w->m_X11ShouldntFocus && !w->m_ruleApplicator->noFocus().valueOrDefault() &&
                w != pIgnoreWindow && !isShadowedByModal(w)) {
                CBox box = (properties & Desktop::View::USE_PROP_TILED) ? w->getWindowBoxUnified(properties) : CBox{w->m_position, w->m_size};
                if ((properties & Desktop::View::INPUT_EXTENTS) && BORDER_GRAB_AREA > 0 && !w->isX11OverrideRedirect()) {
                    const auto  WORKAREA                    = PWORKSPACE->m_space->workArea();
                    static auto isWindowCloseToWorkAreaEdge = [&](const Math::eDirection dir) -> bool {
                        constexpr double STICK_THRESHOLD = 2.0; // This constant is taken from isAdjacent in CCompositor::getWindowInDirection
                        double           aEdge           = -1;
                        double           bEdge           = -1;

                        switch (dir) {
                            case Math::DIRECTION_LEFT:
                                aEdge = WORKAREA.x;
                                bEdge = box.x;
                                break;
                            case Math::DIRECTION_RIGHT:
                                aEdge = WORKAREA.x + WORKAREA.width;
                                bEdge = box.x + box.width;
                                break;
                            case Math::DIRECTION_UP:
                                aEdge = WORKAREA.y;
                                bEdge = box.y;
                                break;
                            case Math::DIRECTION_DOWN:
                                aEdge = WORKAREA.y + WORKAREA.height;
                                bEdge = box.y + box.height;
                                break;
                            default: break;
                        }
                        const double delta = aEdge - bEdge;
                        if (std::abs(delta) < STICK_THRESHOLD)
                            return true;
                        else
                            return false;
                    };

                    if (isWindowCloseToWorkAreaEdge(Math::eDirection::DIRECTION_LEFT)) {
                        box.x -= BORDER_GRAB_AREA;
                        box.width += BORDER_GRAB_AREA;
                    }

                    if (isWindowCloseToWorkAreaEdge(Math::eDirection::DIRECTION_RIGHT))
                        box.width += BORDER_GRAB_AREA;

                    if (isWindowCloseToWorkAreaEdge(Math::eDirection::DIRECTION_UP)) {
                        box.y -= BORDER_GRAB_AREA;
                        box.height += BORDER_GRAB_AREA;
                    }

                    if (isWindowCloseToWorkAreaEdge(Math::eDirection::DIRECTION_DOWN))
                        box.height += BORDER_GRAB_AREA;
                }
                if (box.containsPoint(pos))
                    return w;
            }
        }

        return nullptr;
    };

    // special workspace
    if (PMONITOR->m_activeSpecialWorkspace && !*PSPECIALFALLTHRU)
        return windowForWorkspace(true);

    if (PMONITOR->m_activeSpecialWorkspace) {
        const auto PWINDOW = windowForWorkspace(true);

        if (PWINDOW)
            return PWINDOW;
    }

    return windowForWorkspace(false);
}

SP<CWLSurfaceResource> CCompositor::vectorWindowToSurface(const Vector2D& pos, PHLWINDOW pWindow, Vector2D& sl) {

    if (!validMapped(pWindow))
        return nullptr;

    RASSERT(!pWindow->m_isX11, "Cannot call vectorWindowToSurface on an X11 window!");

    // try popups first
    const auto PPOPUP = pWindow->m_popupHead->at(pos);

    if (PPOPUP) {
        const auto OFF = PPOPUP->coordsRelativeToParent();
        sl             = pos - pWindow->m_realPosition->goal() - OFF;
        return PPOPUP->wlSurface()->resource();
    }

    auto [surf, local] = pWindow->wlSurface()->resource()->at(pos - pWindow->m_realPosition->goal(), true);
    if (surf) {
        sl = local;
        return surf;
    }

    return nullptr;
}

Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindow, SP<CWLSurfaceResource> pSurface) {
    if (!validMapped(pWindow))
        return {};

    if (pWindow->m_isX11)
        return vec - pWindow->m_realPosition->goal();

    const auto PPOPUP = pWindow->m_popupHead->at(vec);
    if (PPOPUP)
        return vec - PPOPUP->coordsGlobal();

    std::tuple<SP<CWLSurfaceResource>, Vector2D> iterData = {pSurface, {-1337, -1337}};

    pWindow->wlSurface()->resource()->breadthfirst(
        [](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) {
            const auto PDATA = sc<std::tuple<SP<CWLSurfaceResource>, Vector2D>*>(data);
            if (surf == std::get<0>(*PDATA))
                std::get<1>(*PDATA) = offset;
        },
        &iterData);

    CBox geom = pWindow->m_xdgSurface->m_current.geometry;

    if (std::get<1>(iterData) == Vector2D{-1337, -1337})
        return vec - pWindow->m_realPosition->goal();

    return vec - pWindow->m_realPosition->goal() - std::get<1>(iterData) + Vector2D{geom.x, geom.y};
}

PHLMONITOR CCompositor::getMonitorFromOutput(SP<Aquamarine::IOutput> out) {
    for (auto const& m : m_monitors) {
        if (m->m_output == out) {
            return m;
        }
    }

    return nullptr;
}

PHLMONITOR CCompositor::getRealMonitorFromOutput(SP<Aquamarine::IOutput> out) {
    for (auto const& m : m_realMonitors) {
        if (m->m_output == out) {
            return m;
        }
    }

    return nullptr;
}

SP<CWLSurfaceResource> CCompositor::vectorToLayerPopupSurface(const Vector2D& pos, PHLMONITOR monitor, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound) {
    for (auto const& lsl : monitor->m_layerSurfaceLayers | std::views::reverse) {
        for (auto const& ls : lsl | std::views::reverse) {
            if (!ls->aliveAndVisible())
                continue;

            auto SURFACEAT = ls->m_popupHead->at(pos, true);

            if (SURFACEAT) {
                *ppLayerSurfaceFound = ls.lock();
                *sCoords             = pos - SURFACEAT->coordsGlobal();
                return SURFACEAT->wlSurface()->resource();
            }
        }
    }

    return nullptr;
}

SP<CWLSurfaceResource> CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector<PHLLSREF>* layerSurfaces, Vector2D* sCoords, PHLLS* ppLayerSurfaceFound,
                                                         bool aboveLockscreen) {

    for (auto const& ls : *layerSurfaces | std::views::reverse) {
        if (!ls->aliveAndVisible() || (aboveLockscreen && ls->m_ruleApplicator->aboveLock().valueOrDefault() != 2))
            continue;

        auto [surf, local] = ls->m_layerSurface->m_surface->at(pos - ls->m_geometry.pos(), true);

        if (surf) {
            if (surf->m_current.input.empty())
                continue;

            *ppLayerSurfaceFound = ls.lock();

            *sCoords = local;

            return surf;
        }
    }

    return nullptr;
}

PHLWINDOW CCompositor::getWindowFromSurface(SP<CWLSurfaceResource> pSurface) {
    if (!pSurface || !pSurface->m_hlSurface)
        return nullptr;

    const auto VIEW = pSurface->m_hlSurface->view();

    if (!VIEW || VIEW->type() != Desktop::View::VIEW_TYPE_WINDOW)
        return nullptr;

    return dynamicPointerCast<Desktop::View::CWindow>(VIEW);
}

PHLWINDOW CCompositor::getWindowFromHandle(uint32_t handle) {
    for (auto const& w : m_windows) {
        if (sc<uint32_t>(rc<uint64_t>(w.get()) & 0xFFFFFFFF) == handle) {
            return w;
        }
    }

    return nullptr;
}

PHLWORKSPACE CCompositor::getWorkspaceByID(const WORKSPACEID& id) {
    for (auto const& w : getWorkspaces()) {
        if (w->m_id == id && !w->inert())
            return w.lock();
    }

    return nullptr;
}

PHLWINDOW CCompositor::getUrgentWindow() {
    for (auto const& w : m_windows) {
        if (w->m_isMapped && w->m_isUrgent)
            return w;
    }

    return nullptr;
}

bool CCompositor::isWindowActive(PHLWINDOW pWindow) {
    if (!Desktop::focusState()->window() && !Desktop::focusState()->surface())
        return false;

    if (!pWindow->m_isMapped)
        return false;

    const auto PSURFACE = pWindow->wlSurface()->resource();

    return PSURFACE == Desktop::focusState()->surface() || pWindow == Desktop::focusState()->window();
}

void CCompositor::changeWindowZOrder(PHLWINDOW pWindow, bool top) {
    if (!validMapped(pWindow))
        return;

    if (top)
        pWindow->m_createdOverFullscreen = true;

    if (pWindow == (top ? m_windows.back() : m_windows.front()))
        return;

    auto moveToZ = [&](PHLWINDOW pw, bool top) -> void {
        if (top) {
            for (auto it = m_windows.begin(); it != m_windows.end(); ++it) {
                if (*it == pw) {
                    std::rotate(it, it + 1, m_windows.end());
                    break;
                }
            }
        } else {
            for (auto it = m_windows.rbegin(); it != m_windows.rend(); ++it) {
                if (*it == pw) {
                    std::rotate(it, it + 1, m_windows.rend());
                    break;
                }
            }
        }

        if (pw->m_isMapped)
            g_pHyprRenderer->damageMonitor(pw->m_monitor.lock());
    };

    if (!pWindow->m_isX11)
        moveToZ(pWindow, top);
    else {
        // move X11 window stack

        std::vector<PHLWINDOW> toMove;

        auto                   x11Stack = [&](PHLWINDOW pw, bool top, auto&& x11Stack) -> void {
            if (top)
                toMove.emplace_back(pw);
            else
                toMove.insert(toMove.begin(), pw);

            for (auto const& w : m_windows) {
                if (w->m_isMapped && !w->isHidden() && w->m_isX11 && w->x11TransientFor() == pw && w != pw && std::ranges::find(toMove, w) == toMove.end()) {
                    x11Stack(w, top, x11Stack);
                }
            }
        };

        x11Stack(pWindow, top, x11Stack);
        for (const auto& it : toMove) {
            moveToZ(it, top);
        }
    }
}

void CCompositor::cleanupFadingOut(const MONITORID& monid) {
    for (auto const& ww : m_windowsFadingOut) {

        auto w = ww.lock();

        if (w->monitorID() != monid && w->m_monitor)
            continue;

        if (!w->m_fadingOut || w->m_alpha->value() == 0.f) {

            w->m_fadingOut = false;

            if (!w->m_readyToDelete)
                continue;

            removeWindowFromVectorSafe(w);

            w.reset();

            Log::logger->log(Log::DEBUG, "Cleanup: destroyed a window");
            return;
        }
    }

    bool layersDirty = false;

    for (auto const& lsr : m_surfacesFadingOut) {

        auto ls = lsr.lock();

        if (!ls) {
            layersDirty = true;
            continue;
        }

        if (ls->monitorID() != monid && ls->m_monitor)
            continue;

        // mark blur for recalc
        if (ls->m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || ls->m_layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM)
            g_pHyprOpenGL->markBlurDirtyForMonitor(getMonitorFromID(monid));

        if (ls->m_fadingOut && ls->m_readyToDelete && ls->isFadedOut()) {
            for (auto const& m : m_monitors) {
                for (auto& lsl : m->m_layerSurfaceLayers) {
                    if (!lsl.empty() && std::ranges::find_if(lsl, [&](auto& other) { return other == ls; }) != lsl.end()) {
                        std::erase_if(lsl, [&](auto& other) { return other == ls || !other; });
                    }
                }
            }

            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; });

            ls.reset();

            Log::logger->log(Log::DEBUG, "Cleanup: destroyed a layersurface");

            return;
        }
    }

    if (layersDirty)
        std::erase_if(m_surfacesFadingOut, [](const auto& el) { return el.expired(); });
}

void CCompositor::addToFadingOutSafe(PHLLS pLS) {
    const auto FOUND = std::ranges::find_if(m_surfacesFadingOut, [&](auto& other) { return other.lock() == pLS; });

    if (FOUND != m_surfacesFadingOut.end())
        return; // if it's already added, don't add it.

    m_surfacesFadingOut.emplace_back(pLS);
}

void CCompositor::removeFromFadingOutSafe(PHLLS ls) {
    std::erase(m_surfacesFadingOut, ls);
}

void CCompositor::addToFadingOutSafe(PHLWINDOW pWindow) {
    const auto FOUND = std::ranges::find_if(m_windowsFadingOut, [&](PHLWINDOWREF& other) { return other.lock() == pWindow; });

    if (FOUND != m_windowsFadingOut.end())
        return; // if it's already added, don't add it.

    m_windowsFadingOut.emplace_back(pWindow);
}

PHLWINDOW CCompositor::getWindowInDirection(PHLWINDOW pWindow, Math::eDirection dir) {
    if (dir == Math::DIRECTION_DEFAULT)
        return nullptr;

    const auto PMONITOR = pWindow->m_monitor.lock();

    if (!PMONITOR)
        return nullptr; // ??

    const auto WINDOWIDEALBB = pWindow->isFullscreen() ? CBox{PMONITOR->m_position, PMONITOR->m_size} : pWindow->getWindowIdealBoundingBoxIgnoreReserved();
    const auto PWORKSPACE    = pWindow->m_workspace;

    if (!PWORKSPACE)
        return nullptr; // ??

    return getWindowInDirection(WINDOWIDEALBB, PWORKSPACE, dir, pWindow, pWindow->m_isFloating);
}

PHLWINDOW CCompositor::getWindowInDirection(const CBox& box, PHLWORKSPACE pWorkspace, Math::eDirection dir, PHLWINDOW ignoreWindow, bool useVectorAngles) {
    if (dir == Math::DIRECTION_DEFAULT)
        return nullptr;

    // 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");

    const auto  POSA  = box.pos();
    const auto  SIZEA = box.size();

    auto        leaderValue  = -1;
    PHLWINDOW   leaderWindow = nullptr;

    if (!useVectorAngles) {
        // helper to check if two rectangles are adjacent along an axis, considering slight overlaps.
        // returns true if: STICKS (delta <= 2) OR rectangles overlap but no more than 50% of the smaller dimension.
        static auto isAdjacent = [](const double aMin, const double aMax, const double bMin, const double bMax) -> bool {
            constexpr double STICK_THRESHOLD   = 2.0;
            constexpr double MAX_OVERLAP_RATIO = 0.5;

            const double     aEdge = aMin;
            const double     bEdge = bMax;
            const double     delta = aEdge - bEdge;

            // old STICKS check for 2px
            if (std::abs(delta) < STICK_THRESHOLD)
                return true;

            if (delta >= 0)
                return false;

            const double overlap = -delta;
            const double sizeA   = aMax - aMin;
            const double sizeB   = bMax - bMin;

            // reject if one rectangle fully contains the other
            if ((bMin <= aMin && bMax >= aMax) || (aMin <= bMin && aMax >= bMax))
                return false;

            // accept if overlap is at most 50% of the smaller dimension
            return overlap <= std::min(sizeA, sizeB) * MAX_OVERLAP_RATIO;
        };

        for (auto const& w : m_windows) {
            if (w == ignoreWindow || !w->m_workspace || !w->m_isMapped || w->isHidden() || (!w->isFullscreen() && w->m_isFloating) || !w->m_workspace->isVisible())
                continue;

            if (pWorkspace->m_monitor == w->m_monitor && pWorkspace != w->m_workspace)
                continue;

            if (pWorkspace->m_hasFullscreenWindow && !w->isFullscreen() && !w->m_createdOverFullscreen)
                continue;

            if (!*PMONITORFALLBACK && pWorkspace->m_monitor != w->m_monitor)
                continue;

            const auto BWINDOWIDEALBB = w->getWindowIdealBoundingBoxIgnoreReserved();

            const auto POSB  = Vector2D(BWINDOWIDEALBB.x, BWINDOWIDEALBB.y);
            const auto SIZEB = Vector2D(BWINDOWIDEALBB.width, BWINDOWIDEALBB.height);

            double     intersectLength = -1;

            switch (dir) {
                case Math::DIRECTION_LEFT:
                    if (isAdjacent(POSA.x, POSA.x + SIZEA.x, POSB.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 Math::DIRECTION_RIGHT:
                    if (isAdjacent(POSB.x, POSB.x + SIZEB.x, POSA.x, POSA.x + SIZEA.x))
                        intersectLength = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
                    break;
                case Math::DIRECTION_UP:
                    if (isAdjacent(POSA.y, POSA.y + SIZEA.y, POSB.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 Math::DIRECTION_DOWN:
                    if (isAdjacent(POSB.y, POSB.y + SIZEB.y, POSA.y, POSA.y + SIZEA.y))
                        intersectLength = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
                    break;
                default: break;
            }

            if (*PMETHOD == 0 /* history */) {
                if (intersectLength > 0) {

                    // get idx
                    int         windowIDX = -1;
                    const auto& HISTORY   = Desktop::History::windowTracker()->fullHistory();
                    for (int64_t i = HISTORY.size() - 1; i >= 0; --i) {
                        if (HISTORY[i] == w) {
                            windowIDX = i;
                            break;
                        }
                    }

                    if (windowIDX > leaderValue) {
                        leaderValue  = windowIDX;
                        leaderWindow = w;
                    }
                }
            } else /* length */ {
                if (intersectLength > leaderValue) {
                    leaderValue  = intersectLength;
                    leaderWindow = w;
                }
            }
        }
    } else {
        static const std::unordered_map<Math::eDirection, Vector2D> VECTORS = {
            {Math::DIRECTION_RIGHT, {1, 0}}, {Math::DIRECTION_UP, {0, -1}}, {Math::DIRECTION_DOWN, {0, 1}}, {Math::DIRECTION_LEFT, {-1, 0}}};

        //
        auto vectorAngles = [](const Vector2D& a, const Vector2D& b) -> double {
            double dot = (a.x * b.x) + (a.y * b.y);
            double ang = std::acos(dot / (a.size() * b.size()));
            return ang;
        };

        float           bestAngleAbs = 2.0 * M_PI;
        constexpr float THRESHOLD    = 0.3 * M_PI;

        for (auto const& w : m_windows) {
            if (w == ignoreWindow || !w->m_isMapped || !w->m_workspace || w->isHidden() || (!w->isFullscreen() && !w->m_isFloating) || !w->m_workspace->isVisible())
                continue;

            if (pWorkspace->m_monitor == w->m_monitor && pWorkspace != w->m_workspace)
                continue;

            if (pWorkspace->m_hasFullscreenWindow && !w->isFullscreen() && !w->m_createdOverFullscreen)
                continue;

            if (!*PMONITORFALLBACK && pWorkspace->m_monitor != w->m_monitor)
                continue;

            const auto DIST  = w->middle().distance(box.middle());
            const auto ANGLE = vectorAngles(Vector2D{w->middle() - box.middle()}, VECTORS.at(dir));

            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;
                leaderWindow = w;
            }
        }

        if (!leaderWindow && pWorkspace->m_hasFullscreenWindow)
            leaderWindow = pWorkspace->getFullscreenWindow();
    }

    if (leaderValue != -1)
        return leaderWindow;

    return nullptr;
}

template <typename WINDOWPTR>
static bool isWorkspaceMatches(WINDOWPTR pWindow, const WINDOWPTR w, bool anyWorkspace) {
    return anyWorkspace ? w->m_workspace && w->m_workspace->isVisible() : w->m_workspace == pWindow->m_workspace;
}

template <typename WINDOWPTR>
static bool isFloatingMatches(WINDOWPTR w, std::optional<bool> floating) {
    return !floating.has_value() || w->m_isFloating == floating.value();
}

template <typename WINDOWPTR>
static bool isWindowAvailableForCycle(WINDOWPTR pWindow, WINDOWPTR w, bool focusableOnly, std::optional<bool> floating, bool anyWorkspace = false) {
    return isFloatingMatches(w, floating) &&
        (w != pWindow && isWorkspaceMatches(pWindow, w, anyWorkspace) && w->m_isMapped && !w->isHidden() && (!focusableOnly || !w->m_ruleApplicator->noFocus().valueOrDefault()));
}

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
    const auto& HISTORY = Desktop::History::windowTracker()->fullHistory();
    return next ? getWeakWindowPred(std::ranges::find(HISTORY, cur), HISTORY.end(), HISTORY.begin(), FINDER) :
                  getWeakWindowPred(std::ranges::find(HISTORY | std::views::reverse, cur), HISTORY.rend(), HISTORY.rbegin(), FINDER);
}

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); };
    return prev ? getWindowPred(std::ranges::find(m_windows | std::views::reverse, cur), m_windows.rend(), m_windows.rbegin(), FINDER) :
                  getWindowPred(std::ranges::find(m_windows, cur), m_windows.end(), m_windows.begin(), FINDER);
}

WORKSPACEID CCompositor::getNextAvailableNamedWorkspace() {
    WORKSPACEID lowest = -1337 + 1;
    for (auto const& w : getWorkspaces()) {
        if (w->m_id < -1 && w->m_id < lowest)
            lowest = w->m_id;
    }

    // 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;
    }

    return lowest - 1;
}

PHLWORKSPACE CCompositor::getWorkspaceByName(const std::string& name) {
    for (auto const& w : getWorkspaces()) {
        if (w->m_name == name && !w->inert())
            return w.lock();
    }

    return nullptr;
}

PHLWORKSPACE CCompositor::getWorkspaceByString(const std::string& str) {
    if (str.starts_with("name:")) {
        return getWorkspaceByName(str.substr(str.find_first_of(':') + 1));
    }

    try {
        return getWorkspaceByID(getWorkspaceIDNameFromString(str).id);
    } catch (std::exception& e) { Log::logger->log(Log::ERR, "Error in getWorkspaceByString, invalid id"); }

    return nullptr;
}

bool CCompositor::isPointOnAnyMonitor(const Vector2D& point) {
    return std::ranges::any_of(
        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); });
}

bool CCompositor::isPointOnReservedArea(const Vector2D& point, const PHLMONITOR pMonitor) {
    const auto PMONITOR = pMonitor ? pMonitor : getMonitorFromVector(point);

    auto       box = PMONITOR->logicalBox();
    if (VECNOTINRECT(point, box.x - 1, box.y - 1, box.x + box.w + 1, box.y + box.h + 1))
        return false;

    PMONITOR->m_reservedArea.applyip(box);

    return VECNOTINRECT(point, box.x, box.y, box.x + box.w, box.y + box.h);
}

std::optional<CBox> CCompositor::calculateX11WorkArea() {
    static auto PXWLFORCESCALEZERO = CConfigValue<Hyprlang::INT>("xwayland:force_zero_scaling");
    // We more than likely won't be able to calculate one
    // and even if we could this is minor
    if (m_monitors.size() > 1 || m_monitors.empty())
        return std::nullopt;

    const auto M = m_monitors.front();

    // we ignore monitor->m_position on purpose
    CBox box = M->logicalBoxMinusReserved().translate(-M->m_position);
    if ((*PXWLFORCESCALEZERO))
        box.scale(M->m_scale);

    return box.translate(M->m_xwaylandPosition);
}

PHLMONITOR CCompositor::getMonitorInDirection(Math::eDirection dir) {
    return getMonitorInDirection(Desktop::focusState()->monitor(), dir);
}

PHLMONITOR CCompositor::getMonitorInDirection(PHLMONITOR pSourceMonitor, Math::eDirection dir) {
    if (!pSourceMonitor)
        return nullptr;

    const auto POSA  = pSourceMonitor->m_position;
    const auto SIZEA = pSourceMonitor->m_size;

    auto       longestIntersect        = -1;
    PHLMONITOR longestIntersectMonitor = nullptr;

    for (auto const& m : m_monitors) {
        if (m == pSourceMonitor)
            continue;

        const auto POSB  = m->m_position;
        const auto SIZEB = m->m_size;
        switch (dir) {
            case Math::DIRECTION_LEFT:
                if (STICKS(POSA.x, POSB.x + SIZEB.x)) {
                    const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
                    if (INTERSECTLEN > longestIntersect) {
                        longestIntersect        = INTERSECTLEN;
                        longestIntersectMonitor = m;
                    }
                }
                break;
            case Math::DIRECTION_RIGHT:
                if (STICKS(POSA.x + SIZEA.x, POSB.x)) {
                    const auto INTERSECTLEN = std::max(0.0, std::min(POSA.y + SIZEA.y, POSB.y + SIZEB.y) - std::max(POSA.y, POSB.y));
                    if (INTERSECTLEN > longestIntersect) {
                        longestIntersect        = INTERSECTLEN;
                        longestIntersectMonitor = m;
                    }
                }
                break;
            case Math::DIRECTION_UP:
                if (STICKS(POSA.y, POSB.y + SIZEB.y)) {
                    const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
                    if (INTERSECTLEN > longestIntersect) {
                        longestIntersect        = INTERSECTLEN;
                        longestIntersectMonitor = m;
                    }
                }
                break;
            case Math::DIRECTION_DOWN:
                if (STICKS(POSA.y + SIZEA.y, POSB.y)) {
                    const auto INTERSECTLEN = std::max(0.0, std::min(POSA.x + SIZEA.x, POSB.x + SIZEB.x) - std::max(POSA.x, POSB.x));
                    if (INTERSECTLEN > longestIntersect) {
                        longestIntersect        = INTERSECTLEN;
                        longestIntersectMonitor = m;
                    }
                }
                break;
            default: break;
        }
    }

    if (longestIntersect != -1)
        return longestIntersectMonitor;

    return nullptr;
}

void CCompositor::updateAllWindowsAnimatedDecorationValues() {
    for (auto const& w : m_windows) {
        if (!w->m_isMapped)
            continue;

        w->updateDecorationValues();
    }
}

MONITORID CCompositor::getNextAvailableMonitorID(std::string const& name) {
    // reuse ID if it's already in the map, and the monitor with that ID is not being used by another monitor
    if (m_monitorIDMap.contains(name) && !std::ranges::any_of(m_realMonitors, [&](auto m) { return m->m_id == m_monitorIDMap[name]; }))
        return m_monitorIDMap[name];

    // otherwise, find minimum available ID that is not in the map
    std::unordered_set<MONITORID> usedIDs;
    for (auto const& monitor : m_realMonitors) {
        usedIDs.insert(monitor->m_id);
    }

    MONITORID nextID = 0;
    while (usedIDs.contains(nextID)) {
        nextID++;
    }
    m_monitorIDMap[name] = nextID;
    return nextID;
}

void CCompositor::swapActiveWorkspaces(PHLMONITOR pMonitorA, PHLMONITOR pMonitorB) {
    const auto PWORKSPACEA = pMonitorA->m_activeWorkspace;
    const auto PWORKSPACEB = pMonitorB->m_activeWorkspace;

    PWORKSPACEA->m_monitor = pMonitorB;
    PWORKSPACEA->m_events.monitorChanged.emit();

    for (auto const& w : m_windows) {
        if (w->m_workspace == PWORKSPACEA) {
            if (w->m_pinned) {
                w->m_workspace = PWORKSPACEB;
                continue;
            }

            w->m_monitor = pMonitorB;

            // additionally, move floating and fs windows manually
            if (w->m_isFloating)
                w->layoutTarget()->setPositionGlobal(w->layoutTarget()->position().translate(-pMonitorA->m_position + pMonitorB->m_position));

            if (w->isFullscreen()) {
                *w->m_realPosition = pMonitorB->m_position;
                *w->m_realSize     = pMonitorB->m_size;
            }

            w->updateToplevel();
        }
    }

    PWORKSPACEB->m_monitor = pMonitorA;
    PWORKSPACEB->m_events.monitorChanged.emit();

    for (auto const& w : m_windows) {
        if (w->m_workspace == PWORKSPACEB) {
            if (w->m_pinned) {
                w->m_workspace = PWORKSPACEA;
                continue;
            }

            w->m_monitor = pMonitorA;

            // additionally, move floating and fs windows manually
            if (w->m_isFloating)
                w->layoutTarget()->setPositionGlobal(w->layoutTarget()->position().translate(-pMonitorB->m_position + pMonitorA->m_position));

            if (w->isFullscreen()) {
                *w->m_realPosition = pMonitorA->m_position;
                *w->m_realSize     = pMonitorA->m_size;
            }

            w->updateToplevel();
        }
    }

    pMonitorA->m_activeWorkspace = PWORKSPACEB;
    pMonitorB->m_activeWorkspace = PWORKSPACEA;

    g_layoutManager->recalculateMonitor(pMonitorA);
    g_layoutManager->recalculateMonitor(pMonitorB);

    g_pHyprRenderer->damageMonitor(pMonitorB);
    g_pHyprRenderer->damageMonitor(pMonitorA);

    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);

    if (pMonitorA->m_id == Desktop::focusState()->monitor()->m_id || pMonitorB->m_id == Desktop::focusState()->monitor()->m_id) {
        const auto LASTWIN = pMonitorA->m_id == Desktop::focusState()->monitor()->m_id ? PWORKSPACEB->getLastFocusedWindow() : PWORKSPACEA->getLastFocusedWindow();
        Desktop::focusState()->fullWindowFocus(
            LASTWIN ? LASTWIN :
                      (g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(),
                                                            Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING)),
            Desktop::FOCUS_REASON_DESKTOP_STATE_CHANGE);

        const auto PNEWWORKSPACE = pMonitorA->m_id == Desktop::focusState()->monitor()->m_id ? PWORKSPACEB : PWORKSPACEA;
        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)});
        Event::bus()->m_events.workspace.active.emit(PNEWWORKSPACE);
    }

    // events
    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)});
    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)});

    Event::bus()->m_events.workspace.moveToMonitor.emit(PWORKSPACEA, pMonitorB);
    Event::bus()->m_events.workspace.moveToMonitor.emit(PWORKSPACEB, pMonitorA);
}

PHLMONITOR CCompositor::getMonitorFromString(const std::string& name) {
    if (name == "current")
        return Desktop::focusState()->monitor();
    else if (isDirection(name))
        return getMonitorInDirection(Math::fromChar(name[0]));
    else if (name[0] == '+' || name[0] == '-') {
        // relative

        if (m_monitors.size() == 1)
            return *m_monitors.begin();

        const auto OFFSET = name[0] == '-' ? name : name.substr(1);

        if (!isNumber(OFFSET)) {
            Log::logger->log(Log::ERR, "Error in getMonitorFromString: Not a number in relative.");
            return nullptr;
        }

        int offsetLeft = std::stoi(OFFSET);
        offsetLeft     = offsetLeft < 0 ? -((-offsetLeft) % m_monitors.size()) : offsetLeft % m_monitors.size();

        int currentPlace = 0;
        for (int i = 0; i < sc<int>(m_monitors.size()); i++) {
            if (m_monitors[i] == Desktop::focusState()->monitor()) {
                currentPlace = i;
                break;
            }
        }

        currentPlace += offsetLeft;

        if (currentPlace < 0) {
            currentPlace = m_monitors.size() + currentPlace;
        } else {
            currentPlace = currentPlace % m_monitors.size();
        }

        if (currentPlace != std::clamp(currentPlace, 0, sc<int>(m_monitors.size()) - 1)) {
            Log::logger->log(Log::WARN, "Error in getMonitorFromString: Vaxry's code sucks.");
            currentPlace = std::clamp(currentPlace, 0, sc<int>(m_monitors.size()) - 1);
        }

        return m_monitors[currentPlace];
    } else if (isNumber(name)) {
        // change by ID
        MONITORID monID = MONITOR_INVALID;
        try {
            monID = std::stoi(name);
        } catch (std::exception& e) {
            // shouldn't happen but jic
            Log::logger->log(Log::ERR, "Error in getMonitorFromString: invalid num");
            return nullptr;
        }

        if (monID > -1 && monID < sc<MONITORID>(m_monitors.size())) {
            return getMonitorFromID(monID);
        } else {
            Log::logger->log(Log::ERR, "Error in getMonitorFromString: invalid arg 1");
            return nullptr;
        }
    } else {
        for (auto const& m : m_monitors) {
            if (!m->m_output)
                continue;

            if (m->matchesStaticSelector(name)) {
                return m;
            }
        }
    }

    return nullptr;
}

void CCompositor::moveWorkspaceToMonitor(PHLWORKSPACE pWorkspace, PHLMONITOR pMonitor, bool noWarpCursor) {
    static auto PHIDESPECIALONWORKSPACECHANGE = CConfigValue<Hyprlang::INT>("binds:hide_special_on_workspace_change");

    if (!pWorkspace || !pMonitor)
        return;

    if (pWorkspace->m_monitor == pMonitor)
        return;

    Log::logger->log(Log::DEBUG, "moveWorkspaceToMonitor: Moving {} to monitor {}", pWorkspace->m_id, pMonitor->m_id);

    const auto POLDMON = pWorkspace->m_monitor.lock();

    const bool SWITCHINGISACTIVE = POLDMON ? POLDMON->m_activeWorkspace == pWorkspace : false;

    // fix old mon
    WORKSPACEID nextWorkspaceOnMonitorID = WORKSPACE_INVALID;
    if (!SWITCHINGISACTIVE)
        nextWorkspaceOnMonitorID = pWorkspace->m_id;
    else {
        PHLWORKSPACE newWorkspace; // for holding a ref to the new workspace that might be created

        for (auto const& w : getWorkspaces()) {
            if (w->m_monitor == POLDMON && w->m_id != pWorkspace->m_id && !w->m_isSpecialWorkspace) {
                nextWorkspaceOnMonitorID = w->m_id;
                break;
            }
        }

        if (nextWorkspaceOnMonitorID == WORKSPACE_INVALID) {
            nextWorkspaceOnMonitorID = 1;

            while (getWorkspaceByID(nextWorkspaceOnMonitorID) || [&]() -> bool {
                const auto B = g_pConfigManager->getBoundMonitorForWS(std::to_string(nextWorkspaceOnMonitorID));
                return B && B != POLDMON;
            }())
                nextWorkspaceOnMonitorID++;

            Log::logger->log(Log::DEBUG, "moveWorkspaceToMonitor: Plugging gap with new {}", nextWorkspaceOnMonitorID);

            if (POLDMON)
                newWorkspace = g_pCompositor->createNewWorkspace(nextWorkspaceOnMonitorID, POLDMON->m_id);
        }

        Log::logger->log(Log::DEBUG, "moveWorkspaceToMonitor: Plugging gap with existing {}", nextWorkspaceOnMonitorID);
        if (POLDMON)
            POLDMON->changeWorkspace(nextWorkspaceOnMonitorID, false, true, true);
    }

    // move the workspace
    pWorkspace->m_monitor = pMonitor;
    pWorkspace->m_space->recheckWorkArea();
    pWorkspace->m_events.monitorChanged.emit();

    for (auto const& w : m_windows) {
        if (w->m_workspace == pWorkspace) {
            if (w->m_pinned) {
                w->m_workspace = g_pCompositor->getWorkspaceByID(nextWorkspaceOnMonitorID);
                continue;
            }

            w->m_monitor = pMonitor;

            // additionally, move floating and fs windows manually
            if (w->m_isMapped && !w->isHidden()) {
                if (POLDMON) {
                    if (w->m_isFloating)
                        w->layoutTarget()->setPositionGlobal(w->layoutTarget()->position().translate(-POLDMON->m_position + pMonitor->m_position));

                    if (w->isFullscreen()) {
                        *w->m_realPosition = pMonitor->m_position;
                        *w->m_realSize     = pMonitor->m_size;
                    }
                } else
                    w->layoutTarget()->setPositionGlobal(CBox{Vector2D{
                                                                  (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,
                                                              },
                                                              w->layoutTarget()->position().size()});
            }

            w->updateToplevel();
        }
    }

    if (SWITCHINGISACTIVE && POLDMON == Desktop::focusState()->monitor()) { // if it was active, preserve its' status. If it wasn't, don't.
        Log::logger->log(Log::DEBUG, "moveWorkspaceToMonitor: SWITCHINGISACTIVE, active {} -> {}", pMonitor->activeWorkspaceID(), pWorkspace->m_id);

        if (valid(pMonitor->m_activeWorkspace)) {
            pMonitor->m_activeWorkspace->m_visible = false;
            g_pDesktopAnimationManager->startAnimation(pWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_OUT, false);
        }

        if (*PHIDESPECIALONWORKSPACECHANGE)
            pMonitor->setSpecialWorkspace(nullptr);

        Desktop::focusState()->rawMonitorFocus(pMonitor);

        auto oldWorkspace           = pMonitor->m_activeWorkspace;
        pMonitor->m_activeWorkspace = pWorkspace;

        if (oldWorkspace)
            oldWorkspace->m_events.activeChanged.emit();

        pWorkspace->m_events.activeChanged.emit();

        g_layoutManager->recalculateMonitor(pMonitor);
        g_pHyprRenderer->damageMonitor(pMonitor);

        g_pDesktopAnimationManager->startAnimation(pWorkspace, CDesktopAnimationManager::ANIMATION_TYPE_IN, true, true);
        pWorkspace->m_visible = true;

        if (!noWarpCursor)
            g_pPointerManager->warpTo(pMonitor->m_position + pMonitor->m_transformedSize / 2.F);

        g_pInputManager->sendMotionEventsToFocused();
    }

    // finalize
    if (POLDMON) {
        g_layoutManager->recalculateMonitor(POLDMON);
        if (valid(POLDMON->m_activeWorkspace))
            g_pDesktopAnimationManager->setFullscreenFadeAnimation(POLDMON->m_activeWorkspace,
                                                                   POLDMON->m_activeWorkspace->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN :
                                                                                                                       CDesktopAnimationManager::ANIMATION_TYPE_OUT);
        updateSuspendedStates();
    }

    g_pDesktopAnimationManager->setFullscreenFadeAnimation(
        pWorkspace, pWorkspace->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);
    updateSuspendedStates();

    // event
    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)});

    Event::bus()->m_events.workspace.moveToMonitor.emit(pWorkspace, pMonitor);
}

bool CCompositor::workspaceIDOutOfBounds(const WORKSPACEID& id) {
    WORKSPACEID lowestID  = INT64_MAX;
    WORKSPACEID highestID = INT64_MIN;

    for (auto const& w : getWorkspaces()) {
        if (w->m_isSpecialWorkspace)
            continue;
        lowestID  = std::min(w->m_id, lowestID);
        highestID = std::max(w->m_id, highestID);
    }

    return std::clamp(id, lowestID, highestID) != id;
}

void CCompositor::changeWindowFullscreenModeClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE, const bool ON) {
    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))));
}

// TODO: move fs functions to Desktop::
void CCompositor::setWindowFullscreenInternal(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) {
    if (PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
        setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = MODE, .client = MODE});
    else
        setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = MODE, .client = PWINDOW->m_fullscreenState.client});
}

void CCompositor::setWindowFullscreenClient(const PHLWINDOW PWINDOW, const eFullscreenMode MODE) {
    if (PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
        setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = MODE, .client = MODE});
    else
        setWindowFullscreenState(PWINDOW, Desktop::View::SFullscreenState{.internal = PWINDOW->m_fullscreenState.internal, .client = MODE});
}

void CCompositor::setWindowFullscreenState(const PHLWINDOW PWINDOW, Desktop::View::SFullscreenState state) {
    static auto PDIRECTSCANOUT      = CConfigValue<Hyprlang::INT>("render:direct_scanout");
    static auto PALLOWPINFULLSCREEN = CConfigValue<Hyprlang::INT>("binds:allow_pin_fullscreen");

    if (!validMapped(PWINDOW) || g_pCompositor->m_unsafeState)
        return;

    state.internal = std::clamp(state.internal, sc<eFullscreenMode>(0), FSMODE_MAX);
    state.client   = std::clamp(state.client, sc<eFullscreenMode>(0), FSMODE_MAX);

    const auto PMONITOR   = PWINDOW->m_monitor.lock();
    const auto PWORKSPACE = PWINDOW->m_workspace;

    if (PWINDOW->m_isFloating && PWINDOW->m_fullscreenState.internal == FSMODE_NONE && state.internal != FSMODE_NONE)
        g_pHyprRenderer->damageWindow(PWINDOW);

    if (*PALLOWPINFULLSCREEN && !PWINDOW->m_pinFullscreened && !PWINDOW->isFullscreen() && PWINDOW->m_pinned) {
        PWINDOW->m_pinned          = false;
        PWINDOW->m_pinFullscreened = true;
    }

    if (PWORKSPACE->m_hasFullscreenWindow && !PWINDOW->isFullscreen())
        setWindowFullscreenInternal(PWORKSPACE->getFullscreenWindow(), FSMODE_NONE);

    const bool CHANGEINTERNAL = !PWINDOW->m_pinned && PWINDOW->m_fullscreenState.internal != state.internal;

    if (*PALLOWPINFULLSCREEN && PWINDOW->m_pinFullscreened && PWINDOW->isFullscreen() && !PWINDOW->m_pinned && state.internal == FSMODE_NONE) {
        PWINDOW->m_pinned          = true;
        PWINDOW->m_pinFullscreened = false;
    }

    // TODO: update the state on syncFullscreen changes
    if (!CHANGEINTERNAL && PWINDOW->m_ruleApplicator->syncFullscreen().valueOrDefault())
        return;

    PWINDOW->m_fullscreenState.client = state.client;
    g_pXWaylandManager->setWindowFullscreen(PWINDOW, state.client & FSMODE_FULLSCREEN);

    if (!CHANGEINTERNAL) {
        PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT |
                                                     Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);
        PWINDOW->updateDecorationValues();
        g_layoutManager->recalculateMonitor(PMONITOR);
        return;
    }

    // "Effective mode" is the fullscreen mode according to which a window is rendered.
    // For fullscreen modes `FSMODE_NONE` (0), `FSMODE_MAXIMIZED` (1), and `FSMODE_FULLSCREEN` (2),
    // the effective mode is the same as the fullscreen mode;
    // for fullscreen mode `FSMODE_MAXIMIZED|FSMODE_FULLSCREEN` (a window is maximized then fullscreened),
    // the effective mode is `FSMODE_FULLSCREEN` (2), since the window is rendered as a fullscreen window.
    // But when the latter window exists fullscreen, it will return to `FSMODE_MAXIMIZED`, rather than `FSMODE_NONE`.
    const eFullscreenMode OLD_EFFECTIVE_MODE = sc<eFullscreenMode>(std::bit_floor(sc<uint8_t>(PWINDOW->m_fullscreenState.internal)));
    const eFullscreenMode NEW_EFFECTIVE_MODE = sc<eFullscreenMode>(std::bit_floor(sc<uint8_t>(state.internal)));

    PWORKSPACE->m_fullscreenMode      = NEW_EFFECTIVE_MODE;
    PWORKSPACE->m_hasFullscreenWindow = NEW_EFFECTIVE_MODE != FSMODE_NONE;

    g_layoutManager->fullscreenRequestForTarget(PWINDOW->layoutTarget(), OLD_EFFECTIVE_MODE, NEW_EFFECTIVE_MODE);

    PWINDOW->m_fullscreenState.internal = state.internal;

    g_pEventManager->postEvent(SHyprIPCEvent{.event = "fullscreen", .data = std::to_string(sc<int>(NEW_EFFECTIVE_MODE) != FSMODE_NONE)});
    Event::bus()->m_events.window.fullscreen.emit(PWINDOW);

    PWINDOW->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_FULLSCREEN | Desktop::Rule::RULE_PROP_FULLSCREENSTATE_CLIENT |
                                                 Desktop::Rule::RULE_PROP_FULLSCREENSTATE_INTERNAL | Desktop::Rule::RULE_PROP_ON_WORKSPACE);

    PWINDOW->updateDecorationValues();
    g_layoutManager->recalculateMonitor(PMONITOR);

    // make all windows and layers on the same workspace under the fullscreen window
    for (auto const& w : m_windows) {
        if (w->m_workspace == PWORKSPACE && !w->isFullscreen() && !w->m_fadingOut && !w->m_pinned)
            w->m_createdOverFullscreen = false;
    }
    for (auto const& ls : m_layers) {
        if (ls->m_monitor == PMONITOR)
            ls->m_aboveFullscreen = false;
    }

    g_pDesktopAnimationManager->setFullscreenFadeAnimation(
        PWORKSPACE, PWORKSPACE->m_hasFullscreenWindow ? CDesktopAnimationManager::ANIMATION_TYPE_IN : CDesktopAnimationManager::ANIMATION_TYPE_OUT);

    PWINDOW->sendWindowSize(true);

    PWORKSPACE->forceReportSizesToWindows();

    g_pInputManager->recheckIdleInhibitorStatus();

    // 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.
    // ignore if DS is disabled.
    if (*PDIRECTSCANOUT == 1 || (*PDIRECTSCANOUT == 2 && PWINDOW->getContentType() == CONTENT_TYPE_GAME)) {
        auto surf = PWINDOW->getSolitaryResource();
        if (surf)
            g_pHyprRenderer->setSurfaceScanoutMode(surf, NEW_EFFECTIVE_MODE != FSMODE_NONE ? PMONITOR->m_self.lock() : nullptr);
    }

    g_pConfigManager->ensureVRR(PMONITOR);
}

PHLWINDOW CCompositor::getX11Parent(PHLWINDOW pWindow) {
    if (!pWindow->m_isX11)
        return nullptr;

    for (auto const& w : m_windows) {
        if (!w->m_isX11)
            continue;

        if (w->m_xwaylandSurface == pWindow->m_xwaylandSurface->m_parent)
            return w;
    }

    return nullptr;
}

void CCompositor::scheduleFrameForMonitor(PHLMONITOR pMonitor, IOutput::scheduleFrameReason reason) {
    if ((m_aqBackend->hasSession() && !m_aqBackend->session->active) || !m_sessionActive)
        return;

    if (!pMonitor->m_enabled)
        return;

    if (pMonitor->m_renderingActive)
        pMonitor->m_pendingFrame = true;

    pMonitor->m_output->scheduleFrame(reason);
}

PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp_) {
    auto regexp = trim(regexp_);

    if (regexp.starts_with("active"))
        return Desktop::focusState()->window();
    else if (regexp.starts_with("floating") || regexp.starts_with("tiled")) {
        // first floating on the current ws
        if (!Desktop::focusState()->window())
            return nullptr;

        const bool FLOAT = regexp.starts_with("floating");

        for (auto const& w : m_windows) {
            if (!w->m_isMapped || w->m_isFloating != FLOAT || w->m_workspace != Desktop::focusState()->window()->m_workspace || w->isHidden())
                continue;

            return w;
        }

        return nullptr;
    }

    eFocusWindowMode mode = MODE_CLASS_REGEX;

    std::string      regexCheck;
    std::string      matchCheck;
    if (regexp.starts_with("class:")) {
        regexCheck = regexp.substr(6);
    } else if (regexp.starts_with("initialclass:")) {
        mode       = MODE_INITIAL_CLASS_REGEX;
        regexCheck = regexp.substr(13);
    } else if (regexp.starts_with("title:")) {
        mode       = MODE_TITLE_REGEX;
        regexCheck = regexp.substr(6);
    } else if (regexp.starts_with("initialtitle:")) {
        mode       = MODE_INITIAL_TITLE_REGEX;
        regexCheck = regexp.substr(13);
    } else if (regexp.starts_with("tag:")) {
        mode       = MODE_TAG_REGEX;
        regexCheck = regexp.substr(4);
    } else if (regexp.starts_with("address:")) {
        mode       = MODE_ADDRESS;
        matchCheck = regexp.substr(8);
    } else if (regexp.starts_with("pid:")) {
        mode       = MODE_PID;
        matchCheck = regexp.substr(4);
    }

    for (auto const& w : g_pCompositor->m_windows) {
        if (!w->m_isMapped)
            continue;

        switch (mode) {
            case MODE_CLASS_REGEX: {
                const auto windowClass = w->m_class;
                if (!RE2::FullMatch(windowClass, regexCheck))
                    continue;
                break;
            }
            case MODE_INITIAL_CLASS_REGEX: {
                const auto initialWindowClass = w->m_initialClass;
                if (!RE2::FullMatch(initialWindowClass, regexCheck))
                    continue;
                break;
            }
            case MODE_TITLE_REGEX: {
                const auto windowTitle = w->m_title;
                if (!RE2::FullMatch(windowTitle, regexCheck))
                    continue;
                break;
            }
            case MODE_INITIAL_TITLE_REGEX: {
                const auto initialWindowTitle = w->m_initialTitle;
                if (!RE2::FullMatch(initialWindowTitle, regexCheck))
                    continue;
                break;
            }
            case MODE_TAG_REGEX: {
                bool tagMatched = false;
                for (auto const& t : w->m_ruleApplicator->m_tagKeeper.getTags()) {
                    if (RE2::FullMatch(t, regexCheck)) {
                        tagMatched = true;
                        break;
                    }
                }
                if (!tagMatched)
                    continue;
                break;
            }
            case MODE_ADDRESS: {
                std::string addr = std::format("0x{:x}", rc<uintptr_t>(w.get()));
                if (matchCheck != addr)
                    continue;
                break;
            }
            case MODE_PID: {
                std::string pid = std::format("{}", w->getPID());
                if (matchCheck != pid)
                    continue;
                break;
            }
            default: break;
        }

        return w;
    }

    return nullptr;
}

void CCompositor::warpCursorTo(const Vector2D& pos, bool force) {

    // warpCursorTo should only be used for warps that
    // should be disabled with no_warps

    static auto PNOWARPS = CConfigValue<Hyprlang::INT>("cursor:no_warps");

    if (*PNOWARPS && !force) {
        const auto PMONITORNEW = getMonitorFromVector(pos);
        Desktop::focusState()->rawMonitorFocus(PMONITORNEW);
        return;
    }

    g_pPointerManager->warpTo(pos);

    const auto PMONITORNEW = getMonitorFromVector(pos);
    Desktop::focusState()->rawMonitorFocus(PMONITORNEW);
}

void CCompositor::closeWindow(PHLWINDOW pWindow) {
    if (pWindow && validMapped(pWindow))
        g_pXWaylandManager->sendCloseWindow(pWindow);
}

PHLLS CCompositor::getLayerSurfaceFromSurface(SP<CWLSurfaceResource> pSurface) {
    std::pair<SP<CWLSurfaceResource>, bool> result = {pSurface, false};

    for (auto const& ls : m_layers) {
        if (!ls->aliveAndVisible())
            continue;

        if (ls->m_layerSurface->m_surface == pSurface)
            return ls;

        ls->m_layerSurface->m_surface->breadthfirst(
            [&result](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) {
                if (surf == result.first) {
                    result.second = true;
                    return;
                }
            },
            nullptr);

        if (result.second)
            return ls;
    }

    return nullptr;
}

// returns a delta
Vector2D CCompositor::parseWindowVectorArgsRelative(const std::string& args, const Vector2D& relativeTo) {
    if (!args.contains(' ') && !args.contains('\t'))
        return relativeTo;

    const auto  PMONITOR = Desktop::focusState()->monitor();

    bool        xIsPercent = false;
    bool        yIsPercent = false;
    bool        isExact    = false;

    CVarList    varList(args, 0, 's', true);
    std::string x = varList[0];
    std::string y = varList[1];

    if (x == "exact") {
        x       = varList[1];
        y       = varList[2];
        isExact = true;
    }

    if (x.contains('%')) {
        xIsPercent = true;
        x          = x.substr(0, x.length() - 1);
    }

    if (y.contains('%')) {
        yIsPercent = true;
        y          = y.substr(0, y.length() - 1);
    }

    if (!isNumber(x) || !isNumber(y)) {
        Log::logger->log(Log::ERR, "parseWindowVectorArgsRelative: args not numbers");
        return relativeTo;
    }

    int X = 0;
    int Y = 0;

    if (isExact) {
        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);
    } else {
        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;
    }

    return Vector2D(X, Y);
}

PHLWORKSPACE CCompositor::createNewWorkspace(const WORKSPACEID& id, const MONITORID& monid, const std::string& name, bool isEmpty) {
    const auto NAME  = name.empty() ? std::to_string(id) : name;
    auto       monID = monid;

    // check if bound
    if (const auto PMONITOR = g_pConfigManager->getBoundMonitorForWS(NAME); PMONITOR)
        monID = PMONITOR->m_id;

    const bool SPECIAL = id >= SPECIAL_WORKSPACE_START && id <= -2;

    const auto PMONITOR = getMonitorFromID(monID);
    if (!PMONITOR) {
        Log::logger->log(Log::ERR, "BUG THIS: No pMonitor for new workspace in createNewWorkspace");
        return nullptr;
    }

    const auto PWORKSPACE = CWorkspace::create(id, PMONITOR, NAME, SPECIAL, isEmpty);

    PWORKSPACE->m_alpha->setValueAndWarp(0);

    return PWORKSPACE;
}

bool CCompositor::isWorkspaceSpecial(const WORKSPACEID& id) {
    return id >= SPECIAL_WORKSPACE_START && id <= -2;
}

WORKSPACEID CCompositor::getNewSpecialID() {
    WORKSPACEID highest = SPECIAL_WORKSPACE_START;
    for (auto const& ws : getWorkspaces()) {
        if (ws->m_isSpecialWorkspace && ws->m_id > highest)
            highest = ws->m_id;
    }

    return highest + 1;
}

void CCompositor::registerWorkspace(PHLWORKSPACE w) {
    m_workspaces.emplace_back(w);
    w->m_events.destroy.listenStatic([this, weak = PHLWORKSPACEREF{w}] { std::erase(m_workspaces, weak); });
}

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;
}

void CCompositor::performUserChecks() {
    static auto PNOCHECKXDG      = CConfigValue<Hyprlang::INT>("misc:disable_xdg_env_checks");
    static auto PNOCHECKGUIUTILS = CConfigValue<Hyprlang::INT>("misc:disable_hyprland_guiutils_check");
    static auto PNOWATCHDOG      = CConfigValue<Hyprlang::INT>("misc:disable_watchdog_warning");

    if (!*PNOCHECKXDG) {
        const auto CURRENT_DESKTOP_ENV = getenv("XDG_CURRENT_DESKTOP");
        if (!CURRENT_DESKTOP_ENV || std::string{CURRENT_DESKTOP_ENV} != "Hyprland") {
            g_pHyprNotificationOverlay->addNotification(
                I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_EXTERNAL_XDG_DESKTOP, {{"value", CURRENT_DESKTOP_ENV ? CURRENT_DESKTOP_ENV : "unset"}}), CHyprColor{}, 15000,
                ICON_WARNING);
        }
    }

    if (!*PNOCHECKGUIUTILS) {
        if (!NFsUtils::executableExistsInPath("hyprland-dialog"))
            g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_NO_GUIUTILS), CHyprColor{}, 15000, ICON_WARNING);
    }

    if (g_pHyprOpenGL->m_failedAssetsNo > 0) {
        g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_FAILED_ASSETS, {{"count", std::to_string(g_pHyprOpenGL->m_failedAssetsNo)}}),
                                                    CHyprColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_ERROR);
    }

    if (!m_watchdogWriteFd.isValid() && !*PNOWATCHDOG)
        g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_NO_WATCHDOG), CHyprColor{1.0, 0.1, 0.1, 1.0}, 15000, ICON_WARNING);

    if (m_safeMode)
        openSafeModeBox();
}

void CCompositor::openSafeModeBox() {
    const auto OPT_LOAD = I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_BUTTON_LOAD_CONFIG);
    const auto OPT_OPEN = I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_BUTTON_OPEN_CRASH_REPORT_DIR);
    const auto OPT_OK   = I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_BUTTON_UNDERSTOOD);

    auto       box = CAsyncDialogBox::create(I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_TITLE), I18n::i18nEngine()->localize(I18n::TXT_KEY_SAFE_MODE_DESCRIPTION),
                                             {
                                           OPT_LOAD,
                                           OPT_OPEN,
                                           OPT_OK,
                                       });

    box->open()->then([OPT_LOAD, OPT_OK, OPT_OPEN, this](SP<CPromiseResult<std::string>> result) {
        if (result->hasError())
            return;

        const auto RES = result->result();

        if (RES.starts_with(OPT_LOAD)) {
            m_safeMode = false;
            g_pConfigManager->reload();
        } else if (RES.starts_with(OPT_OPEN)) {
            std::string reportPath;
            const auto  HOME       = getenv("HOME");
            const auto  CACHE_HOME = getenv("XDG_CACHE_HOME");

            if (CACHE_HOME && CACHE_HOME[0] != '\0') {
                reportPath += CACHE_HOME;
                reportPath += "/hyprland/";
            } else if (HOME && HOME[0] != '\0') {
                reportPath += HOME;
                reportPath += "/.cache/hyprland/";
            }
            Hyprutils::OS::CProcess proc("xdg-open", {reportPath});

            proc.runAsync();

            // reopen
            openSafeModeBox();
        }
    });
}

void CCompositor::moveWindowToWorkspaceSafe(PHLWINDOW pWindow, PHLWORKSPACE pWorkspace) {
    if (!pWindow || !pWorkspace)
        return;

    if (pWindow->m_pinned && pWorkspace->m_isSpecialWorkspace)
        return;

    if (pWindow->m_workspace == pWorkspace)
        return;

    const bool FULLSCREEN     = pWindow->isFullscreen();
    const auto FULLSCREENMODE = pWindow->m_fullscreenState.internal;
    const bool WASVISIBLE     = pWindow->m_workspace && pWindow->m_workspace->isVisible();

    if (FULLSCREEN)
        setWindowFullscreenInternal(pWindow, FSMODE_NONE);

    const PHLWINDOW pFirstWindowOnWorkspace   = pWorkspace->getFirstWindow();
    const int       visibleWindowsOnWorkspace = pWorkspace->getWindows(true, std::nullopt, true);
    const auto      POSTOMON                  = pWindow->m_realPosition->goal() - (pWindow->m_monitor ? pWindow->m_monitor->m_position : Vector2D{});
    const auto      PWORKSPACEMONITOR         = pWorkspace->m_monitor.lock();

    pWindow->moveToWorkspace(pWorkspace);
    pWindow->m_monitor = pWorkspace->m_monitor;

    static auto PGROUPONMOVETOWORKSPACE = CConfigValue<Hyprlang::INT>("group:group_on_movetoworkspace");
    if (*PGROUPONMOVETOWORKSPACE && visibleWindowsOnWorkspace == 1 && pFirstWindowOnWorkspace && pFirstWindowOnWorkspace != pWindow && pFirstWindowOnWorkspace->m_group &&
        pWindow->canBeGroupedInto(pFirstWindowOnWorkspace->m_group)) {
        pFirstWindowOnWorkspace->m_group->add(pWindow);
    } else {
        if (pWindow->m_isFloating)
            pWindow->layoutTarget()->setPositionGlobal(CBox{POSTOMON + PWORKSPACEMONITOR->m_position, pWindow->layoutTarget()->position().size()});
    }

    pWindow->updateToplevel();
    pWindow->m_ruleApplicator->propertiesChanged(Desktop::Rule::RULE_PROP_ON_WORKSPACE);
    pWindow->uncacheWindowDecos();

    if (pWindow->m_group)
        pWindow->m_group->updateWorkspace(pWorkspace);

    g_layoutManager->newTarget(pWindow->layoutTarget(), pWorkspace->m_space);

    if (FULLSCREEN)
        setWindowFullscreenInternal(pWindow, FULLSCREENMODE);

    pWorkspace->updateWindows();
    if (pWindow->m_workspace)
        pWindow->m_workspace->updateWindows();
    g_pCompositor->updateSuspendedStates();

    if (!WASVISIBLE && pWindow->m_workspace && pWindow->m_workspace->isVisible()) {
        pWindow->m_movingFromWorkspaceAlpha->setValueAndWarp(0.F);
        *pWindow->m_movingFromWorkspaceAlpha = 1.F;
    }
}

PHLWINDOW CCompositor::getForceFocus() {
    for (auto const& w : m_windows) {
        if (!w->m_isMapped || w->isHidden() || !w->m_workspace || !w->m_workspace->isVisible())
            continue;

        if (!w->m_ruleApplicator->stayFocused().valueOrDefault())
            continue;

        return w;
    }

    return nullptr;
}

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()) {
            Log::logger->log(Log::ERR, "Monitor {}: detected overlap with layout", m->m_name);
            g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_INVALID_MONITOR_LAYOUT, {{"name", m->m_name}}), CHyprColor{}, 15000,
                                                        ICON_WARNING);

            break;
        }

        monitorRegion.add(m->logicalBox());
    }
}

void CCompositor::arrangeMonitors() {
    static auto* const      PXWLFORCESCALEZERO = rc<Hyprlang::INT* const*>(g_pConfigManager->getConfigValuePtr("xwayland:force_zero_scaling"));

    std::vector<PHLMONITOR> toArrange(m_monitors.begin(), m_monitors.end());
    std::vector<PHLMONITOR> arranged;
    arranged.reserve(toArrange.size());

    Log::logger->log(Log::DEBUG, "arrangeMonitors: {} to arrange", toArrange.size());

    for (auto it = toArrange.begin(); it != toArrange.end();) {
        auto m = *it;

        if (m->m_activeMonitorRule.offset != Vector2D{-INT32_MAX, -INT32_MAX}) {
            // explicit.
            Log::logger->log(Log::DEBUG, "arrangeMonitors: {} explicit {:j}", m->m_name, m->m_activeMonitorRule.offset);

            m->moveTo(m->m_activeMonitorRule.offset);
            arranged.push_back(m);
            it = toArrange.erase(it);

            if (it == toArrange.end())
                break;

            continue;
        }

        ++it;
    }

    // Variables to store the max and min values of monitors on each axis.
    int  maxXOffsetRight = 0;
    int  maxXOffsetLeft  = 0;
    int  maxYOffsetUp    = 0;
    int  maxYOffsetDown  = 0;

    auto recalcMaxOffsets = [&]() {
        maxXOffsetRight = 0;
        maxXOffsetLeft  = 0;
        maxYOffsetUp    = 0;
        maxYOffsetDown  = 0;

        // Finds the max and min values of explicitly placed monitors.
        for (auto const& m : arranged) {
            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);
        }
    };

    // Iterates through all non-explicitly placed monitors.
    for (auto const& m : toArrange) {
        recalcMaxOffsets();

        // Moves the monitor to their appropriate position on the x/y axis and
        // increments/decrements the corresponding max offset.
        Vector2D newPosition = {0, 0};
        switch (m->m_activeMonitorRule.autoDir) {
            case eAutoDirs::DIR_AUTO_UP: newPosition.y = maxYOffsetUp - m->m_size.y; break;
            case eAutoDirs::DIR_AUTO_DOWN: newPosition.y = maxYOffsetDown; break;
            case eAutoDirs::DIR_AUTO_LEFT: newPosition.x = maxXOffsetLeft - m->m_size.x; break;
            case eAutoDirs::DIR_AUTO_RIGHT:
            case eAutoDirs::DIR_AUTO_NONE: newPosition.x = maxXOffsetRight; break;
            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;
            }
            default: UNREACHABLE();
        }
        Log::logger->log(Log::DEBUG, "arrangeMonitors: {} auto {:j}", m->m_name, m->m_position);
        m->moveTo(newPosition);
        arranged.emplace_back(m);
    }

    // reset maxXOffsetRight (reuse)
    // and set xwayland positions aka auto for all
    maxXOffsetRight = 0;
    for (auto const& m : m_monitors) {
        Log::logger->log(Log::DEBUG, "arrangeMonitors: {} xwayland [{}, {}]", m->m_name, maxXOffsetRight, 0);
        m->m_xwaylandPosition = {maxXOffsetRight, 0};
        maxXOffsetRight += (*PXWLFORCESCALEZERO ? m->m_transformedSize.x : m->m_size.x);

        if (*PXWLFORCESCALEZERO)
            m->m_xwaylandScale = m->m_scale;
        else
            m->m_xwaylandScale = 1.f;
    }

    PROTO::xdgOutput->updateAllOutputs();
    Event::bus()->m_events.monitor.layoutChanged.emit();

#ifndef NO_XWAYLAND
    const auto box = g_pCompositor->calculateX11WorkArea();
    if (g_pXWayland && g_pXWayland->m_wm) {
        if (box)
            g_pXWayland->m_wm->updateWorkArea(box->x, box->y, box->w, box->h);
        else
            g_pXWayland->m_wm->updateWorkArea(0, 0, 0, 0);
    }

#endif
}

void CCompositor::enterUnsafeState() {
    if (m_unsafeState)
        return;

    Log::logger->log(Log::DEBUG, "Entering unsafe state");

    if (!m_unsafeOutput->m_enabled)
        m_unsafeOutput->onConnect(false);

    m_unsafeState = true;

    Desktop::focusState()->rawMonitorFocus(m_unsafeOutput.lock());
}

void CCompositor::leaveUnsafeState() {
    if (!m_unsafeState)
        return;

    Log::logger->log(Log::DEBUG, "Leaving unsafe state");

    m_unsafeState = false;

    PHLMONITOR pNewMonitor = nullptr;
    for (auto const& pMonitor : m_monitors) {
        if (pMonitor->m_output != m_unsafeOutput->m_output) {
            pNewMonitor = pMonitor;
            break;
        }
    }

    RASSERT(pNewMonitor, "Tried to leave unsafe without a monitor");

    if (m_unsafeOutput->m_enabled)
        m_unsafeOutput->onDisconnect();

    for (auto const& m : m_monitors) {
        scheduleFrameForMonitor(m);
    }
}

void CCompositor::setPreferredScaleForSurface(SP<CWLSurfaceResource> pSurface, double scale) {
    PROTO::fractional->sendScale(pSurface, scale);
    pSurface->sendPreferredScale(std::ceil(scale));

    const auto PSURFACE = Desktop::View::CWLSurface::fromResource(pSurface);
    if (!PSURFACE) {
        Log::logger->log(Log::WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredScaleForSurface", rc<uintptr_t>(pSurface.get()));
        return;
    }

    PSURFACE->m_lastScaleFloat = scale;
    PSURFACE->m_lastScaleInt   = sc<int32_t>(std::ceil(scale));
}

void CCompositor::setPreferredTransformForSurface(SP<CWLSurfaceResource> pSurface, wl_output_transform transform) {
    pSurface->sendPreferredTransform(transform);

    const auto PSURFACE = Desktop::View::CWLSurface::fromResource(pSurface);
    if (!PSURFACE) {
        Log::logger->log(Log::WARN, "Orphaned CWLSurfaceResource {:x} in setPreferredTransformForSurface", rc<uintptr_t>(pSurface.get()));
        return;
    }

    PSURFACE->m_lastTransform = transform;
}

void CCompositor::updateSuspendedStates() {
    for (auto const& w : g_pCompositor->m_windows) {
        if (!w->m_isMapped)
            continue;

        w->setSuspended(w->isHidden() || !w->m_workspace || !w->m_workspace->isVisible());
    }
}

static void checkDefaultCursorWarp(PHLMONITOR monitor) {
    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) {
        if (*PCURSORMONITOR == monitor->m_name) {
            cursorDefaultDone = true;
            g_pCompositor->warpCursorTo(POS, true);
            g_pInputManager->refocus();
            return;
        }
    }

    // modechange happened check if cursor is on that monitor and warp it to middle to not place it out of bounds if resolution changed.
    if (g_pCompositor->getMonitorFromCursor() == monitor) {
        g_pCompositor->warpCursorTo(POS, true);
        g_pInputManager->refocus();
    }
}

void CCompositor::onNewMonitor(SP<Aquamarine::IOutput> output) {
    // add it to real
    auto PNEWMONITOR = g_pCompositor->m_realMonitors.emplace_back(makeShared<CMonitor>(output));
    if (std::string("HEADLESS-1") == output->name) {
        g_pCompositor->m_unsafeOutput = PNEWMONITOR;
        output->name                  = "FALLBACK"; // we are allowed to do this :)
    }

    Log::logger->log(Log::DEBUG, "New output with name {}", output->name);

    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;

    Event::bus()->m_events.monitor.newMon.emit(PNEWMONITOR);

    if (!FALLBACK)
        PNEWMONITOR->onConnect(false);

    if (!PNEWMONITOR->m_enabled || FALLBACK)
        return;

    // ready to process if we have a real monitor

    if ((!g_pHyprRenderer->m_mostHzMonitor || PNEWMONITOR->m_refreshRate > g_pHyprRenderer->m_mostHzMonitor->m_refreshRate) && PNEWMONITOR->m_enabled)
        g_pHyprRenderer->m_mostHzMonitor = PNEWMONITOR;

    g_pCompositor->m_readyToProcess = true;

    g_pConfigManager->m_wantsMonitorReload = true;
    g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR, IOutput::AQ_SCHEDULE_NEW_MONITOR);

    checkDefaultCursorWarp(PNEWMONITOR);

    for (auto const& w : g_pCompositor->m_windows) {
        if (w->m_monitor == PNEWMONITOR) {
            w->m_lastSurfaceMonitorID = MONITOR_INVALID;
            w->updateSurfaceScaleTransformDetails();
        }
    }

    g_pHyprRenderer->damageMonitor(PNEWMONITOR);
    PNEWMONITOR->m_frameScheduler->onFrame();

    if (PROTO::colorManagement && shouldChangePreferredImageDescription()) {
        Log::logger->log(Log::ERR, "FIXME: color management protocol is enabled, need a preferred image description id");
        PROTO::colorManagement->onImagePreferredChanged(0);
    }
}

PImageDescription CCompositor::getPreferredImageDescription() {
    if (!PROTO::colorManagement) {
        Log::logger->log(Log::ERR, "FIXME: color management protocol is not enabled, returning empty image description");
        return DEFAULT_IMAGE_DESCRIPTION;
    }
    Log::logger->log(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
    return m_monitors.size() == 1 ? m_monitors[0]->m_imageDescription : CImageDescription::from(SImageDescription{.primaries = NColorPrimaries::BT709});
}

PImageDescription CCompositor::getHDRImageDescription() {
    if (!PROTO::colorManagement) {
        Log::logger->log(Log::ERR, "FIXME: color management protocol is not enabled, returning empty image description");
        return DEFAULT_IMAGE_DESCRIPTION;
    }

    return m_monitors.size() == 1 && m_monitors[0]->m_output && m_monitors[0]->m_output->parsedEDID.hdrMetadata.has_value() ?
        CImageDescription::from(SImageDescription{
            .transferFunction    = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
            .primariesNameSet    = true,
            .primariesNamed      = NColorManagement::CM_PRIMARIES_BT2020,
            .primaries           = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
            .masteringPrimaries  = m_monitors[0]->getMasteringPrimaries(),
            .luminances          = {.min = m_monitors[0]->minLuminance(HDR_MIN_LUMINANCE), .max = m_monitors[0]->maxLuminance(HDR_MAX_LUMINANCE), .reference = HDR_REF_LUMINANCE},
            .masteringLuminances = m_monitors[0]->getMasteringLuminances(),
            .maxCLL              = m_monitors[0]->maxCLL(),
            .maxFALL             = m_monitors[0]->maxFALL()}) :
        DEFAULT_HDR_IMAGE_DESCRIPTION;
}

bool CCompositor::shouldChangePreferredImageDescription() {
    Log::logger->log(Log::WARN, "FIXME: color management protocol is enabled and outputs changed, check preferred image description changes");
    return false;
}

void CCompositor::ensurePersistentWorkspacesPresent(const std::vector<SWorkspaceRule>& rules, PHLWORKSPACE pWorkspace) {
    if (!Desktop::focusState()->monitor())
        return;

    std::vector<PHLWORKSPACE> persistentFound;

    for (const auto& rule : rules) {
        if (!rule.isPersistent)
            continue;

        PHLWORKSPACE PWORKSPACE = nullptr;
        if (pWorkspace) {
            if (pWorkspace->matchesStaticSelector(rule.workspaceString))
                PWORKSPACE = pWorkspace;
            else
                continue;
        }

        auto PMONITOR = getMonitorFromString(rule.monitor);

        if (!rule.monitor.empty() && !PMONITOR)
            continue; // don't do anything yet, as the monitor is not yet present.

        if (!PWORKSPACE) {
            WORKSPACEID id     = rule.workspaceId;
            std::string wsname = rule.workspaceName;

            if (id == WORKSPACE_INVALID) {
                const auto R = getWorkspaceIDNameFromString(rule.workspaceString);
                id           = R.id;
                wsname       = R.name;
            }

            if (id == WORKSPACE_INVALID) {
                Log::logger->log(Log::ERR, "ensurePersistentWorkspacesPresent: couldn't resolve id for workspace {}", rule.workspaceString);
                continue;
            }
            PWORKSPACE = getWorkspaceByID(id);
            if (!PMONITOR)
                PMONITOR = Desktop::focusState()->monitor();

            if (!PWORKSPACE)
                PWORKSPACE = createNewWorkspace(id, PMONITOR->m_id, wsname, false);
        }

        if (!PMONITOR) {
            Log::logger->log(Log::ERR, "ensurePersistentWorkspacesPresent: couldn't resolve monitor for {}, skipping", rule.monitor);
            continue;
        }

        if (PWORKSPACE)
            PWORKSPACE->setPersistent(true);

        if (!pWorkspace)
            persistentFound.emplace_back(PWORKSPACE);

        if (PWORKSPACE) {
            if (PWORKSPACE->m_monitor == PMONITOR) {
                Log::logger->log(Log::DEBUG, "ensurePersistentWorkspacesPresent: workspace persistent {} already on {}", rule.workspaceString, PMONITOR->m_name);

                continue;
            }

            Log::logger->log(Log::DEBUG, "ensurePersistentWorkspacesPresent: workspace persistent {} not on {}, moving", rule.workspaceString, PMONITOR->m_name);
            moveWorkspaceToMonitor(PWORKSPACE, PMONITOR);
            continue;
        }
    }

    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);
        }
    }
}

void CCompositor::ensureWorkspacesOnAssignedMonitors() {
    for (auto const& ws : getWorkspacesCopy()) {
        if (!valid(ws) || ws->m_isSpecialWorkspace)
            continue;

        const auto RULE = g_pConfigManager->getWorkspaceRuleFor(ws);
        if (RULE.monitor.empty())
            continue;

        const auto PMONITOR = getMonitorFromString(RULE.monitor);
        if (!PMONITOR)
            continue;

        if (ws->m_monitor == PMONITOR)
            continue;

        Log::logger->log(Log::DEBUG, "ensureWorkspacesOnAssignedMonitors: moving workspace {} to {}", ws->m_name, PMONITOR->m_name);
        moveWorkspaceToMonitor(ws, PMONITOR, true);
    }
}

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;
}

bool CCompositor::isVRRActiveOnAnyMonitor() const {
    return std::ranges::any_of(m_monitors, [](const PHLMONITOR& m) { return m->m_vrrActive; });
}
