#include "Monitor.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "../macros.hpp"
#include "SharedDefs.hpp"
#include "../helpers/TransferFunction.hpp"
#include "../helpers/math/Math.hpp"
#include "../protocols/ColorManagement.hpp"
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
#include "../config/shared/monitor/MonitorRuleManager.hpp"
#include "../config/shared/workspace/WorkspaceRuleManager.hpp"
#include "../config/shared/animation/AnimationTree.hpp"
#include "../protocols/GammaControl.hpp"
#include "../protocols/LayerShell.hpp"
#include "../protocols/PresentationTime.hpp"
#include "../protocols/DRMLease.hpp"
#include "../protocols/DRMSyncobj.hpp"
#include "../protocols/core/Output.hpp"
#include "../protocols/Screencopy.hpp"
#include "../protocols/ToplevelExport.hpp"
#include "../pointer/PointerManager.hpp"
#include "../pointer/PointerController.hpp"
#include "../managers/eventLoop/EventLoopManager.hpp"
#include "../protocols/core/Compositor.hpp"
#include "../protocols/core/DataDevice.hpp"
#include "../render/Renderer.hpp"
#include "../ipc/s2/S2.hpp"
#include "../managers/screenshare/ScreenshareManager.hpp"
#include "../animation/AnimationManager.hpp"
#include "../animation/WorkspaceAnimationController.hpp"
#include "../managers/input/InputManager.hpp"
#include "../managers/fullscreen/FullscreenController.hpp"
#include "../errorOverlay/Overlay.hpp"
#include "../layout/LayoutManager.hpp"
#include "../layout/space/Space.hpp"
#include "../layout/algorithm/Algorithm.hpp"
#include "../i18n/Engine.hpp"
#include "../helpers/cm/ColorManagement.hpp"
#include "../state/MonitorState.hpp"
#include "../state/MonitorLayoutController.hpp"
#include "../state/WorkspacePlacementController.hpp"
#include "../state/WorkspaceState.hpp"
#include "../helpers/time/Time.hpp"
#include "../desktop/view/LayerSurface.hpp"
#include "../desktop/state/GlobalWindowController.hpp"
#include "../desktop/state/FocusState.hpp"
#include "../desktop/state/FadingOutState.hpp"
#include "../event/EventBus.hpp"
#include "../helpers/Drm.hpp"
#include "MonitorFrameScheduler.hpp"
#include <aquamarine/output/Output.hpp>
#include "debug/log/Logger.hpp"
#include "notification/NotificationOverlay.hpp"
#include "MonitorFrameScheduler.hpp"
#include <hyprutils/memory/UniquePtr.hpp>
#include <hyprutils/string/String.hpp>
#include <hyprutils/utils/ScopeGuard.hpp>
#include <cstring>
#include <climits>
#include <optional>
#include <ranges>
#include <vector>
#include <algorithm>

using namespace Hyprutils::String;
using namespace Hyprutils::Utils;
using namespace Hyprutils::OS;
using enum NContentType::eContentType;
using namespace NColorManagement;
using namespace Desktop::View;
using namespace Render::GL;
using namespace Monitor;

constexpr const char* drmFormatToString(uint32_t drmFormat) {
    switch (drmFormat) {
        case DRM_FORMAT_XRGB2101010: return "DRM_FORMAT_XRGB2101010";
        case DRM_FORMAT_XBGR2101010: return "DRM_FORMAT_XBGR2101010";
        case DRM_FORMAT_XRGB8888: return "DRM_FORMAT_XRGB8888";
        case DRM_FORMAT_XBGR8888: return "DRM_FORMAT_XBGR8888";
        default: return "Invalid";
    }
}

CMonitor::CMonitor(SP<Aquamarine::IOutput> output_) : m_name(output_->name), m_state(this), m_output(output_), m_imageDescription(getDefaultImageDescription()) {
    Animation::mgr()->createAnimation(0.f, m_specialFade, Config::animationTree()->getAnimationPropertyConfig("specialWorkspaceIn"), AVARDAMAGE_NONE);
    m_specialFade->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); });
    Animation::mgr()->createAnimation(0.f, m_specialDim, Config::animationTree()->getAnimationPropertyConfig("specialWorkspaceIn"), AVARDAMAGE_NONE);
    m_specialDim->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); });
    Animation::mgr()->createAnimation(0.f, m_specialBlur, Config::animationTree()->getAnimationPropertyConfig("specialWorkspaceIn"), AVARDAMAGE_NONE);
    m_specialBlur->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); });
    static auto PZOOMFACTOR = CConfigValue<Config::FLOAT>("cursor:zoom_factor");
    Animation::mgr()->createAnimation(*PZOOMFACTOR, m_cursorZoom, Config::animationTree()->getAnimationPropertyConfig("zoomFactor"), AVARDAMAGE_NONE);
    m_cursorZoom->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); });
    Animation::mgr()->createAnimation(0.F, m_zoomAnimProgress, Config::animationTree()->getAnimationPropertyConfig("monitorAdded"), AVARDAMAGE_NONE);
    m_zoomAnimProgress->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); });
    Animation::mgr()->createAnimation(0.F, m_backgroundOpacity, Config::animationTree()->getAnimationPropertyConfig("monitorAdded"), AVARDAMAGE_NONE);
    m_backgroundOpacity->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); });
    Animation::mgr()->createAnimation(0.F, m_dpmsBlackOpacity, Config::animationTree()->getAnimationPropertyConfig("fadeDpms"), AVARDAMAGE_NONE);
    m_dpmsBlackOpacity->setUpdateCallback([this](auto) { g_pHyprRenderer->damageMonitor(m_self.lock()); });
}

CMonitor::~CMonitor() {
    m_events.destroy.emit();
    if (g_pHyprRenderer && g_pHyprRenderer->glBackend())
        g_pHyprRenderer->glBackend()->destroyMonitorResources(m_self);
}

void CMonitor::onConnect(bool noRule) {
    Event::bus()->m_events.monitor.preAdded.emit(m_self.lock());
    CScopeGuard x = {[]() { State::monitorLayoutController()->arrange(); }};

    m_zoomAnimProgress->setValueAndWarp(0.F);
    m_zoomAnimFrameCounter = 0;

    g_pEventLoopManager->doLater([] {
        State::workspacePlacementController()->ensurePersistentWorkspacesPresent(
            nullptr, [](PHLWORKSPACE ws, PHLMONITOR mon, bool noWarp) { State::workspacePlacementController()->moveWorkspaceToMonitor(ws, mon, noWarp); });
        State::workspacePlacementController()->ensureWorkspacesOnAssignedMonitors(
            [](PHLWORKSPACE ws, PHLMONITOR mon, bool noWarp) { State::workspacePlacementController()->moveWorkspaceToMonitor(ws, mon, noWarp); });
    });

    m_listeners.frame      = m_output->events.frame.listen([this] {
        if (m_frameScheduler)
            m_frameScheduler->onFrame();
    });
    m_listeners.commit     = m_output->events.commit.listen([this] {
        m_events.commit.emit();

        // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER
        if (true && Screenshare::mgr())
            Screenshare::mgr()->onOutputCommit(m_self.lock());
    });
    m_listeners.needsFrame = m_output->events.needsFrame.listen([this] { scheduleFrame(Aquamarine::IOutput::AQ_SCHEDULE_NEEDS_FRAME); });

    m_listeners.presented = m_output->events.present.listen([this](const Aquamarine::IOutput::SPresentEvent& event) {
        if (m_pendingDpmsAnimation) {
            m_pendingDpmsAnimationCounter++;
            // we give ourselves 5 frames of a buffer. The first presentation event still doesn't usually say that we actually
            // are scanning out to the CRTC, and it could still be modesetting.
            // this is not ideal (some CRTCs will just eat frames) but it's better than nothing

            m_dpmsBlackOpacity->setValueAndWarp(1.F);

            if (m_pendingDpmsAnimationCounter == 5) {
                *m_dpmsBlackOpacity    = 0.F;
                m_pendingDpmsAnimation = false;
            }
        }

        timespec ts{};
        auto     flags = event.flags;

        if (event.when && event.when->tv_sec > 2) {
            // drop this timestamp, it's not valid. Likely drm is cringe. We can't push it further because
            // a) it's wrong, b) our translations aren't 100% accurate and risk underflows
            ts = *event.when;
        } else {
            clock_gettime(CLOCK_MONOTONIC, &ts);
            flags &= ~Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK;
        }

        PROTO::presentation->onPresented(m_self.lock(), ts, event.refresh, event.seq, flags);

        if (m_zoomAnimFrameCounter < 5) {
            m_zoomAnimFrameCounter++;

            // we give ourselves 5 frames of a buffer. The first presentation event still doesn't usually say that we actually
            // are scanning out to the CRTC, and it could still be modesetting.
            // this is not ideal (some CRTCs will just eat frames) but it's better than nothing
            m_zoomAnimProgress->setValueAndWarp(0.F);
            if (m_zoomAnimFrameCounter == 5) {
                // start the animation for realzies
                *m_zoomAnimProgress = 1.F;
            }

            // damage the entire display to force a frame immediately
            g_pEventLoopManager->doLater([self = m_self] {
                if (!self)
                    return;

                g_pHyprRenderer->damageMonitor(self.lock());
            });
        }

        // if the monitor has no pending frames, we wont hit the no damage send frame callback path in rendermonitor
        // this becomes really noticeable in new render scheduling. causing firefox to simply wait for a new frame callback.
        // when nothing is scheduling new frames.
        auto mon = m_self.lock();
        if (!isMirror() && !g_pHyprRenderer->shouldRenderMonitor(mon)) {
            auto const NOW = Time::steadyNow();
            if (m_activeWorkspace)
                g_pHyprRenderer->sendFrameEventsToWorkspace(mon, m_activeWorkspace, NOW);
            if (m_activeSpecialWorkspace)
                g_pHyprRenderer->sendFrameEventsToWorkspace(mon, m_activeSpecialWorkspace, NOW);
        }

        m_frameScheduler->onPresented();

        m_events.presented.emit(Time::fromTimespec(&ts));
    });

    m_listeners.destroy = m_output->events.destroy.listen([this] {
        Log::logger->log(Log::DEBUG, "Destroy called for monitor {}", m_name);

        m_listeners = {};

        onDisconnect(true);

        m_output              = nullptr;
        m_renderingInitPassed = false;

        State::monitorState()->remove(m_self.lock());
    });

    m_listeners.state = m_output->events.state.listen([this](const Aquamarine::IOutput::SStateEvent& event) {
        if (event.size == Vector2D{}) {
            // an indication to re-set state
            // we can't do much for createdByUser displays I think
            if (m_createdByUser)
                return;

            Log::logger->log(Log::DEBUG, "Reapplying monitor rule for {} from a state request", m_name);
            auto cpy = m_activeMonitorRule;
            applyMonitorRule(std::move(cpy));
            return;
        }

        if (!m_createdByUser)
            return;

        const auto SIZE = event.size;

        m_forceSize = SIZE;

        auto rule = m_activeMonitorRule;

        if (SIZE == rule.m_resolution)
            return;

        rule.m_resolution = SIZE;

        applyMonitorRule(std::move(rule));
    });

    m_frameScheduler         = makeUnique<CMonitorFrameScheduler>(m_self.lock());
    m_frameScheduler->m_self = WP<CMonitorFrameScheduler>(m_frameScheduler);

    m_tearingState.canTear = m_output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM;

    m_description = m_output->description;
    // remove comma character from description. This allow monitor specific rules to work on monitor with comma on their description
    std::erase(m_description, ',');

    // field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix
    m_shortDescription = trim(std::format("{} {} {}", m_output->make, m_output->model, m_output->serial));
    std::erase(m_shortDescription, ',');

    if (m_output->getBackend()->type() != Aquamarine::AQ_BACKEND_DRM)
        m_createdByUser = true; // should be true. WL and Headless backends should be addable / removable

    // get monitor rule that matches
    auto monitorRule = Config::monitorRuleMgr()->get(m_self.lock());

    if (m_enabled && !monitorRule.m_disabled) {
        auto cpy = monitorRule;
        applyMonitorRule(std::move(cpy));

        m_output->state->resetExplicitFences();
        m_output->state->setEnabled(true);
        m_state.commit();
        return;
    }

    // if it's disabled, disable and ignore
    if (monitorRule.m_disabled) {

        m_output->state->resetExplicitFences();
        m_output->state->setEnabled(false);
        m_usedAsyncBuffers.clear();

        if (!m_state.commit())
            Log::logger->log(Log::ERR, "Couldn't commit disabled state on output {}", m_name);

        m_enabled = false;

        m_listeners.frame.reset();
        return;
    }

    if (m_output->nonDesktop) {
        Log::logger->log(Log::DEBUG, "Not configuring non-desktop output");

        for (auto& [name, lease] : PROTO::lease) {
            if (!lease || m_output->getBackend() != lease->getBackend())
                continue;

            lease->offer(m_self.lock());
        }

        return;
    }

    m_enabled = true;

    m_output->state->resetExplicitFences();
    m_output->state->setEnabled(true);

    // set mode, also applies
    if (!noRule) {
        auto cpy = monitorRule;
        applyMonitorRule(std::move(cpy));
    }

    if (!m_state.commit())
        Log::logger->log(Log::WARN, "state.commit() failed in CMonitor::onCommit");

    m_damage.setSize(m_transformedSize);

    Log::logger->log(Log::DEBUG, "Added new monitor with name {} at {:j0} with size {:j0}, pointer {:x}", m_name, m_position, m_pixelSize, rc<uintptr_t>(m_output.get()));

    if (!isMirror())
        setupDefaultWS(monitorRule);

    for (auto const& ws : State::workspaceState()->workspacesCopy()) {
        if (!valid(ws))
            continue;

        const auto CURRENTMON = ws->m_monitor.lock();
        const bool ORPHANED   = !CURRENTMON || std::ranges::none_of(State::monitorState()->monitors(), [&](const auto& mon) { return mon == CURRENTMON; });
        const bool RETURNING  = ws->m_lastMonitor == m_name;
        const bool RECOVERY   = ORPHANED &&
            std::ranges::count_if(State::monitorState()->monitors(), [](const auto& mon) { return !mon->m_isUnsafeFallback; }) == 1; // temporarily recover orphaned workspaces

        if (RETURNING || RECOVERY) {
            State::workspacePlacementController()->moveWorkspaceToMonitor(ws, m_self.lock());
            Animation::Workspace::startAnimation(ws, Animation::Workspace::ANIMATION_TYPE_IN, true, true);
            if (RETURNING)
                ws->m_lastMonitor = "";
        }
    }

    m_forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering.
    //

    if (!m_activeMonitorRule.m_mirrorOf.empty())
        setMirror(m_activeMonitorRule.m_mirrorOf);

    if (!Desktop::focusState()->monitor()) // set the last monitor if it isn't set yet
        Desktop::focusState()->rawMonitorFocus(m_self.lock());

    g_pHyprRenderer->arrangeLayersForMonitor(m_id);
    g_layoutManager->recalculateMonitor(m_self.lock());

    // ensure VRR (will enable if necessary)
    Config::monitorRuleMgr()->ensureVRR(m_self.lock());

    // verify last mon valid
    bool found = false;
    for (auto const& m : State::monitorState()->monitors()) {
        if (m == Desktop::focusState()->monitor()) {
            found = true;
            break;
        }
    }

    Log::logger->log(Log::DEBUG, "checking if we have seen this monitor before: {}", m_name);
    // if we saw this monitor before, set it to the workspace it was on
    if (const auto WORKSPACEID = State::workspaceState()->rememberedWorkspaceForMonitor(m_name); WORKSPACEID.has_value()) {
        auto workspaceID = *WORKSPACEID;
        Log::logger->log(Log::DEBUG, "Monitor {} was on workspace {}, setting it to that", m_name, workspaceID);
        auto ws = State::workspaceState()->query().id(workspaceID).run();
        if (ws) {
            State::workspacePlacementController()->moveWorkspaceToMonitor(ws, m_self.lock());
            changeWorkspace(ws, true, false, false);
        }
    } else
        Log::logger->log(Log::DEBUG, "Monitor {} was not on any workspace", m_name);

    if (!found)
        Desktop::focusState()->rawMonitorFocus(m_self.lock());

    scheduleFrame(Aquamarine::IOutput::AQ_SCHEDULE_NEW_MONITOR);

    PROTO::gamma->applyGammaToState(m_self.lock());

    m_events.connect.emit();

    IPC::Socket2::sock()->postEvent({"monitoradded", m_name});
    IPC::Socket2::sock()->postEvent({"monitoraddedv2", std::format("{},{},{}", m_id, m_name, m_shortDescription)});
    Event::bus()->m_events.monitor.added.emit(m_self.lock());
}

void CMonitor::onDisconnect(bool destroy) {
    Event::bus()->m_events.monitor.preRemoved.emit(m_self.lock());
    CScopeGuard x = {[this]() {
        if (g_pCompositor->m_isShuttingDown)
            return;
        IPC::Socket2::sock()->postEvent({"monitorremoved", m_name});
        IPC::Socket2::sock()->postEvent({"monitorremovedv2", std::format("{},{},{}", m_id, m_name, m_shortDescription)});
        Event::bus()->m_events.monitor.removed.emit(m_self.lock());
        State::monitorLayoutController()->scheduleRecheck();
    }};

    m_frameScheduler.reset();
    clearModeRetry();

    if (!m_enabled || g_pCompositor->m_isShuttingDown)
        return;

    Log::logger->log(Log::DEBUG, "onDisconnect called for {}", m_name);

    m_events.disconnect.emit();
    if (g_pHyprRenderer && g_pHyprRenderer->glBackend())
        g_pHyprRenderer->glBackend()->destroyMonitorResources(m_self);

    // record what workspace this monitor was on
    if (m_activeWorkspace) {
        Log::logger->log(Log::DEBUG, "Disconnecting Monitor {} was on workspace {}", m_name, m_activeWorkspace->m_id);
        State::workspaceState()->rememberWorkspaceForMonitor(m_name, m_activeWorkspace->m_id);
    }

    // Cleanup everything. Move windows back, snap cursor, shit.
    PHLMONITOR BACKUPMON = nullptr;
    for (auto const& m : State::monitorState()->monitors()) {
        if (m.get() != this) {
            BACKUPMON = m;
            break;
        }
    }

    // remove mirror
    if (m_mirrorOf) {
        m_mirrorOf->m_mirrors.erase(std::ranges::find_if(m_mirrorOf->m_mirrors, [&](const auto& other) { return other == m_self; }));

        // unlock software for mirrored monitor
        Pointer::mgr()->unlockSoftwareForMonitor(m_mirrorOf.lock());
        m_mirrorOf.reset();
    }

    if (!m_mirrors.empty()) {
        for (auto const& m : m_mirrors) {
            m->setMirror("");
        }

        Config::monitorRuleMgr()->scheduleReload();
    }

    m_listeners.frame.reset();
    m_listeners.presented.reset();
    m_listeners.needsFrame.reset();
    m_listeners.commit.reset();

    for (size_t i = 0; i < 4; ++i) {
        for (auto const& ls : m_layerSurfaceLayers[i]) {
            if (ls->m_layerSurface)
                ls->m_layerSurface->sendClosed();
        }
        m_layerSurfaceLayers[i].clear();
    }

    Log::logger->log(Log::DEBUG, "Removed monitor {}!", m_name);

    m_enabled             = false;
    m_renderingInitPassed = false;

    std::vector<PHLWORKSPACE> wspToMove;
    for (auto const& w : State::workspaceState()->workspaces()) {
        if (w->m_monitor == m_self || !w->m_monitor)
            wspToMove.emplace_back(w.lock());
    }

    // Preserve ownership across cascaded monitor disconnects.
    // The first disconnected monitor "owns" where a workspace should return.
    for (auto const& w : wspToMove) {
        if (w && w->m_lastMonitor.empty())
            w->m_lastMonitor = m_name;
    }

    if (BACKUPMON) {
        // snap cursor
        Pointer::pointerController()->warpTo(BACKUPMON->m_position + BACKUPMON->m_transformedSize / 2.F, true);

        for (auto const& w : wspToMove) {
            State::workspacePlacementController()->moveWorkspaceToMonitor(w, BACKUPMON);
            Animation::Workspace::startAnimation(w, Animation::Workspace::ANIMATION_TYPE_IN, true, true);
        }
    } else {
        Desktop::focusState()->surface().reset();
        Desktop::focusState()->window().reset();
        Desktop::focusState()->monitor().reset();
    }

    if (m_activeWorkspace)
        m_activeWorkspace->m_visible = false;
    m_activeWorkspace.reset();

    if (m_output) {
        m_output->state->resetExplicitFences();
        m_output->state->setAdaptiveSync(false);
        m_output->state->setEnabled(false);

        if (!m_state.commit())
            Log::logger->log(Log::WARN, "state.commit() failed in CMonitor::onDisconnect");
    }

    if (Desktop::focusState()->monitor() == m_self)
        Desktop::focusState()->rawMonitorFocus(BACKUPMON);

    if (g_pHyprRenderer->m_mostHzMonitor == m_self) {
        int        mostHz         = 0;
        PHLMONITOR pMonitorMostHz = nullptr;

        for (auto const& m : State::monitorState()->monitors()) {
            if (m->m_refreshRate > mostHz && m != m_self) {
                pMonitorMostHz = m;
                mostHz         = m->m_refreshRate;
            }
        }

        g_pHyprRenderer->m_mostHzMonitor = pMonitorMostHz;
    }
}

static NColorManagement::eTransferFunction chooseTF(NTransferFunction::eTF tf) {
    const auto sdrEOTF = NTransferFunction::fromConfig();

    switch (tf) {
        case NTransferFunction::TF_GAMMA22:
        case NTransferFunction::TF_FORCED_GAMMA22: return NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22;
        case NTransferFunction::TF_DEFAULT:
        case NTransferFunction::TF_SRGB: return NColorManagement::CM_TRANSFER_FUNCTION_SRGB;

        case NTransferFunction::TF_AUTO: // use global setting
            switch (sdrEOTF) {
                case NTransferFunction::TF_AUTO: return NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22;
                default: return chooseTF(sdrEOTF);
            }

        default: UNREACHABLE();
    }
}

void CMonitor::applyCMType(NCMType::eCMType cmType, NTransferFunction::eTF cmSdrEotf) {
    auto                                                              oldImageDescription = m_imageDescription;
    const auto                                                        chosenSdrEotf       = chooseTF(cmSdrEotf);

    const auto                                                        masteringPrimaries  = getMasteringPrimaries();
    const NColorManagement::SImageDescription::SPCMasteringLuminances masteringLuminances = getMasteringLuminances();

    const auto                                                        maxFALL = this->maxFALL();
    const auto                                                        maxCLL  = this->maxCLL();

    switch (cmType) {
        case NCMType::CM_SRGB:
            m_imageDescription = CImageDescription::from({.transferFunction = chosenSdrEotf,
                                                          .primariesNamed   = NColorManagement::CM_PRIMARIES_SRGB,
                                                          .primaries        = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB)});
            break; // assumes SImageDescription defaults to sRGB
        case NCMType::CM_WIDE:
            m_imageDescription = CImageDescription::from({.transferFunction    = chosenSdrEotf,
                                                          .primariesNameSet    = true,
                                                          .primariesNamed      = NColorManagement::CM_PRIMARIES_BT2020,
                                                          .primaries           = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
                                                          .masteringPrimaries  = masteringPrimaries,
                                                          .masteringLuminances = masteringLuminances,
                                                          .maxCLL              = maxCLL,
                                                          .maxFALL             = maxFALL});
            break;
        case NCMType::CM_DCIP3:
            m_imageDescription = CImageDescription::from({.transferFunction    = chosenSdrEotf,
                                                          .primariesNameSet    = true,
                                                          .primariesNamed      = NColorManagement::CM_PRIMARIES_DCI_P3,
                                                          .primaries           = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_DCI_P3),
                                                          .masteringPrimaries  = masteringPrimaries,
                                                          .masteringLuminances = masteringLuminances,
                                                          .maxCLL              = maxCLL,
                                                          .maxFALL             = maxFALL});
            break;
        case NCMType::CM_DP3:
            m_imageDescription = CImageDescription::from({.transferFunction    = chosenSdrEotf,
                                                          .primariesNameSet    = true,
                                                          .primariesNamed      = NColorManagement::CM_PRIMARIES_DISPLAY_P3,
                                                          .primaries           = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_DISPLAY_P3),
                                                          .masteringPrimaries  = masteringPrimaries,
                                                          .masteringLuminances = masteringLuminances,
                                                          .maxCLL              = maxCLL,
                                                          .maxFALL             = maxFALL});
            break;
        case NCMType::CM_ADOBE:
            m_imageDescription = CImageDescription::from({.transferFunction    = chosenSdrEotf,
                                                          .primariesNameSet    = true,
                                                          .primariesNamed      = NColorManagement::CM_PRIMARIES_ADOBE_RGB,
                                                          .primaries           = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_ADOBE_RGB),
                                                          .masteringPrimaries  = masteringPrimaries,
                                                          .masteringLuminances = masteringLuminances,
                                                          .maxCLL              = maxCLL,
                                                          .maxFALL             = maxFALL});
            break;
        case NCMType::CM_EDID:
            m_imageDescription = CImageDescription::from({.transferFunction    = chosenSdrEotf,
                                                          .primariesNameSet    = false,
                                                          .primariesNamed      = NColorManagement::CM_PRIMARIES_BT2020,
                                                          .primaries           = masteringPrimaries,
                                                          .masteringPrimaries  = masteringPrimaries,
                                                          .masteringLuminances = masteringLuminances,
                                                          .maxCLL              = maxCLL,
                                                          .maxFALL             = maxFALL});
            break;
        case NCMType::CM_HDR:
            m_imageDescription = CImageDescription::from({.transferFunction    = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
                                                          .primariesNameSet    = true,
                                                          .primariesNamed      = NColorManagement::CM_PRIMARIES_BT2020,
                                                          .primaries           = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
                                                          .masteringPrimaries  = masteringPrimaries,
                                                          .luminances          = {.min       = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFMinLuminance(),
                                                                                  .max       = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFMaxLuminance(),
                                                                                  .reference = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFRefLuminance()},
                                                          .masteringLuminances = masteringLuminances,
                                                          .maxCLL              = maxCLL,
                                                          .maxFALL             = maxFALL});
            break;
        case NCMType::CM_HDR_EDID:
            m_imageDescription = CImageDescription::from(
                {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
                 .primariesNameSet = false,
                 .primariesNamed   = NColorManagement::CM_PRIMARIES_BT2020,
                 .primaries = m_output->parsedEDID.chromaticityCoords.has_value() ? masteringPrimaries : NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
                 .masteringPrimaries  = masteringPrimaries,
                 .luminances          = {.min       = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFMinLuminance(),
                                         .max       = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFMaxLuminance(),
                                         .reference = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFRefLuminance()},
                 .masteringLuminances = masteringLuminances,
                 .maxCLL              = maxCLL,
                 .maxFALL             = maxFALL});

            break;
        default: UNREACHABLE();
    }
    if ((minLuminance() >= 0 || maxLuminance() > 0) && (cmType == NCMType::CM_HDR || cmType == NCMType::CM_HDR_EDID))
        m_imageDescription = m_imageDescription->with({
            .min       = minLuminance() >= 0 ? minLuminance() : m_imageDescription->value().luminances.min, //
            .max       = maxLuminance() > 0 ? maxLuminance() : m_imageDescription->value().luminances.max,  //
            .reference = m_imageDescription->value().luminances.reference                                   //
        });

    if (oldImageDescription != m_imageDescription) {
        if (PROTO::colorManagement)
            PROTO::colorManagement->onMonitorImageDescriptionChanged(m_self);
        m_blurFBDirty = true;
    }
}

bool CMonitor::applyMonitorRuleSoft(Config::CMonitorRule&& pMonitorRule) {
    m_activeMonitorRule = std::move(pMonitorRule);
    m_reservedArea.setStatic(m_activeMonitorRule.m_reservedArea);
    m_transform         = m_activeMonitorRule.m_transform;
    m_supportsWideColor = m_activeMonitorRule.m_supportsWideColor;
    m_supportsHDR       = m_activeMonitorRule.m_supportsHDR;

    if (m_activeMonitorRule.m_iccFile.empty()) {
        // only apply explicit cm settings if we have no icc file

        m_cmType = m_activeMonitorRule.m_cmType;
        switch (m_cmType) {
            case NCMType::CM_AUTO: m_cmType = m_enabled10bit && supportsWideColor() ? NCMType::CM_WIDE : NCMType::CM_SRGB; break;
            case NCMType::CM_EDID: m_cmType = m_output->parsedEDID.chromaticityCoords.has_value() ? NCMType::CM_EDID : NCMType::CM_SRGB; break;
            case NCMType::CM_HDR:
            case NCMType::CM_HDR_EDID: m_cmType = supportsHDR() ? m_cmType : NCMType::CM_SRGB; break;
            default: break;
        }

        m_sdrEotf = m_activeMonitorRule.m_sdrEotf;

        m_sdrMinLuminance = m_activeMonitorRule.m_sdrMinLuminance;
        m_sdrMaxLuminance = m_activeMonitorRule.m_sdrMaxLuminance;

        m_minLuminance    = m_activeMonitorRule.m_minLuminance;
        m_maxLuminance    = m_activeMonitorRule.m_maxLuminance;
        m_maxAvgLuminance = m_activeMonitorRule.m_maxAvgLuminance;

        applyCMType(m_cmType, m_sdrEotf);

        m_sdrSaturation = m_activeMonitorRule.m_sdrSaturation;
        m_sdrBrightness = m_activeMonitorRule.m_sdrBrightness;
    } else {
        auto image = NColorManagement::SImageDescription::fromICC(m_activeMonitorRule.m_iccFile);
        if (!image) {
            Log::logger->log(Log::ERR, "icc for {} ({}) failed: {}", m_name, m_activeMonitorRule.m_iccFile, image.error());
            ErrorOverlay::overlay()->queueError(std::format("failed to apply icc {} to {}: {}", m_activeMonitorRule.m_iccFile, m_name, image.error()));
        } else {
            m_imageDescription = CImageDescription::from(*image);
            if (!m_imageDescription) {
                Log::logger->log(Log::ERR, "icc for {} ({}) failed 2: {}", m_name, m_activeMonitorRule.m_iccFile, image.error());
                ErrorOverlay::overlay()->queueError(std::format("failed to apply icc {} to {}: {}", m_activeMonitorRule.m_iccFile, m_name, image.error()));
                m_imageDescription = CImageDescription::from(SImageDescription{});
            }
        }
    }

    Vector2D xfmd     = m_transform % 2 == 1 ? Vector2D{m_pixelSize.y, m_pixelSize.x} : m_pixelSize;
    m_size            = (xfmd / m_scale).round();
    m_transformedSize = xfmd;

    if (m_createdByUser) {
        CBox transformedBox = {0, 0, m_transformedSize.x, m_transformedSize.y};
        transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_transform)), m_transformedSize.x, m_transformedSize.y);

        m_pixelSize = Vector2D(transformedBox.width, transformedBox.height);
    }

    updateMatrix();

    m_damage.setSize(m_transformedSize);

    setMirror(m_activeMonitorRule.m_mirrorOf);

    return true;
}

bool CMonitor::applyMonitorRule(Config::CMonitorRule&& pMonitorRule) {

    static auto PDISABLESCALECHECKS = CConfigValue<Config::INTEGER>("debug:disable_scale_checks");

    Log::logger->log(Log::DEBUG, "Applying monitor rule for {}", m_name);

    if (m_forceSize.has_value())
        m_activeMonitorRule.m_resolution = m_forceSize.value();

    const auto RULE = &pMonitorRule;

    // if it's disabled, disable and ignore
    if (RULE->m_disabled) {
        m_output->state->resetExplicitFences();
        m_output->state->setAdaptiveSync(false);
        m_output->state->setEnabled(false);

        m_activeMonitorRule = std::move(pMonitorRule);

        if (!m_state.commit())
            Log::logger->log(Log::WARN, "state.commit() failed in CMonitor::applyMonitorRule");

        m_events.modeChanged.emit();

        return true;
    }

    // don't touch VR headsets
    if (m_output->nonDesktop)
        return true;

    const bool autoScale = RULE->m_scale <= 0.1;

    m_transform = RULE->m_transform;

    // accumulate requested modes in reverse order (cause inesrting at front is inefficient)
    std::vector<SP<Aquamarine::SOutputMode>> requestedModes;
    std::string                              requestedStr = "unknown";

    // use sortFunc, add best 3 to requestedModes in reverse, since we test in reverse
    auto addBest3Modes = [&](auto const& sortFunc) {
        auto sortedModes = m_output->modes;
        std::ranges::sort(sortedModes, sortFunc);
        if (sortedModes.size() > 3)
            sortedModes.erase(sortedModes.begin() + 3, sortedModes.end());
        requestedModes.insert_range(requestedModes.end(), sortedModes | std::views::reverse);
    };

    // last fallback is always preferred mode
    if (!m_output->preferredMode())
        Log::logger->log(Log::ERR, "Monitor {} has NO PREFERRED MODE", m_name);
    else
        requestedModes.push_back(m_output->preferredMode());

    if (RULE->m_resolution == Vector2D()) {
        requestedStr = "preferred";

        // fallback to first 3 modes if preferred fails/doesn't exist
        requestedModes = m_output->modes;
        if (requestedModes.size() > 3)
            requestedModes.erase(requestedModes.begin() + 3, requestedModes.end());
        std::ranges::reverse(requestedModes.begin(), requestedModes.end());

        if (m_output->preferredMode())
            requestedModes.push_back(m_output->preferredMode());
    } else if (RULE->m_resolution == Vector2D(-1, -1)) {
        requestedStr = "highrr";

        // sort prioritizing refresh rate 1st and resolution 2nd, then add best 3
        addBest3Modes([](auto const& a, auto const& b) {
            if (std::round(a->refreshRate) > std::round(b->refreshRate))
                return true;
            else if (DELTALESSTHAN(sc<float>(a->refreshRate), sc<float>(b->refreshRate), 1.F) && a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y)
                return true;
            return false;
        });
    } else if (RULE->m_resolution == Vector2D(-1, -2)) {
        requestedStr = "highres";

        // sort prioritizing resolution 1st and refresh rate 2nd, then add best 3
        addBest3Modes([](auto const& a, auto const& b) {
            if (a->pixelSize.x > b->pixelSize.x && a->pixelSize.y > b->pixelSize.y)
                return true;
            else if (DELTALESSTHAN(a->pixelSize.x, b->pixelSize.x, 1) && DELTALESSTHAN(a->pixelSize.y, b->pixelSize.y, 1) &&
                     std::round(a->refreshRate) > std::round(b->refreshRate))
                return true;
            return false;
        });
    } else if (RULE->m_resolution == Vector2D(-1, -3)) {
        requestedStr = "maxwidth";

        // sort prioritizing widest resolution 1st and refresh rate 2nd, then add best 3
        addBest3Modes([](auto const& a, auto const& b) {
            if (a->pixelSize.x > b->pixelSize.x)
                return true;
            if (a->pixelSize.x == b->pixelSize.x && std::round(a->refreshRate) > std::round(b->refreshRate))
                return true;
            return false;
        });
    } else if (RULE->m_resolution != Vector2D()) {
        // user requested mode
        requestedStr = std::format("{:X0}@{:.2f}Hz", RULE->m_resolution, RULE->m_refreshRate);

        // sort by closeness to requested, then add best 3
        addBest3Modes([&](auto const& a, auto const& b) {
            if (abs(a->pixelSize.x - RULE->m_resolution.x) < abs(b->pixelSize.x - RULE->m_resolution.x))
                return true;
            if (a->pixelSize.x == b->pixelSize.x && abs(a->pixelSize.y - RULE->m_resolution.y) < abs(b->pixelSize.y - RULE->m_resolution.y))
                return true;
            if (a->pixelSize == b->pixelSize && abs((a->refreshRate / 1000.f) - RULE->m_refreshRate) < abs((b->refreshRate / 1000.f) - RULE->m_refreshRate))
                return true;
            return false;
        });

        // if the best mode isn't close to requested, then try requested as custom mode first
        if (!requestedModes.empty()) {
            auto bestMode = requestedModes.back();
            if (!DELTALESSTHAN(bestMode->pixelSize.x, RULE->m_resolution.x, 1) || !DELTALESSTHAN(bestMode->pixelSize.y, RULE->m_resolution.y, 1) ||
                !DELTALESSTHAN(bestMode->refreshRate / 1000.f, RULE->m_refreshRate, 1))
                requestedModes.push_back(
                    makeShared<Aquamarine::SOutputMode>(Aquamarine::SOutputMode{.pixelSize = RULE->m_resolution, .refreshRate = RULE->m_refreshRate * 1000.f}));
        }

        // then if requested is custom, try custom mode first
        if (RULE->m_drmMode.type == DRM_MODE_TYPE_USERDEF) {
            if (m_output->getBackend()->type() != Aquamarine::eBackendType::AQ_BACKEND_DRM)
                Log::logger->log(Log::ERR, "Tried to set custom modeline on non-DRM output");
            else
                requestedModes.push_back(makeShared<Aquamarine::SOutputMode>(Aquamarine::SOutputMode{
                    .pixelSize = {RULE->m_drmMode.hdisplay, RULE->m_drmMode.vdisplay}, .refreshRate = RULE->m_drmMode.vrefresh, .modeInfo = RULE->m_drmMode}));
        }
    }

    const auto WAS10B             = m_enabled10bit;
    const auto OLDPIXELSIZE       = m_pixelSize;
    const auto OLDTRANSFORMEDSIZE = m_transformedSize;
    bool       success            = false;

    // Needed in case we are switching from a custom modeline to a standard mode
    m_customDrmMode = {};
    m_currentMode   = nullptr;

    const auto initialFormat = m_drmFormat != DRM_FORMAT_INVALID ? m_drmFormat : DRM_FORMAT_XRGB8888;
    m_output->state->setFormat(initialFormat);
    m_prevDrmFormat = m_drmFormat;
    m_drmFormat     = initialFormat;
    m_output->state->resetExplicitFences();

    if (Env::isTrace()) {
        Log::logger->log(Log::TRACE, "Monitor {} requested modes:", m_name);
        if (requestedModes.empty())
            Log::logger->log(Log::TRACE, "| None");
        else {
            for (auto const& mode : requestedModes | std::views::reverse) {
                Log::logger->log(Log::TRACE, "| {:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f);
            }
        }
    }

    for (auto const& mode : requestedModes | std::views::reverse) {
        std::string modeStr = std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f);

        if (mode->modeInfo.has_value() && mode->modeInfo->type == DRM_MODE_TYPE_USERDEF) {
            m_state.applyCustomModeWithSwapchain(mode);

            if (!m_state.test()) {
                Log::logger->log(Log::ERR, "Monitor {}: REJECTED custom mode {}!", m_name, modeStr);
                continue;
            }

            m_customDrmMode = mode->modeInfo.value();
        } else {
            m_state.applyModeWithSwapchain(mode);

            if (!m_state.test()) {
                Log::logger->log(Log::ERR, "Monitor {}: REJECTED available mode {}!", m_name, modeStr);
                if (mode->preferred)
                    Log::logger->log(Log::ERR, "Monitor {}: REJECTED preferred mode!!!", m_name);
                continue;
            }

            m_customDrmMode = {};
        }

        m_refreshRate = mode->refreshRate / 1000.f;
        m_size        = mode->pixelSize;
        m_currentMode = mode;

        success = true;

        if (mode->preferred)
            Log::logger->log(Log::DEBUG, "Monitor {}: requested {}, using preferred mode {}", m_name, requestedStr, modeStr);
        else if (mode->modeInfo.has_value() && mode->modeInfo->type == DRM_MODE_TYPE_USERDEF)
            Log::logger->log(Log::DEBUG, "Monitor {}: requested {}, using custom mode {}", m_name, requestedStr, modeStr);
        else
            Log::logger->log(Log::DEBUG, "Monitor {}: requested {}, using available mode {}", m_name, requestedStr, modeStr);

        break;
    }

    // try requested as custom mode jic it works
    if (!success && RULE->m_resolution != Vector2D() && RULE->m_resolution != Vector2D(-1, -1) && RULE->m_resolution != Vector2D(-1, -2)) {
        auto        refreshRate = m_output->getBackend()->type() == Aquamarine::eBackendType::AQ_BACKEND_DRM ? RULE->m_refreshRate * 1000 : 0;
        auto        mode        = makeShared<Aquamarine::SOutputMode>(Aquamarine::SOutputMode{.pixelSize = RULE->m_resolution, .refreshRate = refreshRate});
        std::string modeStr     = std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f);

        m_state.applyCustomModeWithSwapchain(mode);

        if (m_state.test()) {
            Log::logger->log(Log::DEBUG, "Monitor {}: requested {}, using custom mode {}", m_name, requestedStr, modeStr);

            refreshRate     = mode->refreshRate / 1000.f;
            m_size          = mode->pixelSize;
            m_currentMode   = mode;
            m_customDrmMode = {};

            success = true;
        } else
            Log::logger->log(Log::ERR, "Monitor {}: REJECTED custom mode {}!", m_name, modeStr);
    }

    // try any of the modes if none of the above work
    if (!success) {
        for (auto const& mode : m_output->modes) {
            m_state.applyModeWithSwapchain(mode);

            if (!m_state.test())
                continue;

            auto errorMessage = I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_MONITOR_MODE_FAIL,
                                                             {{"name", m_name}, {"mode", std::format("{:X0}@{:.2f}Hz", mode->pixelSize, mode->refreshRate / 1000.f)}});
            Log::logger->log(Log::WARN, errorMessage);
            Notification::overlay()->addNotification(errorMessage, CHyprColor(0xff0000ff), 5000, ICON_WARNING);

            m_refreshRate   = mode->refreshRate / 1000.f;
            m_size          = mode->pixelSize;
            m_currentMode   = mode;
            m_customDrmMode = {};

            success = true;

            break;
        }
    }

    if (!success) {
        m_activeMonitorRule = std::move(pMonitorRule);
        Log::logger->log(Log::ERR, "Monitor {} has NO FALLBACK MODES, and an INVALID one was requested: {:X0}@{:.2f}Hz", m_name, RULE->m_resolution, RULE->m_refreshRate);
        scheduleModeRetry();
        return true;
    }

    clearModeRetry();

    const auto OLD_PIXEL_SIZE = m_pixelSize;

    m_vrrActive = m_output->state->state().adaptiveSync // disabled here, will be tested in CConfigManager::ensureVRR()
        || m_createdByUser;                             // wayland backend doesn't allow for disabling adaptive_sync

    m_pixelSize = m_size;

    static constexpr auto formats10bit = std::to_array<uint32_t>({DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010});
    static constexpr auto formats8bit  = std::to_array<uint32_t>({DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888});

    const bool            set10bit = RULE->m_enable10bit && trySetFormat(formats10bit);
    if (!set10bit)
        trySetFormat(formats8bit);

    m_enabled10bit = set10bit;

    if (autoScale)
        m_scale = getDefaultScale();
    else
        m_scale = RULE->m_scale;

    m_setScale = m_scale;

    Vector2D logicalSize = m_pixelSize / m_scale;
    if (!*PDISABLESCALECHECKS && (logicalSize.x != std::round(logicalSize.x) || logicalSize.y != std::round(logicalSize.y))) {
        // invalid scale, will produce fractional pixels.
        // find the nearest valid.

        float    searchScale = std::round(m_scale * 120.0);
        bool     found       = false;

        double   scaleZero = searchScale / 120.0;

        Vector2D logicalZero = m_pixelSize / scaleZero;
        if (logicalZero == logicalZero.round())
            m_scale = scaleZero;
        else {
            for (size_t i = 1; i < 90; ++i) {
                double   scaleUp   = (searchScale + i) / 120.0;
                double   scaleDown = (searchScale - i) / 120.0;

                Vector2D logicalUp   = m_pixelSize / scaleUp;
                Vector2D logicalDown = m_pixelSize / scaleDown;

                if (logicalUp == logicalUp.round()) {
                    found       = true;
                    searchScale = scaleUp;
                    break;
                }
                if (logicalDown == logicalDown.round()) {
                    found       = true;
                    searchScale = scaleDown;
                    break;
                }
            }

            if (!found) {
                if (autoScale)
                    m_scale = std::round(scaleZero);
                else {
                    Log::logger->log(Log::ERR, "Invalid scale passed to monitor, {} failed to find a clean divisor", m_scale);
                    ErrorOverlay::overlay()->queueError(std::format("Invalid scale passed to monitor {}, failed to find a clean divisor", m_name));
                    m_scale = getDefaultScale();
                }
            } else {
                if (!autoScale) {
                    Log::logger->log(Log::ERR, "Invalid scale passed to monitor, {} found suggestion {}", m_scale, searchScale);
                    static auto PDISABLENOTIFICATION = CConfigValue<Config::INTEGER>("misc:disable_scale_notification");
                    if (!*PDISABLENOTIFICATION) {
                        Notification::overlay()->addNotification(
                            I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_MONITOR_AUTO_SCALE,
                                                         {{"name", m_name}, {"scale", std::format("{:.2f}", m_scale)}, {"fixed_scale", std::format("{:.2f}", searchScale)}}),
                            CHyprColor(1.0, 0.0, 0.0, 1.0), 5000, ICON_WARNING);
                    }
                }
                m_scale = searchScale;
            }
        }
    }

    m_output->scheduleFrame();

    if (!m_state.commit())
        Log::logger->log(Log::ERR, "Couldn't commit output named {}", m_name);

    Vector2D xfmd     = m_transform % 2 == 1 ? Vector2D{m_pixelSize.y, m_pixelSize.x} : m_pixelSize;
    m_size            = (xfmd / m_scale).round();
    m_transformedSize = xfmd;

    if (WAS10B != m_enabled10bit || OLDPIXELSIZE != m_pixelSize || OLDTRANSFORMEDSIZE != m_transformedSize) {
        m_resources.reset(); // TODO skip for 10bit change and fp16?

        if (g_pHyprRenderer && g_pHyprRenderer->glBackend())
            g_pHyprRenderer->glBackend()->destroyMonitorResources(m_self);
    }

    applyMonitorRuleSoft(std::move(pMonitorRule));

    if (OLD_PIXEL_SIZE != m_pixelSize || OLDTRANSFORMEDSIZE != m_transformedSize) {
        m_background.reset();
        m_splash.reset();
    }

    updateVCGTRamps();

    Log::logger->log(Log::DEBUG, "Monitor {} data dump: res {:X}@{:.2f}Hz, scale {:.2f}, transform {}, pos {:X}, 10b {}", m_name, m_pixelSize, m_refreshRate, m_scale,
                     sc<int>(m_transform), m_position, sc<int>(m_enabled10bit));

    m_events.modeChanged.emit();

    return true;
}

void CMonitor::scheduleModeRetry() {
    static constexpr int  MAX_MODE_RETRIES = 3;
    static constexpr auto RETRY_DELAY      = std::chrono::seconds(1);

    if (m_modeRetryTimer || m_modeRetryCount >= MAX_MODE_RETRIES)
        return;

    m_modeRetryCount++;

    Log::logger->log(Log::WARN, "Monitor {} failed to find a valid mode, retrying in 1s ({}/{})", m_name, m_modeRetryCount, MAX_MODE_RETRIES);

    m_modeRetryTimer = makeShared<CEventLoopTimer>(
        RETRY_DELAY,
        [self = m_self](SP<CEventLoopTimer>, void*) {
            const auto PMONITOR = self.lock();
            if (!PMONITOR)
                return;

            PMONITOR->m_modeRetryTimer.reset();

            if (!PMONITOR->m_output || !PMONITOR->m_enabled)
                return;

            auto rule = PMONITOR->m_activeMonitorRule;
            PMONITOR->applyMonitorRule(std::move(rule));
        },
        nullptr);
    g_pEventLoopManager->addTimer(m_modeRetryTimer);
}

void CMonitor::clearModeRetry() {
    m_modeRetryCount = 0;

    if (!m_modeRetryTimer)
        return;

    m_modeRetryTimer->cancel();
    g_pEventLoopManager->removeTimer(m_modeRetryTimer);
    m_modeRetryTimer.reset();
}

void CMonitor::scheduleFrame(Aquamarine::IOutput::scheduleFrameReason reason) {
    if ((g_pCompositor->m_aqBackend->hasSession() && !g_pCompositor->m_aqBackend->session->active) || !g_pCompositor->m_sessionActive)
        return;

    if (!m_enabled)
        return;

    if (m_renderingActive)
        m_pendingFrame = true;

    m_output->scheduleFrame(reason);
}

void CMonitor::addDamage(const pixman_region32_t* rg) {
    if (m_cursorZoom->value() != 1.f && State::monitorState()->query().vec(Pointer::mgr()->position()).run() == m_self) {
        m_damage.damageEntire();
        scheduleFrame(Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
    } else if (m_damage.damage(rg))
        scheduleFrame(Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}

void CMonitor::addDamage(const CRegion& rg) {
    addDamage(const_cast<CRegion*>(&rg)->pixman());
}

void CMonitor::addDamage(const CBox& box) {
    if (m_cursorZoom->value() != 1.f && State::monitorState()->query().vec(Pointer::mgr()->position()).run() == m_self) {
        m_damage.damageEntire();
        scheduleFrame(Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
        return;
    }

    if (m_damage.damage(box))
        scheduleFrame(Aquamarine::IOutput::AQ_SCHEDULE_DAMAGE);
}

bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
    static auto PNOBREAK = CConfigValue<Config::INTEGER>("cursor:no_break_fs_vrr");
    static auto PMINRR   = CConfigValue<Config::INTEGER>("cursor:min_refresh_rate");

    // skip scheduling extra frames for fullsreen apps with vrr
    const auto FULLSCREEN_WINDOW  = Fullscreen::controller()->getFullscreenWindow(m_self.lock());
    const bool shouldRenderCursor = g_pHyprRenderer->shouldRenderCursor();
    const bool noBreak            = FULLSCREEN_WINDOW && (*PNOBREAK == 1 || (*PNOBREAK == 2 && FULLSCREEN_WINDOW->getContentType() == CONTENT_TYPE_GAME));
    const bool shouldSkip         = (!shouldRenderCursor || noBreak) && m_output->state->state().adaptiveSync;

    // keep requested minimum refresh rate
    if (shouldSkip && *PMINRR && m_lastPresentationTimer.getMillis() > 1000.0f / *PMINRR) {
        // damage whole screen because some previous cursor box damages were skipped
        m_damage.damageEntire();
        return false;
    }

    return shouldSkip;
}

bool CMonitor::isMirror() {
    return m_mirrorOf != nullptr;
}

MONITORID CMonitor::id() const {
    return m_id;
}

std::string_view CMonitor::name() const {
    return m_name;
}

std::string_view CMonitor::description() const {
    return m_description;
}

std::string_view CMonitor::shortDescription() const {
    return m_shortDescription;
}

bool CMonitor::matchesStaticSelector(std::string_view selector) const {
    if (selector.starts_with("desc:")) {
        // match by description
        const auto DESCRIPTIONSELECTOR = trim(selector.substr(5));

        return m_description.starts_with(DESCRIPTIONSELECTOR) || m_shortDescription.starts_with(DESCRIPTIONSELECTOR);
    } else {
        // match by selector
        return m_name == selector;
    }
}

Vector2D CMonitor::position() const {
    return m_position;
}

Vector2D CMonitor::size() const {
    return m_size;
}

Vector2D CMonitor::pixelSize() const {
    return m_pixelSize;
}

Vector2D CMonitor::transformedSize() const {
    return m_transformedSize;
}

float CMonitor::scale() const {
    return m_scale;
}

Hyprutils::Math::eTransform CMonitor::transform() const {
    return Math::wlTransformToHyprutils(m_transform);
}

bool CMonitor::enabled() const {
    return m_enabled;
}

bool CMonitor::hasOutput() const {
    return !!m_output;
}

SP<Aquamarine::IOutput> CMonitor::output() const {
    return m_output;
}

std::optional<Vector2D> CMonitor::explicitPosition() const {
    if (m_activeMonitorRule.m_offset == Vector2D{-INT32_MAX, -INT32_MAX})
        return {};

    return m_activeMonitorRule.m_offset;
}

Config::eAutoDirs CMonitor::autoDirection() const {
    return m_activeMonitorRule.m_autoDir;
}

Vector2D CMonitor::xwaylandPosition() const {
    return m_xwaylandPosition;
}

float CMonitor::xwaylandScale() const {
    return m_xwaylandScale;
}

void CMonitor::setXWaylandPosition(const Vector2D& pos) {
    m_xwaylandPosition = pos;
}

void CMonitor::setXWaylandScale(float scale_) {
    m_xwaylandScale = scale_;
}

WORKSPACEID CMonitor::findAvailableDefaultWS() {
    for (WORKSPACEID i = 1; i < LONG_MAX; ++i) {
        if (State::workspaceState()->query().id(i).run())
            continue;

        if (const auto BOUND = Config::workspaceRuleMgr()->getBoundMonitorStringForWS(std::to_string(i)); !BOUND.empty() && !matchesStaticSelector(BOUND))
            continue;

        return i;
    }

    return LONG_MAX; // shouldn't be reachable
}

void CMonitor::setupDefaultWS(const Config::CMonitorRule& monitorRule) {
    // Workspace
    std::string newDefaultWorkspaceName = "";
    int64_t     wsID                    = WORKSPACE_INVALID;
    const auto  DEFAULTWORKSPACE        = Config::workspaceRuleMgr()->getDefaultWorkspaceFor(*this);
    if (DEFAULTWORKSPACE.empty())
        wsID = findAvailableDefaultWS();
    else {
        const auto ws           = getWorkspaceIDNameFromString(DEFAULTWORKSPACE);
        wsID                    = ws.id;
        newDefaultWorkspaceName = ws.name;
    }

    if (wsID == WORKSPACE_INVALID || (wsID >= SPECIAL_WORKSPACE_START && wsID <= -2)) {
        wsID                    = std::ranges::distance(State::workspaceState()->workspaces()) + 1;
        newDefaultWorkspaceName = std::to_string(wsID);

        Log::logger->log(Log::DEBUG, "Invalid workspace= directive name in monitor parsing, workspace name \"{}\" is invalid.", DEFAULTWORKSPACE);
    }

    auto PNEWWORKSPACE = State::workspaceState()->query().id(wsID).run();

    Log::logger->log(Log::DEBUG, "New monitor: WORKSPACEID {}, exists: {}", wsID, sc<int>(PNEWWORKSPACE != nullptr));

    if (PNEWWORKSPACE) {
        // workspace exists, move it to the newly connected monitor
        State::workspacePlacementController()->moveWorkspaceToMonitor(PNEWWORKSPACE, m_self.lock());
        m_activeWorkspace = PNEWWORKSPACE;
        g_layoutManager->recalculateMonitor(m_self.lock());
        Animation::Workspace::startAnimation(PNEWWORKSPACE, Animation::Workspace::ANIMATION_TYPE_IN, true, true);
    } else {
        if (newDefaultWorkspaceName.empty())
            newDefaultWorkspaceName = std::to_string(wsID);

        PNEWWORKSPACE = CWorkspace::create(wsID, m_self.lock(), newDefaultWorkspaceName);
    }

    m_activeWorkspace = PNEWWORKSPACE;

    PNEWWORKSPACE->m_events.activeChanged.emit();
    PNEWWORKSPACE->m_visible     = true;
    PNEWWORKSPACE->m_lastMonitor = "";
}

void CMonitor::setMirror(const std::string& mirrorOf) {
    const auto PMIRRORMON = State::monitorState()->query().relativeTo(Desktop::focusState()->monitor()).configString(mirrorOf).run();

    if (PMIRRORMON == m_mirrorOf)
        return;

    if (PMIRRORMON && PMIRRORMON->isMirror()) {
        Log::logger->log(Log::ERR, "Cannot mirror a mirror!");
        return;
    }

    if (PMIRRORMON == m_self) {
        Log::logger->log(Log::ERR, "Cannot mirror self!");
        return;
    }

    if (!PMIRRORMON) {
        // disable mirroring

        if (m_mirrorOf) {
            m_mirrorOf->m_mirrors.erase(std::ranges::find_if(m_mirrorOf->m_mirrors, [&](const auto& other) { return other == m_self; }));

            // unlock software for mirrored monitor
            Pointer::mgr()->unlockSoftwareForMonitor(m_mirrorOf.lock());
        }

        m_mirrorOf.reset();

        setupDefaultWS(m_activeMonitorRule);
    } else {
        PHLMONITOR BACKUPMON = nullptr;
        for (auto const& m : State::monitorState()->monitors()) {
            if (m.get() != this) {
                BACKUPMON = m;
                break;
            }
        }

        // move all the WS
        std::vector<PHLWORKSPACE> wspToMove;
        for (auto const& w : State::workspaceState()->workspaces()) {
            if (w->m_monitor == m_self || !w->m_monitor)
                wspToMove.emplace_back(w.lock());
        }

        for (auto const& w : wspToMove) {
            State::workspacePlacementController()->moveWorkspaceToMonitor(w, BACKUPMON);
            Animation::Workspace::startAnimation(w, Animation::Workspace::ANIMATION_TYPE_IN, true, true);
        }

        m_activeWorkspace.reset();

        m_position = PMIRRORMON->m_position;

        m_mirrorOf = PMIRRORMON;

        m_mirrorOf->m_mirrors.push_back(m_self);

        Desktop::focusState()->rawMonitorFocus(State::monitorState()->monitors().front());

        // Software lock mirrored monitor
        Pointer::mgr()->lockSoftwareForMonitor(PMIRRORMON);
    }

    m_events.modeChanged.emit();
    Event::bus()->m_events.monitor.layoutChanged.emit();
}

float CMonitor::getDefaultScale() {
    if (!m_output)
        return 1;

    static constexpr double MMPERINCH = 25.4;

    const auto              DIAGONALPX = sqrt(pow(m_pixelSize.x, 2) + pow(m_pixelSize.y, 2));
    const auto              DIAGONALIN = sqrt(pow(m_output->physicalSize.x / MMPERINCH, 2) + pow(m_output->physicalSize.y / MMPERINCH, 2));

    const auto              PPI = DIAGONALPX / DIAGONALIN;

    if (PPI > 200 /* High PPI, 2x*/)
        return 2;
    else if (PPI > 140 /* Medium PPI, 1.5x*/)
        return 1.5;
    return 1;
}

static bool shouldWraparound(const WORKSPACEID id1, const WORKSPACEID id2) {
    static auto PWORKSPACEWRAPAROUND = CConfigValue<Config::INTEGER>("animations:workspace_wraparound");

    if (!*PWORKSPACEWRAPAROUND)
        return false;

    WORKSPACEID lowestID  = INT64_MAX;
    WORKSPACEID highestID = INT64_MIN;

    for (auto const& w : State::workspaceState()->workspaces()) {
        if (w->m_id < 0 || w->m_isSpecialWorkspace)
            continue;
        lowestID  = std::min(w->m_id, lowestID);
        highestID = std::max(w->m_id, highestID);
    }

    return std::min(id1, id2) == lowestID && std::max(id1, id2) == highestID;
}

void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bool noMouseMove, bool noFocus) {
    if (!pWorkspace)
        return;

    if (pWorkspace->m_isSpecialWorkspace) {
        if (m_activeSpecialWorkspace != pWorkspace) {
            Log::logger->log(Log::DEBUG, "changeworkspace on special, togglespecialworkspace to id {}", pWorkspace->m_id);
            setSpecialWorkspace(pWorkspace, noFocus);
        }
        return;
    }

    if (pWorkspace == m_activeWorkspace)
        return;

    if (!internal) {
        g_pInputManager->unconstrainMouse();
        g_pInputManager->m_emptyFocusCursorSet = false;
        g_pInputManager->releaseAllMouseButtons();
    }

    const auto POLDWORKSPACE = m_activeWorkspace;
    m_activeWorkspace        = pWorkspace;

    if (POLDWORKSPACE) {
        POLDWORKSPACE->m_visible = false;
        POLDWORKSPACE->m_events.activeChanged.emit();
    }

    pWorkspace->m_visible = true;

    if (!internal) {
        const auto ANIMTOLEFT = POLDWORKSPACE && (shouldWraparound(pWorkspace->m_id, POLDWORKSPACE->m_id) ^ (pWorkspace->m_id > POLDWORKSPACE->m_id));
        const auto ANIMSTYLE  = pWorkspace->m_animationStyle;
        if (POLDWORKSPACE)
            Animation::Workspace::startAnimation(POLDWORKSPACE, Animation::Workspace::ANIMATION_TYPE_OUT, ANIMTOLEFT, false, ANIMSTYLE);
        Animation::Workspace::startAnimation(pWorkspace, Animation::Workspace::ANIMATION_TYPE_IN, ANIMTOLEFT, false, ANIMSTYLE);

        // move pinned windows
        for (auto const& w : Desktop::windowState()->windows()) {
            if (w->m_workspace == POLDWORKSPACE && w->m_pinned)
                w->layoutTarget()->assignToSpace(pWorkspace->m_space);
        }

        if (!noFocus && !Desktop::focusState()->monitor()->m_activeSpecialWorkspace &&
            !(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) {
            static auto PFOLLOWMOUSE = CConfigValue<Config::INTEGER>("input:follow_mouse");
            auto pWindow = Fullscreen::controller()->hasFullscreen(pWorkspace) ? Fullscreen::controller()->getFullscreenWindow(pWorkspace) : pWorkspace->getLastFocusedWindow();

            if (!pWindow) {
                if (*PFOLLOWMOUSE == 1)
                    pWindow = Desktop::viewState()->hitTest().windowAt(g_pInputManager->getMouseCoordsInternal(),
                                                                       Desktop::View::RESERVED_EXTENTS | Desktop::View::INPUT_EXTENTS | Desktop::View::ALLOW_FLOATING);

                if (!pWindow)
                    pWindow = pWorkspace->getFocusCandidate();
            }

            Desktop::focusState()->fullWindowFocus(pWindow, Desktop::FOCUS_REASON_WORKSPACE_CHANGE);
        }

        if (!noMouseMove)
            g_pInputManager->simulateMouseMovement();

        g_layoutManager->recalculateMonitor(m_self.lock(), Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_WORKSPACE_CHANGE);

        if (!noFocus) {
            IPC::Socket2::sock()->postEvent({"workspace", pWorkspace->m_name});
            IPC::Socket2::sock()->postEvent({"workspacev2", std::format("{},{}", pWorkspace->m_id, pWorkspace->m_name)});
            Event::bus()->m_events.workspace.active.emit(pWorkspace);
        }
    }

    // set all LSes as not above fullscreen on workspace changes
    for (auto const& ls : Desktop::layerState()->layers()) {
        if (ls->m_monitor == m_self)
            ls->m_aboveFullscreen = false;
    }

    pWorkspace->m_events.activeChanged.emit();

    g_pHyprRenderer->damageMonitor(m_self.lock());

    Animation::Workspace::setFullscreenFadeAnimation(
        pWorkspace, Fullscreen::controller()->hasFullscreen(pWorkspace) ? Animation::Workspace::ANIMATION_TYPE_IN : Animation::Workspace::ANIMATION_TYPE_OUT);

    Config::monitorRuleMgr()->ensureVRR(m_self.lock());

    Desktop::globalWindowController()->updateSuspendedStates();

    if (m_activeSpecialWorkspace)
        Animation::Workspace::setFullscreenFadeAnimation(m_activeSpecialWorkspace,
                                                         Fullscreen::controller()->hasFullscreen(m_activeSpecialWorkspace) ? Animation::Workspace::ANIMATION_TYPE_IN :
                                                                                                                             Animation::Workspace::ANIMATION_TYPE_OUT);
}

void CMonitor::changeWorkspace(const WORKSPACEID& id, bool internal, bool noMouseMove, bool noFocus) {
    changeWorkspace(State::workspaceState()->query().id(id).run(), internal, noMouseMove, noFocus);
}

void CMonitor::setSpecialWorkspaceVisualState(bool active) {
    static auto PDIMSPECIAL  = CConfigValue<Config::FLOAT>("decoration:dim_special");
    static auto PBLURSPECIAL = CConfigValue<Config::INTEGER>("decoration:blur:special");
    static auto PBLUR        = CConfigValue<Config::INTEGER>("decoration:blur:enabled");

    const auto  ANIM = active ? "specialWorkspaceIn" : "specialWorkspaceOut";

    m_specialFade->setConfig(Config::animationTree()->getAnimationPropertyConfig(ANIM));
    *m_specialFade = active ? 1.F : 0.F;
    m_specialDim->setConfig(Config::animationTree()->getAnimationPropertyConfig(ANIM));
    *m_specialDim = active ? *PDIMSPECIAL : 0.F;
    m_specialBlur->setConfig(Config::animationTree()->getAnimationPropertyConfig(ANIM));
    *m_specialBlur = active && *PBLURSPECIAL && *PBLUR ? 1.F : 0.F;
}

void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace, bool noFocus) {
    if (m_activeSpecialWorkspace == pWorkspace)
        return;

    const auto POLDSPECIAL = m_activeSpecialWorkspace;

    setSpecialWorkspaceVisualState(!!pWorkspace);

    g_pHyprRenderer->damageMonitor(m_self.lock());

    if (!pWorkspace) {
        // remove special if exists
        if (m_activeSpecialWorkspace) {
            m_activeSpecialWorkspace->m_visible = false;
            Animation::Workspace::startAnimation(m_activeSpecialWorkspace, Animation::Workspace::ANIMATION_TYPE_OUT, false);
            IPC::Socket2::sock()->postEvent({"activespecial", std::format(",{}", m_name)});
            IPC::Socket2::sock()->postEvent({"activespecialv2", std::format(",,{}", m_name)});

            // Reset layer surface state when closing special workspace
            for (auto const& ls : Desktop::layerState()->layers()) {
                if (ls->m_monitor == m_self)
                    ls->m_aboveFullscreen = false;
            }
        }
        m_activeSpecialWorkspace.reset();

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

        g_layoutManager->recalculateMonitor(m_self.lock(), Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE);

        if (!(noFocus && Desktop::focusState()->monitor() != m_self) &&
            !(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self)) {
            if (const auto PLAST = m_activeWorkspace->getLastFocusedWindow(); PLAST)
                Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE);
            else
                g_pInputManager->refocus();
        }

        Animation::Workspace::setFullscreenFadeAnimation(
            m_activeWorkspace, Fullscreen::controller()->hasFullscreen(m_activeWorkspace) ? Animation::Workspace::ANIMATION_TYPE_IN : Animation::Workspace::ANIMATION_TYPE_OUT);

        Config::monitorRuleMgr()->ensureVRR(m_self.lock());

        Desktop::globalWindowController()->updateSuspendedStates();

        Event::bus()->m_events.workspace.specialActive.emit(nullptr, m_self.lock());

        return;
    }

    if (m_activeSpecialWorkspace) {
        m_activeSpecialWorkspace->m_visible = false;
        Animation::Workspace::startAnimation(m_activeSpecialWorkspace, Animation::Workspace::ANIMATION_TYPE_OUT, false);
    }

    bool wasActive = false;
    //close if open elsewhere
    const auto PMONITOR = pWorkspace->m_monitor.lock();

    if (PMONITOR && PMONITOR->m_activeSpecialWorkspace == pWorkspace) {
        PMONITOR->setSpecialWorkspaceVisualState(false);
        PMONITOR->m_activeSpecialWorkspace.reset();
        g_layoutManager->recalculateMonitor(PMONITOR, Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE);
        g_pHyprRenderer->damageMonitor(PMONITOR);
        if (noFocus &&
            (Desktop::focusState()->monitor() == PMONITOR &&
             !(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == PMONITOR))) {
            // leave focus behind
            if (const auto PLAST = PMONITOR->m_activeWorkspace->getLastFocusedWindow(); PLAST)
                Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE);
            else
                g_pInputManager->refocus();
        }
        IPC::Socket2::sock()->postEvent({"activespecial", std::format(",{}", PMONITOR->m_name)});
        IPC::Socket2::sock()->postEvent({"activespecialv2", std::format(",,{}", PMONITOR->m_name)});

        // Reset layer surfaces on the old monitor when special workspace is stolen
        for (auto const& ls : Desktop::layerState()->layers()) {
            if (ls->m_monitor == PMONITOR)
                ls->m_aboveFullscreen = false;
        }

        const auto PACTIVEWORKSPACE = PMONITOR->m_activeWorkspace;
        Animation::Workspace::setFullscreenFadeAnimation(PACTIVEWORKSPACE,
                                                         PACTIVEWORKSPACE && Fullscreen::controller()->hasFullscreen(PACTIVEWORKSPACE) ? Animation::Workspace::ANIMATION_TYPE_IN :
                                                                                                                                         Animation::Workspace::ANIMATION_TYPE_OUT);

        wasActive = true;
    }

    // open special
    pWorkspace->m_monitor               = m_self;
    m_activeSpecialWorkspace            = pWorkspace;
    m_activeSpecialWorkspace->m_visible = true;

    // Reset layer surface state when opening special workspace
    for (auto const& ls : Desktop::layerState()->layers()) {
        if (ls->m_monitor == m_self)
            ls->m_aboveFullscreen = false;
    }

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

    if (PMONITOR != m_self)
        pWorkspace->m_events.monitorChanged.emit();

    if (!wasActive)
        pWorkspace->m_events.activeChanged.emit();

    if (!wasActive)
        Animation::Workspace::startAnimation(pWorkspace, Animation::Workspace::ANIMATION_TYPE_IN, true);

    for (auto const& w : Desktop::windowState()->windows()) {
        if (w->m_workspace == pWorkspace) {
            w->m_monitor = m_self;
            w->updateSurfaceScaleTransformDetails();
            w->setAnimationsToMove();

            const auto MIDDLE = w->middle();
            if (w->m_isFloating && VECNOTINRECT(MIDDLE, m_position.x, m_position.y, m_position.x + m_size.x, m_position.y + m_size.y) && !w->isX11OverrideRedirect()) {
                // if it's floating and the middle isn't on the current mon, move it to the center
                const auto PMONFROMMIDDLE = State::monitorState()->query().vec(MIDDLE).run();
                Vector2D   pos            = w->position(Desktop::View::IGeometric::GEOMETRIC_GOAL);
                if (VECNOTINRECT(MIDDLE, PMONFROMMIDDLE->m_position.x, PMONFROMMIDDLE->m_position.y, PMONFROMMIDDLE->m_position.x + PMONFROMMIDDLE->m_size.x,
                                 PMONFROMMIDDLE->m_position.y + PMONFROMMIDDLE->m_size.y)) {
                    // not on any monitor, center
                    pos = middle() - w->size(Desktop::View::IGeometric::GEOMETRIC_GOAL) / 2.f;
                } else
                    pos = pos - PMONFROMMIDDLE->m_position + m_position;

                w->layoutTarget()->setPositionGlobal(CBox{pos, w->layoutTarget()->position().size()});
            }
        }
    }

    g_layoutManager->recalculateMonitor(m_self.lock(), Layout::CLayoutManager::RECALCULATE_MONITOR_REASON_TOGGLE_SPECIAL_WORKSPACE);

    if (!noFocus ||
        (Desktop::focusState()->monitor() == m_self &&
         !(Desktop::focusState()->window() && Desktop::focusState()->window()->m_pinned && Desktop::focusState()->window()->m_monitor == m_self))) {
        // focus the workspace we just moved
        if (const auto PLAST = pWorkspace->getLastFocusedWindow(); PLAST)
            Desktop::focusState()->fullWindowFocus(PLAST, Desktop::FOCUS_REASON_TOGGLE_SPECIAL_WORKSPACE);
        else
            g_pInputManager->refocus();
    }

    IPC::Socket2::sock()->postEvent({"activespecial", std::format("{},{}", pWorkspace->m_name, m_name)});
    IPC::Socket2::sock()->postEvent({"activespecialv2", std::format("{},{},{}", pWorkspace->m_id, pWorkspace->m_name, m_name)});

    g_pHyprRenderer->damageMonitor(m_self.lock());

    Animation::Workspace::setFullscreenFadeAnimation(
        pWorkspace, Fullscreen::controller()->hasFullscreen(pWorkspace) ? Animation::Workspace::ANIMATION_TYPE_IN : Animation::Workspace::ANIMATION_TYPE_OUT);

    Config::monitorRuleMgr()->ensureVRR(m_self.lock());

    Desktop::globalWindowController()->updateSuspendedStates();

    Event::bus()->m_events.workspace.specialActive.emit(pWorkspace, m_self.lock());
}

void CMonitor::setSpecialWorkspace(const WORKSPACEID& id, bool noFocus) {
    setSpecialWorkspace(State::workspaceState()->query().id(id).run(), noFocus);
}

PHLWORKSPACE CMonitor::getCurrentWorkspace() {
    const auto MONITOR = m_self.lock();
    if (!MONITOR)
        return nullptr;

    // Check special workspace first as it renders on top of regular workspaces
    if (MONITOR->m_activeSpecialWorkspace)
        return MONITOR->m_activeSpecialWorkspace;

    return MONITOR->m_activeWorkspace;
}

void CMonitor::moveTo(const Vector2D& pos) {
    if (m_position == pos)
        return;

    const auto OLD_POSITION = m_position;

    m_position = pos;

    if (OLD_POSITION == Vector2D{-1, -1})
        return;

    const auto DELTA = pos - OLD_POSITION;

    for (const auto& w : Desktop::windowState()->windows()) {
        if (!validMapped(w) || !w->m_isFloating || w->m_monitor != m_self)
            continue;

        w->layoutTarget()->setPositionGlobal(w->layoutTarget()->position().translate(DELTA));
    }
}

Vector2D CMonitor::middle() const {
    return m_position + m_size / 2.f;
}

const Mat3x3& CMonitor::getTransformMatrix() {
    return m_projMatrix;
}

const Mat3x3& CMonitor::getScaleMatrix() {
    return m_projOutputMatrix;
}

void CMonitor::updateMatrix() {
    m_projMatrix = Mat3x3::identity();
    if (m_transform != WL_OUTPUT_TRANSFORM_NORMAL)
        m_projMatrix.translate(m_pixelSize / 2.0).transform(Math::wlTransformToHyprutils(m_transform)).translate(-m_transformedSize / 2.0);

    m_projOutputMatrix = Mat3x3::outputProjection(m_pixelSize, HYPRUTILS_TRANSFORM_NORMAL);
}

WORKSPACEID CMonitor::activeWorkspaceID() {
    return m_activeWorkspace ? m_activeWorkspace->m_id : 0;
}

WORKSPACEID CMonitor::activeSpecialWorkspaceID() {
    return m_activeSpecialWorkspace ? m_activeSpecialWorkspace->m_id : 0;
}

CBox CMonitor::logicalBox() const {
    return {m_position, m_size};
}

CBox CMonitor::logicalBoxMinusReserved() const {
    return m_reservedArea.apply(logicalBox());
}

void CMonitor::scheduleDone() {
    if (m_doneScheduled)
        return;

    m_doneScheduled = true;

    g_pEventLoopManager->doLater([M = m_self] {
        if (!M) // if M is gone, we got destroyed, doesn't matter.
            return;

        if (!PROTO::outputs.contains(M->m_name))
            return;

        PROTO::outputs.at(M->m_name)->sendDone();
        M->m_doneScheduled = false;
    });
}

void CMonitor::setCTM(const Mat3x3& ctm_) {
    m_ctm        = ctm_;
    m_ctmUpdated = true;
    scheduleFrame(Aquamarine::IOutput::scheduleFrameReason::AQ_SCHEDULE_NEEDS_FRAME);
}

uint32_t CMonitor::isSolitaryBlocked(bool full) {
    uint32_t   reasons = 0;

    const auto PWORKSPACE = m_activeWorkspace;
    if (!PWORKSPACE) {
        reasons |= SC_WORKSPACE;
        return reasons;
    }

    // Monitor considers only FSMODE_FULLSCREEN as FS
    if (Fullscreen::controller()->getFullscreenModes(m_self.lock()).internal != Fullscreen::FSMODE_FULLSCREEN) {
        reasons |= SC_WINDOWED;
        if (!full)
            return reasons;
    }

    if (m_activeSpecialWorkspace) {
        reasons |= SC_SPECIAL;
        if (!full)
            return reasons;
    }

    if (Notification::overlay()->hasAny()) {
        reasons |= SC_NOTIFICATION;
        if (!full)
            return reasons;
    }

    if (ErrorOverlay::overlay()->active() && Desktop::focusState()->monitor() == m_self) {
        reasons |= SC_ERRORBAR;
        if (!full)
            return reasons;
    }

    if (g_pSessionLockManager->isSessionLocked()) {
        reasons |= SC_LOCK;
        if (!full)
            return reasons;
    }

    if (PROTO::data->dndActive()) {
        reasons |= SC_DND;
        if (!full)
            return reasons;
    }

    if (PWORKSPACE->m_alpha->value() != 1.f) {
        reasons |= SC_ALPHA;
        if (!full)
            return reasons;
    }

    if (PWORKSPACE->m_renderOffset->value() != Vector2D{}) {
        reasons |= SC_OFFSET;
        if (!full)
            return reasons;
    }

    const auto PCANDIDATE = Fullscreen::controller()->getFullscreenWindow(m_self.lock());

    if (!PCANDIDATE) {
        reasons |= SC_CANDIDATE;
        return reasons;
    }

    if (!PCANDIDATE->opaque()) {
        reasons |= SC_OPAQUE;
        if (!full)
            return reasons;
    }

    if (PCANDIDATE->size(Desktop::View::IGeometric::GEOMETRIC_CURRENT) != m_size || PCANDIDATE->position(Desktop::View::IGeometric::GEOMETRIC_CURRENT) != m_position ||
        PCANDIDATE->positionAnimation()->isBeingAnimated() || PCANDIDATE->sizeAnimation()->isBeingAnimated()) {
        reasons |= SC_TRANSFORM;
        if (!full)
            return reasons;
    }

    if (!m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY].empty()) {
        reasons |= SC_OVERLAYS;
        if (!full)
            return reasons;
    }

    for (auto const& topls : m_layerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
        if (topls->alpha()[LS_ALPHA_FADE]->value() != 0.F) {
            reasons |= SC_OVERLAYS;
            if (!full)
                return reasons;
        }
    }

    for (auto const& fadeout : Desktop::fadingOutState()->fadeouts()) {
        if (!fadeout || fadeout->monitor() != m_self)
            continue;

        reasons |= SC_FADEOUT;
        if (!full)
            return reasons;
    }

    for (auto const& w : Desktop::windowState()->windows()) {
        if (w == PCANDIDATE || !w->m_isMapped || !w->visible())
            continue;

        if (w->workspaceID() == PCANDIDATE->workspaceID() && w->m_isFloating && w->isAllowedOverFullscreen() && w->visibleOnMonitor(m_self.lock())) {
            reasons |= SC_FLOAT;
            if (!full)
                return reasons;
        }
    }

    for (auto const& ws : State::workspaceState()->workspaces()) {
        if (ws->m_alpha->value() <= 0.F || !ws->m_isSpecialWorkspace || ws->m_monitor != m_self)
            continue;

        reasons |= SC_WORKSPACES;
        if (!full)
            return reasons;
    }

    // check if it did not open any subsurfaces or shit
    if (!PCANDIDATE->getSolitaryResource())
        reasons |= SC_SURFACES;

    return reasons;
}

void CMonitor::recheckSolitary() {
    m_solitaryClient.reset(); // reset it, if we find one it will be set.

    const auto PWORKSPACE = m_activeWorkspace;
    if (!PWORKSPACE)
        return;

    if (isSolitaryBlocked())
        return;

    m_solitaryClient = Fullscreen::controller()->getFullscreenWindow(m_self.lock());
}

uint8_t CMonitor::isTearingBlocked(bool full) {
    uint8_t     reasons = 0;

    static auto PTEARINGENABLED = CConfigValue<Config::INTEGER>("general:allow_tearing");

    if (!m_tearingState.nextRenderTorn) {
        reasons |= TC_NOT_TORN;
        if (!full)
            return reasons;
    }

    if (!*PTEARINGENABLED) {
        reasons |= TC_USER;
        if (!full) {
            Log::logger->log(Log::WARN, "Tearing commit requested but the master switch general:allow_tearing is off, ignoring");
            return reasons;
        }
    }

    if (g_pHyprRenderer->m_renderData.mouseZoomFactor != 1.0) {
        reasons |= TC_ZOOM;
        if (!full) {
            Log::logger->log(Log::WARN, "Tearing commit requested but scale factor is not 1, ignoring");
            return reasons;
        }
    }

    if (!m_tearingState.canTear) {
        reasons |= TC_SUPPORT;
        if (!full) {
            Log::logger->log(Log::WARN, "Tearing commit requested but monitor doesn't support it, ignoring");
            return reasons;
        }
    }

    // TODO: remove this when kernel allows tearing + hw cursor updated
    if (Pointer::mgr()->hasVisibleHWCursor(m_self.lock()))
        reasons |= TC_HW_CURSOR;

    if (m_solitaryClient.expired()) {
        reasons |= TC_CANDIDATE;
        return reasons;
    }

    if (!m_solitaryClient->canBeTorn())
        reasons |= TC_WINDOW;

    return reasons;
}

void CMonitor::updateSurfaceScaleTransformDetails() {
    for (auto const& w : Desktop::windowState()->windows()) {
        if (w->m_monitor == m_self)
            w->updateSurfaceScaleTransformDetails();
    }

    for (auto const& lsl : m_layerSurfaceLayers) {
        for (auto const& ls : lsl) {
            ls->updateSurfaceScaleTransformDetails();
        }
    }
}

bool CMonitor::updateTearing() {
    m_tearingState.activelyTearing = !isTearingBlocked();
    m_tearingState.nextRenderTorn  = false;
    return m_tearingState.activelyTearing;
}

uint16_t CMonitor::isDSBlocked(bool full) {
    uint16_t    reasons        = 0;
    static auto PDIRECTSCANOUT = CConfigValue<Config::INTEGER>("render:direct_scanout");
    static auto PNONSHADER     = CConfigValue<Config::INTEGER>("render:non_shader_cm");
    const auto  PWORKSPACE     = m_activeWorkspace;

    // Fast reject for the hot render path; full=true callers still collect
    // the remaining blockers for hyprctl/debug output below.
    if (!canAttemptDirectScanoutFast()) {
        reasons |= DS_BLOCK_CANDIDATE;
        if (!full)
            return reasons;
    }

    if (*PDIRECTSCANOUT == 0) {
        reasons |= DS_BLOCK_USER;
        if (!full)
            return reasons;
    }

    if (*PDIRECTSCANOUT == 2) {
        const auto FSWINDOW = Fullscreen::controller()->getFullscreenWindow(m_self.lock());

        if (!PWORKSPACE || !FSWINDOW || Fullscreen::controller()->getFullscreenModes(FSWINDOW).internal != Fullscreen::FSMODE_FULLSCREEN) {
            reasons |= DS_BLOCK_WINDOWED;
            if (!full)
                return reasons;
        } else if (FSWINDOW->getContentType() != CONTENT_TYPE_GAME) {
            reasons |= DS_BLOCK_CONTENT;
            if (!full)
                return reasons;
        }
    }

    if (!m_mirrors.empty() || isMirror()) {
        reasons |= DS_BLOCK_MIRROR;
        if (!full)
            return reasons;
    }

    if (g_pHyprRenderer->m_directScanoutBlocked) {
        reasons |= DS_BLOCK_RECORD;
        if (!full)
            return reasons;
    }

    if (Pointer::mgr()->softwareLockedFor(m_self.lock())) {
        reasons |= DS_BLOCK_SW;
        if (!full)
            return reasons;
    }

    const auto PCANDIDATE = m_solitaryClient.lock();
    if (!PCANDIDATE) {
        reasons |= DS_BLOCK_CANDIDATE;
        return reasons;
    }

    if (PCANDIDATE->m_transformers.blocksDirectScanout()) {
        reasons |= DS_BLOCK_TRANSFORM;
        if (!full)
            return reasons;
    }

    const auto PSURFACE = PCANDIDATE->getSolitaryResource();
    if (!PSURFACE || !PSURFACE->m_current.texture || !PSURFACE->m_current.buffer) {
        reasons |= DS_BLOCK_SURFACE;
        return reasons;
    }

    if (PSURFACE->m_current.bufferSize != m_pixelSize || PSURFACE->m_current.transform != m_transform) {
        reasons |= DS_BLOCK_TRANSFORM;
        if (!full)
            return reasons;
    }

    // we can't scanout shm buffers.
    const auto params = PSURFACE->m_current.buffer->dmabuf();
    if (!params.success || !PSURFACE->m_current.texture->isDMA() /* dmabuf */) {
        reasons |= DS_BLOCK_DMA;
        if (!full)
            return reasons;
    }

    const bool surfaceIsHDR   = PSURFACE->m_colorManagement.valid() && PSURFACE->m_colorManagement->isHDR();
    const bool surfaceIsScRGB = surfaceIsHDR && PSURFACE->m_colorManagement->isWindowsScRGB();

    if (surfaceIsScRGB)
        reasons |= DS_BLOCK_CM; // block scRGB
    else if (*PNONSHADER != CM_NS_IGNORE) {
        if (!surfaceIsHDR && needsCM() && !canNoShaderCM(true))
            reasons |= DS_BLOCK_CM; // block SDR that needs CM while non-shader CM isn't available
        else if (surfaceIsHDR && !inHDR())
            reasons |= DS_BLOCK_CM; // block HDR while monitor isn't in HDR mode
    }

    return reasons;
}

bool CMonitor::attemptDirectScanout() {
    static const auto PSAME     = CConfigValue<Config::INTEGER>("debug:ds_handle_same_buffer");
    static const auto PSAMEFIFO = CConfigValue<Config::INTEGER>("debug:ds_handle_same_buffer_fifo");

    const auto        blockedReason = isDSBlocked();
    if (blockedReason)
        return false;

    const auto PCANDIDATE = m_solitaryClient.lock();
    const auto PSURFACE   = PCANDIDATE->getSolitaryResource();
    auto       PBUFFER    = PSURFACE->m_current.buffer.m_buffer;

    // #TODO this entire bit needs figuring out, vrr goes down the drain without it
    if (PBUFFER == m_output->state->state().buffer && *PSAME) {
        PSURFACE->presentFeedback(Time::steadyNow(), m_self.lock());

        if (m_scanoutNeedsCursorUpdate) {
            if (!m_state.test()) {
                Log::logger->log(Log::TRACE, "attemptDirectScanout: failed basic test on cursor update");
                return false;
            }

            if (!m_output->commit()) {
                Log::logger->log(Log::TRACE, "attemptDirectScanout: failed to commit cursor update");
                m_lastScanout.reset();
                return false;
            }

            m_scanoutNeedsCursorUpdate = false;
        }

        //#TODO this entire bit is bootleg deluxe, above bit is to not make vrr go down the drain, returning early here means fifo gets forever locked.
        if (PSURFACE->m_fifo && !m_tearingState.activelyTearing && *PSAMEFIFO)
            PSURFACE->m_stateQueue.unlockFirst(LOCK_REASON_FIFO);

        return true;
    }

    const auto params = PSURFACE->m_current.buffer->dmabuf();

    Log::logger->log(Log::TRACE, "attemptDirectScanout: surface {:x} passed, will attempt, buffer {} fmt: {} -> {} (mod {})", rc<uintptr_t>(PSURFACE.get()),
                     rc<uintptr_t>(PSURFACE->m_current.buffer.m_buffer.get()), m_drmFormat, params.format, params.modifier);

    // FIXME: make sure the buffer actually follows the available scanout dmabuf formats
    // and comes from the appropriate device. This may implode on multi-gpu!!

    // entering into scanout, so save monitor format
    if (m_lastScanout.expired())
        m_prevDrmFormat = m_drmFormat;

    const auto  previousFormat           = m_drmFormat;
    const auto  previousBuffer           = m_output->state->state().buffer;
    const auto  previousPresentationMode = m_output->state->state().presentationMode;
    bool        scanoutCommitted         = false;
    CScopeGuard rollbackState            = {[this, previousFormat, previousBuffer, previousPresentationMode, &scanoutCommitted]() {
        if (scanoutCommitted)
            return;

        m_drmFormat = previousFormat;
        m_output->state->setFormat(previousFormat);
        m_output->state->setBuffer(previousBuffer);
        m_output->state->setPresentationMode(previousPresentationMode);
        m_output->state->resetExplicitFences();
    }};

    const bool  NEEDS_TEST = !m_lastScanout || m_drmFormat != params.format; // do not retest while it's active
    if (m_drmFormat != params.format) {
        m_output->state->setFormat(params.format);
        m_drmFormat = params.format;
    }

    m_output->state->setBuffer(PBUFFER);
    Log::logger->log(Log::TRACE, "attemptDirectScanout: setting presentation mode");
    m_output->state->setPresentationMode(m_tearingState.activelyTearing ? Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE :
                                                                          Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_VSYNC);

    if (NEEDS_TEST && !m_state.test()) {
        Log::logger->log(Log::TRACE, "attemptDirectScanout: failed basic test");
        return false;
    }

    PSURFACE->presentFeedback(Time::steadyNow(), m_self.lock());

    // the commit path already added the damage into the damagering
    CRegion scanoutDamage = m_damage.getBufferDamage(1);
    // the ring is in transformed space, the fb we hand to KMS is in pixel space
    scanoutDamage.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_transform)), m_transformedSize.x, m_transformedSize.y);
    // expand to not miss pixels from rounding, being a bit over is safe, going below is stale pixels.
    scanoutDamage.expand(1).intersect(CBox{{}, m_pixelSize});
    m_output->state->addDamage(scanoutDamage);

    // multigpu needs a fence to trigger fence syncing blits and also committing with the recreated dgpu fence
    if (g_pHyprRenderer->explicitSyncSupported() && isMultiGPU()) {
        auto sync = g_pHyprRenderer->createSyncFDManager();

        if (sync && sync->isValid()) {
            m_inFence = sync->takeFd();
            m_output->state->setExplicitInFence(m_inFence.get());
        } else {
            m_inFence.reset();
            m_output->state->resetExplicitFences(); // good luck.
        }
    } else
        m_output->state->resetExplicitFences();

    // no need to do explicit sync here as surface current can only ever be ready to read

    bool ok = m_output->commit();

    if (!ok) {
        Log::logger->log(Log::TRACE, "attemptDirectScanout: failed to scanout surface");
        m_lastScanout.reset();
        return false;
    }

    scanoutCommitted = true;

    // the flip has used the damage, rotate it.
    m_damage.rotate();

    if (m_lastScanout.expired()) {
        m_lastScanout = PCANDIDATE;
        Log::logger->log(Log::DEBUG, "Entered a direct scanout to {:x}: \"{}\"", rc<uintptr_t>(PCANDIDATE.get()), PCANDIDATE->m_title);
    }

    m_scanoutNeedsCursorUpdate = false;

    if (!PBUFFER->lockedByBackend || PBUFFER->m_hlEvents.backendRelease)
        return true;

    // lock buffer while DRM/KMS is using it, then release it when page flip happens since DRM/KMS should be done by then
    // btw buffer's syncReleaser will take care of signaling release point, so we don't do that here
    PBUFFER->lock();
    PBUFFER->onBackendRelease([wb = WP<IHLBuffer>{PBUFFER}] {
        if (wb)
            wb->unlock();
    });

    return true;
}

void CMonitor::handleDSleave() {
    Log::logger->log(Log::DEBUG, "Left a direct scanout.");
    m_lastScanout.reset();
    m_previousFSWindow.reset(); // recalc fs settings
    m_directScanoutIsActive = false;

    // reset DRM format, but only if needed since it might modeset
    if (m_output->state->state().drmFormat != m_prevDrmFormat)
        m_output->state->setFormat(m_prevDrmFormat);

    m_drmFormat   = m_prevDrmFormat;
    m_blurFBDirty = true;
    m_damage.damageEntire();
}

bool CMonitor::canAttemptDirectScanoutFast() const {
    return !m_solitaryClient.expired() || !m_lastScanout.expired() || m_directScanoutIsActive;
}

bool CMonitor::isMultiGPU() {
    if (!m_output || !g_pCompositor)
        return false;

    const auto PREFERREDALLOCATOR = m_output->getBackend()->preferredAllocator();
    const int  allocatorFD        = PREFERREDALLOCATOR ? PREFERREDALLOCATOR->drmFD() : -1;
    const int  compositorFD       = g_pCompositor->m_drm.fd;

    if (allocatorFD < 0 || compositorFD < 0) {
        m_cachedAllocatorDRMDev.reset();
        m_cachedCompositorDRMDev.reset();
        m_cachedAllocatorDRMFD  = allocatorFD;
        m_cachedCompositorDRMFD = compositorFD;
        m_cachedSameGPU         = true;
        return false;
    }

    const auto allocatorDev  = DRM::devIDFromFD(allocatorFD);
    const auto compositorDev = DRM::devIDFromFD(compositorFD);

    // AQ can reopen DRM nodes for refcounting, so raw fd numbers are not a stable cache key.
    const bool useDeviceIDCache = allocatorDev.has_value() && compositorDev.has_value();
    const bool cacheStale       = !m_cachedSameGPU ||
        (useDeviceIDCache ? m_cachedAllocatorDRMDev != allocatorDev || m_cachedCompositorDRMDev != compositorDev :
                            m_cachedAllocatorDRMFD != allocatorFD || m_cachedCompositorDRMFD != compositorFD);

    if (cacheStale) {
        m_cachedAllocatorDRMDev  = allocatorDev;
        m_cachedCompositorDRMDev = compositorDev;
        m_cachedAllocatorDRMFD   = allocatorFD;
        m_cachedCompositorDRMFD  = compositorFD;
        m_cachedSameGPU          = DRM::sameGpu(allocatorFD, compositorFD);
    }

    return !*m_cachedSameGPU;
}

bool CMonitor::shouldUseSoftwareCursors() {
    static auto PNOHW      = CConfigValue<Config::INTEGER>("cursor:no_hardware_cursors");
    static auto PINVISIBLE = CConfigValue<Config::INTEGER>("cursor:invisible");

    if (m_tearingState.activelyTearing)
        return true;

    if (*PINVISIBLE != 0)
        return true;

    switch (*PNOHW) {
        case 0: return false;
        case 1: return true;
        case 2: return g_pHyprRenderer->isNvidia() && (g_pHyprRenderer->isMgpu() || State::monitorLayoutController()->isVRRActiveOnAnyMonitor());
        default: break;
    }

    return true;
}

void CMonitor::setDPMS(bool on) {
    // Don't trigger animation if the target state is the same
    if (m_dpmsStatus == on)
        return;

    m_dpmsStatus = on;
    m_events.dpmsChanged.emit();

    if (on) {
        // enable the monitor. Wait for the frame to be presented, then begin animation
        m_dpmsBlackOpacity->setCallbackOnEnd(nullptr);
        m_dpmsBlackOpacity->setValueAndWarp(1.F);
        m_pendingDpmsAnimation        = true;
        m_pendingDpmsAnimationCounter = 0;
        commitDPMSState(true);
    } else {
        // disable the monitor. Begin the animation, then do dpms on its end.
        m_dpmsBlackOpacity->setCallbackOnEnd(nullptr);
        m_dpmsBlackOpacity->setValueAndWarp(0.F);
        *m_dpmsBlackOpacity = 1.F;
        m_dpmsBlackOpacity->setCallbackOnEnd(
            [this, self = m_self](auto) {
                if (!self)
                    return;

                // commit DPMS to disable the monitor, it's fully black now
                commitDPMSState(false);
                m_usedAsyncBuffers.clear();
            },
            true);
    }
}

void CMonitor::commitDPMSState(bool state) {
    m_output->state->resetExplicitFences();
    m_output->state->setEnabled(state);
    if (!state)
        m_usedAsyncBuffers.clear();

    if (!m_state.commit()) {
        Log::logger->log(Log::ERR, "Couldn't commit output {} for DPMS = {}, will retry.", m_name, state);

        // retry in 2 frames. This could happen when the DRM backend rejects our commit
        // because disable + enable were sent almost instantly

        m_dpmsRetryTimer = makeShared<CEventLoopTimer>(
            std::chrono::milliseconds(2000 / sc<int>(m_refreshRate)),
            [this, self = m_self](SP<CEventLoopTimer> s, void* d) {
                if (!self)
                    return;

                m_output->state->resetExplicitFences();
                m_output->state->setEnabled(m_dpmsStatus);
                if (!m_dpmsStatus)
                    m_usedAsyncBuffers.clear();

                if (!m_state.commit()) {
                    Log::logger->log(Log::ERR, "Couldn't retry committing output {} for DPMS = {}", m_name, m_dpmsStatus);
                    return;
                }

                m_dpmsRetryTimer.reset();
            },
            nullptr);
        g_pEventLoopManager->addTimer(m_dpmsRetryTimer);

        return;
    }

    if (state)
        g_pHyprRenderer->damageMonitor(m_self.lock());
}

void CMonitor::debugLastPresentation(const std::string& message) {
    Log::logger->log(Log::TRACE, "{} (last presentation {} - {} fps)", message, m_lastPresentationTimer.getMillis(),
                     m_lastPresentationTimer.getMillis() > 0 ? 1000.0f / m_lastPresentationTimer.getMillis() : 0.0f);
}

void CMonitor::onCursorMovedOnMonitor() {
    if (!m_tearingState.activelyTearing || !m_solitaryClient || !g_pHyprRenderer->shouldRenderCursor())
        return;

    // submit a frame immediately. This will only update the cursor pos.
    // output->state->setBuffer(output->state->state().buffer);
    // output->state->addDamage(CRegion{});
    // output->state->setPresentationMode(Aquamarine::eOutputPresentationMode::AQ_OUTPUT_PRESENTATION_IMMEDIATE);
    // if (!output->commit())
    //     Log::logger->log(Log::ERR, "onCursorMovedOnMonitor: tearing and wanted to update cursor, failed.");

    // FIXME: try to do the above. We currently can't just render because drm is a fucking bitch
    // and throws a "nO pRoP cAn Be ChAnGeD dUrInG AsYnC fLiP" on crtc_x
    // this will throw too but fix it if we use sw cursors

    m_tearingState.frameScheduledWhileBusy = true;
}

bool CMonitor::supportsWideColor() {
    switch (m_supportsWideColor) {
        case -1: return false;
        case 1: return true;
        default: return m_output->parsedEDID.supportsBT2020;
    }
}

bool CMonitor::supportsHDR() {
    if (!supportsWideColor())
        return false;

    switch (m_supportsHDR) {
        case -1: return false;
        case 1: return true;
        default: return m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->supportsPQ : false;
    }
}

float CMonitor::minLuminance(float defaultValue) {
    return m_minLuminance >= 0 ? m_minLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredContentMinLuminance : defaultValue);
}

int CMonitor::maxLuminance(int defaultValue) {
    return m_maxLuminance >= 0 ? m_maxLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance : defaultValue);
}

int CMonitor::maxAvgLuminance(int defaultValue) {
    return m_maxAvgLuminance >= 0 ? m_maxAvgLuminance :
                                    (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance : defaultValue);
}

float CMonitor::maxFALL() {
    return m_maxAvgLuminance >= 0 ? m_maxAvgLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance : 0);
}

float CMonitor::maxCLL() {
    return m_maxLuminance >= 0 ? m_maxLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance : 0);
}

bool CMonitor::wantsWideColor() {
    return supportsWideColor() && (wantsHDR() || m_imageDescription->value().primariesNamed == CM_PRIMARIES_BT2020);
}

bool CMonitor::wantsHDR() {
    return supportsHDR() && inHDR();
}

bool CMonitor::inHDR() {
    return m_output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2;
}

std::optional<NColorManagement::PImageDescription> CMonitor::getFSImageDescription() {

    const auto FS_WINDOW = Fullscreen::controller()->getFullscreenWindow(m_self.lock());

    if (!FS_WINDOW)
        return {};

    const auto ROOT_SURF = FS_WINDOW->wlSurface()->resource();
    const auto SURF      = ROOT_SURF->findWithCM();
    return SURF ? NColorManagement::CImageDescription::from(SURF->m_colorManagement->imageDescription()) : getDefaultImageDescription();
}

NColorManagement::SPCPRimaries CMonitor::getMasteringPrimaries() {
    return m_output->parsedEDID.chromaticityCoords.has_value() ?
        NColorManagement::SPCPRimaries{
            .red   = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y},
            .green = {.x = m_output->parsedEDID.chromaticityCoords->green.x, .y = m_output->parsedEDID.chromaticityCoords->green.y},
            .blue  = {.x = m_output->parsedEDID.chromaticityCoords->blue.x, .y = m_output->parsedEDID.chromaticityCoords->blue.y},
            .white = {.x = m_output->parsedEDID.chromaticityCoords->white.x, .y = m_output->parsedEDID.chromaticityCoords->white.y},
        } :
        NColorManagement::SPCPRimaries{};
}

NColorManagement::SImageDescription::SPCMasteringLuminances CMonitor::getMasteringLuminances() {
    return {
        .min = m_minLuminance >= 0 ? m_minLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredContentMinLuminance : 0),
        .max = m_maxLuminance >= 0 ? m_maxLuminance : (m_output->parsedEDID.hdrMetadata.has_value() ? m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance : 0),
    };
}

uint32_t CMonitor::getPreferredReadFormat() {
    static const auto PFORCE8BIT = CConfigValue<Config::INTEGER>("misc:screencopy_force_8b");

    auto              monFmt = m_output->state->state().drmFormat;

    if (*PFORCE8BIT)
        if (monFmt == DRM_FORMAT_BGRA1010102 || monFmt == DRM_FORMAT_ARGB2101010 || monFmt == DRM_FORMAT_XRGB2101010 || monFmt == DRM_FORMAT_BGRX1010102 ||
            monFmt == DRM_FORMAT_XBGR2101010)
            monFmt = DRM_FORMAT_XRGB8888;

    return monFmt;
}

bool CMonitor::needsCM() {
    const auto SRC_DESC = getFSImageDescription();
    return SRC_DESC.has_value() && SRC_DESC.value() != m_imageDescription;
}

static bool isCompatibleTF(eTransferFunction sourceTF, eTransferFunction targetTF) {
    static auto PNONSHADER = CConfigValue<Hyprlang::INT>("render:non_shader_cm");
    const auto  sdrEOTF    = NTransferFunction::fromConfig();
    return sourceTF == targetTF                                                                                                         // same
        || (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && sourceTF == NColorManagement::CM_TRANSFER_FUNCTION_SRGB                  // forced source gamma22 to output gamma22
            && targetTF == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22)                                                              //
        || (*PNONSHADER == CM_NS_ONDEMAND                                                                                               // FIXME incorrect but good enough for DS
            && (sourceTF == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 || sourceTF == NColorManagement::CM_TRANSFER_FUNCTION_SRGB)  //
            && (targetTF == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 || targetTF == NColorManagement::CM_TRANSFER_FUNCTION_SRGB)) //
        ;
}

// TODO support more drm properties
bool CMonitor::canNoShaderCM(bool forDSmode) {
    static auto PNONSHADER = CConfigValue<Config::INTEGER>("render:non_shader_cm");
    if (*PNONSHADER == CM_NS_DISABLE)
        return false;

    const auto SRC_DESC = getFSImageDescription();
    if (!SRC_DESC.has_value())
        return false;

    const auto& DST_DESC = forDSmode ? m_imageDescription : resources()->m_imageDescription;
    if (SRC_DESC.value() == DST_DESC)
        return true; // no CM needed

    const auto& SRC_DESC_VALUE = SRC_DESC.value()->value();

    if (m_imageDescription->value().icc.present)
        return false;

    Log::logger->log(Log::TRACE, "CM: can no shder compares src={} to output={}", SRC_DESC_VALUE, m_imageDescription->value());

    // only primaries differ
    return (
        isCompatibleTF(SRC_DESC_VALUE.transferFunction, DST_DESC->value().transferFunction) //
        && SRC_DESC_VALUE.transferFunctionPower == DST_DESC->value().transferFunctionPower  //
        && (!inHDR() || SRC_DESC_VALUE.luminances == DST_DESC->value().luminances)
        // not used by shaders atm
        // && SRC_DESC_VALUE.masteringLuminances == m_imageDescription->value().masteringLuminances && SRC_DESC_VALUE.maxCLL == m_imageDescription->value().maxCLL && SRC_DESC_VALUE.maxFALL == m_imageDescription->value().maxFALL
    );
}

bool CMonitor::doesNoShaderCM() {
    return m_noShaderCTM;
}

static std::vector<uint16_t> resampleInterleavedToKms(const SVCGTTable16& t, size_t gammaSize) {
    const size_t entries = sc<size_t>(t.entries);

    if (gammaSize == 0 || entries == 0)
        return {};

    if (t.ch[0].size() < entries || t.ch[1].size() < entries || t.ch[2].size() < entries)
        return {};

    std::vector<uint16_t> out(gammaSize * 3);

    // No resampling is needed when the VCGT already matches the KMS LUT size.
    if (gammaSize == entries) {
        const uint16_t* __restrict red   = t.ch[0].data();
        const uint16_t* __restrict green = t.ch[1].data();
        const uint16_t* __restrict blue  = t.ch[2].data();
        uint16_t* __restrict dst         = out.data();

        for (size_t i = 0; i < gammaSize; ++i) {
            const size_t dstIndex = i * 3;

            dst[dstIndex + 0] = red[i];
            dst[dstIndex + 1] = green[i];
            dst[dstIndex + 2] = blue[i];
        }

        return out;
    }

    auto sample = [&](int c, float x) -> uint16_t {
        const float maxX = t.entries - 1;
        x                = std::clamp(x, 0.F, maxX);

        const size_t i0 = sc<size_t>(std::floor(x));
        const size_t i1 = std::min(i0 + 1, sc<size_t>(t.entries - 1));
        const float  f  = x - sc<float>(i0);

        const float  v0 = sc<float>(t.ch[c][i0]);
        const float  v1 = sc<float>(t.ch[c][i1]);
        const float  v  = v0 + ((v1 - v0) * f);

        int64_t      vi = std::round(v);
        vi              = std::clamp(vi, sc<int64_t>(0), sc<int64_t>(65535));

        return sc<uint16_t>(vi);
    };

    for (size_t i = 0; i < gammaSize; ++i) {
        const float x = sc<float>(i) * sc<float>(t.entries - 1) / sc<float>(gammaSize - 1);

        out[i * 3 + 0] = sample(0, x);
        out[i * 3 + 1] = sample(1, x);
        out[i * 3 + 2] = sample(2, x);
    }

    return out;
}

void CMonitor::updateVCGTRamps() {
    auto gammaSize = m_output->getGammaSize();

    if (gammaSize <= 10) {
        Log::logger->log(Log::DEBUG, "CMonitor::updateVCGTRamps: skipping, no gamma ramp for output");
        return;
    }

    if (!m_imageDescription->value().icc.vcgt) {
        if (m_vcgtRampsSet)
            m_output->state->setGammaLut({});

        m_vcgtRampsSet = false;
        return;
    }

    // build table
    auto table = resampleInterleavedToKms(*m_imageDescription->value().icc.vcgt, gammaSize);

    m_output->state->setGammaLut(table);

    m_vcgtRampsSet = true;
}

bool CMonitor::gammaRampsInUse() {
    return m_vcgtRampsSet;
}

CMonitorState::CMonitorState(CMonitor* owner) : m_owner(owner) {
    ;
}

void CMonitorState::ensureBufferPresent() {
    const auto& STATE = m_owner->m_output->state->state();
    if (!STATE.enabled) {
        Log::logger->log(Log::TRACE, "CMonitorState::ensureBufferPresent: Ignoring, monitor is not enabled");
        return;
    }

    if (STATE.buffer) {
        if (const auto params = STATE.buffer->dmabuf(); params.success && params.format == m_owner->m_drmFormat)
            return;
    }

    // this is required for modesetting being possible and might be missing in case of first tests in the renderer
    // where we test modes and buffers
    Log::logger->log(Log::DEBUG, "CMonitorState::ensureBufferPresent: no buffer or mismatched format, attaching one from the swapchain for modeset being possible");
    m_owner->m_output->state->setBuffer(m_owner->m_output->swapchain->next(nullptr));
    m_owner->m_output->swapchain->rollback(); // restore the counter, don't advance the swapchain
}

bool CMonitorState::commit() {
    if (!updateSwapchain())
        return false;

    Event::bus()->m_events.monitor.preCommit.emit(m_owner->m_self.lock());

    ensureBufferPresent();

    bool ret = m_owner->m_output->commit();
    return ret;
}

bool CMonitorState::test() {
    if (!updateSwapchain())
        return false;

    ensureBufferPresent();

    return m_owner->m_output->test();
}

bool CMonitorState::updateSwapchain() {
    if (!m_owner->m_output)
        return false;

    const auto& OPTIONS = m_owner->m_output->swapchain->currentOptions();
    const auto& STATE   = m_owner->m_output->state->state();
    const auto& MODE    = STATE.mode ? STATE.mode : STATE.customMode;
    if (!MODE) {
        Log::logger->log(Log::WARN, "updateSwapchain: No mode?");
        return true;
    }

    if (OPTIONS.format == m_owner->m_drmFormat && OPTIONS.scanout && OPTIONS.length == 3 && OPTIONS.size == MODE->pixelSize)
        return true;

    auto options    = OPTIONS;
    options.format  = m_owner->m_drmFormat;
    options.scanout = true;
    options.length  = 3;
    options.size    = MODE->pixelSize;
    return m_owner->m_output->swapchain->reconfigure(options);
}
void CMonitorState::applyModeWithSwapchain(const SP<Aquamarine::SOutputMode>& mode) {
    m_owner->m_output->state->setMode(mode);
    updateSwapchain();
}

void CMonitorState::applyCustomModeWithSwapchain(const SP<Aquamarine::SOutputMode>& mode) {
    m_owner->m_output->state->setCustomMode(mode);
    updateSwapchain();
}

bool CMonitor::needsACopyFB() {
    return !m_mirrors.empty() || Screenshare::mgr()->outputNeedsCopyFB(m_self.lock());
}

bool CMonitor::needsUnmodifiedCopy() {
    static const auto PKEEP = CConfigValue<Hyprlang::INT>("render:keep_unmodified_copy");
    if (*PKEEP == 1)
        return true;

    const bool HAS_MODS = m_sdrMinLuminance != SDR_MIN_LUMINANCE || m_sdrMaxLuminance != SDR_MAX_LUMINANCE || (m_sdrBrightness > 0 && m_sdrBrightness != 1.0) ||
        (m_sdrSaturation > 0 && m_sdrSaturation != 1.0);

    if (!HAS_MODS)
        return false;

    // TODO handle some FP16 cases
    if (m_imageDescription->value().transferFunction != CM_TRANSFER_FUNCTION_ST2084_PQ && m_imageDescription->value().transferFunction != CM_TRANSFER_FUNCTION_HLG)
        return false;

    return *PKEEP == 2 ? true : needsACopyFB();
}

bool CMonitor::useFP16() {
    static const auto PFP16 = CConfigValue<Hyprlang::INT>("render:use_fp16");

    auto              isSRGB = [this] {
        if (m_imageDescription->value().transferFunction != CM_TRANSFER_FUNCTION_SRGB && m_imageDescription->value().transferFunction != CM_TRANSFER_FUNCTION_GAMMA22)
            return false;

        if (m_imageDescription->value().primariesNamed != CM_PRIMARIES_SRGB)
            return false;

        return true;
    };

    // Auto: use FP16 if the monitor is not sRGB or is 10 bit
    bool        shouldUse  = g_pHyprRenderer->fp16Supported() && (*PFP16 == 1 || (*PFP16 == 2 && (!isSRGB() || m_enabled10bit)));
    static bool usedBefore = shouldUse;
    if (usedBefore != shouldUse) {
        usedBefore    = shouldUse;
        m_blurFBDirty = true;
    }
    return shouldUse;
}

PImageDescription CMonitor::workBufferImageDescription() {
    static const auto PFP16TF = CConfigValue<Hyprlang::INT>("render:fp16_sdr_tf");

    if (!useFP16() && !m_imageDescription->value().icc.present)
        return m_imageDescription;

    const auto& value = m_imageDescription->value();

    const bool  isHDRLikeTF =
        value.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ || value.transferFunction == CM_TRANSFER_FUNCTION_HLG || value.transferFunction == CM_TRANSFER_FUNCTION_EXT_LINEAR;

    const auto& cached = m_cachedInternalDescription->value();

    // HDR
    if (isHDRLikeTF || *PFP16TF != 0) {
        if (cached.transferFunction != LINEAR_IMAGE_DESCRIPTION->value().transferFunction || cached.luminances != value.luminances)
            m_cachedInternalDescription = LINEAR_IMAGE_DESCRIPTION->with(value.luminances);
        return m_cachedInternalDescription;
    }

    // SDR
    if (cached.transferFunction != chooseTF(m_sdrEotf))
        m_cachedInternalDescription = CImageDescription::from(SImageDescription{
            .transferFunction = chooseTF(m_sdrEotf),
            .primariesNameSet = true,
            // render:keep_unmodified_copy and other conditions that trigger MRT for screen sharing expect a work buffer with sRGB primaries
            .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB,
            .primaries      = NColorPrimaries::BT709,
        });

    return m_cachedInternalDescription;
}

WP<CMonitorResources> CMonitor::resources() {
    const auto DRM_FORMAT = useFP16() ? DRM_FORMAT_ABGR16161616F : m_output->state->state().drmFormat;
    const auto DESC       = workBufferImageDescription();

    if (!m_resources || m_resources->m_drmFormat != DRM_FORMAT || m_resources->m_size != m_transformedSize)
        m_resources = makeUnique<CMonitorResources>(m_self, DRM_FORMAT, m_transformedSize, DESC);

    if (m_resources->m_imageDescription != DESC)
        m_resources->setImageDescription(DESC);

    return m_resources;
}

bool CMonitor::trySetFormat(std::span<const uint32_t> formats) {
    for (auto fmt : formats) {
        m_output->state->setFormat(fmt);
        m_prevDrmFormat = m_drmFormat;
        m_drmFormat     = fmt;

        const auto fmtName = drmFormatToString(fmt);
        if (!m_state.test()) {
            Log::logger->log(Log::ERR, "output {} failed basic test on format {}", m_name, fmtName);
        } else {
            Log::logger->log(Log::DEBUG, "output {} succeeded basic test on format {}", m_name, fmtName);
            return true;
        }
    }
    return false;
}
