summaryrefslogtreecommitdiff
path: root/src/Compositor.cpp
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2024-07-21 13:09:54 +0200
committerGitHub <noreply@github.com>2024-07-21 13:09:54 +0200
commit016da234d0e852de3ef20eb2e89ac58d2a85f6e7 (patch)
tree496085987176551b0794fffc069877d44943e2f1 /src/Compositor.cpp
parentf642fb97df5c69267a03452533de383ff8023570 (diff)
Core: Move to aquamarine (#6608)
Moves Hyprland from wlroots to aquamarine for the backend. --------- Signed-off-by: Vaxry <vaxry@vaxry.net> Co-authored-by: Mihai Fufezan <mihai@fufexan.net> Co-authored-by: Jan Beich <jbeich@FreeBSD.org> Co-authored-by: vaxerski <vaxerski@users.noreply.github.com> Co-authored-by: UjinT34 <41110182+UjinT34@users.noreply.github.com> Co-authored-by: Tom Englund <tomenglund26@gmail.com> Co-authored-by: Ikalco <73481042+ikalco@users.noreply.github.com> Co-authored-by: diniamo <diniamo53@gmail.com>
Diffstat (limited to 'src/Compositor.cpp')
-rw-r--r--src/Compositor.cpp391
1 files changed, 282 insertions, 109 deletions
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index 7ffccf36..c7a0cfb1 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -6,7 +6,10 @@
#include "managers/PointerManager.hpp"
#include "managers/SeatManager.hpp"
#include "managers/eventLoop/EventLoopManager.hpp"
+#include <aquamarine/output/Output.hpp>
#include <random>
+#include <cstring>
+#include <filesystem>
#include <unordered_set>
#include "debug/HyprCtl.hpp"
#include "debug/CrashReporter.hpp"
@@ -22,16 +25,20 @@
#include "protocols/core/Compositor.hpp"
#include "protocols/core/Subcompositor.hpp"
#include "desktop/LayerSurface.hpp"
+#include "render/Renderer.hpp"
#include "xwayland/XWayland.hpp"
#include <hyprutils/string/String.hpp>
-using namespace Hyprutils::String;
+#include <aquamarine/input/Input.hpp>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/resource.h>
+using namespace Hyprutils::String;
+using namespace Aquamarine;
+
int handleCritSignal(int signo, void* data) {
Debug::log(LOG, "Hyprland received signal {}", signo);
@@ -72,6 +79,23 @@ void handleUserSignal(int sig) {
}
}
+static LogLevel aqLevelToHl(Aquamarine::eBackendLogLevel level) {
+ switch (level) {
+ case Aquamarine::eBackendLogLevel::AQ_LOG_TRACE: return TRACE;
+ case Aquamarine::eBackendLogLevel::AQ_LOG_DEBUG: return LOG;
+ case Aquamarine::eBackendLogLevel::AQ_LOG_ERROR: return ERR;
+ case Aquamarine::eBackendLogLevel::AQ_LOG_WARNING: return WARN;
+ case Aquamarine::eBackendLogLevel::AQ_LOG_CRITICAL: return CRIT;
+ default: break;
+ }
+
+ return NONE;
+}
+
+void aqLog(Aquamarine::eBackendLogLevel level, std::string msg) {
+ Debug::log(aqLevelToHl(level), "[AQ] {}", msg);
+}
+
void CCompositor::bumpNofile() {
if (!getrlimit(RLIMIT_NOFILE, &m_sOriginalNofile))
Debug::log(LOG, "Old rlimit: soft -> {}, hard -> {}", m_sOriginalNofile.rlim_cur, m_sOriginalNofile.rlim_max);
@@ -180,6 +204,9 @@ void CCompositor::setRandomSplash() {
m_szCurrentSplash = SPLASHES[distribution(engine)];
}
+static std::vector<SP<Aquamarine::IOutput>> pendingOutputs;
+
+//
void CCompositor::initServer() {
m_sWLDisplay = wl_display_create();
@@ -200,102 +227,179 @@ void CCompositor::initServer() {
if (envEnabled("HYPRLAND_TRACE"))
Debug::trace = true;
- wlr_log_init(WLR_INFO, NULL);
+ Aquamarine::SBackendOptions options;
+ options.logFunction = aqLog;
- if (envEnabled("HYPRLAND_LOG_WLR"))
- wlr_log_init(WLR_DEBUG, Debug::wlrLog);
- else
- wlr_log_init(WLR_ERROR, Debug::wlrLog);
+ std::vector<Aquamarine::SBackendImplementationOptions> implementations;
+ Aquamarine::SBackendImplementationOptions option;
+ option.backendType = Aquamarine::eBackendType::AQ_BACKEND_HEADLESS;
+ option.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_MANDATORY;
+ implementations.emplace_back(option);
+ option.backendType = Aquamarine::eBackendType::AQ_BACKEND_DRM;
+ option.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_IF_AVAILABLE;
+ implementations.emplace_back(option);
+ option.backendType = Aquamarine::eBackendType::AQ_BACKEND_WAYLAND;
+ option.backendRequestMode = Aquamarine::eBackendRequestMode::AQ_BACKEND_REQUEST_FALLBACK;
+ implementations.emplace_back(option);
- m_sWLRBackend = wlr_backend_autocreate(m_sWLEventLoop, &m_sWLRSession);
+ m_pAqBackend = CBackend::create(implementations, options);
- if (!m_sWLRBackend) {
- Debug::log(CRIT, "m_sWLRBackend was NULL! This usually means wlroots could not find a GPU or enountered some issues.");
- throwError("wlr_backend_autocreate() failed!");
+ if (!m_pAqBackend) {
+ Debug::log(CRIT,
+ "m_pAqBackend was null! This usually means aquamarine could not find a GPU or enountered some issues. Make sure you're running either on a tty or on a Wayland "
+ "session, NOT an X11 one.");
+ throwError("CBackend::create() failed!");
}
- bool isHeadlessOnly = true;
- wlr_multi_for_each_backend(
- m_sWLRBackend,
- [](wlr_backend* backend, void* isHeadlessOnly) {
- if (!wlr_backend_is_headless(backend) && !wlr_backend_is_libinput(backend))
- *(bool*)isHeadlessOnly = false;
- },
- &isHeadlessOnly);
+ // TODO: headless only
- if (isHeadlessOnly) {
- m_sWLRRenderer = wlr_renderer_autocreate(m_sWLRBackend); // TODO: remove this, it's barely needed now.
- } else {
- m_iDRMFD = wlr_backend_get_drm_fd(m_sWLRBackend);
- if (m_iDRMFD < 0) {
- Debug::log(CRIT, "Couldn't query the DRM FD!");
- throwError("wlr_backend_get_drm_fd() failed!");
- }
+ initAllSignals();
- m_sWLRRenderer = wlr_gles2_renderer_create_with_drm_fd(m_iDRMFD);
+ if (!m_pAqBackend->start()) {
+ Debug::log(CRIT,
+ "m_pAqBackend couldn't start! This usually means aquamarine could not find a GPU or enountered some issues. Make sure you're running either on a tty or on a "
+ "Wayland session, NOT an X11 one.");
+ throwError("CBackend::create() failed!");
}
- if (!m_sWLRRenderer) {
- Debug::log(CRIT, "m_sWLRRenderer was NULL! This usually means wlroots could not find a GPU or enountered some issues.");
- throwError("wlr_gles2_renderer_create_with_drm_fd() failed!");
- }
+ m_bInitialized = true;
- m_sWLRAllocator = wlr_allocator_autocreate(m_sWLRBackend, m_sWLRRenderer);
+ m_iDRMFD = m_pAqBackend->drmFD();
+ Debug::log(LOG, "Running on DRMFD: {}", m_iDRMFD);
- if (!m_sWLRAllocator) {
- Debug::log(CRIT, "m_sWLRAllocator was NULL!");
- throwError("wlr_allocator_autocreate() failed!");
+ // get socket, avoid using 0
+ for (int candidate = 1; candidate <= 32; candidate++) {
+ const auto CANDIDATESTR = ("wayland-" + std::to_string(candidate));
+ const auto RETVAL = wl_display_add_socket(m_sWLDisplay, CANDIDATESTR.c_str());
+ if (RETVAL >= 0) {
+ m_szWLDisplaySocket = CANDIDATESTR;
+ Debug::log(LOG, "wl_display_add_socket for {} succeeded with {}", CANDIDATESTR, RETVAL);
+ break;
+ } else {
+ Debug::log(WARN, "wl_display_add_socket for {} returned {}: skipping candidate {}", CANDIDATESTR, RETVAL, candidate);
+ }
}
- m_sWLREGL = wlr_gles2_renderer_get_egl(m_sWLRRenderer);
+ if (m_szWLDisplaySocket.empty()) {
+ Debug::log(WARN, "All candidates failed, trying wl_display_add_socket_auto");
+ const auto SOCKETSTR = wl_display_add_socket_auto(m_sWLDisplay);
+ if (SOCKETSTR)
+ m_szWLDisplaySocket = SOCKETSTR;
+ }
- if (!m_sWLREGL) {
- Debug::log(CRIT, "m_sWLREGL was NULL!");
- throwError("wlr_gles2_renderer_get_egl() failed!");
+ if (m_szWLDisplaySocket.empty()) {
+ Debug::log(CRIT, "m_szWLDisplaySocket NULL!");
+ throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
}
+ Debug::log(LOG, "Setting WAYLAND_DISPLAY to {}", m_szWLDisplaySocket);
+ setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
+ setenv("XDG_SESSION_TYPE", "wayland", 1);
+
initManagers(STAGE_BASICINIT);
- m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend);
- if (!m_sWRLDRMLeaseMgr) {
- Debug::log(INFO, "Failed to create wlr_drm_lease_v1_manager");
- Debug::log(INFO, "VR will not be available");
+ initManagers(STAGE_LATE);
+
+ for (auto& o : pendingOutputs) {
+ onNewMonitor(o);
}
+ pendingOutputs.clear();
+}
- m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLEventLoop);
+void CCompositor::initAllSignals() {
+ m_pAqBackend->events.newOutput.registerStaticListener(
+ [this](void* p, std::any data) {
+ auto output = std::any_cast<SP<Aquamarine::IOutput>>(data);
+ Debug::log(LOG, "New aquamarine output with name {}", output->name);
+ if (m_bInitialized)
+ onNewMonitor(output);
+ else
+ pendingOutputs.emplace_back(output);
+ },
+ nullptr);
+
+ m_pAqBackend->events.newPointer.registerStaticListener(
+ [](void* data, std::any d) {
+ auto dev = std::any_cast<SP<Aquamarine::IPointer>>(d);
+ Debug::log(LOG, "New aquamarine pointer with name {}", dev->getName());
+ g_pInputManager->newMouse(dev);
+ g_pInputManager->updateCapabilities();
+ },
+ nullptr);
+
+ m_pAqBackend->events.newKeyboard.registerStaticListener(
+ [](void* data, std::any d) {
+ auto dev = std::any_cast<SP<Aquamarine::IKeyboard>>(d);
+ Debug::log(LOG, "New aquamarine keyboard with name {}", dev->getName());
+ g_pInputManager->newKeyboard(dev);
+ g_pInputManager->updateCapabilities();
+ },
+ nullptr);
+
+ m_pAqBackend->events.newTouch.registerStaticListener(
+ [](void* data, std::any d) {
+ auto dev = std::any_cast<SP<Aquamarine::ITouch>>(d);
+ Debug::log(LOG, "New aquamarine touch with name {}", dev->getName());
+ g_pInputManager->newTouchDevice(dev);
+ g_pInputManager->updateCapabilities();
+ },
+ nullptr);
- if (!m_sWLRHeadlessBackend) {
- Debug::log(CRIT, "Couldn't create the headless backend");
- throwError("wlr_headless_backend_create() failed!");
- }
+ m_pAqBackend->events.newSwitch.registerStaticListener(
+ [](void* data, std::any d) {
+ auto dev = std::any_cast<SP<Aquamarine::ISwitch>>(d);
+ Debug::log(LOG, "New aquamarine switch with name {}", dev->getName());
+ g_pInputManager->newSwitch(dev);
+ },
+ nullptr);
- wlr_multi_backend_add(m_sWLRBackend, m_sWLRHeadlessBackend);
+ m_pAqBackend->events.newTablet.registerStaticListener(
+ [](void* data, std::any d) {
+ auto dev = std::any_cast<SP<Aquamarine::ITablet>>(d);
+ Debug::log(LOG, "New aquamarine tablet with name {}", dev->getName());
+ g_pInputManager->newTablet(dev);
+ },
+ nullptr);
- initManagers(STAGE_LATE);
-}
+ m_pAqBackend->events.newTabletPad.registerStaticListener(
+ [](void* data, std::any d) {
+ auto dev = std::any_cast<SP<Aquamarine::ITabletPad>>(d);
+ Debug::log(LOG, "New aquamarine tablet pad with name {}", dev->getName());
+ g_pInputManager->newTabletPad(dev);
+ },
+ nullptr);
-void CCompositor::initAllSignals() {
- addWLSignal(&m_sWLRBackend->events.new_output, &Events::listen_newOutput, m_sWLRBackend, "Backend");
- addWLSignal(&m_sWLRBackend->events.new_input, &Events::listen_newInput, m_sWLRBackend, "Backend");
- addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
+ if (m_pAqBackend->hasSession()) {
+ m_pAqBackend->session->events.changeActive.registerStaticListener(
+ [this](void*, std::any) {
+ if (m_pAqBackend->session->active) {
+ Debug::log(LOG, "Session got activated!");
- if (m_sWRLDRMLeaseMgr)
- addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM");
+ m_bSessionActive = true;
- if (m_sWLRSession)
- addWLSignal(&m_sWLRSession->events.active, &Events::listen_sessionActive, m_sWLRSession, "Session");
-}
+ for (auto& m : m_vMonitors) {
+ scheduleFrameForMonitor(m.get());
+ g_pHyprRenderer->applyMonitorRule(m.get(), &m->activeMonitorRule, true);
+ }
-void CCompositor::removeAllSignals() {
- removeWLSignal(&Events::listen_newOutput);
- removeWLSignal(&Events::listen_newInput);
- removeWLSignal(&Events::listen_RendererDestroy);
+ g_pConfigManager->m_bWantsMonitorReload = true;
+ } else {
+ Debug::log(LOG, "Session got deactivated!");
- if (m_sWRLDRMLeaseMgr)
- removeWLSignal(&Events::listen_leaseRequest);
+ m_bSessionActive = false;
+
+ for (auto& m : m_vMonitors) {
+ m->noFrameSchedule = true;
+ m->framesToSkip = 1;
+ }
+ }
+ },
+ nullptr);
+ }
+}
- if (m_sWLRSession)
- removeWLSignal(&Events::listen_sessionActive);
+void CCompositor::removeAllSignals() {
+ ;
}
void CCompositor::cleanEnvironment() {
@@ -309,7 +413,7 @@ void CCompositor::cleanEnvironment() {
unsetenv("XDG_BACKEND");
unsetenv("XDG_CURRENT_DESKTOP");
- if (m_sWLRSession) {
+ if (m_pAqBackend->hasSession()) {
const auto CMD =
#ifdef USES_SYSTEMD
"systemctl --user unset-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash "
@@ -352,7 +456,7 @@ void CCompositor::cleanup() {
for (auto& m : m_vMonitors) {
g_pHyprOpenGL->destroyMonitorResources(m.get());
- wlr_output_state_set_enabled(m->state.wlr(), false);
+ m->output->state->setEnabled(false);
m->state.commit();
}
@@ -389,14 +493,8 @@ void CCompositor::cleanup() {
g_pHyprCtl.reset();
g_pEventLoopManager.reset();
- if (m_sWLRRenderer)
- wlr_renderer_destroy(m_sWLRRenderer);
-
- if (m_sWLRAllocator)
- wlr_allocator_destroy(m_sWLRAllocator);
-
- if (m_sWLRBackend)
- wlr_backend_destroy(m_sWLRBackend);
+ if (m_pAqBackend)
+ m_pAqBackend.reset();
if (m_critSigSource)
wl_event_source_remove(m_critSigSource);
@@ -442,6 +540,9 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the PointerManager!");
g_pPointerManager = std::make_unique<CPointerManager>();
+
+ Debug::log(LOG, "Creating the EventManager!");
+ g_pEventManager = std::make_unique<CEventManager>();
} break;
case STAGE_BASICINIT: {
Debug::log(LOG, "Creating the CHyprOpenGLImpl!");
@@ -472,9 +573,6 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the SessionLockManager!");
g_pSessionLockManager = std::make_unique<CSessionLockManager>();
- Debug::log(LOG, "Creating the EventManager!");
- g_pEventManager = std::make_unique<CEventManager>();
-
Debug::log(LOG, "Creating the HyprDebugOverlay!");
g_pDebugOverlay = std::make_unique<CHyprDebugOverlay>();
@@ -517,26 +615,23 @@ void CCompositor::removeLockFile() {
void CCompositor::prepareFallbackOutput() {
// create a backup monitor
- wlr_backend* headless = nullptr;
- wlr_multi_for_each_backend(
- m_sWLRBackend,
- [](wlr_backend* b, void* data) {
- if (wlr_backend_is_headless(b))
- *((wlr_backend**)data) = b;
- },
- &headless);
+ SP<Aquamarine::IBackendImplementation> headless;
+ for (auto& impl : m_pAqBackend->getImplementations()) {
+ if (impl->type() == Aquamarine::AQ_BACKEND_HEADLESS) {
+ headless = impl;
+ break;
+ }
+ }
if (!headless) {
- Debug::log(WARN, "Unsafe state will be ineffective, no fallback output");
+ Debug::log(WARN, "No headless in prepareFallbackOutput?!");
return;
}
- wlr_headless_add_output(headless, 1920, 1080);
+ headless->createOutput();
}
void CCompositor::startCompositor(std::string socketName, int socketFd) {
- initAllSignals();
-
if (!socketName.empty() && socketFd != -1) {
fcntl(socketFd, F_SETFD, FD_CLOEXEC);
const auto RETVAL = wl_display_add_socket_fd(m_sWLDisplay, socketFd);
@@ -568,15 +663,15 @@ void CCompositor::startCompositor(std::string socketName, int socketFd) {
if (m_szWLDisplaySocket.empty()) {
Debug::log(CRIT, "m_szWLDisplaySocket NULL!");
- wlr_backend_destroy(m_sWLRBackend);
throwError("m_szWLDisplaySocket was null! (wl_display_add_socket and wl_display_add_socket_auto failed)");
}
setenv("WAYLAND_DISPLAY", m_szWLDisplaySocket.c_str(), 1);
+ setenv("XDG_SESSION_TYPE", "wayland", 1);
signal(SIGPIPE, SIG_IGN);
- if (m_sWLRSession /* Session-less Hyprland usually means a nest, don't update the env in that case */) {
+ if (m_pAqBackend->hasSession() /* Session-less Hyprland usually means a nest, don't update the env in that case */) {
const auto CMD =
#ifdef USES_SYSTEMD
"systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME PATH XDG_DATA_DIRS && hash "
@@ -588,13 +683,6 @@ void CCompositor::startCompositor(std::string socketName, int socketFd) {
Debug::log(LOG, "Running on WAYLAND_DISPLAY: {}", m_szWLDisplaySocket);
- if (!wlr_backend_start(m_sWLRBackend)) {
- Debug::log(CRIT, "Backend did not start!");
- wlr_backend_destroy(m_sWLRBackend);
- wl_display_destroy(m_sWLDisplay);
- throwError("The backend could not start!");
- }
-
prepareFallbackOutput();
g_pHyprRenderer->setCursorFromName("left_ptr");
@@ -882,7 +970,7 @@ Vector2D CCompositor::vectorToSurfaceLocal(const Vector2D& vec, PHLWINDOW pWindo
return vec - pWindow->m_vRealPosition.goal() - std::get<1>(iterData) + Vector2D{geom.x, geom.y};
}
-CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) {
+CMonitor* CCompositor::getMonitorFromOutput(SP<Aquamarine::IOutput> out) {
for (auto& m : m_vMonitors) {
if (m->output == out) {
return m.get();
@@ -892,7 +980,7 @@ CMonitor* CCompositor::getMonitorFromOutput(wlr_output* out) {
return nullptr;
}
-CMonitor* CCompositor::getRealMonitorFromOutput(wlr_output* out) {
+CMonitor* CCompositor::getRealMonitorFromOutput(SP<Aquamarine::IOutput> out) {
for (auto& m : m_vRealMonitors) {
if (m->output == out) {
return m.get();
@@ -2240,8 +2328,12 @@ void CCompositor::setWindowFullscreen(PHLWINDOW pWindow, bool on, eFullscreenMod
g_pInputManager->recheckIdleInhibitorStatus();
- // DMAbuf stuff for direct scanout
- g_pHyprRenderer->setWindowScanoutMode(pWindow);
+ // further updates require a monitor
+ if (!PMONITOR)
+ return;
+
+ // send a scanout tranche if we are entering fullscreen, and send a regular one if we aren't.
+ g_pHyprRenderer->setSurfaceScanoutMode(pWindow->m_pWLSurface->resource(), on ? PMONITOR->self.lock() : nullptr);
g_pConfigManager->ensureVRR(PMONITOR);
}
@@ -2282,8 +2374,8 @@ void CCompositor::updateWorkspaceWindowData(const int& id) {
}
}
-void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor) {
- if ((m_sWLRSession && !m_sWLRSession->active) || !m_bSessionActive)
+void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor, IOutput::scheduleFrameReason reason) {
+ if ((m_pAqBackend->hasSession() && !m_pAqBackend->session->active) || !m_bSessionActive)
return;
if (!pMonitor->m_bEnabled)
@@ -2292,7 +2384,7 @@ void CCompositor::scheduleFrameForMonitor(CMonitor* pMonitor) {
if (pMonitor->renderingActive)
pMonitor->pendingFrame = true;
- wlr_output_schedule_frame(pMonitor->output);
+ pMonitor->output->scheduleFrame(reason);
}
PHLWINDOW CCompositor::getWindowByRegex(const std::string& regexp) {
@@ -2808,3 +2900,84 @@ PHLWINDOW CCompositor::windowForCPointer(CWindow* pWindow) {
return {};
}
+
+static void checkDefaultCursorWarp(SP<CMonitor> PNEWMONITOR, std::string monitorName) {
+ static auto PCURSORMONITOR = CConfigValue<std::string>("cursor:default_monitor");
+ static auto firstMonitorAdded = std::chrono::system_clock::now();
+ static bool cursorDefaultDone = false;
+ static bool firstLaunch = true;
+
+ const auto POS = PNEWMONITOR->middle();
+
+ // by default, cursor should be set to first monitor detected
+ // this is needed as a default if the monitor given in config above doesn't exist
+ if (firstLaunch) {
+ firstLaunch = false;
+ g_pCompositor->warpCursorTo(POS, true);
+ g_pInputManager->refocus();
+ }
+
+ if (cursorDefaultDone || *PCURSORMONITOR == STRVAL_EMPTY)
+ return;
+
+ // after 10s, don't set cursor to default monitor
+ auto timePassedSec = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - firstMonitorAdded);
+ if (timePassedSec.count() > 10) {
+ cursorDefaultDone = true;
+ return;
+ }
+
+ if (*PCURSORMONITOR == monitorName) {
+ cursorDefaultDone = true;
+ g_pCompositor->warpCursorTo(POS, true);
+ g_pInputManager->refocus();
+ }
+}
+
+void CCompositor::onNewMonitor(SP<Aquamarine::IOutput> output) {
+ // add it to real
+ auto PNEWMONITOR = g_pCompositor->m_vRealMonitors.emplace_back(makeShared<CMonitor>());
+ if (std::string("HEADLESS-1") == output->name) {
+ g_pCompositor->m_pUnsafeOutput = PNEWMONITOR.get();
+ output->name = "FALLBACK"; // we are allowed to do this :)
+ }
+
+ Debug::log(LOG, "New output with name {}", output->name);
+
+ PNEWMONITOR->szName = output->name;
+ PNEWMONITOR->output = output;
+ PNEWMONITOR->self = PNEWMONITOR;
+ const bool FALLBACK = g_pCompositor->m_pUnsafeOutput ? output == g_pCompositor->m_pUnsafeOutput->output : false;
+ PNEWMONITOR->ID = FALLBACK ? -1 : g_pCompositor->getNextAvailableMonitorID(output->name);
+ PNEWMONITOR->isUnsafeFallback = FALLBACK;
+
+ EMIT_HOOK_EVENT("newMonitor", PNEWMONITOR);
+
+ if (!FALLBACK)
+ PNEWMONITOR->onConnect(false);
+
+ if (!PNEWMONITOR->m_bEnabled || FALLBACK)
+ return;
+
+ // ready to process if we have a real monitor
+
+ if ((!g_pHyprRenderer->m_pMostHzMonitor || PNEWMONITOR->refreshRate > g_pHyprRenderer->m_pMostHzMonitor->refreshRate) && PNEWMONITOR->m_bEnabled)
+ g_pHyprRenderer->m_pMostHzMonitor = PNEWMONITOR.get();
+
+ g_pCompositor->m_bReadyToProcess = true;
+
+ g_pConfigManager->m_bWantsMonitorReload = true;
+ g_pCompositor->scheduleFrameForMonitor(PNEWMONITOR.get(), IOutput::AQ_SCHEDULE_NEW_MONITOR);
+
+ checkDefaultCursorWarp(PNEWMONITOR, output->name);
+
+ for (auto& w : g_pCompositor->m_vWindows) {
+ if (w->m_iMonitorID == PNEWMONITOR->ID) {
+ w->m_iLastSurfaceMonitorID = -1;
+ w->updateSurfaceScaleTransformDetails();
+ }
+ }
+
+ g_pHyprRenderer->damageMonitor(PNEWMONITOR.get());
+ Events::listener_monitorFrame(PNEWMONITOR.get(), nullptr);
+}