Files
obs-studio/frontend/widgets/OBSBasic_Projectors.cpp
2025-12-17 21:43:44 -05:00

268 lines
7.7 KiB
C++

/******************************************************************************
Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>
Zachary Lund <admin@computerquip.com>
Philippe Groarke <philippe.groarke@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "OBSBasic.hpp"
#include "OBSProjector.hpp"
obs_data_array_t *OBSBasic::SaveProjectors()
{
obs_data_array_t *savedProjectors = obs_data_array_create();
auto saveProjector = [savedProjectors](OBSProjector *projector) {
if (!projector)
return;
OBSDataAutoRelease data = obs_data_create();
ProjectorType type = projector->GetProjectorType();
switch (type) {
case ProjectorType::Scene:
case ProjectorType::Source: {
OBSSource source = projector->GetSource();
const char *name = obs_source_get_name(source);
obs_data_set_string(data, "name", name);
break;
}
default:
break;
}
obs_data_set_int(data, "monitor", projector->GetMonitor());
obs_data_set_int(data, "type", static_cast<int>(type));
obs_data_set_string(data, "geometry", projector->saveGeometry().toBase64().constData());
if (projector->IsAlwaysOnTopOverridden())
obs_data_set_bool(data, "alwaysOnTop", projector->IsAlwaysOnTop());
obs_data_set_bool(data, "alwaysOnTopOverridden", projector->IsAlwaysOnTopOverridden());
obs_data_array_push_back(savedProjectors, data);
};
for (size_t i = 0; i < projectors.size(); i++)
saveProjector(static_cast<OBSProjector *>(projectors[i]));
return savedProjectors;
}
void OBSBasic::LoadSavedProjectors(obs_data_array_t *array)
{
size_t num = obs_data_array_count(array);
for (size_t i = 0; i < num; i++) {
OBSDataAutoRelease data = obs_data_array_item(array, i);
SavedProjectorInfo info = {};
info.monitor = obs_data_get_int(data, "monitor");
info.type = static_cast<ProjectorType>(obs_data_get_int(data, "type"));
info.geometry = std::string(obs_data_get_string(data, "geometry"));
info.name = std::string(obs_data_get_string(data, "name"));
info.alwaysOnTop = obs_data_get_bool(data, "alwaysOnTop");
info.alwaysOnTopOverridden = obs_data_get_bool(data, "alwaysOnTopOverridden");
OpenSavedProjector(&info);
}
}
void OBSBasic::updateMultiviewProjectorMenu()
{
ui->multiviewProjectorMenu->clear();
AddProjectorMenuMonitors(ui->multiviewProjectorMenu, this, &OBSBasic::OpenMultiviewProjector);
ui->multiviewProjectorMenu->addSeparator();
ui->multiviewProjectorMenu->addAction(QTStr("Projector.Window"), this, &OBSBasic::openMultiviewWindow);
}
void OBSBasic::ClearProjectors()
{
for (size_t i = 0; i < projectors.size(); i++) {
if (projectors[i])
delete projectors[i];
}
projectors.clear();
}
QList<QString> OBSBasic::GetProjectorMenuMonitorsFormatted()
{
QList<QString> projectorsFormatted;
QList<QScreen *> screens = QGuiApplication::screens();
for (int i = 0; i < screens.size(); i++) {
QScreen *screen = screens[i];
QRect screenGeometry = screen->geometry();
qreal screenPixelRatio = screen->devicePixelRatio();
QString name = "";
#if defined(__APPLE__) || defined(_WIN32)
name = screen->name();
#else
name = screen->model().simplified();
if (name.length() > 1 && name.endsWith("-"))
name.chop(1);
#endif
name = name.simplified();
if (name.length() == 0) {
name = QString("%1 %2").arg(QTStr("Display")).arg(QString::number(i + 1));
}
int screenPixelWidth = std::round((screenGeometry.width() * screenPixelRatio) * 0.5f) * 2;
int screenPixelHeight = std::round((screenGeometry.height() * screenPixelRatio) * 0.5f) * 2;
QString str = QString("%1: %2x%3 @ %4,%5")
.arg(name, QString::number(screenPixelWidth), QString::number(screenPixelHeight),
QString::number(screenGeometry.x()), QString::number(screenGeometry.y()));
projectorsFormatted.push_back(str);
}
return projectorsFormatted;
}
void OBSBasic::DeleteProjector(OBSProjector *projector)
{
for (size_t i = 0; i < projectors.size(); i++) {
if (projectors[i] == projector) {
projectors[i]->deleteLater();
projectors.erase(projectors.begin() + i);
break;
}
}
}
OBSProjector *OBSBasic::OpenProjector(obs_source_t *source, int monitor, ProjectorType type)
{
/* seriously? 10 monitors? */
if (monitor > 9 || monitor > QGuiApplication::screens().size() - 1)
return nullptr;
bool closeProjectors = config_get_bool(App()->GetUserConfig(), "BasicWindow", "CloseExistingProjectors");
if (closeProjectors && monitor > -1) {
for (size_t i = projectors.size(); i > 0; i--) {
size_t idx = i - 1;
if (projectors[idx]->GetMonitor() == monitor)
DeleteProjector(projectors[idx]);
}
}
OBSProjector *projector = new OBSProjector(nullptr, source, monitor, type);
projectors.emplace_back(projector);
return projector;
}
void OBSBasic::OpenPreviewProjector()
{
int monitor = sender()->property("monitor").toInt();
OpenProjector(nullptr, monitor, ProjectorType::Preview);
}
void OBSBasic::OpenSourceProjector()
{
int monitor = sender()->property("monitor").toInt();
OBSSceneItem item = GetCurrentSceneItem();
if (!item)
return;
OpenProjector(obs_sceneitem_get_source(item), monitor, ProjectorType::Source);
}
void OBSBasic::OpenMultiviewProjector()
{
int monitor = sender()->property("monitor").toInt();
OpenProjector(nullptr, monitor, ProjectorType::Multiview);
}
void OBSBasic::OpenSceneProjector()
{
int monitor = sender()->property("monitor").toInt();
OBSScene scene = GetCurrentScene();
if (!scene)
return;
OpenProjector(obs_scene_get_source(scene), monitor, ProjectorType::Scene);
}
void OBSBasic::OpenPreviewWindow()
{
OpenProjector(nullptr, -1, ProjectorType::Preview);
}
void OBSBasic::OpenSourceWindow()
{
OBSSceneItem item = GetCurrentSceneItem();
if (!item)
return;
OBSSource source = obs_sceneitem_get_source(item);
OpenProjector(obs_sceneitem_get_source(item), -1, ProjectorType::Source);
}
void OBSBasic::OpenSceneWindow()
{
OBSScene scene = GetCurrentScene();
if (!scene)
return;
OBSSource source = obs_scene_get_source(scene);
OpenProjector(obs_scene_get_source(scene), -1, ProjectorType::Scene);
}
void OBSBasic::OpenSavedProjector(SavedProjectorInfo *info)
{
if (info) {
OBSProjector *projector = nullptr;
switch (info->type) {
case ProjectorType::Source:
case ProjectorType::Scene: {
OBSSourceAutoRelease source = obs_get_source_by_name(info->name.c_str());
if (!source)
return;
projector = OpenProjector(source, info->monitor, info->type);
break;
}
default: {
projector = OpenProjector(nullptr, info->monitor, info->type);
break;
}
}
if (projector && !info->geometry.empty() && info->monitor < 0) {
QByteArray byteArray = QByteArray::fromBase64(QByteArray(info->geometry.c_str()));
projector->restoreGeometry(byteArray);
if (!WindowPositionValid(projector->normalGeometry())) {
QRect rect = QGuiApplication::primaryScreen()->geometry();
projector->setGeometry(
QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, size(), rect));
}
if (info->alwaysOnTopOverridden)
projector->SetIsAlwaysOnTop(info->alwaysOnTop, true);
}
}
}
void OBSBasic::openMultiviewWindow()
{
OpenProjector(nullptr, -1, ProjectorType::Multiview);
}