Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/cmake/ui-widgets.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ target_sources(
widgets/OBSProjector.hpp
widgets/OBSQTDisplay.cpp
widgets/OBSQTDisplay.hpp
widgets/OBSSourceWidget.cpp
widgets/OBSSourceWidget.hpp
widgets/StatusBarWidget.cpp
widgets/StatusBarWidget.hpp
widgets/VolControl.cpp
Expand Down
4 changes: 2 additions & 2 deletions frontend/forms/OBSBasicFilters.ui
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<item>
<widget class="QFrame" name="asyncWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
Expand Down Expand Up @@ -249,7 +249,7 @@
<item>
<widget class="QFrame" name="effectWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
Expand Down
256 changes: 256 additions & 0 deletions frontend/widgets/OBSSourceWidget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
#include "OBSSourceWidget.hpp"

#include <OBSApp.hpp>
#include <utility/display-helpers.hpp>
#include <utility/platform.hpp>
#include <widgets/OBSBasic.hpp>

#include <qt-wrappers.hpp>

#include <QScreen>
#include <QScrollArea>
#include <QWindow>

#include "moc_OBSSourceWidget.cpp"

OBSSourceWidget::OBSSourceWidget(QWidget *parent) : QFrame(parent), fixedAspectRatio(0.0)
{
layout = new QVBoxLayout();
setLayout(layout);

layout->setContentsMargins(0, 0, 0, 0);
setMinimumSize(QSize(240, 135));

setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);

if (window()) {
window()->installEventFilter(this);
}

if (parent) {
parent->installEventFilter(this);
}

QObject *checkParent = parent;

while (checkParent) {
QScrollArea *scrollParent = qobject_cast<QScrollArea *>(checkParent);
if (scrollParent && scrollParent->widget()) {
scrollParent->widget()->installEventFilter(this);
}

if (!checkParent->parent() || checkParent->parent() == checkParent) {
break;
}

checkParent = checkParent->parent();
}
}

OBSSourceWidget::OBSSourceWidget(QWidget *parent, obs_source_t *source) : OBSSourceWidget(parent)
{
setSource(source);
}

void OBSSourceWidget::setFixedAspectRatio(double ratio)
{
if (ratio > 0.0) {
fixedAspectRatio = ratio;
} else {
fixedAspectRatio = 0;
}
}

void OBSSourceWidget::setSource(obs_source_t *source)
{
if (!sourceView) {
sourceView = new OBSSourceWidgetView(this, source);
layout->addWidget(sourceView);

connect(sourceView, &OBSSourceWidgetView::viewReady, this, &OBSSourceWidget::resizeSourceView);
}

sourceView->setSource(source);
}

void OBSSourceWidget::resizeSourceView()
{
if (!sourceView) {
return;
}

if (sourceView->sourceWidth() <= 0 || sourceView->sourceHeight() <= 0) {
return;
}

double aspectRatio = fixedAspectRatio > 0
? fixedAspectRatio
: (double)sourceView->sourceWidth() / (double)sourceView->sourceHeight();

// Widget only expands in one direction
bool singleExpandDirection = (sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag) !=
(sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag);

int scaledWidth = std::floor(height() / aspectRatio);
int scaledHeight = std::floor(width() / aspectRatio);

if (fixedAspectRatio) {
setMaximumWidth(QWIDGETSIZE_MAX);
setMaximumHeight(scaledHeight);
} else if (singleExpandDirection) {
setMaximumWidth(QWIDGETSIZE_MAX);
setMaximumHeight(QWIDGETSIZE_MAX);

if ((sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag) == QSizePolicy::ExpandFlag) {
setMaximumHeight(scaledHeight);
} else {
setMaximumWidth(scaledWidth);
}
}

QWindow *nativeWindow = sourceView->windowHandle();
QRegion visible = sourceView->visibleRegion();
if (nativeWindow) {
QPoint position = sourceView->mapTo(sourceView->nativeParentWidget(), QPoint());
nativeWindow->setGeometry(QRect(position, sourceView->geometry().size()));

if (!visible.isNull()) {
if (visible.boundingRect().width() > 0 && visible.boundingRect().height() > 0) {
nativeWindow->setMask(visible.boundingRect());
}
} else {
nativeWindow->setMask(QRegion(0, 0, 1, 1));
}
}
}

bool OBSSourceWidget::eventFilter(QObject *, QEvent *event)
{
if (event->type() == QEvent::Resize) {
resizeSourceView();
} else if (event->type() == QEvent::Move) {
resizeSourceView();
}

return false;
}

void OBSSourceWidget::moveEvent(QMoveEvent *event)
{
QFrame::moveEvent(event);
resizeSourceView();
}

void OBSSourceWidget::resizeEvent(QResizeEvent *event)
{
QFrame::resizeEvent(event);
resizeSourceView();
}

OBSSourceWidget::~OBSSourceWidget() {}

OBSSourceWidgetView::OBSSourceWidgetView(OBSSourceWidget *widget, obs_source_t *source)
: OBSQTDisplay(widget, Qt::Widget)
{
setSource(source);
show();
}

OBSSourceWidgetView::~OBSSourceWidgetView()
{
obs_display_remove_draw_callback(GetDisplay(), OBSRender, this);

OBSSource source = GetSource();
if (source) {
obs_source_dec_showing(source);
}
}

void OBSSourceWidgetView::setSourceWidth(int width)
{
if (sourceWidth() == width) {
return;
}

sourceWidth_ = width;
emit viewReady();
}

void OBSSourceWidgetView::setSourceHeight(int height)
{
if (sourceHeight() == height) {
return;
}

sourceHeight_ = height;
emit viewReady();
}

OBSSource OBSSourceWidgetView::GetSource()
{
return OBSGetStrongRef(weakSource);
}

void OBSSourceWidgetView::OBSRender(void *data, uint32_t cx, uint32_t cy)
{
OBSSourceWidgetView *view = reinterpret_cast<OBSSourceWidgetView *>(data);

OBSSource source = view->GetSource();
if (!source) {
return;
}

uint32_t sourceCX = std::max(obs_source_get_width(source), 1u);
uint32_t sourceCY = std::max(obs_source_get_height(source), 1u);

int x, y;
int newCX, newCY;
float scale;

GetScaleAndCenterPos(sourceCX, sourceCY, cx, cy, x, y, scale);

newCX = int(scale * float(sourceCX));
newCY = int(scale * float(sourceCY));

gs_viewport_push();
gs_projection_push();
const bool previous = gs_set_linear_srgb(true);

gs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), -100.0f, 100.0f);
gs_set_viewport(x, y, newCX, newCY);
obs_source_video_render(source);

gs_set_linear_srgb(previous);
gs_projection_pop();
gs_viewport_pop();

view->setSourceWidth(sourceCX);
view->setSourceHeight(sourceCY);
}

void OBSSourceWidgetView::setSource(obs_source_t *source)
{
if (weakSource) {
obs_source_t *prevSource = OBSGetStrongRef(weakSource);
if (prevSource) {
obs_source_dec_showing(prevSource);
}
}

weakSource = OBSGetWeakRef(source);
obs_source_inc_showing(source);

enum obs_source_type type = obs_source_get_type(source);
bool drawable_type = type == OBS_SOURCE_TYPE_INPUT || type == OBS_SOURCE_TYPE_SCENE;

auto addDrawCallback = [this]() {
obs_display_add_draw_callback(GetDisplay(), OBSRender, this);
};

uint32_t caps = obs_source_get_output_flags(source);
if ((caps & OBS_SOURCE_VIDEO) != 0) {
if (drawable_type) {
connect(this, &OBSQTDisplay::DisplayCreated, addDrawCallback);
}
}
}
63 changes: 63 additions & 0 deletions frontend/widgets/OBSSourceWidget.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#pragma once

#include "OBSQTDisplay.hpp"

#include <QFrame>
#include <QVBoxLayout>

class OBSSourceWidgetView;

class OBSSourceWidget : public QFrame {
Q_OBJECT

private:
OBSSourceWidgetView *sourceView = nullptr;
QVBoxLayout *layout;

double fixedAspectRatio;

void moveEvent(QMoveEvent *event) override;
void resizeEvent(QResizeEvent *event) override;

public:
OBSSourceWidget(QWidget *parent);
OBSSourceWidget(QWidget *parent, obs_source_t *source);
~OBSSourceWidget();

void setFixedAspectRatio(double ratio);
void setSource(obs_source_t *source);

void resizeSourceView();

protected:
bool eventFilter(QObject *obj, QEvent *event) override;
};

class OBSSourceWidgetView : public OBSQTDisplay {
Q_OBJECT

private:
OBSWeakSource weakSource = nullptr;

static void OBSRender(void *data, uint32_t cx, uint32_t cy);

QRect prevGeometry;

int32_t sourceWidth_;
int32_t sourceHeight_;

public:
OBSSourceWidgetView(OBSSourceWidget *parent, obs_source_t *source);
~OBSSourceWidgetView();

void setSource(obs_source_t *source);
void setSourceWidth(int width);
void setSourceHeight(int height);
int sourceWidth() { return sourceWidth_; }
int sourceHeight() { return sourceHeight_; }

OBSSource GetSource();

signals:
void viewReady();
};
Loading