mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-09-20 04:42:18 +02:00
710d99ef4d
When a source file contains an explicit include with a filename following the "moc_<actual-filename>.cpp" pattern, then CMake's AUTOMOC generation tool will recognize the matching pair and generate the replacement header file and add the required include directory entries. For all files which do contain Q_OBJECT or similar declarations but do not have an explicit include directive, the global mocs_compilation.cpp file will still be generated (which groups all "missing" generated headers). The larger this global file is, the more expensive incremental compilation will be as this file (and all its contained generated headers) will be re-generated regardless of whether actual changes occurred.
527 lines
12 KiB
C++
527 lines
12 KiB
C++
#include <QAction>
|
|
#include <QGuiApplication>
|
|
#include <QMouseEvent>
|
|
#include <QMenu>
|
|
#include <QScreen>
|
|
#include <qt-wrappers.hpp>
|
|
#include "moc_window-projector.cpp"
|
|
#include "obs-app.hpp"
|
|
#include "window-basic-main.hpp"
|
|
#include "display-helpers.hpp"
|
|
#include "platform.hpp"
|
|
#include "multiview.hpp"
|
|
|
|
static QList<OBSProjector *> multiviewProjectors;
|
|
|
|
static bool updatingMultiview = false, mouseSwitching, transitionOnDoubleClick;
|
|
|
|
OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor,
|
|
ProjectorType type_)
|
|
: OBSQTDisplay(widget, Qt::Window),
|
|
weakSource(OBSGetWeakRef(source_))
|
|
{
|
|
OBSSource source = GetSource();
|
|
if (source) {
|
|
sigs.emplace_back(obs_source_get_signal_handler(source),
|
|
"rename", OBSSourceRenamed, this);
|
|
sigs.emplace_back(obs_source_get_signal_handler(source),
|
|
"destroy", OBSSourceDestroyed, this);
|
|
}
|
|
|
|
isAlwaysOnTop = config_get_bool(GetGlobalConfig(), "BasicWindow",
|
|
"ProjectorAlwaysOnTop");
|
|
|
|
if (isAlwaysOnTop)
|
|
setWindowFlags(Qt::WindowStaysOnTopHint);
|
|
|
|
// Mark the window as a projector so SetDisplayAffinity
|
|
// can skip it
|
|
windowHandle()->setProperty("isOBSProjectorWindow", true);
|
|
|
|
#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
|
// Prevents resizing of projector windows
|
|
setAttribute(Qt::WA_PaintOnScreen, false);
|
|
#endif
|
|
|
|
type = type_;
|
|
#ifdef __APPLE__
|
|
setWindowIcon(
|
|
QIcon::fromTheme("obs", QIcon(":/res/images/obs_256x256.png")));
|
|
#else
|
|
setWindowIcon(QIcon::fromTheme("obs", QIcon(":/res/images/obs.png")));
|
|
#endif
|
|
|
|
if (monitor == -1)
|
|
resize(480, 270);
|
|
else
|
|
SetMonitor(monitor);
|
|
|
|
if (source)
|
|
UpdateProjectorTitle(QT_UTF8(obs_source_get_name(source)));
|
|
else
|
|
UpdateProjectorTitle(QString());
|
|
|
|
QAction *action = new QAction(this);
|
|
action->setShortcut(Qt::Key_Escape);
|
|
addAction(action);
|
|
connect(action, &QAction::triggered, this,
|
|
&OBSProjector::EscapeTriggered);
|
|
|
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
|
|
|
//disable application quit when last window closed
|
|
setAttribute(Qt::WA_QuitOnClose, false);
|
|
|
|
installEventFilter(CreateShortcutFilter());
|
|
|
|
auto addDrawCallback = [this]() {
|
|
bool isMultiview = type == ProjectorType::Multiview;
|
|
obs_display_add_draw_callback(
|
|
GetDisplay(),
|
|
isMultiview ? OBSRenderMultiview : OBSRender, this);
|
|
obs_display_set_background_color(GetDisplay(), 0x000000);
|
|
};
|
|
|
|
connect(this, &OBSQTDisplay::DisplayCreated, addDrawCallback);
|
|
connect(App(), &QGuiApplication::screenRemoved, this,
|
|
&OBSProjector::ScreenRemoved);
|
|
|
|
if (type == ProjectorType::Multiview) {
|
|
multiview = new Multiview();
|
|
|
|
UpdateMultiview();
|
|
|
|
multiviewProjectors.push_back(this);
|
|
}
|
|
|
|
App()->IncrementSleepInhibition();
|
|
|
|
if (source)
|
|
obs_source_inc_showing(source);
|
|
|
|
ready = true;
|
|
|
|
show();
|
|
|
|
// We need it here to allow keyboard input in X11 to listen to Escape
|
|
activateWindow();
|
|
}
|
|
|
|
OBSProjector::~OBSProjector()
|
|
{
|
|
sigs.clear();
|
|
|
|
bool isMultiview = type == ProjectorType::Multiview;
|
|
obs_display_remove_draw_callback(
|
|
GetDisplay(), isMultiview ? OBSRenderMultiview : OBSRender,
|
|
this);
|
|
|
|
OBSSource source = GetSource();
|
|
if (source)
|
|
obs_source_dec_showing(source);
|
|
|
|
if (isMultiview) {
|
|
delete multiview;
|
|
multiviewProjectors.removeAll(this);
|
|
}
|
|
|
|
App()->DecrementSleepInhibition();
|
|
|
|
screen = nullptr;
|
|
}
|
|
|
|
void OBSProjector::SetMonitor(int monitor)
|
|
{
|
|
savedMonitor = monitor;
|
|
screen = QGuiApplication::screens()[monitor];
|
|
setGeometry(screen->geometry());
|
|
showFullScreen();
|
|
SetHideCursor();
|
|
}
|
|
|
|
void OBSProjector::SetHideCursor()
|
|
{
|
|
if (savedMonitor == -1)
|
|
return;
|
|
|
|
bool hideCursor = config_get_bool(GetGlobalConfig(), "BasicWindow",
|
|
"HideProjectorCursor");
|
|
|
|
if (hideCursor && type != ProjectorType::Multiview)
|
|
setCursor(Qt::BlankCursor);
|
|
else
|
|
setCursor(Qt::ArrowCursor);
|
|
}
|
|
|
|
void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy)
|
|
{
|
|
OBSProjector *window = (OBSProjector *)data;
|
|
|
|
if (updatingMultiview || !window->ready)
|
|
return;
|
|
|
|
window->multiview->Render(cx, cy);
|
|
}
|
|
|
|
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());
|
|
OBSSource source = window->GetSource();
|
|
|
|
uint32_t targetCX;
|
|
uint32_t targetCY;
|
|
int x, y;
|
|
int newCX, newCY;
|
|
float scale;
|
|
|
|
if (source) {
|
|
targetCX = std::max(obs_source_get_width(source), 1u);
|
|
targetCY = std::max(obs_source_get_height(source), 1u);
|
|
} else {
|
|
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);
|
|
|
|
newCX = int(scale * float(targetCX));
|
|
newCY = int(scale * float(targetCY));
|
|
|
|
startRegion(x, y, newCX, newCY, 0.0f, float(targetCX), 0.0f,
|
|
float(targetCY));
|
|
|
|
if (window->type == ProjectorType::Preview &&
|
|
main->IsPreviewProgramMode()) {
|
|
OBSSource curSource = main->GetCurrentSceneSource();
|
|
|
|
if (source != curSource) {
|
|
obs_source_dec_showing(source);
|
|
obs_source_inc_showing(curSource);
|
|
source = curSource;
|
|
window->weakSource = OBSGetWeakRef(source);
|
|
}
|
|
} else if (window->type == ProjectorType::Preview &&
|
|
!main->IsPreviewProgramMode()) {
|
|
window->weakSource = nullptr;
|
|
}
|
|
|
|
if (source)
|
|
obs_source_video_render(source);
|
|
else
|
|
obs_render_main_texture();
|
|
|
|
endRegion();
|
|
}
|
|
|
|
void OBSProjector::OBSSourceRenamed(void *data, calldata_t *params)
|
|
{
|
|
OBSProjector *window = reinterpret_cast<OBSProjector *>(data);
|
|
QString oldName = calldata_string(params, "prev_name");
|
|
QString newName = calldata_string(params, "new_name");
|
|
|
|
QMetaObject::invokeMethod(window, "RenameProjector",
|
|
Q_ARG(QString, oldName),
|
|
Q_ARG(QString, newName));
|
|
}
|
|
|
|
void OBSProjector::OBSSourceDestroyed(void *data, calldata_t *)
|
|
{
|
|
OBSProjector *window = reinterpret_cast<OBSProjector *>(data);
|
|
QMetaObject::invokeMethod(window, "EscapeTriggered");
|
|
}
|
|
|
|
void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event)
|
|
{
|
|
OBSQTDisplay::mouseDoubleClickEvent(event);
|
|
|
|
if (!mouseSwitching)
|
|
return;
|
|
|
|
if (!transitionOnDoubleClick)
|
|
return;
|
|
|
|
// Only MultiView projectors handle double click
|
|
if (this->type != ProjectorType::Multiview)
|
|
return;
|
|
|
|
OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();
|
|
if (!main->IsPreviewProgramMode())
|
|
return;
|
|
|
|
if (event->button() == Qt::LeftButton) {
|
|
QPoint pos = event->pos();
|
|
OBSSource src =
|
|
multiview->GetSourceByPosition(pos.x(), pos.y());
|
|
if (!src)
|
|
return;
|
|
|
|
if (main->GetProgramSource() != src)
|
|
main->TransitionToScene(src);
|
|
}
|
|
}
|
|
|
|
void OBSProjector::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
OBSQTDisplay::mousePressEvent(event);
|
|
|
|
if (event->button() == Qt::RightButton) {
|
|
QMenu *projectorMenu = new QMenu(QTStr("Fullscreen"));
|
|
OBSBasic::AddProjectorMenuMonitors(
|
|
projectorMenu, this,
|
|
&OBSProjector::OpenFullScreenProjector);
|
|
|
|
QMenu popup(this);
|
|
popup.addMenu(projectorMenu);
|
|
|
|
if (GetMonitor() > -1) {
|
|
popup.addAction(QTStr("Windowed"), this,
|
|
&OBSProjector::OpenWindowedProjector);
|
|
|
|
} else if (!this->isMaximized()) {
|
|
popup.addAction(QTStr("ResizeProjectorWindowToContent"),
|
|
this, &OBSProjector::ResizeToContent);
|
|
}
|
|
|
|
QAction *alwaysOnTopButton = new QAction(
|
|
QTStr("Basic.MainMenu.View.AlwaysOnTop"), this);
|
|
alwaysOnTopButton->setCheckable(true);
|
|
alwaysOnTopButton->setChecked(isAlwaysOnTop);
|
|
|
|
connect(alwaysOnTopButton, &QAction::toggled, this,
|
|
&OBSProjector::AlwaysOnTopToggled);
|
|
|
|
popup.addAction(alwaysOnTopButton);
|
|
|
|
popup.addAction(QTStr("Close"), this,
|
|
&OBSProjector::EscapeTriggered);
|
|
popup.exec(QCursor::pos());
|
|
} else if (event->button() == Qt::LeftButton) {
|
|
// Only MultiView projectors handle left click
|
|
if (this->type != ProjectorType::Multiview)
|
|
return;
|
|
|
|
if (!mouseSwitching)
|
|
return;
|
|
|
|
QPoint pos = event->pos();
|
|
OBSSource src =
|
|
multiview->GetSourceByPosition(pos.x(), pos.y());
|
|
if (!src)
|
|
return;
|
|
|
|
OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();
|
|
if (main->GetCurrentSceneSource() != src)
|
|
main->SetCurrentScene(src, false);
|
|
}
|
|
}
|
|
|
|
void OBSProjector::EscapeTriggered()
|
|
{
|
|
OBSBasic *main = reinterpret_cast<OBSBasic *>(App()->GetMainWindow());
|
|
main->DeleteProjector(this);
|
|
}
|
|
|
|
void OBSProjector::UpdateMultiview()
|
|
{
|
|
MultiviewLayout multiviewLayout = static_cast<MultiviewLayout>(
|
|
config_get_int(GetGlobalConfig(), "BasicWindow",
|
|
"MultiviewLayout"));
|
|
|
|
bool drawLabel = config_get_bool(GetGlobalConfig(), "BasicWindow",
|
|
"MultiviewDrawNames");
|
|
|
|
bool drawSafeArea = config_get_bool(GetGlobalConfig(), "BasicWindow",
|
|
"MultiviewDrawAreas");
|
|
|
|
mouseSwitching = config_get_bool(GetGlobalConfig(), "BasicWindow",
|
|
"MultiviewMouseSwitch");
|
|
|
|
transitionOnDoubleClick = config_get_bool(
|
|
GetGlobalConfig(), "BasicWindow", "TransitionOnDoubleClick");
|
|
|
|
multiview->Update(multiviewLayout, drawLabel, drawSafeArea);
|
|
}
|
|
|
|
void OBSProjector::UpdateProjectorTitle(QString name)
|
|
{
|
|
bool window = (GetMonitor() == -1);
|
|
|
|
QString title = nullptr;
|
|
switch (type) {
|
|
case ProjectorType::Scene:
|
|
if (!window)
|
|
title = QTStr("SceneProjector") + " - " + name;
|
|
else
|
|
title = QTStr("SceneWindow") + " - " + name;
|
|
break;
|
|
case ProjectorType::Source:
|
|
if (!window)
|
|
title = QTStr("SourceProjector") + " - " + name;
|
|
else
|
|
title = QTStr("SourceWindow") + " - " + name;
|
|
break;
|
|
case ProjectorType::Preview:
|
|
if (!window)
|
|
title = QTStr("PreviewProjector");
|
|
else
|
|
title = QTStr("PreviewWindow");
|
|
break;
|
|
case ProjectorType::StudioProgram:
|
|
if (!window)
|
|
title = QTStr("StudioProgramProjector");
|
|
else
|
|
title = QTStr("StudioProgramWindow");
|
|
break;
|
|
case ProjectorType::Multiview:
|
|
if (!window)
|
|
title = QTStr("MultiviewProjector");
|
|
else
|
|
title = QTStr("MultiviewWindowed");
|
|
break;
|
|
default:
|
|
title = name;
|
|
break;
|
|
}
|
|
|
|
setWindowTitle(title);
|
|
}
|
|
|
|
OBSSource OBSProjector::GetSource()
|
|
{
|
|
return OBSGetStrongRef(weakSource);
|
|
}
|
|
|
|
ProjectorType OBSProjector::GetProjectorType()
|
|
{
|
|
return type;
|
|
}
|
|
|
|
int OBSProjector::GetMonitor()
|
|
{
|
|
return savedMonitor;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
void OBSProjector::RenameProjector(QString oldName, QString newName)
|
|
{
|
|
if (oldName == newName)
|
|
return;
|
|
|
|
UpdateProjectorTitle(newName);
|
|
}
|
|
|
|
void OBSProjector::OpenFullScreenProjector()
|
|
{
|
|
if (!isFullScreen())
|
|
prevGeometry = geometry();
|
|
|
|
int monitor = sender()->property("monitor").toInt();
|
|
SetMonitor(monitor);
|
|
|
|
OBSSource source = GetSource();
|
|
UpdateProjectorTitle(QT_UTF8(obs_source_get_name(source)));
|
|
}
|
|
|
|
void OBSProjector::OpenWindowedProjector()
|
|
{
|
|
showFullScreen();
|
|
showNormal();
|
|
setCursor(Qt::ArrowCursor);
|
|
|
|
if (!prevGeometry.isNull())
|
|
setGeometry(prevGeometry);
|
|
else
|
|
resize(480, 270);
|
|
|
|
savedMonitor = -1;
|
|
|
|
OBSSource source = GetSource();
|
|
UpdateProjectorTitle(QT_UTF8(obs_source_get_name(source)));
|
|
screen = nullptr;
|
|
}
|
|
|
|
void OBSProjector::ResizeToContent()
|
|
{
|
|
OBSSource source = GetSource();
|
|
uint32_t targetCX;
|
|
uint32_t targetCY;
|
|
int x, y, newX, newY;
|
|
float scale;
|
|
|
|
if (source) {
|
|
targetCX = std::max(obs_source_get_width(source), 1u);
|
|
targetCY = std::max(obs_source_get_height(source), 1u);
|
|
} else {
|
|
struct obs_video_info ovi;
|
|
obs_get_video_info(&ovi);
|
|
targetCX = ovi.base_width;
|
|
targetCY = ovi.base_height;
|
|
}
|
|
|
|
QSize size = this->size();
|
|
GetScaleAndCenterPos(targetCX, targetCY, size.width(), size.height(), x,
|
|
y, scale);
|
|
|
|
newX = size.width() - (x * 2);
|
|
newY = size.height() - (y * 2);
|
|
resize(newX, newY);
|
|
}
|
|
|
|
void OBSProjector::AlwaysOnTopToggled(bool isAlwaysOnTop)
|
|
{
|
|
SetIsAlwaysOnTop(isAlwaysOnTop, true);
|
|
}
|
|
|
|
void OBSProjector::closeEvent(QCloseEvent *event)
|
|
{
|
|
EscapeTriggered();
|
|
event->accept();
|
|
}
|
|
|
|
bool OBSProjector::IsAlwaysOnTop() const
|
|
{
|
|
return isAlwaysOnTop;
|
|
}
|
|
|
|
bool OBSProjector::IsAlwaysOnTopOverridden() const
|
|
{
|
|
return isAlwaysOnTopOverridden;
|
|
}
|
|
|
|
void OBSProjector::SetIsAlwaysOnTop(bool isAlwaysOnTop, bool isOverridden)
|
|
{
|
|
this->isAlwaysOnTop = isAlwaysOnTop;
|
|
this->isAlwaysOnTopOverridden = isOverridden;
|
|
|
|
SetAlwaysOnTop(this, isAlwaysOnTop);
|
|
}
|
|
|
|
void OBSProjector::ScreenRemoved(QScreen *screen_)
|
|
{
|
|
if (GetMonitor() < 0 || !screen)
|
|
return;
|
|
|
|
if (screen == screen_)
|
|
EscapeTriggered();
|
|
}
|