Hyprland/src/helpers/Monitor.cpp

818 lines
28 KiB
C++
Raw Normal View History

2022-07-27 12:32:00 +02:00
#include "Monitor.hpp"
#include "MiscFunctions.hpp"
2022-07-27 12:32:00 +02:00
#include "../Compositor.hpp"
#include "../config/ConfigValue.hpp"
2024-04-22 19:21:03 +02:00
#include "../protocols/GammaControl.hpp"
#include "../devices/ITouch.hpp"
#include "../protocols/LayerShell.hpp"
2024-05-10 03:27:54 +02:00
#include "../protocols/PresentationTime.hpp"
#include "../managers/PointerManager.hpp"
#include <hyprutils/string/String.hpp>
using namespace Hyprutils::String;
int ratHandler(void* data) {
g_pHyprRenderer->renderMonitor((CMonitor*)data);
return 1;
}
CMonitor::CMonitor() : state(this) {
;
2023-04-07 13:18:40 +02:00
}
CMonitor::~CMonitor() {
2023-07-18 15:36:27 +02:00
hyprListener_monitorDestroy.removeCallback();
hyprListener_monitorFrame.removeCallback();
hyprListener_monitorStateRequest.removeCallback();
hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback();
hyprListener_monitorCommit.removeCallback();
hyprListener_monitorBind.removeCallback();
2024-04-22 19:21:03 +02:00
events.destroy.emit();
2023-04-07 13:18:40 +02:00
}
2024-05-10 03:27:54 +02:00
static void onPresented(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
auto E = (wlr_output_event_present*)data;
PROTO::presentation->onPresented(PMONITOR, E->when, E->refresh, E->seq, E->flags);
}
2022-07-27 12:32:00 +02:00
void CMonitor::onConnect(bool noRule) {
2022-11-19 14:14:55 +01:00
hyprListener_monitorDestroy.removeCallback();
hyprListener_monitorFrame.removeCallback();
2022-11-19 17:28:04 +01:00
hyprListener_monitorStateRequest.removeCallback();
hyprListener_monitorDamage.removeCallback();
2023-04-07 18:25:56 +02:00
hyprListener_monitorNeedsFrame.removeCallback();
2023-04-12 22:40:51 +02:00
hyprListener_monitorCommit.removeCallback();
hyprListener_monitorBind.removeCallback();
2024-05-10 03:27:54 +02:00
hyprListener_monitorPresented.removeCallback();
hyprListener_monitorFrame.initCallback(&output->events.frame, &Events::listener_monitorFrame, this, "CMonitor");
hyprListener_monitorDestroy.initCallback(&output->events.destroy, &Events::listener_monitorDestroy, this, "CMonitor");
hyprListener_monitorStateRequest.initCallback(&output->events.request_state, &Events::listener_monitorStateRequest, this, "CMonitor");
hyprListener_monitorDamage.initCallback(&output->events.damage, &Events::listener_monitorDamage, this, "CMonitor");
hyprListener_monitorNeedsFrame.initCallback(&output->events.needs_frame, &Events::listener_monitorNeedsFrame, this, "CMonitor");
hyprListener_monitorCommit.initCallback(&output->events.commit, &Events::listener_monitorCommit, this, "CMonitor");
hyprListener_monitorBind.initCallback(&output->events.bind, &Events::listener_monitorBind, this, "CMonitor");
hyprListener_monitorPresented.initCallback(&output->events.present, ::onPresented, this, "CMonitor");
2022-11-19 14:14:55 +01:00
tearingState.canTear = wlr_backend_is_drm(output->backend); // tearing only works on drm
2022-11-19 14:14:55 +01:00
if (m_bEnabled) {
wlr_output_state_set_enabled(state.wlr(), true);
state.commit();
2022-08-03 16:05:25 +02:00
return;
2022-11-19 14:14:55 +01:00
}
2022-08-03 16:05:25 +02:00
szName = output->name;
szDescription = output->description ? output->description : "";
// remove comma character from description. This allow monitor specific rules to work on monitor with comma on their description
std::erase(szDescription, ',');
// field is backwards-compatible with intended usage of `szDescription` but excludes the parenthesized DRM node name suffix
szShortDescription = trim(std::format("{} {} {}", output->make ? output->make : "", output->model ? output->model : "", output->serial ? output->serial : ""));
std::erase(szShortDescription, ',');
if (!wlr_backend_is_drm(output->backend))
createdByUser = true; // should be true. WL, X11 and Headless backends should be addable / removable
2022-07-27 12:32:00 +02:00
// get monitor rule that matches
SMonitorRule monitorRule = g_pConfigManager->getMonitorRuleFor(*this);
2022-07-27 12:32:00 +02:00
// if it's disabled, disable and ignore
if (monitorRule.disabled) {
wlr_output_state_set_scale(state.wlr(), 1);
wlr_output_state_set_transform(state.wlr(), WL_OUTPUT_TRANSFORM_NORMAL);
auto PREFSTATE = wlr_output_preferred_mode(output);
if (!PREFSTATE) {
wlr_output_mode* mode;
wl_list_for_each(mode, &output->modes, link) {
wlr_output_state_set_mode(state.wlr(), mode);
if (!wlr_output_test_state(output, state.wlr()))
continue;
2022-09-25 20:07:48 +02:00
PREFSTATE = mode;
break;
}
}
if (PREFSTATE)
wlr_output_state_set_mode(state.wlr(), PREFSTATE);
else
2023-09-06 12:51:36 +02:00
Debug::log(WARN, "No mode found for disabled output {}", output->name);
wlr_output_state_set_enabled(state.wlr(), 0);
2022-09-25 20:07:48 +02:00
if (!state.commit())
2023-09-06 12:51:36 +02:00
Debug::log(ERR, "Couldn't commit disabled state on output {}", output->name);
m_bEnabled = false;
2022-07-27 12:32:00 +02:00
hyprListener_monitorFrame.removeCallback();
return;
}
if (output->non_desktop) {
Debug::log(LOG, "Not configuring non-desktop output");
if (g_pCompositor->m_sWRLDRMLeaseMgr) {
wlr_drm_lease_v1_manager_offer_output(g_pCompositor->m_sWRLDRMLeaseMgr, output);
}
return;
}
if (!m_bRenderingInitPassed) {
2022-09-19 21:45:00 +02:00
output->allocator = nullptr;
output->renderer = nullptr;
wlr_output_init_render(output, g_pCompositor->m_sWLRAllocator, g_pCompositor->m_sWLRRenderer);
m_bRenderingInitPassed = true;
}
SP<CMonitor>* thisWrapper = nullptr;
2022-08-03 17:42:19 +02:00
// find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->ID == ID) {
thisWrapper = &m;
break;
2022-08-03 17:42:19 +02:00
}
}
RASSERT(thisWrapper->get(), "CMonitor::onConnect: Had no wrapper???");
if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) == g_pCompositor->m_vMonitors.end())
g_pCompositor->m_vMonitors.push_back(*thisWrapper);
2022-09-25 20:07:48 +02:00
2022-07-27 12:32:00 +02:00
m_bEnabled = true;
wlr_output_state_set_enabled(state.wlr(), 1);
2022-07-27 12:32:00 +02:00
// set mode, also applies
if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
if (!state.commit())
Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onCommit");
damage.setSize(vecTransformedSize);
2023-04-07 13:18:40 +02:00
Debug::log(LOG, "Added new monitor with name {} at {:j0} with size {:j0}, pointer {:x}", output->name, vecPosition, vecPixelSize, (uintptr_t)output);
2022-07-27 12:32:00 +02:00
2022-09-13 15:25:42 +02:00
setupDefaultWS(monitorRule);
2022-07-27 12:32:00 +02:00
2023-05-03 16:15:56 +02:00
for (auto& ws : g_pCompositor->m_vWorkspaces) {
if (!valid(ws))
continue;
2023-05-06 17:49:46 +02:00
if (ws->m_szLastMonitor == szName || g_pCompositor->m_vMonitors.size() == 1 /* avoid lost workspaces on recover */) {
g_pCompositor->moveWorkspaceToMonitor(ws, this);
2023-05-03 16:15:56 +02:00
ws->startAnim(true, true, true);
ws->m_szLastMonitor = "";
}
}
2022-07-27 12:32:00 +02:00
scale = monitorRule.scale;
2023-04-04 23:54:35 +02:00
if (scale < 0.1)
scale = getDefaultScale();
2022-07-27 12:32:00 +02:00
forceFullFrames = 3; // force 3 full frames to make sure there is no blinking due to double-buffering.
2022-07-27 12:32:00 +02:00
//
if (!activeMonitorRule.mirrorOf.empty())
setMirror(activeMonitorRule.mirrorOf);
2022-11-19 17:41:36 +01:00
g_pEventManager->postEvent(SHyprIPCEvent{"monitoradded", szName});
g_pEventManager->postEvent(SHyprIPCEvent{"monitoraddedv2", std::format("{},{},{}", ID, szName, szShortDescription)});
EMIT_HOOK_EVENT("monitorAdded", this);
2022-11-19 17:41:36 +01:00
if (!g_pCompositor->m_pLastMonitor) // set the last monitor if it isnt set yet
2022-11-19 17:41:36 +01:00
g_pCompositor->setActiveMonitor(this);
2022-07-27 12:32:00 +02:00
2022-08-10 21:54:09 +02:00
g_pHyprRenderer->arrangeLayersForMonitor(ID);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
2022-10-22 22:45:17 +02:00
// ensure VRR (will enable if necessary)
g_pConfigManager->ensureVRR(this);
2022-12-12 21:51:20 +01:00
// verify last mon valid
bool found = false;
for (auto& m : g_pCompositor->m_vMonitors) {
if (m == g_pCompositor->m_pLastMonitor) {
2022-12-12 21:51:20 +01:00
found = true;
break;
}
}
if (!found)
g_pCompositor->setActiveMonitor(this);
renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this);
g_pCompositor->scheduleFrameForMonitor(this);
2024-04-22 19:21:03 +02:00
PROTO::gamma->applyGammaToState(this);
events.connect.emit();
2022-07-27 12:32:00 +02:00
}
void CMonitor::onDisconnect(bool destroy) {
2022-08-03 16:05:25 +02:00
if (renderTimer) {
wl_event_source_remove(renderTimer);
renderTimer = nullptr;
}
2022-10-06 19:43:50 +02:00
if (!m_bEnabled || g_pCompositor->m_bIsShuttingDown)
2022-08-03 16:05:25 +02:00
return;
2023-09-06 12:51:36 +02:00
Debug::log(LOG, "onDisconnect called for {}", output->name);
2024-04-22 19:21:03 +02:00
events.disconnect.emit();
2022-07-27 12:32:00 +02:00
// Cleanup everything. Move windows back, snap cursor, shit.
CMonitor* BACKUPMON = nullptr;
for (auto& m : g_pCompositor->m_vMonitors) {
if (m.get() != this) {
BACKUPMON = m.get();
break;
}
}
2022-09-13 15:25:42 +02:00
// remove mirror
if (pMirrorOf) {
pMirrorOf->mirrors.erase(std::find_if(pMirrorOf->mirrors.begin(), pMirrorOf->mirrors.end(), [&](const auto& other) { return other == this; }));
// unlock software for mirrored monitor
g_pPointerManager->unlockSoftwareForMonitor(pMirrorOf);
2022-09-13 15:25:42 +02:00
pMirrorOf = nullptr;
}
if (!mirrors.empty()) {
for (auto& m : mirrors) {
m->setMirror("");
}
g_pConfigManager->m_bWantsMonitorReload = true;
}
2022-07-27 12:32:00 +02:00
hyprListener_monitorFrame.removeCallback();
2024-05-10 03:27:54 +02:00
hyprListener_monitorPresented.removeCallback();
hyprListener_monitorDamage.removeCallback();
2023-04-07 18:25:56 +02:00
hyprListener_monitorNeedsFrame.removeCallback();
2023-04-12 22:40:51 +02:00
hyprListener_monitorCommit.removeCallback();
2023-07-18 15:22:49 +02:00
hyprListener_monitorBind.removeCallback();
2022-07-27 12:32:00 +02:00
2023-01-02 16:16:28 +01:00
for (size_t i = 0; i < 4; ++i) {
for (auto& ls : m_aLayerSurfaceLayers[i]) {
2023-01-23 19:23:44 +01:00
if (ls->layerSurface && !ls->fadingOut)
ls->layerSurface->sendClosed();
2023-01-02 16:16:28 +01:00
}
m_aLayerSurfaceLayers[i].clear();
2023-01-02 16:16:28 +01:00
}
Debug::log(LOG, "Removed monitor {}!", szName);
2023-01-19 16:27:04 +01:00
g_pEventManager->postEvent(SHyprIPCEvent{"monitorremoved", szName});
EMIT_HOOK_EVENT("monitorRemoved", this);
2023-01-19 16:27:04 +01:00
2022-08-10 21:54:09 +02:00
if (!BACKUPMON) {
Debug::log(WARN, "Unplugged last monitor, entering an unsafe state. Good luck my friend.");
g_pCompositor->enterUnsafeState();
2022-08-10 21:54:09 +02:00
}
m_bEnabled = false;
m_bRenderingInitPassed = false;
2023-09-01 22:03:56 +02:00
if (BACKUPMON) {
// snap cursor
g_pCompositor->warpCursorTo(BACKUPMON->vecPosition + BACKUPMON->vecTransformedSize / 2.F, true);
2022-07-27 12:32:00 +02:00
2023-09-01 22:03:56 +02:00
// move workspaces
std::deque<PHLWORKSPACE> wspToMove;
2023-09-01 22:03:56 +02:00
for (auto& w : g_pCompositor->m_vWorkspaces) {
if (w->m_iMonitorID == ID || !g_pCompositor->getMonitorFromID(w->m_iMonitorID)) {
wspToMove.push_back(w);
2023-09-01 22:03:56 +02:00
}
2022-07-27 12:32:00 +02:00
}
2023-09-01 22:03:56 +02:00
for (auto& w : wspToMove) {
w->m_szLastMonitor = szName;
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
w->startAnim(true, true, true);
}
} else {
g_pCompositor->m_pLastFocus.reset();
g_pCompositor->m_pLastWindow.reset();
g_pCompositor->m_pLastMonitor.reset();
2022-07-27 12:32:00 +02:00
}
if (activeWorkspace)
activeWorkspace->m_bVisible = false;
activeWorkspace.reset();
2022-07-27 12:32:00 +02:00
wlr_output_state_set_enabled(state.wlr(), false);
2022-07-27 12:32:00 +02:00
if (!state.commit())
Debug::log(WARN, "wlr_output_commit_state failed in CMonitor::onDisconnect");
2022-07-27 12:32:00 +02:00
if (g_pCompositor->m_pLastMonitor.get() == this)
g_pCompositor->setActiveMonitor(BACKUPMON ? BACKUPMON : g_pCompositor->m_pUnsafeOutput);
if (g_pHyprRenderer->m_pMostHzMonitor == this) {
int mostHz = 0;
CMonitor* pMonitorMostHz = nullptr;
for (auto& m : g_pCompositor->m_vMonitors) {
if (m->refreshRate > mostHz && m.get() != this) {
pMonitorMostHz = m.get();
mostHz = m->refreshRate;
}
}
g_pHyprRenderer->m_pMostHzMonitor = pMonitorMostHz;
}
std::erase_if(g_pCompositor->m_vMonitors, [&](SP<CMonitor>& el) { return el.get() == this; });
2022-07-27 12:32:00 +02:00
}
2022-08-23 16:07:47 +02:00
void CMonitor::addDamage(const pixman_region32_t* rg) {
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
damage.damageEntire();
2023-04-16 15:48:38 +02:00
g_pCompositor->scheduleFrameForMonitor(this);
} else if (damage.damage(rg))
2023-04-07 18:25:56 +02:00
g_pCompositor->scheduleFrameForMonitor(this);
2022-08-23 16:07:47 +02:00
}
2023-07-19 20:09:49 +02:00
void CMonitor::addDamage(const CRegion* rg) {
addDamage(const_cast<CRegion*>(rg)->pixman());
}
void CMonitor::addDamage(const CBox* box) {
static auto PZOOMFACTOR = CConfigValue<Hyprlang::FLOAT>("cursor:zoom_factor");
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
damage.damageEntire();
2023-04-16 15:48:38 +02:00
g_pCompositor->scheduleFrameForMonitor(this);
}
if (damage.damage(*box))
2023-04-07 18:25:56 +02:00
g_pCompositor->scheduleFrameForMonitor(this);
2022-08-23 16:07:47 +02:00
}
2022-09-13 15:25:42 +02:00
bool CMonitor::shouldSkipScheduleFrameOnMouseEvent() {
static auto PNOBREAK = CConfigValue<Hyprlang::INT>("cursor:no_break_fs_vrr");
static auto PMINRR = CConfigValue<Hyprlang::INT>("cursor:min_refresh_rate");
// skip scheduling extra frames for fullsreen apps with vrr
bool shouldSkip = *PNOBREAK && output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED && activeWorkspace && activeWorkspace->m_bHasFullscreenWindow &&
activeWorkspace->m_efFullscreenMode == FULLSCREEN_FULL;
// keep requested minimum refresh rate
if (shouldSkip && *PMINRR && lastPresentationTimer.getMillis() > 1000 / *PMINRR) {
// damage whole screen because some previous cursor box damages were skipped
damage.damageEntire();
return false;
}
return shouldSkip;
}
2022-09-13 15:25:42 +02:00
bool CMonitor::isMirror() {
return pMirrorOf != nullptr;
}
bool CMonitor::matchesStaticSelector(const std::string& selector) const {
if (selector.starts_with("desc:")) {
// match by description
const auto DESCRIPTIONSELECTOR = selector.substr(5);
return DESCRIPTIONSELECTOR == szShortDescription || DESCRIPTIONSELECTOR == szDescription;
} else {
// match by selector
return szName == selector;
}
}
2022-12-09 18:17:02 +01:00
int CMonitor::findAvailableDefaultWS() {
for (size_t i = 1; i < INT32_MAX; ++i) {
if (g_pCompositor->getWorkspaceByID(i))
continue;
if (const auto BOUND = g_pConfigManager->getBoundMonitorStringForWS(std::to_string(i)); !BOUND.empty() && BOUND != szName)
continue;
2022-12-09 18:17:02 +01:00
return i;
}
return INT32_MAX; // shouldn't be reachable
}
2022-09-13 15:25:42 +02:00
void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
// Workspace
std::string newDefaultWorkspaceName = "";
int64_t wsID = WORKSPACE_INVALID;
if (g_pConfigManager->getDefaultWorkspaceFor(szName).empty())
wsID = findAvailableDefaultWS();
else {
const auto ws = getWorkspaceIDNameFromString(g_pConfigManager->getDefaultWorkspaceFor(szName));
wsID = ws.id;
newDefaultWorkspaceName = ws.name;
}
2022-09-13 15:25:42 +02:00
if (wsID == WORKSPACE_INVALID || (wsID >= SPECIAL_WORKSPACE_START && wsID <= -2)) {
wsID = g_pCompositor->m_vWorkspaces.size() + 1;
newDefaultWorkspaceName = std::to_string(wsID);
2022-09-13 15:25:42 +02:00
Debug::log(LOG, "Invalid workspace= directive name in monitor parsing, workspace name \"{}\" is invalid.", g_pConfigManager->getDefaultWorkspaceFor(szName));
2022-09-13 15:25:42 +02:00
}
auto PNEWWORKSPACE = g_pCompositor->getWorkspaceByID(wsID);
2022-09-13 15:25:42 +02:00
Debug::log(LOG, "New monitor: WORKSPACEID {}, exists: {}", wsID, (int)(PNEWWORKSPACE != nullptr));
2022-09-13 15:25:42 +02:00
if (PNEWWORKSPACE) {
// workspace exists, move it to the newly connected monitor
g_pCompositor->moveWorkspaceToMonitor(PNEWWORKSPACE, this);
activeWorkspace = PNEWWORKSPACE;
2022-09-13 15:25:42 +02:00
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
PNEWWORKSPACE->startAnim(true, true, true);
} else {
if (newDefaultWorkspaceName == "")
newDefaultWorkspaceName = std::to_string(wsID);
2022-09-13 15:25:42 +02:00
PNEWWORKSPACE = g_pCompositor->m_vWorkspaces.emplace_back(CWorkspace::create(wsID, ID, newDefaultWorkspaceName));
2022-09-13 15:25:42 +02:00
}
activeWorkspace = PNEWWORKSPACE;
2022-09-13 15:25:42 +02:00
PNEWWORKSPACE->setActive(true);
2024-04-03 11:09:42 +02:00
PNEWWORKSPACE->m_bVisible = true;
2023-05-03 16:15:56 +02:00
PNEWWORKSPACE->m_szLastMonitor = "";
2022-09-13 15:25:42 +02:00
}
void CMonitor::setMirror(const std::string& mirrorOf) {
const auto PMIRRORMON = g_pCompositor->getMonitorFromString(mirrorOf);
if (PMIRRORMON == pMirrorOf)
return;
if (PMIRRORMON && PMIRRORMON->isMirror()) {
Debug::log(ERR, "Cannot mirror a mirror!");
return;
}
if (PMIRRORMON == this) {
Debug::log(ERR, "Cannot mirror self!");
return;
}
if (!PMIRRORMON) {
// disable mirroring
if (pMirrorOf) {
pMirrorOf->mirrors.erase(std::find_if(pMirrorOf->mirrors.begin(), pMirrorOf->mirrors.end(), [&](const auto& other) { return other == this; }));
// unlock software for mirrored monitor
g_pPointerManager->unlockSoftwareForMonitor(pMirrorOf);
2022-09-13 15:25:42 +02:00
}
pMirrorOf = nullptr;
// set rule
const auto RULE = g_pConfigManager->getMonitorRuleFor(*this);
2022-09-13 15:25:42 +02:00
vecPosition = RULE.offset;
// push to mvmonitors
SP<CMonitor>* thisWrapper = nullptr;
// find the wrap
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (m->ID == ID) {
thisWrapper = &m;
break;
2022-09-13 15:25:42 +02:00
}
}
RASSERT(thisWrapper->get(), "CMonitor::setMirror: Had no wrapper???");
if (std::find_if(g_pCompositor->m_vMonitors.begin(), g_pCompositor->m_vMonitors.end(), [&](auto& other) { return other.get() == this; }) ==
g_pCompositor->m_vMonitors.end()) {
g_pCompositor->m_vMonitors.push_back(*thisWrapper);
2022-09-13 15:25:42 +02:00
}
setupDefaultWS(RULE);
g_pHyprRenderer->applyMonitorRule(this, (SMonitorRule*)&RULE, true); // will apply the offset and stuff
2022-09-13 15:25:42 +02:00
} else {
CMonitor* BACKUPMON = nullptr;
for (auto& m : g_pCompositor->m_vMonitors) {
if (m.get() != this) {
BACKUPMON = m.get();
break;
}
}
// move all the WS
std::deque<PHLWORKSPACE> wspToMove;
2022-09-13 15:25:42 +02:00
for (auto& w : g_pCompositor->m_vWorkspaces) {
if (w->m_iMonitorID == ID) {
wspToMove.push_back(w);
2022-09-13 15:25:42 +02:00
}
}
for (auto& w : wspToMove) {
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
w->startAnim(true, true, true);
}
activeWorkspace.reset();
2022-09-13 15:25:42 +02:00
2023-03-04 01:48:02 +01:00
vecPosition = PMIRRORMON->vecPosition;
2022-09-13 15:25:42 +02:00
pMirrorOf = PMIRRORMON;
pMirrorOf->mirrors.push_back(this);
// remove from mvmonitors
std::erase_if(g_pCompositor->m_vMonitors, [&](const auto& other) { return other.get() == this; });
2022-09-13 15:25:42 +02:00
g_pCompositor->arrangeMonitors();
2022-11-19 17:41:36 +01:00
g_pCompositor->setActiveMonitor(g_pCompositor->m_vMonitors.front().get());
g_pCompositor->sanityCheckWorkspaces();
// Software lock mirrored monitor
g_pPointerManager->lockSoftwareForMonitor(PMIRRORMON);
2022-09-13 15:25:42 +02:00
}
events.modeChanged.emit();
}
2022-12-14 18:57:18 +01:00
float CMonitor::getDefaultScale() {
if (!m_bEnabled)
return 1;
static constexpr double MMPERINCH = 25.4;
const auto DIAGONALPX = sqrt(pow(vecPixelSize.x, 2) + pow(vecPixelSize.y, 2));
const auto DIAGONALIN = sqrt(pow(output->phys_width / MMPERINCH, 2) + pow(output->phys_height / MMPERINCH, 2));
2022-12-14 18:57:18 +01:00
const auto PPI = DIAGONALPX / DIAGONALIN;
2022-12-14 18:57:18 +01:00
if (PPI > 200 /* High PPI, 2x*/)
return 2;
2022-12-20 14:33:29 +01:00
else if (PPI > 140 /* Medium PPI, 1.5x*/)
2022-12-14 18:57:18 +01:00
return 1.5;
return 1;
}
void CMonitor::changeWorkspace(const PHLWORKSPACE& pWorkspace, bool internal, bool noMouseMove, bool noFocus) {
if (!pWorkspace)
return;
if (pWorkspace->m_bIsSpecialWorkspace) {
if (activeSpecialWorkspace != pWorkspace) {
2023-09-06 12:51:36 +02:00
Debug::log(LOG, "changeworkspace on special, togglespecialworkspace to id {}", pWorkspace->m_iID);
setSpecialWorkspace(pWorkspace);
}
return;
}
if (pWorkspace == activeWorkspace)
return;
2024-04-03 11:09:42 +02:00
const auto POLDWORKSPACE = activeWorkspace;
POLDWORKSPACE->m_bVisible = false;
pWorkspace->m_bVisible = true;
activeWorkspace = pWorkspace;
if (!internal) {
const auto ANIMTOLEFT = pWorkspace->m_iID > POLDWORKSPACE->m_iID;
POLDWORKSPACE->startAnim(false, ANIMTOLEFT);
pWorkspace->startAnim(true, ANIMTOLEFT);
// move pinned windows
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace == POLDWORKSPACE && w->m_bPinned)
w->moveToWorkspace(pWorkspace);
}
if (!noFocus && !g_pCompositor->m_pLastMonitor->activeSpecialWorkspace &&
!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_iMonitorID == ID)) {
static auto PFOLLOWMOUSE = CConfigValue<Hyprlang::INT>("input:follow_mouse");
auto pWindow = pWorkspace->getLastFocusedWindow();
if (!pWindow) {
if (*PFOLLOWMOUSE == 1)
pWindow = g_pCompositor->vectorToWindowUnified(g_pInputManager->getMouseCoordsInternal(), RESERVED_EXTENTS | INPUT_EXTENTS | ALLOW_FLOATING);
if (!pWindow)
pWindow = g_pCompositor->getTopLeftWindowOnWorkspace(pWorkspace->m_iID);
if (!pWindow)
pWindow = g_pCompositor->getFirstWindowOnWorkspace(pWorkspace->m_iID);
}
g_pCompositor->focusWindow(pWindow);
}
if (!noMouseMove)
g_pInputManager->simulateMouseMovement();
2023-04-16 02:11:57 +02:00
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
g_pEventManager->postEvent(SHyprIPCEvent{"workspace", pWorkspace->m_szName});
g_pEventManager->postEvent(SHyprIPCEvent{"workspacev2", std::format("{},{}", pWorkspace->m_iID, pWorkspace->m_szName)});
EMIT_HOOK_EVENT("workspace", pWorkspace);
}
g_pHyprRenderer->damageMonitor(this);
g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace);
g_pConfigManager->ensureVRR(this);
g_pCompositor->updateSuspendedStates();
if (activeSpecialWorkspace)
g_pCompositor->updateFullscreenFadeOnWorkspace(activeSpecialWorkspace);
}
void CMonitor::changeWorkspace(const int& id, bool internal, bool noMouseMove, bool noFocus) {
changeWorkspace(g_pCompositor->getWorkspaceByID(id), internal, noMouseMove, noFocus);
}
void CMonitor::setSpecialWorkspace(const PHLWORKSPACE& pWorkspace) {
if (activeSpecialWorkspace == pWorkspace)
return;
g_pHyprRenderer->damageMonitor(this);
if (!pWorkspace) {
// remove special if exists
if (activeSpecialWorkspace) {
2024-04-03 11:09:42 +02:00
activeSpecialWorkspace->m_bVisible = false;
activeSpecialWorkspace->startAnim(false, false);
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + szName});
}
activeSpecialWorkspace.reset();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_iMonitorID == ID)) {
if (const auto PLAST = activeWorkspace->getLastFocusedWindow(); PLAST)
g_pCompositor->focusWindow(PLAST);
else
g_pInputManager->refocus();
}
g_pCompositor->updateFullscreenFadeOnWorkspace(activeWorkspace);
g_pConfigManager->ensureVRR(this);
g_pCompositor->updateSuspendedStates();
return;
}
2024-04-03 11:09:42 +02:00
if (activeSpecialWorkspace) {
activeSpecialWorkspace->m_bVisible = false;
activeSpecialWorkspace->startAnim(false, false);
2024-04-03 11:09:42 +02:00
}
bool animate = true;
//close if open elsewhere
const auto PMONITORWORKSPACEOWNER = g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID);
if (PMONITORWORKSPACEOWNER->activeSpecialWorkspace == pWorkspace) {
PMONITORWORKSPACEOWNER->activeSpecialWorkspace.reset();
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITORWORKSPACEOWNER->ID);
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", "," + PMONITORWORKSPACEOWNER->szName});
const auto PACTIVEWORKSPACE = PMONITORWORKSPACEOWNER->activeWorkspace;
g_pCompositor->updateFullscreenFadeOnWorkspace(PACTIVEWORKSPACE);
animate = false;
}
// open special
2024-04-03 11:09:42 +02:00
pWorkspace->m_iMonitorID = ID;
activeSpecialWorkspace = pWorkspace;
activeSpecialWorkspace->m_bVisible = true;
if (animate)
pWorkspace->startAnim(true, true);
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_pWorkspace == pWorkspace) {
w->m_iMonitorID = ID;
w->updateSurfaceScaleTransformDetails();
w->setAnimationsToMove();
const auto MIDDLE = w->middle();
if (w->m_bIsFloating && !VECINRECT(MIDDLE, vecPosition.x, vecPosition.y, vecPosition.x + vecSize.x, vecPosition.y + vecSize.y) && w->m_iX11Type != 2) {
// if it's floating and the middle isnt on the current mon, move it to the center
const auto PMONFROMMIDDLE = g_pCompositor->getMonitorFromVector(MIDDLE);
Vector2D pos = w->m_vRealPosition.goal();
if (!VECINRECT(MIDDLE, PMONFROMMIDDLE->vecPosition.x, PMONFROMMIDDLE->vecPosition.y, PMONFROMMIDDLE->vecPosition.x + PMONFROMMIDDLE->vecSize.x,
PMONFROMMIDDLE->vecPosition.y + PMONFROMMIDDLE->vecSize.y)) {
// not on any monitor, center
pos = middle() / 2.f - w->m_vRealSize.goal() / 2.f;
} else
pos = pos - PMONFROMMIDDLE->vecPosition + vecPosition;
w->m_vRealPosition = pos;
w->m_vPosition = pos;
}
}
}
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
if (!(g_pCompositor->m_pLastWindow.lock() && g_pCompositor->m_pLastWindow->m_bPinned && g_pCompositor->m_pLastWindow->m_iMonitorID == ID)) {
if (const auto PLAST = pWorkspace->getLastFocusedWindow(); PLAST)
g_pCompositor->focusWindow(PLAST);
else
g_pInputManager->refocus();
}
g_pEventManager->postEvent(SHyprIPCEvent{"activespecial", pWorkspace->m_szName + "," + szName});
g_pHyprRenderer->damageMonitor(this);
g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace);
g_pConfigManager->ensureVRR(this);
g_pCompositor->updateSuspendedStates();
}
void CMonitor::setSpecialWorkspace(const int& id) {
setSpecialWorkspace(g_pCompositor->getWorkspaceByID(id));
}
void CMonitor::moveTo(const Vector2D& pos) {
vecPosition = pos;
}
Vector2D CMonitor::middle() {
return vecPosition + vecSize / 2.f;
}
void CMonitor::updateMatrix() {
wlr_matrix_identity(projMatrix.data());
if (transform != WL_OUTPUT_TRANSFORM_NORMAL) {
wlr_matrix_translate(projMatrix.data(), vecPixelSize.x / 2.0, vecPixelSize.y / 2.0);
wlr_matrix_transform(projMatrix.data(), transform);
wlr_matrix_translate(projMatrix.data(), -vecTransformedSize.x / 2.0, -vecTransformedSize.y / 2.0);
}
}
int64_t CMonitor::activeWorkspaceID() {
return activeWorkspace ? activeWorkspace->m_iID : 0;
}
int64_t CMonitor::activeSpecialWorkspaceID() {
return activeSpecialWorkspace ? activeSpecialWorkspace->m_iID : 0;
}
CBox CMonitor::logicalBox() {
return {vecPosition, vecSize};
}
CMonitorState::CMonitorState(CMonitor* owner) {
m_pOwner = owner;
wlr_output_state_init(&m_state);
}
CMonitorState::~CMonitorState() {
wlr_output_state_finish(&m_state);
}
wlr_output_state* CMonitorState::wlr() {
return &m_state;
}
void CMonitorState::clear() {
wlr_output_state_finish(&m_state);
m_state = {0};
wlr_output_state_init(&m_state);
}
bool CMonitorState::commit() {
bool ret = wlr_output_commit_state(m_pOwner->output, &m_state);
clear();
return ret;
}
bool CMonitorState::test() {
return wlr_output_test_state(m_pOwner->output, &m_state);
}