UI: Add Multiview projector
Jim note: - Refactored code significantly - Added a context menu option to exclude specific scenes from projectors - Made it so multiview projectors update when scenes are added/removed/renamed - Increased text quality - Removed the color sources and replaced them with simple solid rectangles - Increased the border size of "program" and "preview" scenes in the lower scene list Closes jp9000/obs-studio#1068
This commit is contained in:
parent
78411de75e
commit
721cb3dea5
@ -78,6 +78,9 @@ Defaults="Defaults"
|
||||
HideMixer="Hide in Mixer"
|
||||
TransitionOverride="Transition Override"
|
||||
None="None"
|
||||
StudioMode.Preview="Preview"
|
||||
StudioMode.Program="Program"
|
||||
ShowInMultiview="Show in Multiview"
|
||||
|
||||
# warning if program already open
|
||||
AlreadyRunning.Title="OBS is already running"
|
||||
|
@ -139,6 +139,7 @@ OBSBasic::OBSBasic(QWidget *parent)
|
||||
|
||||
projectorArray.resize(10, "");
|
||||
previewProjectorArray.resize(10, 0);
|
||||
multiviewProjectorArray.resize(10, 0);
|
||||
studioProgramProjectorArray.resize(10, 0);
|
||||
|
||||
setAcceptDrops(true);
|
||||
@ -320,7 +321,8 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder,
|
||||
OBSScene &scene, OBSSource &curProgramScene,
|
||||
obs_data_array_t *savedProjectorList,
|
||||
obs_data_array_t *savedPreviewProjectorList,
|
||||
obs_data_array_t *savedStudioProgramProjectorList)
|
||||
obs_data_array_t *savedStudioProgramProjectorList,
|
||||
obs_data_array_t *savedMultiviewProjectorList)
|
||||
{
|
||||
obs_data_t *saveData = obs_data_create();
|
||||
|
||||
@ -366,6 +368,8 @@ static obs_data_t *GenerateSaveData(obs_data_array_t *sceneOrder,
|
||||
savedPreviewProjectorList);
|
||||
obs_data_set_array(saveData, "saved_studio_preview_projectors",
|
||||
savedStudioProgramProjectorList);
|
||||
obs_data_set_array(saveData, "saved_multiview_projectors",
|
||||
savedMultiviewProjectorList);
|
||||
obs_data_array_release(sourcesArray);
|
||||
|
||||
obs_data_set_string(saveData, "current_transition",
|
||||
@ -468,6 +472,21 @@ obs_data_array_t *OBSBasic::SaveStudioProgramProjectors()
|
||||
return saveProjector;
|
||||
}
|
||||
|
||||
obs_data_array_t *OBSBasic::SaveMultiviewProjectors()
|
||||
{
|
||||
obs_data_array_t *saveProjector = obs_data_array_create();
|
||||
|
||||
for (size_t i = 0; i < multiviewProjectorArray.size(); i++) {
|
||||
obs_data_t *data = obs_data_create();
|
||||
obs_data_set_int(data, "saved_multiview_projectors",
|
||||
multiviewProjectorArray.at(i));
|
||||
obs_data_array_push_back(saveProjector, data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
return saveProjector;
|
||||
}
|
||||
|
||||
void OBSBasic::Save(const char *file)
|
||||
{
|
||||
OBSScene scene = GetCurrentScene();
|
||||
@ -482,11 +501,14 @@ void OBSBasic::Save(const char *file)
|
||||
obs_data_array_t *savedPreviewProjectorList = SavePreviewProjectors();
|
||||
obs_data_array_t *savedStudioProgramProjectorList =
|
||||
SaveStudioProgramProjectors();
|
||||
obs_data_array_t *savedMultiviewProjectorList =
|
||||
SaveMultiviewProjectors();
|
||||
obs_data_t *saveData = GenerateSaveData(sceneOrder, quickTrData,
|
||||
ui->transitionDuration->value(), transitions,
|
||||
scene, curProgramScene, savedProjectorList,
|
||||
savedPreviewProjectorList,
|
||||
savedStudioProgramProjectorList);
|
||||
savedStudioProgramProjectorList,
|
||||
savedMultiviewProjectorList);
|
||||
|
||||
obs_data_set_bool(saveData, "preview_locked", ui->preview->Locked());
|
||||
obs_data_set_bool(saveData, "scaling_enabled",
|
||||
@ -515,6 +537,7 @@ void OBSBasic::Save(const char *file)
|
||||
obs_data_array_release(savedProjectorList);
|
||||
obs_data_array_release(savedPreviewProjectorList);
|
||||
obs_data_array_release(savedStudioProgramProjectorList);
|
||||
obs_data_array_release(savedMultiviewProjectorList);
|
||||
}
|
||||
|
||||
static void LoadAudioDevice(const char *name, int channel, obs_data_t *parent)
|
||||
@ -653,6 +676,19 @@ void OBSBasic::LoadSavedStudioProgramProjectors(obs_data_array_t *array)
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasic::LoadSavedMultiviewProjectors(obs_data_array_t *array)
|
||||
{
|
||||
size_t num = obs_data_array_count(array);
|
||||
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
obs_data_t *data = obs_data_array_item(array, i);
|
||||
multiviewProjectorArray.at(i) = obs_data_get_int(data,
|
||||
"saved_multiview_projectors");
|
||||
|
||||
obs_data_release(data);
|
||||
}
|
||||
}
|
||||
|
||||
static void LogFilter(obs_source_t*, obs_source_t *filter, void *v_val)
|
||||
{
|
||||
const char *name = obs_source_get_name(filter);
|
||||
@ -784,6 +820,8 @@ void OBSBasic::Load(const char *file)
|
||||
ui->transitionDuration->setValue(newDuration);
|
||||
SetTransition(curTransition);
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
obs_data_array_t *savedProjectors = obs_data_get_array(data,
|
||||
"saved_projectors");
|
||||
|
||||
@ -792,6 +830,8 @@ void OBSBasic::Load(const char *file)
|
||||
|
||||
obs_data_array_release(savedProjectors);
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
obs_data_array_t *savedPreviewProjectors = obs_data_get_array(data,
|
||||
"saved_preview_projectors");
|
||||
|
||||
@ -800,6 +840,8 @@ void OBSBasic::Load(const char *file)
|
||||
|
||||
obs_data_array_release(savedPreviewProjectors);
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
obs_data_array_t *savedStudioProgramProjectors = obs_data_get_array(data,
|
||||
"saved_studio_preview_projectors");
|
||||
|
||||
@ -808,6 +850,16 @@ void OBSBasic::Load(const char *file)
|
||||
|
||||
obs_data_array_release(savedStudioProgramProjectors);
|
||||
|
||||
/* ------------------- */
|
||||
|
||||
obs_data_array_t *savedMultiviewProjectors = obs_data_get_array(data,
|
||||
"saved_multiview_projectors");
|
||||
|
||||
if (savedMultiviewProjectors)
|
||||
LoadSavedMultiviewProjectors(savedMultiviewProjectors);
|
||||
|
||||
obs_data_array_release(savedMultiviewProjectors);
|
||||
|
||||
|
||||
retryScene:
|
||||
curScene = obs_get_source_by_name(sceneName);
|
||||
@ -1927,6 +1979,11 @@ void OBSBasic::SaveProjectDeferred()
|
||||
Save(savePath);
|
||||
}
|
||||
|
||||
OBSSource OBSBasic::GetProgramSource()
|
||||
{
|
||||
return OBSGetStrongRef(programScene);
|
||||
}
|
||||
|
||||
OBSScene OBSBasic::GetCurrentScene()
|
||||
{
|
||||
QListWidgetItem *item = ui->scenes->currentItem();
|
||||
@ -2088,6 +2145,8 @@ void OBSBasic::AddScene(OBSSource source)
|
||||
obs_source_t *source = obs_scene_get_source(scene);
|
||||
blog(LOG_INFO, "User added scene '%s'",
|
||||
obs_source_get_name(source));
|
||||
|
||||
OBSProjector::UpdateMultiviewProjectors();
|
||||
}
|
||||
|
||||
if (api)
|
||||
@ -2122,6 +2181,8 @@ void OBSBasic::RemoveScene(OBSSource source)
|
||||
if (!disableSaving) {
|
||||
blog(LOG_INFO, "User Removed scene '%s'",
|
||||
obs_source_get_name(source));
|
||||
|
||||
OBSProjector::UpdateMultiviewProjectors();
|
||||
}
|
||||
|
||||
if (api)
|
||||
@ -2203,7 +2264,8 @@ static void RenameListValues(QListWidget *listWidget, const QString &newName,
|
||||
items[i]->setText(newName);
|
||||
}
|
||||
|
||||
void OBSBasic::RenameSources(QString newName, QString prevName)
|
||||
void OBSBasic::RenameSources(OBSSource source, QString newName,
|
||||
QString prevName)
|
||||
{
|
||||
RenameListValues(ui->scenes, newName, prevName);
|
||||
|
||||
@ -2221,6 +2283,10 @@ void OBSBasic::RenameSources(QString newName, QString prevName)
|
||||
}
|
||||
|
||||
SaveProject();
|
||||
|
||||
obs_scene_t *scene = obs_scene_from_source(source);
|
||||
if (scene)
|
||||
OBSProjector::UpdateMultiviewProjectors();
|
||||
}
|
||||
|
||||
void OBSBasic::SelectSceneItem(OBSScene scene, OBSSceneItem item, bool select)
|
||||
@ -2816,11 +2882,13 @@ void OBSBasic::SourceDeactivated(void *data, calldata_t *params)
|
||||
|
||||
void OBSBasic::SourceRenamed(void *data, calldata_t *params)
|
||||
{
|
||||
obs_source_t *source = (obs_source_t*)calldata_ptr(params, "source");
|
||||
const char *newName = calldata_string(params, "new_name");
|
||||
const char *prevName = calldata_string(params, "prev_name");
|
||||
|
||||
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
|
||||
"RenameSources",
|
||||
Q_ARG(OBSSource, source),
|
||||
Q_ARG(QString, QT_UTF8(newName)),
|
||||
Q_ARG(QString, QT_UTF8(prevName)));
|
||||
|
||||
@ -3457,6 +3525,7 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
|
||||
popup.addMenu(&order);
|
||||
|
||||
popup.addSeparator();
|
||||
|
||||
sceneProjectorMenu = new QMenu(QTStr("SceneProjector"));
|
||||
AddProjectorMenuMonitors(sceneProjectorMenu, this,
|
||||
SLOT(OpenSceneProjector()));
|
||||
@ -3475,6 +3544,36 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
|
||||
|
||||
QMenu *transitionMenu = CreatePerSceneTransitionMenu();
|
||||
popup.addMenu(transitionMenu);
|
||||
|
||||
/* ---------------------- */
|
||||
|
||||
if (IsPreviewProgramMode()) {
|
||||
QAction *multiviewAction = popup.addAction(
|
||||
QTStr("ShowInMultiview"));
|
||||
|
||||
OBSSource source = GetCurrentSceneSource();
|
||||
OBSData data = obs_source_get_private_settings(source);
|
||||
obs_data_release(data);
|
||||
|
||||
obs_data_set_default_bool(data, "show_in_multiview",
|
||||
true);
|
||||
bool show = obs_data_get_bool(data, "show_in_multiview");
|
||||
|
||||
multiviewAction->setCheckable(true);
|
||||
multiviewAction->setChecked(show);
|
||||
|
||||
auto showInMultiview = [this] (OBSData data)
|
||||
{
|
||||
bool show = obs_data_get_bool(data,
|
||||
"show_in_multiview");
|
||||
obs_data_set_bool(data, "show_in_multiview",
|
||||
!show);
|
||||
OBSProjector::UpdateMultiviewProjectors();
|
||||
};
|
||||
|
||||
connect(multiviewAction, &QAction::triggered,
|
||||
std::bind(showInMultiview, data));
|
||||
}
|
||||
}
|
||||
|
||||
popup.exec(QCursor::pos());
|
||||
@ -3719,6 +3818,7 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
|
||||
QMenu popup(this);
|
||||
QPointer<QMenu> previewProjector;
|
||||
QPointer<QMenu> sourceProjector;
|
||||
QPointer<QMenu> multiviewProjectorMenu;
|
||||
|
||||
if (preview) {
|
||||
QAction *action = popup.addAction(
|
||||
@ -3733,6 +3833,20 @@ void OBSBasic::CreateSourcePopupMenu(QListWidgetItem *item, bool preview)
|
||||
popup.addAction(ui->actionLockPreview);
|
||||
popup.addMenu(ui->scalingMenu);
|
||||
|
||||
if (IsPreviewProgramMode()) {
|
||||
multiviewProjectorMenu = new QMenu(
|
||||
"Multiview Projector");
|
||||
AddProjectorMenuMonitors(multiviewProjectorMenu, this,
|
||||
SLOT(OpenMultiviewProjector()));
|
||||
popup.addMenu(multiviewProjectorMenu);
|
||||
|
||||
QAction *multiviewWindow = popup.addAction(
|
||||
"Multiview Windowed",
|
||||
this, SLOT(OpenMultiviewWindow()));
|
||||
|
||||
popup.addAction(multiviewWindow);
|
||||
}
|
||||
|
||||
previewProjector = new QMenu(QTStr("PreviewProjector"));
|
||||
AddProjectorMenuMonitors(previewProjector, this,
|
||||
SLOT(OpenPreviewProjector()));
|
||||
@ -5417,17 +5531,12 @@ void OBSBasic::NudgeLeft() {Nudge(1, MoveDir::Left);}
|
||||
void OBSBasic::NudgeRight() {Nudge(1, MoveDir::Right);}
|
||||
|
||||
void OBSBasic::OpenProjector(obs_source_t *source, int monitor, bool window,
|
||||
QString title, bool studioProgram)
|
||||
QString title, ProjectorType type)
|
||||
{
|
||||
/* seriously? 10 monitors? */
|
||||
if (monitor > 9 || monitor > QGuiApplication::screens().size() - 1)
|
||||
return;
|
||||
|
||||
bool isPreview = false;
|
||||
|
||||
if (source == nullptr)
|
||||
isPreview = true;
|
||||
|
||||
if (!window) {
|
||||
delete projectors[monitor];
|
||||
projectors[monitor].clear();
|
||||
@ -5438,20 +5547,22 @@ void OBSBasic::OpenProjector(obs_source_t *source, int monitor, bool window,
|
||||
const char *name = obs_source_get_name(source);
|
||||
|
||||
if (!window) {
|
||||
if (studioProgram) {
|
||||
if (type == ProjectorType::StudioProgram) {
|
||||
studioProgramProjectorArray.at((size_t)monitor) = 1;
|
||||
} else if (isPreview) {
|
||||
} else if (type == ProjectorType::Preview) {
|
||||
previewProjectorArray.at((size_t)monitor) = 1;
|
||||
} else if (type == ProjectorType::Multiview) {
|
||||
multiviewProjectorArray.at((size_t)monitor) = 1;
|
||||
} else {
|
||||
projectorArray.at((size_t)monitor) = name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!window) {
|
||||
projector->Init(monitor, false, nullptr, studioProgram);
|
||||
projector->Init(monitor, false, nullptr, type);
|
||||
projectors[monitor] = projector;
|
||||
} else {
|
||||
projector->Init(monitor, true, title, studioProgram);
|
||||
projector->Init(monitor, true, title, type);
|
||||
|
||||
for (auto &projPtr : windowProjectors) {
|
||||
if (!projPtr) {
|
||||
@ -5468,13 +5579,14 @@ void OBSBasic::OpenProjector(obs_source_t *source, int monitor, bool window,
|
||||
void OBSBasic::OpenStudioProgramProjector()
|
||||
{
|
||||
int monitor = sender()->property("monitor").toInt();
|
||||
OpenProjector(nullptr, monitor, false, nullptr, true);
|
||||
OpenProjector(nullptr, monitor, false, nullptr,
|
||||
ProjectorType::StudioProgram);
|
||||
}
|
||||
|
||||
void OBSBasic::OpenPreviewProjector()
|
||||
{
|
||||
int monitor = sender()->property("monitor").toInt();
|
||||
OpenProjector(nullptr, monitor, false);
|
||||
OpenProjector(nullptr, monitor, false, nullptr, ProjectorType::Preview);
|
||||
}
|
||||
|
||||
void OBSBasic::OpenSourceProjector()
|
||||
@ -5487,6 +5599,13 @@ void OBSBasic::OpenSourceProjector()
|
||||
OpenProjector(obs_sceneitem_get_source(item), monitor, false);
|
||||
}
|
||||
|
||||
void OBSBasic::OpenMultiviewProjector()
|
||||
{
|
||||
int monitor = sender()->property("monitor").toInt();
|
||||
OpenProjector(nullptr, monitor, false, nullptr,
|
||||
ProjectorType::Multiview);
|
||||
}
|
||||
|
||||
void OBSBasic::OpenSceneProjector()
|
||||
{
|
||||
int monitor = sender()->property("monitor").toInt();
|
||||
@ -5501,14 +5620,15 @@ void OBSBasic::OpenStudioProgramWindow()
|
||||
{
|
||||
int monitor = sender()->property("monitor").toInt();
|
||||
QString title = QTStr("StudioProgramWindow");
|
||||
OpenProjector(nullptr, monitor, true, title, true);
|
||||
OpenProjector(nullptr, monitor, true, title,
|
||||
ProjectorType::StudioProgram);
|
||||
}
|
||||
|
||||
void OBSBasic::OpenPreviewWindow()
|
||||
{
|
||||
int monitor = sender()->property("monitor").toInt();
|
||||
QString title = QTStr("PreviewWindow");
|
||||
OpenProjector(nullptr, monitor, true, title);
|
||||
OpenProjector(nullptr, monitor, true, nullptr, ProjectorType::Preview);
|
||||
}
|
||||
|
||||
void OBSBasic::OpenSourceWindow()
|
||||
@ -5526,6 +5646,13 @@ void OBSBasic::OpenSourceWindow()
|
||||
OpenProjector(obs_sceneitem_get_source(item), monitor, true, title);
|
||||
}
|
||||
|
||||
void OBSBasic::OpenMultiviewWindow()
|
||||
{
|
||||
int monitor = sender()->property("monitor").toInt();
|
||||
OpenProjector(nullptr, monitor, true, "Multiview",
|
||||
ProjectorType::Multiview);
|
||||
}
|
||||
|
||||
void OBSBasic::OpenSceneWindow()
|
||||
{
|
||||
int monitor = sender()->property("monitor").toInt();
|
||||
@ -5566,13 +5693,21 @@ void OBSBasic::OpenSavedProjectors()
|
||||
for (size_t i = 0; i < studioProgramProjectorArray.size(); i++) {
|
||||
if (studioProgramProjectorArray.at(i) == 1) {
|
||||
OpenProjector(nullptr, (int)i, false, nullptr,
|
||||
true);
|
||||
ProjectorType::StudioProgram);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < previewProjectorArray.size(); i++) {
|
||||
if (previewProjectorArray.at(i) == 1) {
|
||||
OpenProjector(nullptr, (int)i, false);
|
||||
OpenProjector(nullptr, (int)i, false, nullptr,
|
||||
ProjectorType::Preview);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < multiviewProjectorArray.size(); i++) {
|
||||
if (multiviewProjectorArray.at(i) == 1) {
|
||||
OpenProjector(nullptr, (int)i, false, nullptr,
|
||||
ProjectorType::Multiview);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5581,6 +5716,7 @@ void OBSBasic::OpenSavedProjectors()
|
||||
void OBSBasic::RemoveSavedProjectors(int monitor)
|
||||
{
|
||||
studioProgramProjectorArray.at((size_t)monitor) = 0;
|
||||
multiviewProjectorArray.at((size_t)monitor) = 0;
|
||||
previewProjectorArray.at((size_t)monitor) = 0;
|
||||
projectorArray.at((size_t)monitor) = "";
|
||||
}
|
||||
|
@ -67,6 +67,13 @@ enum class QtDataRole {
|
||||
OBSSignals,
|
||||
};
|
||||
|
||||
enum class ProjectorType {
|
||||
Source,
|
||||
Preview,
|
||||
StudioProgram,
|
||||
Multiview
|
||||
};
|
||||
|
||||
struct QuickTransition {
|
||||
QPushButton *button = nullptr;
|
||||
OBSSource source;
|
||||
@ -115,6 +122,7 @@ private:
|
||||
|
||||
std::vector<std::string> projectorArray;
|
||||
std::vector<int> studioProgramProjectorArray;
|
||||
std::vector<int> multiviewProjectorArray;
|
||||
std::vector<int> previewProjectorArray;
|
||||
|
||||
bool loaded = false;
|
||||
@ -241,7 +249,8 @@ private:
|
||||
|
||||
void Nudge(int dist, MoveDir dir);
|
||||
void OpenProjector(obs_source_t *source, int monitor, bool window,
|
||||
QString title = nullptr, bool studioProgram = false);
|
||||
QString title = nullptr,
|
||||
ProjectorType type = ProjectorType::Source);
|
||||
|
||||
void GetAudioSourceFilters();
|
||||
void GetAudioSourceProperties();
|
||||
@ -361,6 +370,10 @@ private:
|
||||
void LoadSavedStudioProgramProjectors(
|
||||
obs_data_array_t *savedStudioProgramProjectors);
|
||||
|
||||
obs_data_array_t *SaveMultiviewProjectors();
|
||||
void LoadSavedMultiviewProjectors(
|
||||
obs_data_array_t *savedMultiviewProjectors);
|
||||
|
||||
public slots:
|
||||
void StartStreaming();
|
||||
void StopStreaming();
|
||||
@ -404,7 +417,7 @@ private slots:
|
||||
void RemoveSceneItem(OBSSceneItem item);
|
||||
void AddScene(OBSSource source);
|
||||
void RemoveScene(OBSSource source);
|
||||
void RenameSources(QString newName, QString prevName);
|
||||
void RenameSources(OBSSource source, QString newName, QString prevName);
|
||||
|
||||
void SelectSceneItem(OBSScene scene, OBSSceneItem item, bool select);
|
||||
|
||||
@ -477,7 +490,8 @@ private:
|
||||
static void HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed);
|
||||
|
||||
public:
|
||||
OBSScene GetCurrentScene();
|
||||
OBSSource GetProgramSource();
|
||||
OBSScene GetCurrentScene();
|
||||
|
||||
void SysTrayNotify(const QString &text, QSystemTrayIcon::MessageIcon n);
|
||||
|
||||
@ -689,11 +703,13 @@ private slots:
|
||||
void OpenStudioProgramProjector();
|
||||
void OpenPreviewProjector();
|
||||
void OpenSourceProjector();
|
||||
void OpenMultiviewProjector();
|
||||
void OpenSceneProjector();
|
||||
|
||||
void OpenStudioProgramWindow();
|
||||
void OpenPreviewWindow();
|
||||
void OpenSourceWindow();
|
||||
void OpenMultiviewWindow();
|
||||
void OpenSceneWindow();
|
||||
|
||||
public slots:
|
||||
|
@ -7,7 +7,9 @@
|
||||
#include "display-helpers.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "obs-app.hpp"
|
||||
|
||||
static QList<OBSProjector *> multiviewProjectors;
|
||||
static bool updatingMultiview = false;
|
||||
|
||||
OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, bool window)
|
||||
: OBSQTDisplay (widget,
|
||||
@ -30,7 +32,10 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, bool window)
|
||||
|
||||
auto addDrawCallback = [this] ()
|
||||
{
|
||||
obs_display_add_draw_callback(GetDisplay(), OBSRender, this);
|
||||
bool isMultiview = type == ProjectorType::Multiview;
|
||||
obs_display_add_draw_callback(GetDisplay(),
|
||||
isMultiview ? OBSRenderMultiview : OBSRender,
|
||||
this);
|
||||
obs_display_set_background_color(GetDisplay(), 0x000000);
|
||||
};
|
||||
|
||||
@ -50,13 +55,73 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, bool window)
|
||||
|
||||
OBSProjector::~OBSProjector()
|
||||
{
|
||||
bool isMultiview = type == ProjectorType::Multiview;
|
||||
obs_display_remove_draw_callback(GetDisplay(),
|
||||
isMultiview ? OBSRenderMultiview : OBSRender, this);
|
||||
|
||||
if (source)
|
||||
obs_source_dec_showing(source);
|
||||
|
||||
if (isMultiview) {
|
||||
for (OBSWeakSource &weakSrc : multiviewScenes) {
|
||||
OBSSource src = OBSGetStrongRef(weakSrc);
|
||||
if (src)
|
||||
obs_source_dec_showing(src);
|
||||
}
|
||||
|
||||
obs_enter_graphics();
|
||||
gs_vertexbuffer_destroy(outerBox);
|
||||
gs_vertexbuffer_destroy(innerBox);
|
||||
gs_vertexbuffer_destroy(leftVLine);
|
||||
gs_vertexbuffer_destroy(rightVLine);
|
||||
gs_vertexbuffer_destroy(leftLine);
|
||||
gs_vertexbuffer_destroy(topLine);
|
||||
gs_vertexbuffer_destroy(rightLine);
|
||||
obs_leave_graphics();
|
||||
}
|
||||
|
||||
if (type == ProjectorType::Multiview)
|
||||
multiviewProjectors.removeAll(this);
|
||||
|
||||
App()->DecrementSleepInhibition();
|
||||
}
|
||||
|
||||
static OBSSource CreateLabel(const char *name, size_t h)
|
||||
{
|
||||
obs_data_t *settings = obs_data_create();
|
||||
obs_data_t *font = obs_data_create();
|
||||
|
||||
std::string text;
|
||||
text += " ";
|
||||
text += name;
|
||||
text += " ";
|
||||
|
||||
#if defined(_WIN32)
|
||||
obs_data_set_string(font, "face", "Arial");
|
||||
#elif defined(__APPLE__)
|
||||
obs_data_set_string(font, "face", "Helvetica");
|
||||
#else
|
||||
obs_data_set_string(font, "face", "Monospace");
|
||||
#endif
|
||||
obs_data_set_int(font, "flags", 0);
|
||||
obs_data_set_int(font, "size", int(h / 9.81));
|
||||
|
||||
obs_data_set_obj(settings, "font", font);
|
||||
obs_data_set_string(settings, "text", text.c_str());
|
||||
obs_data_set_bool(settings, "outline", true);
|
||||
|
||||
OBSSource txtSource = obs_source_create_private("text_ft2_source", name,
|
||||
settings);
|
||||
obs_source_release(txtSource);
|
||||
|
||||
obs_data_release(font);
|
||||
obs_data_release(settings);
|
||||
|
||||
return txtSource;
|
||||
}
|
||||
|
||||
void OBSProjector::Init(int monitor, bool window, QString title,
|
||||
bool studioProgram)
|
||||
ProjectorType type_)
|
||||
{
|
||||
QScreen *screen = QGuiApplication::screens()[monitor];
|
||||
|
||||
@ -86,13 +151,342 @@ void OBSProjector::Init(int monitor, bool window, QString title,
|
||||
}
|
||||
|
||||
savedMonitor = monitor;
|
||||
isWindow = window;
|
||||
useStudioProgram = studioProgram;
|
||||
isWindow = window;
|
||||
type = type_;
|
||||
|
||||
if (type == ProjectorType::Multiview) {
|
||||
obs_enter_graphics();
|
||||
gs_render_start(true);
|
||||
gs_vertex2f(0.001f, 0.001f);
|
||||
gs_vertex2f(0.001f, 0.997f);
|
||||
gs_vertex2f(0.997f, 0.997f);
|
||||
gs_vertex2f(0.997f, 0.001f);
|
||||
gs_vertex2f(0.001f, 0.001f);
|
||||
outerBox = gs_render_save();
|
||||
|
||||
gs_render_start(true);
|
||||
gs_vertex2f(0.04f, 0.04f);
|
||||
gs_vertex2f(0.04f, 0.96f);
|
||||
gs_vertex2f(0.96f, 0.96f);
|
||||
gs_vertex2f(0.96f, 0.04f);
|
||||
gs_vertex2f(0.04f, 0.04f);
|
||||
innerBox = gs_render_save();
|
||||
|
||||
gs_render_start(true);
|
||||
gs_vertex2f(0.15f, 0.04f);
|
||||
gs_vertex2f(0.15f, 0.96f);
|
||||
leftVLine = gs_render_save();
|
||||
|
||||
gs_render_start(true);
|
||||
gs_vertex2f(0.85f, 0.04f);
|
||||
gs_vertex2f(0.85f, 0.96f);
|
||||
rightVLine = gs_render_save();
|
||||
|
||||
gs_render_start(true);
|
||||
gs_vertex2f(0.0f, 0.5f);
|
||||
gs_vertex2f(0.075f, 0.5f);
|
||||
leftLine = gs_render_save();
|
||||
|
||||
gs_render_start(true);
|
||||
gs_vertex2f(0.5f, 0.0f);
|
||||
gs_vertex2f(0.5f, 0.09f);
|
||||
topLine = gs_render_save();
|
||||
|
||||
gs_render_start(true);
|
||||
gs_vertex2f(0.925f, 0.5f);
|
||||
gs_vertex2f(1.0f, 0.5f);
|
||||
rightLine = gs_render_save();
|
||||
obs_leave_graphics();
|
||||
|
||||
UpdateMultiview();
|
||||
|
||||
multiviewProjectors.push_back(this);
|
||||
}
|
||||
|
||||
ready = true;
|
||||
}
|
||||
|
||||
static inline void renderVB(gs_effect_t *effect, gs_vertbuffer_t *vb,
|
||||
int cx, int cy)
|
||||
{
|
||||
if (!vb)
|
||||
return;
|
||||
|
||||
matrix4 transform;
|
||||
matrix4_identity(&transform);
|
||||
transform.x.x = cx;
|
||||
transform.y.y = cy;
|
||||
|
||||
gs_load_vertexbuffer(vb);
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_mul(&transform);
|
||||
|
||||
while (gs_effect_loop(effect, "Solid"))
|
||||
gs_draw(GS_LINESTRIP, 0, 0);
|
||||
|
||||
gs_matrix_pop();
|
||||
}
|
||||
|
||||
static inline uint32_t labelOffset(obs_source_t *label, uint32_t cx)
|
||||
{
|
||||
uint32_t w = obs_source_get_width(label);
|
||||
w = uint32_t(float(w) * 0.5f);
|
||||
return (cx / 2) - w;
|
||||
}
|
||||
|
||||
void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy)
|
||||
{
|
||||
OBSProjector *window = (OBSProjector *)data;
|
||||
|
||||
if (updatingMultiview || !window->ready)
|
||||
return;
|
||||
|
||||
OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();
|
||||
uint32_t targetCX, targetCY;
|
||||
int x, y;
|
||||
float fX, fY, halfCX, halfCY, sourceX, sourceY,
|
||||
quarterCX, quarterCY, scale, targetCXF, targetCYF,
|
||||
hiCX, hiCY, qiX, qiY, qiCX, qiCY,
|
||||
hiScaleX, hiScaleY, qiScaleX, qiScaleY;
|
||||
uint32_t offset;
|
||||
gs_rect rect;
|
||||
|
||||
gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
|
||||
gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
|
||||
|
||||
struct obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
targetCX = ovi.base_width;
|
||||
targetCY = ovi.base_height;
|
||||
|
||||
GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale);
|
||||
|
||||
targetCXF = float(targetCX);
|
||||
targetCYF = float(targetCY);
|
||||
fX = float(x);
|
||||
fY = float(y);
|
||||
|
||||
halfCX = (targetCXF + 1) / 2;
|
||||
halfCY = (targetCYF + 1) / 2;
|
||||
hiCX = (halfCX - 4.0);
|
||||
hiCY = (halfCY - 4.0);
|
||||
hiScaleX = hiCX / targetCXF;
|
||||
hiScaleY = hiCY / targetCYF;
|
||||
|
||||
quarterCX = (halfCX + 1) / 2;
|
||||
quarterCY = (halfCY + 1) / 2;
|
||||
qiCX = (quarterCX - 8.0);
|
||||
qiCY = (quarterCY - 8.0);
|
||||
qiScaleX = qiCX / targetCXF;
|
||||
qiScaleY = qiCY / targetCYF;
|
||||
|
||||
OBSSource previewSrc = main->GetCurrentSceneSource();
|
||||
OBSSource programSrc = main->GetProgramSource();
|
||||
|
||||
bool studioMode = main->IsPreviewProgramMode();
|
||||
|
||||
auto drawBox = [solid, color] (float cx, float cy,
|
||||
uint32_t colorVal)
|
||||
{
|
||||
gs_effect_set_color(color, colorVal);
|
||||
while (gs_effect_loop(solid, "Solid"))
|
||||
gs_draw_sprite(nullptr, 0, (uint32_t)cx, (uint32_t)cy);
|
||||
};
|
||||
|
||||
/* ----------------------------- */
|
||||
/* draw sources */
|
||||
|
||||
gs_projection_push();
|
||||
gs_viewport_push();
|
||||
gs_set_viewport(x, y, targetCX * scale, targetCY * scale);
|
||||
gs_ortho(0.0f, targetCXF, 0.0f, targetCYF, -100.0f, 100.0f);
|
||||
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
OBSSource src = OBSGetStrongRef(window->multiviewScenes[i]);
|
||||
obs_source *label = window->multiviewLabels[i + 2];
|
||||
|
||||
if (!src)
|
||||
continue;
|
||||
if (!label)
|
||||
continue;
|
||||
|
||||
if (i < 4) {
|
||||
sourceX = (float(i) * quarterCX);
|
||||
sourceY = halfCY;
|
||||
} else {
|
||||
sourceX = (float(i - 4) * quarterCX);
|
||||
sourceY = halfCY + quarterCY;
|
||||
}
|
||||
|
||||
qiX = sourceX + 4.0f;
|
||||
qiY = sourceY + 4.0f;
|
||||
|
||||
rect.x = int(fX + qiX * scale);
|
||||
rect.y = int(fY + qiY * scale);
|
||||
rect.cx = int(qiCX * scale);
|
||||
rect.cy = int(qiCY * scale);
|
||||
|
||||
/* ----------- */
|
||||
|
||||
if (src == previewSrc || src == programSrc) {
|
||||
uint32_t colorVal = src == programSrc
|
||||
? 0xFFFF0000
|
||||
: 0xFF00FF00;
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(sourceX, sourceY, 0.0f);
|
||||
drawBox(quarterCX, quarterCY, colorVal);
|
||||
gs_matrix_pop();
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(qiX, qiY, 0.0f);
|
||||
drawBox(qiCX, qiCY, 0xFF000000);
|
||||
gs_matrix_pop();
|
||||
}
|
||||
|
||||
/* ----------- */
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(qiX, qiY, 0.0f);
|
||||
gs_matrix_scale3f(qiScaleX, qiScaleY, 1.0f);
|
||||
|
||||
gs_effect_set_color(color, 0xFF000000);
|
||||
gs_set_scissor_rect(&rect);
|
||||
obs_source_video_render(src);
|
||||
gs_set_scissor_rect(nullptr);
|
||||
gs_effect_set_color(color, 0xFFFFFFFF);
|
||||
renderVB(solid, window->outerBox, targetCX, targetCY);
|
||||
|
||||
gs_matrix_pop();
|
||||
|
||||
/* ----------- */
|
||||
|
||||
|
||||
offset = labelOffset(label, quarterCX);
|
||||
cx = obs_source_get_width(label);
|
||||
cy = obs_source_get_height(label);
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(sourceX + offset,
|
||||
(quarterCY * 0.8f) + sourceY, 0.0f);
|
||||
|
||||
drawBox(cx, cy + int(quarterCX * 0.015f), 0xD91F1F1F);
|
||||
obs_source_video_render(label);
|
||||
|
||||
gs_matrix_pop();
|
||||
}
|
||||
|
||||
gs_effect_set_color(color, 0xFFFFFFFF);
|
||||
|
||||
/* ----------------------------- */
|
||||
/* draw preview */
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(2.0f, 2.0f, 0.0f);
|
||||
gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f);
|
||||
|
||||
rect.x = int(fX + 2.0f * scale);
|
||||
rect.y = int(fY + 2.0f * scale);
|
||||
rect.cx = int(hiCX * scale);
|
||||
rect.cy = int(hiCY * scale);
|
||||
gs_set_scissor_rect(&rect);
|
||||
|
||||
if (studioMode) {
|
||||
obs_source_video_render(previewSrc);
|
||||
} else {
|
||||
obs_render_main_view();
|
||||
}
|
||||
|
||||
gs_set_scissor_rect(nullptr);
|
||||
|
||||
gs_matrix_pop();
|
||||
|
||||
/* ----------- */
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_scale3f(0.5f, 0.5f, 1.0f);
|
||||
|
||||
renderVB(solid, window->outerBox, targetCX, targetCY);
|
||||
renderVB(solid, window->innerBox, targetCX, targetCY);
|
||||
renderVB(solid, window->leftVLine, targetCX, targetCY);
|
||||
renderVB(solid, window->rightVLine, targetCX, targetCY);
|
||||
renderVB(solid, window->leftLine, targetCX, targetCY);
|
||||
renderVB(solid, window->topLine, targetCX, targetCY);
|
||||
renderVB(solid, window->rightLine, targetCX, targetCY);
|
||||
|
||||
gs_matrix_pop();
|
||||
|
||||
/* ----------- */
|
||||
|
||||
obs_source_t *previewLabel = window->multiviewLabels[0];
|
||||
offset = labelOffset(previewLabel, halfCX);
|
||||
cx = obs_source_get_width(previewLabel);
|
||||
cy = obs_source_get_height(previewLabel);
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(offset, (halfCY * 0.8f), 0.0f);
|
||||
|
||||
drawBox(cx, cy + int(halfCX * 0.015f), 0xD91F1F1F);
|
||||
obs_source_video_render(previewLabel);
|
||||
|
||||
gs_matrix_pop();
|
||||
|
||||
/* ----------------------------- */
|
||||
/* draw program */
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(halfCX + 2.0, 2.0f, 0.0f);
|
||||
gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f);
|
||||
|
||||
rect.x = int(fX + (halfCX + 2.0f) * scale);
|
||||
rect.y = int(fY + 2.0f * scale);
|
||||
gs_set_scissor_rect(&rect);
|
||||
|
||||
obs_render_main_view();
|
||||
|
||||
gs_set_scissor_rect(nullptr);
|
||||
|
||||
gs_matrix_pop();
|
||||
|
||||
/* ----------- */
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(halfCX, 0.0f, 0.0f);
|
||||
gs_matrix_scale3f(0.5f, 0.5f, 1.0f);
|
||||
|
||||
renderVB(solid, window->outerBox, targetCX, targetCY);
|
||||
|
||||
gs_matrix_pop();
|
||||
|
||||
/* ----------- */
|
||||
|
||||
obs_source_t *programLabel = window->multiviewLabels[1];
|
||||
offset = labelOffset(programLabel, halfCX);
|
||||
cx = obs_source_get_width(programLabel);
|
||||
cy = obs_source_get_height(programLabel);
|
||||
|
||||
gs_matrix_push();
|
||||
gs_matrix_translate3f(halfCX + offset, (halfCY * 0.8f), 0.0f);
|
||||
|
||||
drawBox(cx, cy + int(halfCX * 0.015f), 0xD91F1F1F);
|
||||
obs_source_video_render(programLabel);
|
||||
|
||||
gs_matrix_pop();
|
||||
|
||||
/* ----------------------------- */
|
||||
|
||||
gs_viewport_pop();
|
||||
gs_projection_pop();
|
||||
}
|
||||
|
||||
void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)
|
||||
{
|
||||
OBSProjector *window = reinterpret_cast<OBSProjector*>(data);
|
||||
|
||||
if (!window->ready)
|
||||
return;
|
||||
|
||||
OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
||||
|
||||
uint32_t targetCX;
|
||||
@ -123,7 +517,8 @@ void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)
|
||||
|
||||
OBSSource source = window->source;
|
||||
|
||||
if (!window->useStudioProgram && main->IsPreviewProgramMode()) {
|
||||
if (window->type == ProjectorType::Preview &&
|
||||
main->IsPreviewProgramMode()) {
|
||||
OBSSource curSource = main->GetCurrentSceneSource();
|
||||
|
||||
if (window->source != curSource) {
|
||||
@ -166,11 +561,71 @@ void OBSProjector::mousePressEvent(QMouseEvent *event)
|
||||
void OBSProjector::EscapeTriggered()
|
||||
{
|
||||
if (!isWindow) {
|
||||
OBSBasic *main =
|
||||
reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
|
||||
|
||||
OBSBasic *main = (OBSBasic*)obs_frontend_get_main_window();
|
||||
main->RemoveSavedProjectors(savedMonitor);
|
||||
}
|
||||
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
void OBSProjector::UpdateMultiview()
|
||||
{
|
||||
for (OBSWeakSource &val : multiviewScenes)
|
||||
val = nullptr;
|
||||
for (OBSSource &val : multiviewLabels)
|
||||
val = nullptr;
|
||||
|
||||
struct obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
|
||||
uint32_t h = ovi.base_height;
|
||||
|
||||
struct obs_frontend_source_list scenes = {};
|
||||
obs_frontend_get_scenes(&scenes);
|
||||
|
||||
int curIdx = 0;
|
||||
|
||||
multiviewLabels[0] = CreateLabel(Str("StudioMode.Preview"), h / 2);
|
||||
multiviewLabels[1] = CreateLabel(Str("StudioMode.Program"), h / 2);
|
||||
|
||||
for (size_t i = 0; i < scenes.sources.num && curIdx < 8; i++) {
|
||||
obs_source_t *src = scenes.sources.array[i];
|
||||
OBSData data = obs_source_get_private_settings(src);
|
||||
obs_data_release(data);
|
||||
|
||||
obs_data_set_default_bool(data, "show_in_multiview", true);
|
||||
if (!obs_data_get_bool(data, "show_in_multiview"))
|
||||
continue;
|
||||
|
||||
multiviewScenes[curIdx] = OBSGetWeakRef(src);
|
||||
obs_source_inc_showing(src);
|
||||
|
||||
std::string name;
|
||||
name += std::to_string(curIdx + 1);
|
||||
name += " - ";
|
||||
name += obs_source_get_name(src);
|
||||
|
||||
if (name.size() > 15)
|
||||
name.resize(15);
|
||||
|
||||
multiviewLabels[curIdx + 2] = CreateLabel(name.c_str(), h / 4);
|
||||
|
||||
curIdx++;
|
||||
}
|
||||
|
||||
obs_frontend_source_list_free(&scenes);
|
||||
}
|
||||
|
||||
void OBSProjector::UpdateMultiviewProjectors()
|
||||
{
|
||||
obs_enter_graphics();
|
||||
updatingMultiview = true;
|
||||
obs_leave_graphics();
|
||||
|
||||
for (auto &projector : multiviewProjectors)
|
||||
projector->UpdateMultiview();
|
||||
|
||||
obs_enter_graphics();
|
||||
updatingMultiview = false;
|
||||
obs_leave_graphics();
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ private:
|
||||
OBSSource source;
|
||||
OBSSignal removedSignal;
|
||||
|
||||
static void OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy);
|
||||
static void OBSRender(void *data, uint32_t cx, uint32_t cy);
|
||||
static void OBSSourceRemoved(void *data, calldata_t *params);
|
||||
|
||||
@ -20,7 +21,19 @@ private:
|
||||
|
||||
int savedMonitor = 0;
|
||||
bool isWindow = false;
|
||||
bool useStudioProgram = false;
|
||||
ProjectorType type = ProjectorType::Source;
|
||||
OBSWeakSource multiviewScenes[8];
|
||||
OBSSource multiviewLabels[10];
|
||||
gs_vertbuffer_t *outerBox = nullptr;
|
||||
gs_vertbuffer_t *innerBox = nullptr;
|
||||
gs_vertbuffer_t *leftVLine = nullptr;
|
||||
gs_vertbuffer_t *rightVLine = nullptr;
|
||||
gs_vertbuffer_t *leftLine = nullptr;
|
||||
gs_vertbuffer_t *topLine = nullptr;
|
||||
gs_vertbuffer_t *rightLine = nullptr;
|
||||
bool ready = false;
|
||||
|
||||
void UpdateMultiview();
|
||||
|
||||
private slots:
|
||||
void EscapeTriggered();
|
||||
@ -30,5 +43,7 @@ public:
|
||||
~OBSProjector();
|
||||
|
||||
void Init(int monitor, bool window, QString title,
|
||||
bool studioProgram = false);
|
||||
ProjectorType type = ProjectorType::Source);
|
||||
|
||||
static void UpdateMultiviewProjectors();
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user