frontend: Split Qt UI Widget implementations into single files per class
This commit is contained in:
parent
9f887c76d3
commit
654ddcd409
File diff suppressed because it is too large
Load Diff
@ -1,303 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <components/AbsoluteSlider.hpp>
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QAccessibleWidget>
|
||||
#include "absolute-slider.hpp"
|
||||
|
||||
class QPushButton;
|
||||
class VolumeMeterTimer;
|
||||
class VolumeSlider;
|
||||
|
||||
class VolumeMeter : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||
|
||||
// Levels are denoted in dBFS.
|
||||
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||
|
||||
// Rates are denoted in dB/second.
|
||||
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||
|
||||
// Time in seconds for the VU meter to integrate over.
|
||||
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||
DESIGNABLE true)
|
||||
|
||||
// Duration is denoted in seconds.
|
||||
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||
DESIGNABLE true)
|
||||
|
||||
friend class VolControl;
|
||||
|
||||
private:
|
||||
obs_volmeter_t *obs_volmeter;
|
||||
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||
|
||||
inline void resetLevels();
|
||||
inline void doLayout();
|
||||
inline bool detectIdle(uint64_t ts);
|
||||
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||
|
||||
inline int convertToInt(float number);
|
||||
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||
|
||||
QMutex dataMutex;
|
||||
|
||||
bool recalculateLayout = true;
|
||||
uint64_t currentLastUpdateTime = 0;
|
||||
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||
|
||||
int displayNrAudioChannels = 0;
|
||||
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
|
||||
QFont tickFont;
|
||||
QColor backgroundNominalColor;
|
||||
QColor backgroundWarningColor;
|
||||
QColor backgroundErrorColor;
|
||||
QColor foregroundNominalColor;
|
||||
QColor foregroundWarningColor;
|
||||
QColor foregroundErrorColor;
|
||||
|
||||
QColor backgroundNominalColorDisabled;
|
||||
QColor backgroundWarningColorDisabled;
|
||||
QColor backgroundErrorColorDisabled;
|
||||
QColor foregroundNominalColorDisabled;
|
||||
QColor foregroundWarningColorDisabled;
|
||||
QColor foregroundErrorColorDisabled;
|
||||
|
||||
QColor clipColor;
|
||||
QColor magnitudeColor;
|
||||
QColor majorTickColor;
|
||||
QColor minorTickColor;
|
||||
|
||||
int meterThickness;
|
||||
qreal meterFontScaling;
|
||||
|
||||
qreal minimumLevel;
|
||||
qreal warningLevel;
|
||||
qreal errorLevel;
|
||||
qreal clipLevel;
|
||||
qreal minimumInputLevel;
|
||||
qreal peakDecayRate;
|
||||
qreal magnitudeIntegrationTime;
|
||||
qreal peakHoldDuration;
|
||||
qreal inputPeakHoldDuration;
|
||||
|
||||
QColor p_backgroundNominalColor;
|
||||
QColor p_backgroundWarningColor;
|
||||
QColor p_backgroundErrorColor;
|
||||
QColor p_foregroundNominalColor;
|
||||
QColor p_foregroundWarningColor;
|
||||
QColor p_foregroundErrorColor;
|
||||
|
||||
uint64_t lastRedrawTime = 0;
|
||||
int channels = 0;
|
||||
bool clipping = false;
|
||||
bool vertical;
|
||||
bool muted = false;
|
||||
|
||||
public:
|
||||
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||
~VolumeMeter();
|
||||
|
||||
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
QRect getBarRect() const;
|
||||
bool needLayoutChange();
|
||||
|
||||
QColor getBackgroundNominalColor() const;
|
||||
void setBackgroundNominalColor(QColor c);
|
||||
QColor getBackgroundWarningColor() const;
|
||||
void setBackgroundWarningColor(QColor c);
|
||||
QColor getBackgroundErrorColor() const;
|
||||
void setBackgroundErrorColor(QColor c);
|
||||
QColor getForegroundNominalColor() const;
|
||||
void setForegroundNominalColor(QColor c);
|
||||
QColor getForegroundWarningColor() const;
|
||||
void setForegroundWarningColor(QColor c);
|
||||
QColor getForegroundErrorColor() const;
|
||||
void setForegroundErrorColor(QColor c);
|
||||
|
||||
QColor getBackgroundNominalColorDisabled() const;
|
||||
void setBackgroundNominalColorDisabled(QColor c);
|
||||
QColor getBackgroundWarningColorDisabled() const;
|
||||
void setBackgroundWarningColorDisabled(QColor c);
|
||||
QColor getBackgroundErrorColorDisabled() const;
|
||||
void setBackgroundErrorColorDisabled(QColor c);
|
||||
QColor getForegroundNominalColorDisabled() const;
|
||||
void setForegroundNominalColorDisabled(QColor c);
|
||||
QColor getForegroundWarningColorDisabled() const;
|
||||
void setForegroundWarningColorDisabled(QColor c);
|
||||
QColor getForegroundErrorColorDisabled() const;
|
||||
void setForegroundErrorColorDisabled(QColor c);
|
||||
|
||||
QColor getClipColor() const;
|
||||
void setClipColor(QColor c);
|
||||
QColor getMagnitudeColor() const;
|
||||
void setMagnitudeColor(QColor c);
|
||||
QColor getMajorTickColor() const;
|
||||
void setMajorTickColor(QColor c);
|
||||
QColor getMinorTickColor() const;
|
||||
void setMinorTickColor(QColor c);
|
||||
int getMeterThickness() const;
|
||||
void setMeterThickness(int v);
|
||||
qreal getMeterFontScaling() const;
|
||||
void setMeterFontScaling(qreal v);
|
||||
qreal getMinimumLevel() const;
|
||||
void setMinimumLevel(qreal v);
|
||||
qreal getWarningLevel() const;
|
||||
void setWarningLevel(qreal v);
|
||||
qreal getErrorLevel() const;
|
||||
void setErrorLevel(qreal v);
|
||||
qreal getClipLevel() const;
|
||||
void setClipLevel(qreal v);
|
||||
qreal getMinimumInputLevel() const;
|
||||
void setMinimumInputLevel(qreal v);
|
||||
qreal getPeakDecayRate() const;
|
||||
void setPeakDecayRate(qreal v);
|
||||
qreal getMagnitudeIntegrationTime() const;
|
||||
void setMagnitudeIntegrationTime(qreal v);
|
||||
qreal getPeakHoldDuration() const;
|
||||
void setPeakHoldDuration(qreal v);
|
||||
qreal getInputPeakHoldDuration() const;
|
||||
void setInputPeakHoldDuration(qreal v);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *e) override;
|
||||
};
|
||||
|
||||
class VolumeMeterTimer : public QTimer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline VolumeMeterTimer() : QTimer() {}
|
||||
|
||||
void AddVolControl(VolumeMeter *meter);
|
||||
void RemoveVolControl(VolumeMeter *meter);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
QList<VolumeMeter *> volumeMeters;
|
||||
};
|
||||
|
||||
class QLabel;
|
||||
class VolumeSlider;
|
||||
class MuteCheckBox;
|
||||
class OBSSourceLabel;
|
||||
|
||||
class VolControl : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
OBSSource source;
|
||||
std::vector<OBSSignal> sigs;
|
||||
OBSSourceLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
VolumeMeter *volMeter;
|
||||
VolumeSlider *slider;
|
||||
MuteCheckBox *mute;
|
||||
QPushButton *config = nullptr;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
OBSFader obs_fader;
|
||||
OBSVolMeter obs_volmeter;
|
||||
bool vertical;
|
||||
QMenu *contextMenu;
|
||||
|
||||
static void OBSVolumeChanged(void *param, float db);
|
||||
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||
|
||||
void EmitConfigClicked();
|
||||
|
||||
private slots:
|
||||
void VolumeChanged();
|
||||
void VolumeMuted(bool muted);
|
||||
void MixersOrMonitoringChanged();
|
||||
|
||||
void SetMuted(bool checked);
|
||||
void SliderChanged(int vol);
|
||||
void updateText();
|
||||
|
||||
signals:
|
||||
void ConfigClicked();
|
||||
|
||||
public:
|
||||
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||
~VolControl();
|
||||
|
||||
inline obs_source_t *GetSource() const { return source; }
|
||||
|
||||
void SetMeterDecayRate(qreal q);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
|
||||
void EnableSlider(bool enable);
|
||||
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||
|
||||
void refreshColors();
|
||||
};
|
||||
|
||||
class VolumeSlider : public AbsoluteSlider {
|
||||
Q_OBJECT
|
||||
@ -318,24 +23,3 @@ private:
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||
|
||||
public:
|
||||
VolumeAccessibleInterface(QWidget *w);
|
||||
|
||||
QVariant currentValue() const;
|
||||
void setCurrentValue(const QVariant &value);
|
||||
|
||||
QVariant maximumValue() const;
|
||||
QVariant minimumValue() const;
|
||||
|
||||
QVariant minimumStepSize() const;
|
||||
|
||||
private:
|
||||
VolumeSlider *slider() const;
|
||||
|
||||
protected:
|
||||
virtual QAccessible::Role role() const override;
|
||||
virtual QString text(QAccessible::Text t) const override;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility/MultitrackVideoOutput.hpp>
|
||||
|
||||
#include "multitrack-video-output.hpp"
|
||||
#include <obs.hpp>
|
||||
#include <util/dstr.hpp>
|
||||
|
||||
#include <future>
|
||||
|
||||
#define RTMP_PROTOCOL "rtmp"
|
||||
#define SRT_PROTOCOL "srt"
|
||||
#define RIST_PROTOCOL "rist"
|
||||
|
||||
class OBSBasic;
|
||||
|
||||
@ -58,7 +63,7 @@ struct BasicOutputHandler {
|
||||
OBSSignal replayBufferStopping;
|
||||
OBSSignal replayBufferSaved;
|
||||
|
||||
inline BasicOutputHandler(OBSBasic *main_);
|
||||
BasicOutputHandler(OBSBasic *main_);
|
||||
|
||||
virtual ~BasicOutputHandler(){};
|
||||
|
||||
@ -103,3 +108,39 @@ protected:
|
||||
|
||||
BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main);
|
||||
BasicOutputHandler *CreateAdvancedOutputHandler(OBSBasic *main);
|
||||
|
||||
void OBSStreamStarting(void *data, calldata_t *params);
|
||||
void OBSStreamStopping(void *data, calldata_t *params);
|
||||
void OBSStartStreaming(void *data, calldata_t *params);
|
||||
void OBSStopStreaming(void *data, calldata_t *params);
|
||||
void OBSStartRecording(void *data, calldata_t *params);
|
||||
void OBSStopRecording(void *data, calldata_t *params);
|
||||
void OBSRecordStopping(void *data, calldata_t *params);
|
||||
void OBSRecordFileChanged(void *data, calldata_t *params);
|
||||
void OBSStartReplayBuffer(void *data, calldata_t *params);
|
||||
void OBSStopReplayBuffer(void *data, calldata_t *params);
|
||||
void OBSReplayBufferStopping(void *data, calldata_t *params);
|
||||
void OBSReplayBufferSaved(void *data, calldata_t *params);
|
||||
|
||||
inline bool can_use_output(const char *prot, const char *output, const char *prot_test1,
|
||||
const char *prot_test2 = nullptr)
|
||||
{
|
||||
return (strcmp(prot, prot_test1) == 0 || (prot_test2 && strcmp(prot, prot_test2) == 0)) &&
|
||||
(obs_get_output_flags(output) & OBS_OUTPUT_SERVICE) != 0;
|
||||
}
|
||||
|
||||
const char *GetStreamOutputType(const obs_service_t *service);
|
||||
|
||||
inline bool ServiceSupportsVodTrack(const char *service)
|
||||
{
|
||||
static const char *vodTrackServices[] = {"Twitch"};
|
||||
|
||||
for (const char *vodTrackService : vodTrackServices) {
|
||||
if (astrcmpi(vodTrackService, service) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void clear_archive_encoder(obs_output_t *output, const char *expected_name);
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -15,8 +15,9 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "window-basic-main.hpp"
|
||||
#include "screenshot-obj.hpp"
|
||||
#include "ScreenshotObj.hpp"
|
||||
|
||||
#include <widgets/OBSBasic.hpp>
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
@ -27,9 +28,9 @@
|
||||
#pragma comment(lib, "windowscodecs.lib")
|
||||
#endif
|
||||
|
||||
static void ScreenshotTick(void *param, float);
|
||||
#include "moc_ScreenshotObj.cpp"
|
||||
|
||||
/* ========================================================================= */
|
||||
static void ScreenshotTick(void *param, float);
|
||||
|
||||
ScreenshotObj::ScreenshotObj(obs_source_t *source) : weakSource(OBSGetWeakRef(source))
|
||||
{
|
||||
@ -278,8 +279,6 @@ void ScreenshotObj::MuxAndFinish()
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
#define STAGE_SCREENSHOT 0
|
||||
#define STAGE_DOWNLOAD 1
|
||||
#define STAGE_COPY_AND_SAVE 2
|
||||
@ -313,35 +312,3 @@ static void ScreenshotTick(void *param, float)
|
||||
|
||||
data->stage++;
|
||||
}
|
||||
|
||||
void OBSBasic::Screenshot(OBSSource source)
|
||||
{
|
||||
if (!!screenshotData) {
|
||||
blog(LOG_WARNING, "Cannot take new screenshot, "
|
||||
"screenshot currently in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
screenshotData = new ScreenshotObj(source);
|
||||
}
|
||||
|
||||
void OBSBasic::ScreenshotSelectedSource()
|
||||
{
|
||||
OBSSceneItem item = GetCurrentSceneItem();
|
||||
if (item) {
|
||||
Screenshot(obs_sceneitem_get_source(item));
|
||||
} else {
|
||||
blog(LOG_INFO, "Could not take a source screenshot: "
|
||||
"no source selected");
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasic::ScreenshotProgram()
|
||||
{
|
||||
Screenshot(GetProgramSource());
|
||||
}
|
||||
|
||||
void OBSBasic::ScreenshotScene()
|
||||
{
|
||||
Screenshot(GetCurrentSceneSource());
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,25 +1,9 @@
|
||||
#include "moc_qt-display.cpp"
|
||||
#include "display-helpers.hpp"
|
||||
#include <QWindow>
|
||||
#include <QScreen>
|
||||
#include <QResizeEvent>
|
||||
#include <QShowEvent>
|
||||
#pragma once
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <obs-config.h>
|
||||
#include <widgets/OBSQTDisplay.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
#include <obs-nix-platform.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_WAYLAND
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
#include <QObject>
|
||||
#include <QPlatformSurfaceEvent>
|
||||
|
||||
class SurfaceEventFilter : public QObject {
|
||||
OBSQTDisplay *display;
|
||||
@ -52,192 +36,3 @@ protected:
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
static inline long long color_to_int(const QColor &color)
|
||||
{
|
||||
auto shift = [&](unsigned val, int shift) {
|
||||
return ((val & 0xff) << shift);
|
||||
};
|
||||
|
||||
return shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);
|
||||
}
|
||||
|
||||
static inline QColor rgba_to_color(uint32_t rgba)
|
||||
{
|
||||
return QColor::fromRgb(rgba & 0xFF, (rgba >> 8) & 0xFF, (rgba >> 16) & 0xFF, (rgba >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
static bool QTToGSWindow(QWindow *window, gs_window &gswindow)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
#ifdef _WIN32
|
||||
gswindow.hwnd = (HWND)window->winId();
|
||||
#elif __APPLE__
|
||||
gswindow.view = (id)window->winId();
|
||||
#else
|
||||
switch (obs_get_nix_platform()) {
|
||||
case OBS_NIX_PLATFORM_X11_EGL:
|
||||
gswindow.id = window->winId();
|
||||
gswindow.display = obs_get_nix_platform_display();
|
||||
break;
|
||||
#ifdef ENABLE_WAYLAND
|
||||
case OBS_NIX_PLATFORM_WAYLAND: {
|
||||
QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
|
||||
gswindow.display = native->nativeResourceForWindow("surface", window);
|
||||
success = gswindow.display != nullptr;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return success;
|
||||
}
|
||||
|
||||
OBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags)
|
||||
{
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
setAttribute(Qt::WA_StaticContents);
|
||||
setAttribute(Qt::WA_NoSystemBackground);
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
setAttribute(Qt::WA_DontCreateNativeAncestors);
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
|
||||
auto windowVisible = [this](bool visible) {
|
||||
if (!visible) {
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
display = nullptr;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (!display) {
|
||||
CreateDisplay();
|
||||
} else {
|
||||
QSize size = GetPixelSize(this);
|
||||
obs_display_resize(display, size.width(), size.height());
|
||||
}
|
||||
};
|
||||
|
||||
auto screenChanged = [this](QScreen *) {
|
||||
CreateDisplay();
|
||||
|
||||
QSize size = GetPixelSize(this);
|
||||
obs_display_resize(display, size.width(), size.height());
|
||||
};
|
||||
|
||||
connect(windowHandle(), &QWindow::visibleChanged, windowVisible);
|
||||
connect(windowHandle(), &QWindow::screenChanged, screenChanged);
|
||||
|
||||
windowHandle()->installEventFilter(new SurfaceEventFilter(this));
|
||||
}
|
||||
|
||||
QColor OBSQTDisplay::GetDisplayBackgroundColor() const
|
||||
{
|
||||
return rgba_to_color(backgroundColor);
|
||||
}
|
||||
|
||||
void OBSQTDisplay::SetDisplayBackgroundColor(const QColor &color)
|
||||
{
|
||||
uint32_t newBackgroundColor = (uint32_t)color_to_int(color);
|
||||
|
||||
if (newBackgroundColor != backgroundColor) {
|
||||
backgroundColor = newBackgroundColor;
|
||||
UpdateDisplayBackgroundColor();
|
||||
}
|
||||
}
|
||||
|
||||
void OBSQTDisplay::UpdateDisplayBackgroundColor()
|
||||
{
|
||||
obs_display_set_background_color(display, backgroundColor);
|
||||
}
|
||||
|
||||
void OBSQTDisplay::CreateDisplay()
|
||||
{
|
||||
if (display)
|
||||
return;
|
||||
|
||||
if (destroying)
|
||||
return;
|
||||
|
||||
if (!windowHandle()->isExposed())
|
||||
return;
|
||||
|
||||
QSize size = GetPixelSize(this);
|
||||
|
||||
gs_init_data info = {};
|
||||
info.cx = size.width();
|
||||
info.cy = size.height();
|
||||
info.format = GS_BGRA;
|
||||
info.zsformat = GS_ZS_NONE;
|
||||
|
||||
if (!QTToGSWindow(windowHandle(), info.window))
|
||||
return;
|
||||
|
||||
display = obs_display_create(&info, backgroundColor);
|
||||
|
||||
emit DisplayCreated(this);
|
||||
}
|
||||
|
||||
void OBSQTDisplay::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
CreateDisplay();
|
||||
|
||||
QWidget::paintEvent(event);
|
||||
}
|
||||
|
||||
void OBSQTDisplay::moveEvent(QMoveEvent *event)
|
||||
{
|
||||
QWidget::moveEvent(event);
|
||||
|
||||
OnMove();
|
||||
}
|
||||
|
||||
bool OBSQTDisplay::nativeEvent(const QByteArray &, void *message, qintptr *)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const MSG &msg = *static_cast<MSG *>(message);
|
||||
switch (msg.message) {
|
||||
case WM_DISPLAYCHANGE:
|
||||
OnDisplayChange();
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(message);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void OBSQTDisplay::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
|
||||
CreateDisplay();
|
||||
|
||||
if (isVisible() && display) {
|
||||
QSize size = GetPixelSize(this);
|
||||
obs_display_resize(display, size.width(), size.height());
|
||||
}
|
||||
|
||||
emit DisplayResized();
|
||||
}
|
||||
|
||||
QPaintEngine *OBSQTDisplay::paintEngine() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OBSQTDisplay::OnMove()
|
||||
{
|
||||
if (display)
|
||||
obs_display_update_color_space(display);
|
||||
}
|
||||
|
||||
void OBSQTDisplay::OnDisplayChange()
|
||||
{
|
||||
if (display)
|
||||
obs_display_update_color_space(display);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,230 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QAccessibleWidget>
|
||||
#include "absolute-slider.hpp"
|
||||
|
||||
class QPushButton;
|
||||
class VolumeMeterTimer;
|
||||
class VolumeSlider;
|
||||
|
||||
class VolumeMeter : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||
|
||||
// Levels are denoted in dBFS.
|
||||
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||
|
||||
// Rates are denoted in dB/second.
|
||||
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||
|
||||
// Time in seconds for the VU meter to integrate over.
|
||||
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||
DESIGNABLE true)
|
||||
|
||||
// Duration is denoted in seconds.
|
||||
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||
DESIGNABLE true)
|
||||
|
||||
friend class VolControl;
|
||||
|
||||
private:
|
||||
obs_volmeter_t *obs_volmeter;
|
||||
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||
|
||||
inline void resetLevels();
|
||||
inline void doLayout();
|
||||
inline bool detectIdle(uint64_t ts);
|
||||
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||
|
||||
inline int convertToInt(float number);
|
||||
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||
|
||||
QMutex dataMutex;
|
||||
|
||||
bool recalculateLayout = true;
|
||||
uint64_t currentLastUpdateTime = 0;
|
||||
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||
|
||||
int displayNrAudioChannels = 0;
|
||||
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
|
||||
QFont tickFont;
|
||||
QColor backgroundNominalColor;
|
||||
QColor backgroundWarningColor;
|
||||
QColor backgroundErrorColor;
|
||||
QColor foregroundNominalColor;
|
||||
QColor foregroundWarningColor;
|
||||
QColor foregroundErrorColor;
|
||||
|
||||
QColor backgroundNominalColorDisabled;
|
||||
QColor backgroundWarningColorDisabled;
|
||||
QColor backgroundErrorColorDisabled;
|
||||
QColor foregroundNominalColorDisabled;
|
||||
QColor foregroundWarningColorDisabled;
|
||||
QColor foregroundErrorColorDisabled;
|
||||
|
||||
QColor clipColor;
|
||||
QColor magnitudeColor;
|
||||
QColor majorTickColor;
|
||||
QColor minorTickColor;
|
||||
|
||||
int meterThickness;
|
||||
qreal meterFontScaling;
|
||||
|
||||
qreal minimumLevel;
|
||||
qreal warningLevel;
|
||||
qreal errorLevel;
|
||||
qreal clipLevel;
|
||||
qreal minimumInputLevel;
|
||||
qreal peakDecayRate;
|
||||
qreal magnitudeIntegrationTime;
|
||||
qreal peakHoldDuration;
|
||||
qreal inputPeakHoldDuration;
|
||||
|
||||
QColor p_backgroundNominalColor;
|
||||
QColor p_backgroundWarningColor;
|
||||
QColor p_backgroundErrorColor;
|
||||
QColor p_foregroundNominalColor;
|
||||
QColor p_foregroundWarningColor;
|
||||
QColor p_foregroundErrorColor;
|
||||
|
||||
uint64_t lastRedrawTime = 0;
|
||||
int channels = 0;
|
||||
bool clipping = false;
|
||||
bool vertical;
|
||||
bool muted = false;
|
||||
|
||||
public:
|
||||
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||
~VolumeMeter();
|
||||
|
||||
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
QRect getBarRect() const;
|
||||
bool needLayoutChange();
|
||||
|
||||
QColor getBackgroundNominalColor() const;
|
||||
void setBackgroundNominalColor(QColor c);
|
||||
QColor getBackgroundWarningColor() const;
|
||||
void setBackgroundWarningColor(QColor c);
|
||||
QColor getBackgroundErrorColor() const;
|
||||
void setBackgroundErrorColor(QColor c);
|
||||
QColor getForegroundNominalColor() const;
|
||||
void setForegroundNominalColor(QColor c);
|
||||
QColor getForegroundWarningColor() const;
|
||||
void setForegroundWarningColor(QColor c);
|
||||
QColor getForegroundErrorColor() const;
|
||||
void setForegroundErrorColor(QColor c);
|
||||
|
||||
QColor getBackgroundNominalColorDisabled() const;
|
||||
void setBackgroundNominalColorDisabled(QColor c);
|
||||
QColor getBackgroundWarningColorDisabled() const;
|
||||
void setBackgroundWarningColorDisabled(QColor c);
|
||||
QColor getBackgroundErrorColorDisabled() const;
|
||||
void setBackgroundErrorColorDisabled(QColor c);
|
||||
QColor getForegroundNominalColorDisabled() const;
|
||||
void setForegroundNominalColorDisabled(QColor c);
|
||||
QColor getForegroundWarningColorDisabled() const;
|
||||
void setForegroundWarningColorDisabled(QColor c);
|
||||
QColor getForegroundErrorColorDisabled() const;
|
||||
void setForegroundErrorColorDisabled(QColor c);
|
||||
|
||||
QColor getClipColor() const;
|
||||
void setClipColor(QColor c);
|
||||
QColor getMagnitudeColor() const;
|
||||
void setMagnitudeColor(QColor c);
|
||||
QColor getMajorTickColor() const;
|
||||
void setMajorTickColor(QColor c);
|
||||
QColor getMinorTickColor() const;
|
||||
void setMinorTickColor(QColor c);
|
||||
int getMeterThickness() const;
|
||||
void setMeterThickness(int v);
|
||||
qreal getMeterFontScaling() const;
|
||||
void setMeterFontScaling(qreal v);
|
||||
qreal getMinimumLevel() const;
|
||||
void setMinimumLevel(qreal v);
|
||||
qreal getWarningLevel() const;
|
||||
void setWarningLevel(qreal v);
|
||||
qreal getErrorLevel() const;
|
||||
void setErrorLevel(qreal v);
|
||||
qreal getClipLevel() const;
|
||||
void setClipLevel(qreal v);
|
||||
qreal getMinimumInputLevel() const;
|
||||
void setMinimumInputLevel(qreal v);
|
||||
qreal getPeakDecayRate() const;
|
||||
void setPeakDecayRate(qreal v);
|
||||
qreal getMagnitudeIntegrationTime() const;
|
||||
void setMagnitudeIntegrationTime(qreal v);
|
||||
qreal getPeakHoldDuration() const;
|
||||
void setPeakHoldDuration(qreal v);
|
||||
qreal getInputPeakHoldDuration() const;
|
||||
void setInputPeakHoldDuration(qreal v);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *e) override;
|
||||
};
|
||||
class VolumeMeter;
|
||||
|
||||
class VolumeMeterTimer : public QTimer {
|
||||
Q_OBJECT
|
||||
@ -239,103 +17,3 @@ protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
QList<VolumeMeter *> volumeMeters;
|
||||
};
|
||||
|
||||
class QLabel;
|
||||
class VolumeSlider;
|
||||
class MuteCheckBox;
|
||||
class OBSSourceLabel;
|
||||
|
||||
class VolControl : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
OBSSource source;
|
||||
std::vector<OBSSignal> sigs;
|
||||
OBSSourceLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
VolumeMeter *volMeter;
|
||||
VolumeSlider *slider;
|
||||
MuteCheckBox *mute;
|
||||
QPushButton *config = nullptr;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
OBSFader obs_fader;
|
||||
OBSVolMeter obs_volmeter;
|
||||
bool vertical;
|
||||
QMenu *contextMenu;
|
||||
|
||||
static void OBSVolumeChanged(void *param, float db);
|
||||
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||
|
||||
void EmitConfigClicked();
|
||||
|
||||
private slots:
|
||||
void VolumeChanged();
|
||||
void VolumeMuted(bool muted);
|
||||
void MixersOrMonitoringChanged();
|
||||
|
||||
void SetMuted(bool checked);
|
||||
void SliderChanged(int vol);
|
||||
void updateText();
|
||||
|
||||
signals:
|
||||
void ConfigClicked();
|
||||
|
||||
public:
|
||||
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||
~VolControl();
|
||||
|
||||
inline obs_source_t *GetSource() const { return source; }
|
||||
|
||||
void SetMeterDecayRate(qreal q);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
|
||||
void EnableSlider(bool enable);
|
||||
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||
|
||||
void refreshColors();
|
||||
};
|
||||
|
||||
class VolumeSlider : public AbsoluteSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
obs_fader_t *fad;
|
||||
|
||||
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||
|
||||
bool getDisplayTicks() const;
|
||||
void setDisplayTicks(bool display);
|
||||
|
||||
private:
|
||||
bool displayTicks = false;
|
||||
QColor tickColor;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||
|
||||
public:
|
||||
VolumeAccessibleInterface(QWidget *w);
|
||||
|
||||
QVariant currentValue() const;
|
||||
void setCurrentValue(const QVariant &value);
|
||||
|
||||
QVariant maximumValue() const;
|
||||
QVariant minimumValue() const;
|
||||
|
||||
QVariant minimumStepSize() const;
|
||||
|
||||
private:
|
||||
VolumeSlider *slider() const;
|
||||
|
||||
protected:
|
||||
virtual QAccessible::Role role() const override;
|
||||
virtual QString text(QAccessible::Text t) const override;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,10 @@
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include "obs-app.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "moc_window-basic-status-bar.cpp"
|
||||
#include "window-basic-main-outputs.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "platform.hpp"
|
||||
|
||||
#include "OBSBasicStatusBar.hpp"
|
||||
#include "ui_StatusBarWidget.h"
|
||||
|
||||
#include <widgets/OBSBasic.hpp>
|
||||
|
||||
#include "moc_OBSBasicStatusBar.cpp"
|
||||
|
||||
static constexpr int bitrateUpdateSeconds = 2;
|
||||
static constexpr int congestionUpdateSeconds = 4;
|
||||
static constexpr float excellentThreshold = 0.0f;
|
||||
@ -16,13 +12,6 @@ static constexpr float goodThreshold = 0.3333f;
|
||||
static constexpr float mediocreThreshold = 0.6667f;
|
||||
static constexpr float badThreshold = 1.0f;
|
||||
|
||||
StatusBarWidget::StatusBarWidget(QWidget *parent) : QWidget(parent), ui(new Ui::StatusBarWidget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
StatusBarWidget::~StatusBarWidget() {}
|
||||
|
||||
OBSBasicStatusBar::OBSBasicStatusBar(QWidget *parent)
|
||||
: QStatusBar(parent),
|
||||
excellentPixmap(QIcon(":/res/images/network-excellent.svg").pixmap(QSize(16, 16))),
|
||||
|
@ -1,25 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <QStatusBar>
|
||||
#include "StatusBarWidget.hpp"
|
||||
|
||||
#include <obs.hpp>
|
||||
|
||||
#include <QPointer>
|
||||
#include <QTimer>
|
||||
#include <obs.h>
|
||||
#include <memory>
|
||||
#include <QStatusBar>
|
||||
|
||||
class Ui_StatusBarWidget;
|
||||
|
||||
class StatusBarWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
friend class OBSBasicStatusBar;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui_StatusBarWidget> ui;
|
||||
|
||||
public:
|
||||
StatusBarWidget(QWidget *parent = nullptr);
|
||||
~StatusBarWidget();
|
||||
};
|
||||
class QTimer;
|
||||
|
||||
class OBSBasicStatusBar : public QStatusBar {
|
||||
Q_OBJECT
|
||||
|
@ -15,23 +15,28 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <QDir>
|
||||
#include <QThread>
|
||||
#include <QMessageBox>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "window-basic-main.hpp"
|
||||
|
||||
#include <random>
|
||||
#include "OBSBasic.hpp"
|
||||
|
||||
#ifdef BROWSER_AVAILABLE
|
||||
#include <browser-panel.hpp>
|
||||
#include <dialogs/OBSExtraBrowsers.hpp>
|
||||
#include <docks/BrowserDock.hpp>
|
||||
|
||||
#include <json11.hpp>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
using namespace json11;
|
||||
#endif
|
||||
|
||||
#include <random>
|
||||
|
||||
struct QCef;
|
||||
struct QCefCookieManager;
|
||||
|
||||
extern QCef *cef;
|
||||
extern QCefCookieManager *panel_cookies;
|
||||
QCef *cef = nullptr;
|
||||
QCefCookieManager *panel_cookies = nullptr;
|
||||
bool cef_js_avail = false;
|
||||
|
||||
static std::string GenId()
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,32 @@
|
||||
#include <QDragEnterEvent>
|
||||
#include <QDragLeaveEvent>
|
||||
#include <QDragMoveEvent>
|
||||
#include <QDropEvent>
|
||||
/******************************************************************************
|
||||
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 <qt-wrappers.hpp>
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QMimeData>
|
||||
#include <QUrlQuery>
|
||||
#ifdef _WIN32
|
||||
#include <QSettings>
|
||||
#endif
|
||||
#include <string>
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#include "window-basic-main.hpp"
|
||||
#include <QUrlQuery>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,23 @@
|
||||
#include <window-basic-main.hpp>
|
||||
/******************************************************************************
|
||||
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"
|
||||
|
||||
QIcon OBSBasic::GetSourceIcon(const char *id) const
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -15,21 +15,17 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
#include <obs.hpp>
|
||||
#include <util/platform.h>
|
||||
#include <util/util.hpp>
|
||||
#include <QMessageBox>
|
||||
#include <QVariant>
|
||||
#include <QFileDialog>
|
||||
#include "OBSBasic.hpp"
|
||||
|
||||
#ifdef YOUTUBE_ENABLED
|
||||
#include <docks/YouTubeAppDock.hpp>
|
||||
#endif
|
||||
#include <wizards/AutoConfig.hpp>
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include "window-basic-main.hpp"
|
||||
#include "window-basic-auto-config.hpp"
|
||||
#include "window-namedialog.hpp"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
|
||||
// MARK: Constant Expressions
|
||||
|
||||
@ -38,6 +34,8 @@ constexpr std::string_view OBSProfileSettingsFile = "basic.ini";
|
||||
|
||||
// MARK: Forward Declarations
|
||||
|
||||
extern bool restart;
|
||||
|
||||
extern void DestroyPanelCookieManager();
|
||||
extern void DuplicateCurrentCookieProfile(ConfigFile &config);
|
||||
extern void CheckExistingCookieId();
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -15,305 +15,12 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "window-basic-main.hpp"
|
||||
#include "screenshot-obj.hpp"
|
||||
#include "OBSBasic.hpp"
|
||||
|
||||
#include <utility/ScreenshotObj.hpp>
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <wincodec.h>
|
||||
#include <wincodecsdk.h>
|
||||
#include <wrl/client.h>
|
||||
#pragma comment(lib, "windowscodecs.lib")
|
||||
#endif
|
||||
|
||||
static void ScreenshotTick(void *param, float);
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
ScreenshotObj::ScreenshotObj(obs_source_t *source) : weakSource(OBSGetWeakRef(source))
|
||||
{
|
||||
obs_add_tick_callback(ScreenshotTick, this);
|
||||
}
|
||||
|
||||
ScreenshotObj::~ScreenshotObj()
|
||||
{
|
||||
obs_enter_graphics();
|
||||
gs_stagesurface_destroy(stagesurf);
|
||||
gs_texrender_destroy(texrender);
|
||||
obs_leave_graphics();
|
||||
|
||||
obs_remove_tick_callback(ScreenshotTick, this);
|
||||
|
||||
if (th.joinable()) {
|
||||
th.join();
|
||||
|
||||
if (cx && cy) {
|
||||
OBSBasic *main = OBSBasic::Get();
|
||||
main->ShowStatusBarMessage(
|
||||
QTStr("Basic.StatusBar.ScreenshotSavedTo").arg(QT_UTF8(path.c_str())));
|
||||
|
||||
main->lastScreenshot = path;
|
||||
|
||||
main->OnEvent(OBS_FRONTEND_EVENT_SCREENSHOT_TAKEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenshotObj::Screenshot()
|
||||
{
|
||||
OBSSource source = OBSGetStrongRef(weakSource);
|
||||
|
||||
if (source) {
|
||||
cx = obs_source_get_width(source);
|
||||
cy = obs_source_get_height(source);
|
||||
} else {
|
||||
obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
cx = ovi.base_width;
|
||||
cy = ovi.base_height;
|
||||
}
|
||||
|
||||
if (!cx || !cy) {
|
||||
blog(LOG_WARNING, "Cannot screenshot, invalid target size");
|
||||
obs_remove_tick_callback(ScreenshotTick, this);
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
enum gs_color_space space = obs_source_get_color_space(source, 0, nullptr);
|
||||
if (space == GS_CS_709_EXTENDED) {
|
||||
/* Convert for JXR */
|
||||
space = GS_CS_709_SCRGB;
|
||||
}
|
||||
#else
|
||||
/* Tonemap to SDR if HDR */
|
||||
const enum gs_color_space space = GS_CS_SRGB;
|
||||
#endif
|
||||
const enum gs_color_format format = gs_get_format_from_space(space);
|
||||
|
||||
texrender = gs_texrender_create(format, GS_ZS_NONE);
|
||||
stagesurf = gs_stagesurface_create(cx, cy, format);
|
||||
|
||||
if (gs_texrender_begin_with_color_space(texrender, cx, cy, space)) {
|
||||
vec4 zero;
|
||||
vec4_zero(&zero);
|
||||
|
||||
gs_clear(GS_CLEAR_COLOR, &zero, 0.0f, 0);
|
||||
gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);
|
||||
|
||||
gs_blend_state_push();
|
||||
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
||||
|
||||
if (source) {
|
||||
obs_source_inc_showing(source);
|
||||
obs_source_video_render(source);
|
||||
obs_source_dec_showing(source);
|
||||
} else {
|
||||
obs_render_main_texture();
|
||||
}
|
||||
|
||||
gs_blend_state_pop();
|
||||
gs_texrender_end(texrender);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenshotObj::Download()
|
||||
{
|
||||
gs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));
|
||||
}
|
||||
|
||||
void ScreenshotObj::Copy()
|
||||
{
|
||||
uint8_t *videoData = nullptr;
|
||||
uint32_t videoLinesize = 0;
|
||||
|
||||
if (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {
|
||||
if (gs_stagesurface_get_color_format(stagesurf) == GS_RGBA16F) {
|
||||
const uint32_t linesize = cx * 8;
|
||||
half_bytes.reserve(cx * cy * 8);
|
||||
|
||||
for (uint32_t y = 0; y < cy; y++) {
|
||||
const uint8_t *const line = videoData + (y * videoLinesize);
|
||||
half_bytes.insert(half_bytes.end(), line, line + linesize);
|
||||
}
|
||||
} else {
|
||||
image = QImage(cx, cy, QImage::Format::Format_RGBX8888);
|
||||
|
||||
int linesize = image.bytesPerLine();
|
||||
for (int y = 0; y < (int)cy; y++)
|
||||
memcpy(image.scanLine(y), videoData + (y * videoLinesize), linesize);
|
||||
}
|
||||
|
||||
gs_stagesurface_unmap(stagesurf);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenshotObj::Save()
|
||||
{
|
||||
OBSBasic *main = OBSBasic::Get();
|
||||
config_t *config = main->Config();
|
||||
|
||||
const char *mode = config_get_string(config, "Output", "Mode");
|
||||
const char *type = config_get_string(config, "AdvOut", "RecType");
|
||||
const char *adv_path = strcmp(type, "Standard") ? config_get_string(config, "AdvOut", "FFFilePath")
|
||||
: config_get_string(config, "AdvOut", "RecFilePath");
|
||||
const char *rec_path = strcmp(mode, "Advanced") ? config_get_string(config, "SimpleOutput", "FilePath")
|
||||
: adv_path;
|
||||
|
||||
bool noSpace = config_get_bool(config, "SimpleOutput", "FileNameWithoutSpace");
|
||||
const char *filenameFormat = config_get_string(config, "Output", "FilenameFormatting");
|
||||
bool overwriteIfExists = config_get_bool(config, "Output", "OverwriteIfExists");
|
||||
|
||||
const char *ext = half_bytes.empty() ? "png" : "jxr";
|
||||
path = GetOutputFilename(rec_path, ext, noSpace, overwriteIfExists,
|
||||
GetFormatString(filenameFormat, "Screenshot", nullptr).c_str());
|
||||
|
||||
th = std::thread([this] { MuxAndFinish(); });
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static HRESULT SaveJxrImage(LPCWSTR path, uint8_t *pixels, uint32_t cx, uint32_t cy, IWICBitmapFrameEncode *frameEncode,
|
||||
IPropertyBag2 *options)
|
||||
{
|
||||
wchar_t lossless[] = L"Lossless";
|
||||
PROPBAG2 bag = {};
|
||||
bag.pstrName = lossless;
|
||||
VARIANT value = {};
|
||||
value.vt = VT_BOOL;
|
||||
value.bVal = TRUE;
|
||||
HRESULT hr = options->Write(1, &bag, &value);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = frameEncode->Initialize(options);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = frameEncode->SetSize(cx, cy);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = frameEncode->SetResolution(72, 72);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
WICPixelFormatGUID pixelFormat = GUID_WICPixelFormat64bppRGBAHalf;
|
||||
hr = frameEncode->SetPixelFormat(&pixelFormat);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
if (memcmp(&pixelFormat, &GUID_WICPixelFormat64bppRGBAHalf, sizeof(WICPixelFormatGUID)) != 0)
|
||||
return E_FAIL;
|
||||
|
||||
hr = frameEncode->WritePixels(cy, cx * 8, cx * cy * 8, pixels);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = frameEncode->Commit();
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT SaveJxr(LPCWSTR path, uint8_t *pixels, uint32_t cx, uint32_t cy)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IWICImagingFactory> factory;
|
||||
HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(factory.GetAddressOf()));
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
Microsoft::WRL::ComPtr<IWICStream> stream;
|
||||
hr = factory->CreateStream(stream.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = stream->InitializeFromFilename(path, GENERIC_WRITE);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
Microsoft::WRL::ComPtr<IWICBitmapEncoder> encoder = NULL;
|
||||
hr = factory->CreateEncoder(GUID_ContainerFormatWmp, NULL, encoder.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = encoder->Initialize(stream.Get(), WICBitmapEncoderNoCache);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
Microsoft::WRL::ComPtr<IWICBitmapFrameEncode> frameEncode;
|
||||
Microsoft::WRL::ComPtr<IPropertyBag2> options;
|
||||
hr = encoder->CreateNewFrame(frameEncode.GetAddressOf(), options.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
hr = SaveJxrImage(path, pixels, cx, cy, frameEncode.Get(), options.Get());
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
encoder->Commit();
|
||||
return S_OK;
|
||||
}
|
||||
#endif // #ifdef _WIN32
|
||||
|
||||
void ScreenshotObj::MuxAndFinish()
|
||||
{
|
||||
if (half_bytes.empty()) {
|
||||
image.save(QT_UTF8(path.c_str()));
|
||||
blog(LOG_INFO, "Saved screenshot to '%s'", path.c_str());
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
wchar_t *path_w = nullptr;
|
||||
os_utf8_to_wcs_ptr(path.c_str(), 0, &path_w);
|
||||
if (path_w) {
|
||||
SaveJxr(path_w, half_bytes.data(), cx, cy);
|
||||
bfree(path_w);
|
||||
}
|
||||
#endif // #ifdef _WIN32
|
||||
}
|
||||
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
/* ========================================================================= */
|
||||
|
||||
#define STAGE_SCREENSHOT 0
|
||||
#define STAGE_DOWNLOAD 1
|
||||
#define STAGE_COPY_AND_SAVE 2
|
||||
#define STAGE_FINISH 3
|
||||
|
||||
static void ScreenshotTick(void *param, float)
|
||||
{
|
||||
ScreenshotObj *data = reinterpret_cast<ScreenshotObj *>(param);
|
||||
|
||||
if (data->stage == STAGE_FINISH) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs_enter_graphics();
|
||||
|
||||
switch (data->stage) {
|
||||
case STAGE_SCREENSHOT:
|
||||
data->Screenshot();
|
||||
break;
|
||||
case STAGE_DOWNLOAD:
|
||||
data->Download();
|
||||
break;
|
||||
case STAGE_COPY_AND_SAVE:
|
||||
data->Copy();
|
||||
QMetaObject::invokeMethod(data, "Save");
|
||||
obs_remove_tick_callback(ScreenshotTick, data);
|
||||
break;
|
||||
}
|
||||
|
||||
obs_leave_graphics();
|
||||
|
||||
data->stage++;
|
||||
}
|
||||
|
||||
void OBSBasic::Screenshot(OBSSource source)
|
||||
{
|
||||
if (!!screenshotData) {
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,57 +1,24 @@
|
||||
#include "moc_qt-display.cpp"
|
||||
#include "display-helpers.hpp"
|
||||
#include <QWindow>
|
||||
#include <QScreen>
|
||||
#include <QResizeEvent>
|
||||
#include <QShowEvent>
|
||||
#include "OBSQTDisplay.hpp"
|
||||
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <obs-config.h>
|
||||
#include <utility/display-helpers.hpp>
|
||||
#include <utility/SurfaceEventFilter.hpp>
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
#include <obs-nix-platform.h>
|
||||
#endif
|
||||
|
||||
#include <QWindow>
|
||||
#ifdef ENABLE_WAYLAND
|
||||
#include <QApplication>
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||
#include <obs-nix-platform.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_WAYLAND
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#endif
|
||||
|
||||
class SurfaceEventFilter : public QObject {
|
||||
OBSQTDisplay *display;
|
||||
|
||||
public:
|
||||
SurfaceEventFilter(OBSQTDisplay *src) : QObject(src), display(src) {}
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override
|
||||
{
|
||||
bool result = QObject::eventFilter(obj, event);
|
||||
QPlatformSurfaceEvent *surfaceEvent;
|
||||
|
||||
switch (event->type()) {
|
||||
case QEvent::PlatformSurface:
|
||||
surfaceEvent = static_cast<QPlatformSurfaceEvent *>(event);
|
||||
|
||||
switch (surfaceEvent->surfaceEventType()) {
|
||||
case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:
|
||||
display->DestroyDisplay();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
#include "moc_OBSQTDisplay.cpp"
|
||||
|
||||
static inline long long color_to_int(const QColor &color)
|
||||
{
|
||||
|
@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <obs.hpp>
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#define GREY_COLOR_BACKGROUND 0xFF4C4C4C
|
||||
|
||||
class OBSQTDisplay : public QWidget {
|
||||
|
@ -1,20 +1,6 @@
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include "obs-app.hpp"
|
||||
#include "window-basic-main.hpp"
|
||||
#include "moc_window-basic-status-bar.cpp"
|
||||
#include "window-basic-main-outputs.hpp"
|
||||
#include "qt-wrappers.hpp"
|
||||
#include "platform.hpp"
|
||||
|
||||
#include "StatusBarWidget.hpp"
|
||||
#include "ui_StatusBarWidget.h"
|
||||
|
||||
static constexpr int bitrateUpdateSeconds = 2;
|
||||
static constexpr int congestionUpdateSeconds = 4;
|
||||
static constexpr float excellentThreshold = 0.0f;
|
||||
static constexpr float goodThreshold = 0.3333f;
|
||||
static constexpr float mediocreThreshold = 0.6667f;
|
||||
static constexpr float badThreshold = 1.0f;
|
||||
#include "moc_StatusBarWidget.cpp"
|
||||
|
||||
StatusBarWidget::StatusBarWidget(QWidget *parent) : QWidget(parent), ui(new Ui::StatusBarWidget)
|
||||
{
|
||||
@ -22,580 +8,3 @@ StatusBarWidget::StatusBarWidget(QWidget *parent) : QWidget(parent), ui(new Ui::
|
||||
}
|
||||
|
||||
StatusBarWidget::~StatusBarWidget() {}
|
||||
|
||||
OBSBasicStatusBar::OBSBasicStatusBar(QWidget *parent)
|
||||
: QStatusBar(parent),
|
||||
excellentPixmap(QIcon(":/res/images/network-excellent.svg").pixmap(QSize(16, 16))),
|
||||
goodPixmap(QIcon(":/res/images/network-good.svg").pixmap(QSize(16, 16))),
|
||||
mediocrePixmap(QIcon(":/res/images/network-mediocre.svg").pixmap(QSize(16, 16))),
|
||||
badPixmap(QIcon(":/res/images/network-bad.svg").pixmap(QSize(16, 16))),
|
||||
recordingActivePixmap(QIcon(":/res/images/recording-active.svg").pixmap(QSize(16, 16))),
|
||||
recordingPausePixmap(QIcon(":/res/images/recording-pause.svg").pixmap(QSize(16, 16))),
|
||||
streamingActivePixmap(QIcon(":/res/images/streaming-active.svg").pixmap(QSize(16, 16)))
|
||||
{
|
||||
congestionArray.reserve(congestionUpdateSeconds);
|
||||
|
||||
statusWidget = new StatusBarWidget(this);
|
||||
statusWidget->ui->delayInfo->setText("");
|
||||
statusWidget->ui->droppedFrames->setText(QTStr("DroppedFrames").arg("0", "0.0"));
|
||||
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||
statusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);
|
||||
statusWidget->ui->streamTime->setDisabled(true);
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);
|
||||
statusWidget->ui->recordTime->setDisabled(true);
|
||||
statusWidget->ui->delayFrame->hide();
|
||||
statusWidget->ui->issuesFrame->hide();
|
||||
statusWidget->ui->kbps->hide();
|
||||
|
||||
addPermanentWidget(statusWidget, 1);
|
||||
setMinimumHeight(statusWidget->height());
|
||||
|
||||
UpdateIcons();
|
||||
connect(App(), &OBSApp::StyleChanged, this, &OBSBasicStatusBar::UpdateIcons);
|
||||
|
||||
messageTimer = new QTimer(this);
|
||||
messageTimer->setSingleShot(true);
|
||||
connect(messageTimer, &QTimer::timeout, this, &OBSBasicStatusBar::clearMessage);
|
||||
|
||||
clearMessage();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::Activate()
|
||||
{
|
||||
if (!active) {
|
||||
refreshTimer = new QTimer(this);
|
||||
connect(refreshTimer, &QTimer::timeout, this, &OBSBasicStatusBar::UpdateStatusBar);
|
||||
|
||||
int skipped = video_output_get_skipped_frames(obs_get_video());
|
||||
int total = video_output_get_total_frames(obs_get_video());
|
||||
|
||||
totalStreamSeconds = 0;
|
||||
totalRecordSeconds = 0;
|
||||
lastSkippedFrameCount = 0;
|
||||
startSkippedFrameCount = skipped;
|
||||
startTotalFrameCount = total;
|
||||
|
||||
refreshTimer->start(1000);
|
||||
active = true;
|
||||
|
||||
if (streamOutput) {
|
||||
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||
}
|
||||
}
|
||||
|
||||
if (streamOutput) {
|
||||
statusWidget->ui->streamIcon->setPixmap(streamingActivePixmap);
|
||||
statusWidget->ui->streamTime->setDisabled(false);
|
||||
statusWidget->ui->issuesFrame->show();
|
||||
statusWidget->ui->kbps->show();
|
||||
firstCongestionUpdate = true;
|
||||
}
|
||||
|
||||
if (recordOutput) {
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingActivePixmap);
|
||||
statusWidget->ui->recordTime->setDisabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::Deactivate()
|
||||
{
|
||||
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||
if (!main)
|
||||
return;
|
||||
|
||||
if (!streamOutput) {
|
||||
statusWidget->ui->streamTime->setText(QString("00:00:00"));
|
||||
statusWidget->ui->streamTime->setDisabled(true);
|
||||
statusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);
|
||||
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||
statusWidget->ui->delayFrame->hide();
|
||||
statusWidget->ui->issuesFrame->hide();
|
||||
statusWidget->ui->kbps->hide();
|
||||
totalStreamSeconds = 0;
|
||||
congestionArray.clear();
|
||||
disconnected = false;
|
||||
firstCongestionUpdate = false;
|
||||
}
|
||||
|
||||
if (!recordOutput) {
|
||||
statusWidget->ui->recordTime->setText(QString("00:00:00"));
|
||||
statusWidget->ui->recordTime->setDisabled(true);
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);
|
||||
totalRecordSeconds = 0;
|
||||
}
|
||||
|
||||
if (main->outputHandler && !main->outputHandler->Active()) {
|
||||
delete refreshTimer;
|
||||
|
||||
statusWidget->ui->delayInfo->setText("");
|
||||
statusWidget->ui->droppedFrames->setText(QTStr("DroppedFrames").arg("0", "0.0"));
|
||||
statusWidget->ui->kbps->setText("0 kbps");
|
||||
|
||||
delaySecTotal = 0;
|
||||
delaySecStarting = 0;
|
||||
delaySecStopping = 0;
|
||||
reconnectTimeout = 0;
|
||||
active = false;
|
||||
overloadedNotify = true;
|
||||
|
||||
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateDelayMsg()
|
||||
{
|
||||
QString msg;
|
||||
|
||||
if (delaySecTotal) {
|
||||
if (delaySecStarting && !delaySecStopping) {
|
||||
msg = QTStr("Basic.StatusBar.DelayStartingIn");
|
||||
msg = msg.arg(QString::number(delaySecStarting));
|
||||
|
||||
} else if (!delaySecStarting && delaySecStopping) {
|
||||
msg = QTStr("Basic.StatusBar.DelayStoppingIn");
|
||||
msg = msg.arg(QString::number(delaySecStopping));
|
||||
|
||||
} else if (delaySecStarting && delaySecStopping) {
|
||||
msg = QTStr("Basic.StatusBar.DelayStartingStoppingIn");
|
||||
msg = msg.arg(QString::number(delaySecStopping), QString::number(delaySecStarting));
|
||||
} else {
|
||||
msg = QTStr("Basic.StatusBar.Delay");
|
||||
msg = msg.arg(QString::number(delaySecTotal));
|
||||
}
|
||||
|
||||
if (!statusWidget->ui->delayFrame->isVisible())
|
||||
statusWidget->ui->delayFrame->show();
|
||||
|
||||
statusWidget->ui->delayInfo->setText(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateBandwidth()
|
||||
{
|
||||
if (!streamOutput)
|
||||
return;
|
||||
|
||||
if (++seconds < bitrateUpdateSeconds)
|
||||
return;
|
||||
|
||||
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
uint64_t bytesSent = obs_output_get_total_bytes(output);
|
||||
uint64_t bytesSentTime = os_gettime_ns();
|
||||
|
||||
if (bytesSent < lastBytesSent)
|
||||
bytesSent = 0;
|
||||
if (bytesSent == 0)
|
||||
lastBytesSent = 0;
|
||||
|
||||
uint64_t bitsBetween = (bytesSent - lastBytesSent) * 8;
|
||||
|
||||
double timePassed = double(bytesSentTime - lastBytesSentTime) / 1000000000.0;
|
||||
|
||||
double kbitsPerSec = double(bitsBetween) / timePassed / 1000.0;
|
||||
|
||||
QString text;
|
||||
text += QString::number(kbitsPerSec, 'f', 0) + QString(" kbps");
|
||||
|
||||
statusWidget->ui->kbps->setText(text);
|
||||
statusWidget->ui->kbps->setMinimumWidth(statusWidget->ui->kbps->width());
|
||||
|
||||
if (!statusWidget->ui->kbps->isVisible())
|
||||
statusWidget->ui->kbps->show();
|
||||
|
||||
lastBytesSent = bytesSent;
|
||||
lastBytesSentTime = bytesSentTime;
|
||||
seconds = 0;
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateCPUUsage()
|
||||
{
|
||||
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||
if (!main)
|
||||
return;
|
||||
|
||||
QString text;
|
||||
text += QString("CPU: ") + QString::number(main->GetCPUUsage(), 'f', 1) + QString("%");
|
||||
|
||||
statusWidget->ui->cpuUsage->setText(text);
|
||||
statusWidget->ui->cpuUsage->setMinimumWidth(statusWidget->ui->cpuUsage->width());
|
||||
|
||||
UpdateCurrentFPS();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateCurrentFPS()
|
||||
{
|
||||
struct obs_video_info ovi;
|
||||
obs_get_video_info(&ovi);
|
||||
float targetFPS = (float)ovi.fps_num / (float)ovi.fps_den;
|
||||
|
||||
QString text = QString::asprintf("%.2f / %.2f FPS", obs_get_active_fps(), targetFPS);
|
||||
|
||||
statusWidget->ui->fpsCurrent->setText(text);
|
||||
statusWidget->ui->fpsCurrent->setMinimumWidth(statusWidget->ui->fpsCurrent->width());
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateStreamTime()
|
||||
{
|
||||
totalStreamSeconds++;
|
||||
|
||||
int seconds = totalStreamSeconds % 60;
|
||||
int totalMinutes = totalStreamSeconds / 60;
|
||||
int minutes = totalMinutes % 60;
|
||||
int hours = totalMinutes / 60;
|
||||
|
||||
QString text = QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
statusWidget->ui->streamTime->setText(text);
|
||||
if (streamOutput && !statusWidget->ui->streamTime->isEnabled())
|
||||
statusWidget->ui->streamTime->setDisabled(false);
|
||||
|
||||
if (reconnectTimeout > 0) {
|
||||
QString msg = QTStr("Basic.StatusBar.Reconnecting")
|
||||
.arg(QString::number(retries), QString::number(reconnectTimeout));
|
||||
showMessage(msg);
|
||||
disconnected = true;
|
||||
statusWidget->ui->statusIcon->setPixmap(disconnectedPixmap);
|
||||
congestionArray.clear();
|
||||
reconnectTimeout--;
|
||||
|
||||
} else if (retries > 0) {
|
||||
QString msg = QTStr("Basic.StatusBar.AttemptingReconnect");
|
||||
showMessage(msg.arg(QString::number(retries)));
|
||||
}
|
||||
|
||||
if (delaySecStopping > 0 || delaySecStarting > 0) {
|
||||
if (delaySecStopping > 0)
|
||||
--delaySecStopping;
|
||||
if (delaySecStarting > 0)
|
||||
--delaySecStarting;
|
||||
UpdateDelayMsg();
|
||||
}
|
||||
}
|
||||
|
||||
extern volatile bool recording_paused;
|
||||
|
||||
void OBSBasicStatusBar::UpdateRecordTime()
|
||||
{
|
||||
bool paused = os_atomic_load_bool(&recording_paused);
|
||||
|
||||
if (!paused) {
|
||||
totalRecordSeconds++;
|
||||
|
||||
if (recordOutput && !statusWidget->ui->recordTime->isEnabled())
|
||||
statusWidget->ui->recordTime->setDisabled(false);
|
||||
} else {
|
||||
statusWidget->ui->recordIcon->setPixmap(streamPauseIconToggle ? recordingPauseInactivePixmap
|
||||
: recordingPausePixmap);
|
||||
|
||||
streamPauseIconToggle = !streamPauseIconToggle;
|
||||
}
|
||||
|
||||
UpdateRecordTimeLabel();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateRecordTimeLabel()
|
||||
{
|
||||
int seconds = totalRecordSeconds % 60;
|
||||
int totalMinutes = totalRecordSeconds / 60;
|
||||
int minutes = totalMinutes % 60;
|
||||
int hours = totalMinutes / 60;
|
||||
|
||||
QString text = QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
if (os_atomic_load_bool(&recording_paused)) {
|
||||
text += QStringLiteral(" (PAUSED)");
|
||||
}
|
||||
|
||||
statusWidget->ui->recordTime->setText(text);
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateDroppedFrames()
|
||||
{
|
||||
if (!streamOutput)
|
||||
return;
|
||||
|
||||
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
int totalDropped = obs_output_get_frames_dropped(output);
|
||||
int totalFrames = obs_output_get_total_frames(output);
|
||||
double percent = (double)totalDropped / (double)totalFrames * 100.0;
|
||||
|
||||
if (!totalFrames)
|
||||
return;
|
||||
|
||||
QString text = QTStr("DroppedFrames");
|
||||
text = text.arg(QString::number(totalDropped), QString::number(percent, 'f', 1));
|
||||
statusWidget->ui->droppedFrames->setText(text);
|
||||
|
||||
if (!statusWidget->ui->issuesFrame->isVisible())
|
||||
statusWidget->ui->issuesFrame->show();
|
||||
|
||||
/* ----------------------------------- *
|
||||
* calculate congestion color */
|
||||
|
||||
float congestion = obs_output_get_congestion(output);
|
||||
float avgCongestion = (congestion + lastCongestion) * 0.5f;
|
||||
if (avgCongestion < congestion)
|
||||
avgCongestion = congestion;
|
||||
if (avgCongestion > 1.0f)
|
||||
avgCongestion = 1.0f;
|
||||
|
||||
lastCongestion = congestion;
|
||||
|
||||
if (disconnected)
|
||||
return;
|
||||
|
||||
bool update = firstCongestionUpdate;
|
||||
float congestionOverTime = avgCongestion;
|
||||
|
||||
if (congestionArray.size() >= congestionUpdateSeconds) {
|
||||
congestionOverTime = accumulate(congestionArray.begin(), congestionArray.end(), 0.0f) /
|
||||
(float)congestionArray.size();
|
||||
congestionArray.clear();
|
||||
update = true;
|
||||
} else {
|
||||
congestionArray.emplace_back(avgCongestion);
|
||||
}
|
||||
|
||||
if (update) {
|
||||
if (congestionOverTime <= excellentThreshold + EPSILON)
|
||||
statusWidget->ui->statusIcon->setPixmap(excellentPixmap);
|
||||
else if (congestionOverTime <= goodThreshold)
|
||||
statusWidget->ui->statusIcon->setPixmap(goodPixmap);
|
||||
else if (congestionOverTime <= mediocreThreshold)
|
||||
statusWidget->ui->statusIcon->setPixmap(mediocrePixmap);
|
||||
else if (congestionOverTime <= badThreshold)
|
||||
statusWidget->ui->statusIcon->setPixmap(badPixmap);
|
||||
|
||||
firstCongestionUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::OBSOutputReconnect(void *data, calldata_t *params)
|
||||
{
|
||||
OBSBasicStatusBar *statusBar = reinterpret_cast<OBSBasicStatusBar *>(data);
|
||||
|
||||
int seconds = (int)calldata_int(params, "timeout_sec");
|
||||
QMetaObject::invokeMethod(statusBar, "Reconnect", Q_ARG(int, seconds));
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::OBSOutputReconnectSuccess(void *data, calldata_t *)
|
||||
{
|
||||
OBSBasicStatusBar *statusBar = reinterpret_cast<OBSBasicStatusBar *>(data);
|
||||
|
||||
QMetaObject::invokeMethod(statusBar, "ReconnectSuccess");
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::Reconnect(int seconds)
|
||||
{
|
||||
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||
|
||||
if (!retries)
|
||||
main->SysTrayNotify(QTStr("Basic.SystemTray.Message.Reconnecting"), QSystemTrayIcon::Warning);
|
||||
|
||||
reconnectTimeout = seconds;
|
||||
|
||||
if (streamOutput) {
|
||||
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
delaySecTotal = obs_output_get_active_delay(output);
|
||||
UpdateDelayMsg();
|
||||
|
||||
retries++;
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::ReconnectClear()
|
||||
{
|
||||
retries = 0;
|
||||
reconnectTimeout = 0;
|
||||
seconds = -1;
|
||||
lastBytesSent = 0;
|
||||
lastBytesSentTime = os_gettime_ns();
|
||||
delaySecTotal = 0;
|
||||
UpdateDelayMsg();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::ReconnectSuccess()
|
||||
{
|
||||
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||
|
||||
QString msg = QTStr("Basic.StatusBar.ReconnectSuccessful");
|
||||
showMessage(msg, 4000);
|
||||
main->SysTrayNotify(msg, QSystemTrayIcon::Information);
|
||||
ReconnectClear();
|
||||
|
||||
if (streamOutput) {
|
||||
OBSOutput output = OBSGetStrongRef(streamOutput);
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
delaySecTotal = obs_output_get_active_delay(output);
|
||||
UpdateDelayMsg();
|
||||
disconnected = false;
|
||||
firstCongestionUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateStatusBar()
|
||||
{
|
||||
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||
|
||||
UpdateBandwidth();
|
||||
|
||||
if (streamOutput)
|
||||
UpdateStreamTime();
|
||||
|
||||
if (recordOutput)
|
||||
UpdateRecordTime();
|
||||
|
||||
UpdateDroppedFrames();
|
||||
|
||||
int skipped = video_output_get_skipped_frames(obs_get_video());
|
||||
int total = video_output_get_total_frames(obs_get_video());
|
||||
|
||||
skipped -= startSkippedFrameCount;
|
||||
total -= startTotalFrameCount;
|
||||
|
||||
int diff = skipped - lastSkippedFrameCount;
|
||||
double percentage = double(skipped) / double(total) * 100.0;
|
||||
|
||||
if (diff > 10 && percentage >= 0.1f) {
|
||||
showMessage(QTStr("HighResourceUsage"), 4000);
|
||||
if (!main->isVisible() && overloadedNotify) {
|
||||
main->SysTrayNotify(QTStr("HighResourceUsage"), QSystemTrayIcon::Warning);
|
||||
overloadedNotify = false;
|
||||
}
|
||||
}
|
||||
|
||||
lastSkippedFrameCount = skipped;
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::StreamDelayStarting(int sec)
|
||||
{
|
||||
OBSBasic *main = qobject_cast<OBSBasic *>(parent());
|
||||
if (!main || !main->outputHandler)
|
||||
return;
|
||||
|
||||
OBSOutputAutoRelease output = obs_frontend_get_streaming_output();
|
||||
streamOutput = OBSGetWeakRef(output);
|
||||
|
||||
delaySecTotal = delaySecStarting = sec;
|
||||
UpdateDelayMsg();
|
||||
Activate();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::StreamDelayStopping(int sec)
|
||||
{
|
||||
delaySecTotal = delaySecStopping = sec;
|
||||
UpdateDelayMsg();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::StreamStarted(obs_output_t *output)
|
||||
{
|
||||
streamOutput = OBSGetWeakRef(output);
|
||||
|
||||
streamSigs.emplace_back(obs_output_get_signal_handler(output), "reconnect", OBSOutputReconnect, this);
|
||||
streamSigs.emplace_back(obs_output_get_signal_handler(output), "reconnect_success", OBSOutputReconnectSuccess,
|
||||
this);
|
||||
|
||||
retries = 0;
|
||||
lastBytesSent = 0;
|
||||
lastBytesSentTime = os_gettime_ns();
|
||||
Activate();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::StreamStopped()
|
||||
{
|
||||
if (streamOutput) {
|
||||
streamSigs.clear();
|
||||
|
||||
ReconnectClear();
|
||||
streamOutput = nullptr;
|
||||
clearMessage();
|
||||
Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::RecordingStarted(obs_output_t *output)
|
||||
{
|
||||
recordOutput = OBSGetWeakRef(output);
|
||||
Activate();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::RecordingStopped()
|
||||
{
|
||||
recordOutput = nullptr;
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::RecordingPaused()
|
||||
{
|
||||
if (recordOutput) {
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingPausePixmap);
|
||||
streamPauseIconToggle = true;
|
||||
}
|
||||
|
||||
UpdateRecordTimeLabel();
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::RecordingUnpaused()
|
||||
{
|
||||
if (recordOutput) {
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingActivePixmap);
|
||||
}
|
||||
|
||||
UpdateRecordTimeLabel();
|
||||
}
|
||||
|
||||
static QPixmap GetPixmap(const QString &filename)
|
||||
{
|
||||
QString path = obs_frontend_is_theme_dark() ? "theme:Dark/" : ":/res/images/";
|
||||
return QIcon(path + filename).pixmap(QSize(16, 16));
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::UpdateIcons()
|
||||
{
|
||||
disconnectedPixmap = GetPixmap("network-disconnected.svg");
|
||||
inactivePixmap = GetPixmap("network-inactive.svg");
|
||||
|
||||
streamingInactivePixmap = GetPixmap("streaming-inactive.svg");
|
||||
|
||||
recordingInactivePixmap = GetPixmap("recording-inactive.svg");
|
||||
recordingPauseInactivePixmap = GetPixmap("recording-pause-inactive.svg");
|
||||
|
||||
bool streaming = obs_frontend_streaming_active();
|
||||
|
||||
if (!streaming) {
|
||||
statusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);
|
||||
statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
|
||||
} else {
|
||||
if (disconnected)
|
||||
statusWidget->ui->statusIcon->setPixmap(disconnectedPixmap);
|
||||
}
|
||||
|
||||
bool recording = obs_frontend_recording_active();
|
||||
|
||||
if (!recording)
|
||||
statusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::showMessage(const QString &message, int timeout)
|
||||
{
|
||||
messageTimer->stop();
|
||||
|
||||
statusWidget->ui->message->setText(message);
|
||||
|
||||
if (timeout)
|
||||
messageTimer->start(timeout);
|
||||
}
|
||||
|
||||
void OBSBasicStatusBar::clearMessage()
|
||||
{
|
||||
statusWidget->ui->message->setText("");
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QStatusBar>
|
||||
#include <QPointer>
|
||||
#include <QTimer>
|
||||
#include <obs.h>
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
class OBSBasicStatusBar;
|
||||
class Ui_StatusBarWidget;
|
||||
|
||||
class StatusBarWidget : public QWidget {
|
||||
@ -20,100 +17,3 @@ public:
|
||||
StatusBarWidget(QWidget *parent = nullptr);
|
||||
~StatusBarWidget();
|
||||
};
|
||||
|
||||
class OBSBasicStatusBar : public QStatusBar {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
StatusBarWidget *statusWidget = nullptr;
|
||||
|
||||
OBSWeakOutputAutoRelease streamOutput;
|
||||
std::vector<OBSSignal> streamSigs;
|
||||
OBSWeakOutputAutoRelease recordOutput;
|
||||
bool active = false;
|
||||
bool overloadedNotify = true;
|
||||
bool streamPauseIconToggle = false;
|
||||
bool disconnected = false;
|
||||
bool firstCongestionUpdate = false;
|
||||
|
||||
std::vector<float> congestionArray;
|
||||
|
||||
int retries = 0;
|
||||
int totalStreamSeconds = 0;
|
||||
int totalRecordSeconds = 0;
|
||||
|
||||
int reconnectTimeout = 0;
|
||||
|
||||
int delaySecTotal = 0;
|
||||
int delaySecStarting = 0;
|
||||
int delaySecStopping = 0;
|
||||
|
||||
int startSkippedFrameCount = 0;
|
||||
int startTotalFrameCount = 0;
|
||||
int lastSkippedFrameCount = 0;
|
||||
|
||||
int seconds = 0;
|
||||
uint64_t lastBytesSent = 0;
|
||||
uint64_t lastBytesSentTime = 0;
|
||||
|
||||
QPixmap excellentPixmap;
|
||||
QPixmap goodPixmap;
|
||||
QPixmap mediocrePixmap;
|
||||
QPixmap badPixmap;
|
||||
QPixmap disconnectedPixmap;
|
||||
QPixmap inactivePixmap;
|
||||
|
||||
QPixmap recordingActivePixmap;
|
||||
QPixmap recordingPausePixmap;
|
||||
QPixmap recordingPauseInactivePixmap;
|
||||
QPixmap recordingInactivePixmap;
|
||||
QPixmap streamingActivePixmap;
|
||||
QPixmap streamingInactivePixmap;
|
||||
|
||||
float lastCongestion = 0.0f;
|
||||
|
||||
QPointer<QTimer> refreshTimer;
|
||||
QPointer<QTimer> messageTimer;
|
||||
|
||||
obs_output_t *GetOutput();
|
||||
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
|
||||
void UpdateDelayMsg();
|
||||
void UpdateBandwidth();
|
||||
void UpdateStreamTime();
|
||||
void UpdateRecordTime();
|
||||
void UpdateRecordTimeLabel();
|
||||
void UpdateDroppedFrames();
|
||||
|
||||
static void OBSOutputReconnect(void *data, calldata_t *params);
|
||||
static void OBSOutputReconnectSuccess(void *data, calldata_t *params);
|
||||
|
||||
public slots:
|
||||
void UpdateCPUUsage();
|
||||
|
||||
void clearMessage();
|
||||
void showMessage(const QString &message, int timeout = 0);
|
||||
|
||||
private slots:
|
||||
void Reconnect(int seconds);
|
||||
void ReconnectSuccess();
|
||||
void UpdateStatusBar();
|
||||
void UpdateCurrentFPS();
|
||||
void UpdateIcons();
|
||||
|
||||
public:
|
||||
OBSBasicStatusBar(QWidget *parent);
|
||||
|
||||
void StreamDelayStarting(int sec);
|
||||
void StreamDelayStopping(int sec);
|
||||
void StreamStarted(obs_output_t *output);
|
||||
void StreamStopped();
|
||||
void RecordingStarted(obs_output_t *output);
|
||||
void RecordingStopped();
|
||||
void RecordingPaused();
|
||||
void RecordingUnpaused();
|
||||
|
||||
void ReconnectClear();
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,249 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QAccessibleWidget>
|
||||
#include "absolute-slider.hpp"
|
||||
|
||||
class QPushButton;
|
||||
class VolumeMeterTimer;
|
||||
class VolumeSlider;
|
||||
#include <QFrame>
|
||||
|
||||
class VolumeMeter : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||
|
||||
// Levels are denoted in dBFS.
|
||||
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||
|
||||
// Rates are denoted in dB/second.
|
||||
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||
|
||||
// Time in seconds for the VU meter to integrate over.
|
||||
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||
DESIGNABLE true)
|
||||
|
||||
// Duration is denoted in seconds.
|
||||
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||
DESIGNABLE true)
|
||||
|
||||
friend class VolControl;
|
||||
|
||||
private:
|
||||
obs_volmeter_t *obs_volmeter;
|
||||
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||
|
||||
inline void resetLevels();
|
||||
inline void doLayout();
|
||||
inline bool detectIdle(uint64_t ts);
|
||||
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||
|
||||
inline int convertToInt(float number);
|
||||
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||
|
||||
QMutex dataMutex;
|
||||
|
||||
bool recalculateLayout = true;
|
||||
uint64_t currentLastUpdateTime = 0;
|
||||
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||
|
||||
int displayNrAudioChannels = 0;
|
||||
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
|
||||
QFont tickFont;
|
||||
QColor backgroundNominalColor;
|
||||
QColor backgroundWarningColor;
|
||||
QColor backgroundErrorColor;
|
||||
QColor foregroundNominalColor;
|
||||
QColor foregroundWarningColor;
|
||||
QColor foregroundErrorColor;
|
||||
|
||||
QColor backgroundNominalColorDisabled;
|
||||
QColor backgroundWarningColorDisabled;
|
||||
QColor backgroundErrorColorDisabled;
|
||||
QColor foregroundNominalColorDisabled;
|
||||
QColor foregroundWarningColorDisabled;
|
||||
QColor foregroundErrorColorDisabled;
|
||||
|
||||
QColor clipColor;
|
||||
QColor magnitudeColor;
|
||||
QColor majorTickColor;
|
||||
QColor minorTickColor;
|
||||
|
||||
int meterThickness;
|
||||
qreal meterFontScaling;
|
||||
|
||||
qreal minimumLevel;
|
||||
qreal warningLevel;
|
||||
qreal errorLevel;
|
||||
qreal clipLevel;
|
||||
qreal minimumInputLevel;
|
||||
qreal peakDecayRate;
|
||||
qreal magnitudeIntegrationTime;
|
||||
qreal peakHoldDuration;
|
||||
qreal inputPeakHoldDuration;
|
||||
|
||||
QColor p_backgroundNominalColor;
|
||||
QColor p_backgroundWarningColor;
|
||||
QColor p_backgroundErrorColor;
|
||||
QColor p_foregroundNominalColor;
|
||||
QColor p_foregroundWarningColor;
|
||||
QColor p_foregroundErrorColor;
|
||||
|
||||
uint64_t lastRedrawTime = 0;
|
||||
int channels = 0;
|
||||
bool clipping = false;
|
||||
bool vertical;
|
||||
bool muted = false;
|
||||
|
||||
public:
|
||||
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||
~VolumeMeter();
|
||||
|
||||
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
QRect getBarRect() const;
|
||||
bool needLayoutChange();
|
||||
|
||||
QColor getBackgroundNominalColor() const;
|
||||
void setBackgroundNominalColor(QColor c);
|
||||
QColor getBackgroundWarningColor() const;
|
||||
void setBackgroundWarningColor(QColor c);
|
||||
QColor getBackgroundErrorColor() const;
|
||||
void setBackgroundErrorColor(QColor c);
|
||||
QColor getForegroundNominalColor() const;
|
||||
void setForegroundNominalColor(QColor c);
|
||||
QColor getForegroundWarningColor() const;
|
||||
void setForegroundWarningColor(QColor c);
|
||||
QColor getForegroundErrorColor() const;
|
||||
void setForegroundErrorColor(QColor c);
|
||||
|
||||
QColor getBackgroundNominalColorDisabled() const;
|
||||
void setBackgroundNominalColorDisabled(QColor c);
|
||||
QColor getBackgroundWarningColorDisabled() const;
|
||||
void setBackgroundWarningColorDisabled(QColor c);
|
||||
QColor getBackgroundErrorColorDisabled() const;
|
||||
void setBackgroundErrorColorDisabled(QColor c);
|
||||
QColor getForegroundNominalColorDisabled() const;
|
||||
void setForegroundNominalColorDisabled(QColor c);
|
||||
QColor getForegroundWarningColorDisabled() const;
|
||||
void setForegroundWarningColorDisabled(QColor c);
|
||||
QColor getForegroundErrorColorDisabled() const;
|
||||
void setForegroundErrorColorDisabled(QColor c);
|
||||
|
||||
QColor getClipColor() const;
|
||||
void setClipColor(QColor c);
|
||||
QColor getMagnitudeColor() const;
|
||||
void setMagnitudeColor(QColor c);
|
||||
QColor getMajorTickColor() const;
|
||||
void setMajorTickColor(QColor c);
|
||||
QColor getMinorTickColor() const;
|
||||
void setMinorTickColor(QColor c);
|
||||
int getMeterThickness() const;
|
||||
void setMeterThickness(int v);
|
||||
qreal getMeterFontScaling() const;
|
||||
void setMeterFontScaling(qreal v);
|
||||
qreal getMinimumLevel() const;
|
||||
void setMinimumLevel(qreal v);
|
||||
qreal getWarningLevel() const;
|
||||
void setWarningLevel(qreal v);
|
||||
qreal getErrorLevel() const;
|
||||
void setErrorLevel(qreal v);
|
||||
qreal getClipLevel() const;
|
||||
void setClipLevel(qreal v);
|
||||
qreal getMinimumInputLevel() const;
|
||||
void setMinimumInputLevel(qreal v);
|
||||
qreal getPeakDecayRate() const;
|
||||
void setPeakDecayRate(qreal v);
|
||||
qreal getMagnitudeIntegrationTime() const;
|
||||
void setMagnitudeIntegrationTime(qreal v);
|
||||
qreal getPeakHoldDuration() const;
|
||||
void setPeakHoldDuration(qreal v);
|
||||
qreal getInputPeakHoldDuration() const;
|
||||
void setInputPeakHoldDuration(qreal v);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *e) override;
|
||||
};
|
||||
|
||||
class VolumeMeterTimer : public QTimer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline VolumeMeterTimer() : QTimer() {}
|
||||
|
||||
void AddVolControl(VolumeMeter *meter);
|
||||
void RemoveVolControl(VolumeMeter *meter);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
QList<VolumeMeter *> volumeMeters;
|
||||
};
|
||||
|
||||
class QLabel;
|
||||
class OBSSourceLabel;
|
||||
class VolumeMeter;
|
||||
class VolumeSlider;
|
||||
class MuteCheckBox;
|
||||
class OBSSourceLabel;
|
||||
class QLabel;
|
||||
class QPushButton;
|
||||
|
||||
class VolControl : public QFrame {
|
||||
Q_OBJECT
|
||||
@ -298,44 +64,3 @@ public:
|
||||
|
||||
void refreshColors();
|
||||
};
|
||||
|
||||
class VolumeSlider : public AbsoluteSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
obs_fader_t *fad;
|
||||
|
||||
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||
|
||||
bool getDisplayTicks() const;
|
||||
void setDisplayTicks(bool display);
|
||||
|
||||
private:
|
||||
bool displayTicks = false;
|
||||
QColor tickColor;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||
|
||||
public:
|
||||
VolumeAccessibleInterface(QWidget *w);
|
||||
|
||||
QVariant currentValue() const;
|
||||
void setCurrentValue(const QVariant &value);
|
||||
|
||||
QVariant maximumValue() const;
|
||||
QVariant minimumValue() const;
|
||||
|
||||
QVariant minimumStepSize() const;
|
||||
|
||||
private:
|
||||
VolumeSlider *slider() const;
|
||||
|
||||
protected:
|
||||
virtual QAccessible::Role role() const override;
|
||||
virtual QString text(QAccessible::Text t) const override;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,324 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QAccessibleWidget>
|
||||
#include "absolute-slider.hpp"
|
||||
|
||||
class QPushButton;
|
||||
class VolumeMeterTimer;
|
||||
class VolumeSlider;
|
||||
|
||||
class VolumeMeter : public QWidget {
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor
|
||||
DESIGNABLE true)
|
||||
Q_PROPERTY(
|
||||
QColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE
|
||||
setBackgroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE
|
||||
setBackgroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE
|
||||
setBackgroundErrorColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE
|
||||
setForegroundNominalColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE
|
||||
setForegroundWarningColorDisabled DESIGNABLE true)
|
||||
Q_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE
|
||||
setForegroundErrorColorDisabled DESIGNABLE true)
|
||||
|
||||
Q_PROPERTY(QColor clipColor READ getClipColor WRITE setClipColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)
|
||||
Q_PROPERTY(int meterThickness READ getMeterThickness WRITE setMeterThickness DESIGNABLE true)
|
||||
Q_PROPERTY(qreal meterFontScaling READ getMeterFontScaling WRITE setMeterFontScaling DESIGNABLE true)
|
||||
|
||||
// Levels are denoted in dBFS.
|
||||
Q_PROPERTY(qreal minimumLevel READ getMinimumLevel WRITE setMinimumLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal warningLevel READ getWarningLevel WRITE setWarningLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal errorLevel READ getErrorLevel WRITE setErrorLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal clipLevel READ getClipLevel WRITE setClipLevel DESIGNABLE true)
|
||||
Q_PROPERTY(qreal minimumInputLevel READ getMinimumInputLevel WRITE setMinimumInputLevel DESIGNABLE true)
|
||||
|
||||
// Rates are denoted in dB/second.
|
||||
Q_PROPERTY(qreal peakDecayRate READ getPeakDecayRate WRITE setPeakDecayRate DESIGNABLE true)
|
||||
|
||||
// Time in seconds for the VU meter to integrate over.
|
||||
Q_PROPERTY(qreal magnitudeIntegrationTime READ getMagnitudeIntegrationTime WRITE setMagnitudeIntegrationTime
|
||||
DESIGNABLE true)
|
||||
|
||||
// Duration is denoted in seconds.
|
||||
Q_PROPERTY(qreal peakHoldDuration READ getPeakHoldDuration WRITE setPeakHoldDuration DESIGNABLE true)
|
||||
Q_PROPERTY(qreal inputPeakHoldDuration READ getInputPeakHoldDuration WRITE setInputPeakHoldDuration
|
||||
DESIGNABLE true)
|
||||
|
||||
friend class VolControl;
|
||||
|
||||
private:
|
||||
obs_volmeter_t *obs_volmeter;
|
||||
static std::weak_ptr<VolumeMeterTimer> updateTimer;
|
||||
std::shared_ptr<VolumeMeterTimer> updateTimerRef;
|
||||
|
||||
inline void resetLevels();
|
||||
inline void doLayout();
|
||||
inline bool detectIdle(uint64_t ts);
|
||||
inline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);
|
||||
inline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);
|
||||
|
||||
inline int convertToInt(float number);
|
||||
void paintInputMeter(QPainter &painter, int x, int y, int width, int height, float peakHold);
|
||||
void paintHMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintHTicks(QPainter &painter, int x, int y, int width);
|
||||
void paintVMeter(QPainter &painter, int x, int y, int width, int height, float magnitude, float peak,
|
||||
float peakHold);
|
||||
void paintVTicks(QPainter &painter, int x, int y, int height);
|
||||
|
||||
QMutex dataMutex;
|
||||
|
||||
bool recalculateLayout = true;
|
||||
uint64_t currentLastUpdateTime = 0;
|
||||
float currentMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float currentPeak[MAX_AUDIO_CHANNELS];
|
||||
float currentInputPeak[MAX_AUDIO_CHANNELS];
|
||||
|
||||
int displayNrAudioChannels = 0;
|
||||
float displayMagnitude[MAX_AUDIO_CHANNELS];
|
||||
float displayPeak[MAX_AUDIO_CHANNELS];
|
||||
float displayPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
float displayInputPeakHold[MAX_AUDIO_CHANNELS];
|
||||
uint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];
|
||||
|
||||
QFont tickFont;
|
||||
QColor backgroundNominalColor;
|
||||
QColor backgroundWarningColor;
|
||||
QColor backgroundErrorColor;
|
||||
QColor foregroundNominalColor;
|
||||
QColor foregroundWarningColor;
|
||||
QColor foregroundErrorColor;
|
||||
|
||||
QColor backgroundNominalColorDisabled;
|
||||
QColor backgroundWarningColorDisabled;
|
||||
QColor backgroundErrorColorDisabled;
|
||||
QColor foregroundNominalColorDisabled;
|
||||
QColor foregroundWarningColorDisabled;
|
||||
QColor foregroundErrorColorDisabled;
|
||||
|
||||
QColor clipColor;
|
||||
QColor magnitudeColor;
|
||||
QColor majorTickColor;
|
||||
QColor minorTickColor;
|
||||
|
||||
int meterThickness;
|
||||
qreal meterFontScaling;
|
||||
|
||||
qreal minimumLevel;
|
||||
qreal warningLevel;
|
||||
qreal errorLevel;
|
||||
qreal clipLevel;
|
||||
qreal minimumInputLevel;
|
||||
qreal peakDecayRate;
|
||||
qreal magnitudeIntegrationTime;
|
||||
qreal peakHoldDuration;
|
||||
qreal inputPeakHoldDuration;
|
||||
|
||||
QColor p_backgroundNominalColor;
|
||||
QColor p_backgroundWarningColor;
|
||||
QColor p_backgroundErrorColor;
|
||||
QColor p_foregroundNominalColor;
|
||||
QColor p_foregroundWarningColor;
|
||||
QColor p_foregroundErrorColor;
|
||||
|
||||
uint64_t lastRedrawTime = 0;
|
||||
int channels = 0;
|
||||
bool clipping = false;
|
||||
bool vertical;
|
||||
bool muted = false;
|
||||
|
||||
public:
|
||||
explicit VolumeMeter(QWidget *parent = nullptr, obs_volmeter_t *obs_volmeter = nullptr, bool vertical = false);
|
||||
~VolumeMeter();
|
||||
|
||||
void setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
|
||||
const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
QRect getBarRect() const;
|
||||
bool needLayoutChange();
|
||||
|
||||
QColor getBackgroundNominalColor() const;
|
||||
void setBackgroundNominalColor(QColor c);
|
||||
QColor getBackgroundWarningColor() const;
|
||||
void setBackgroundWarningColor(QColor c);
|
||||
QColor getBackgroundErrorColor() const;
|
||||
void setBackgroundErrorColor(QColor c);
|
||||
QColor getForegroundNominalColor() const;
|
||||
void setForegroundNominalColor(QColor c);
|
||||
QColor getForegroundWarningColor() const;
|
||||
void setForegroundWarningColor(QColor c);
|
||||
QColor getForegroundErrorColor() const;
|
||||
void setForegroundErrorColor(QColor c);
|
||||
|
||||
QColor getBackgroundNominalColorDisabled() const;
|
||||
void setBackgroundNominalColorDisabled(QColor c);
|
||||
QColor getBackgroundWarningColorDisabled() const;
|
||||
void setBackgroundWarningColorDisabled(QColor c);
|
||||
QColor getBackgroundErrorColorDisabled() const;
|
||||
void setBackgroundErrorColorDisabled(QColor c);
|
||||
QColor getForegroundNominalColorDisabled() const;
|
||||
void setForegroundNominalColorDisabled(QColor c);
|
||||
QColor getForegroundWarningColorDisabled() const;
|
||||
void setForegroundWarningColorDisabled(QColor c);
|
||||
QColor getForegroundErrorColorDisabled() const;
|
||||
void setForegroundErrorColorDisabled(QColor c);
|
||||
|
||||
QColor getClipColor() const;
|
||||
void setClipColor(QColor c);
|
||||
QColor getMagnitudeColor() const;
|
||||
void setMagnitudeColor(QColor c);
|
||||
QColor getMajorTickColor() const;
|
||||
void setMajorTickColor(QColor c);
|
||||
QColor getMinorTickColor() const;
|
||||
void setMinorTickColor(QColor c);
|
||||
int getMeterThickness() const;
|
||||
void setMeterThickness(int v);
|
||||
qreal getMeterFontScaling() const;
|
||||
void setMeterFontScaling(qreal v);
|
||||
qreal getMinimumLevel() const;
|
||||
void setMinimumLevel(qreal v);
|
||||
qreal getWarningLevel() const;
|
||||
void setWarningLevel(qreal v);
|
||||
qreal getErrorLevel() const;
|
||||
void setErrorLevel(qreal v);
|
||||
qreal getClipLevel() const;
|
||||
void setClipLevel(qreal v);
|
||||
qreal getMinimumInputLevel() const;
|
||||
void setMinimumInputLevel(qreal v);
|
||||
qreal getPeakDecayRate() const;
|
||||
void setPeakDecayRate(qreal v);
|
||||
qreal getMagnitudeIntegrationTime() const;
|
||||
void setMagnitudeIntegrationTime(qreal v);
|
||||
qreal getPeakHoldDuration() const;
|
||||
void setPeakHoldDuration(qreal v);
|
||||
qreal getInputPeakHoldDuration() const;
|
||||
void setInputPeakHoldDuration(qreal v);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void wheelEvent(QWheelEvent *event) override;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *e) override;
|
||||
};
|
||||
|
||||
class VolumeMeterTimer : public QTimer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline VolumeMeterTimer() : QTimer() {}
|
||||
|
||||
void AddVolControl(VolumeMeter *meter);
|
||||
void RemoveVolControl(VolumeMeter *meter);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
QList<VolumeMeter *> volumeMeters;
|
||||
};
|
||||
|
||||
class QLabel;
|
||||
class VolumeSlider;
|
||||
class MuteCheckBox;
|
||||
class OBSSourceLabel;
|
||||
|
||||
class VolControl : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
OBSSource source;
|
||||
std::vector<OBSSignal> sigs;
|
||||
OBSSourceLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
VolumeMeter *volMeter;
|
||||
VolumeSlider *slider;
|
||||
MuteCheckBox *mute;
|
||||
QPushButton *config = nullptr;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
OBSFader obs_fader;
|
||||
OBSVolMeter obs_volmeter;
|
||||
bool vertical;
|
||||
QMenu *contextMenu;
|
||||
|
||||
static void OBSVolumeChanged(void *param, float db);
|
||||
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||
|
||||
void EmitConfigClicked();
|
||||
|
||||
private slots:
|
||||
void VolumeChanged();
|
||||
void VolumeMuted(bool muted);
|
||||
void MixersOrMonitoringChanged();
|
||||
|
||||
void SetMuted(bool checked);
|
||||
void SliderChanged(int vol);
|
||||
void updateText();
|
||||
|
||||
signals:
|
||||
void ConfigClicked();
|
||||
|
||||
public:
|
||||
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||
~VolControl();
|
||||
|
||||
inline obs_source_t *GetSource() const { return source; }
|
||||
|
||||
void SetMeterDecayRate(qreal q);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
|
||||
void EnableSlider(bool enable);
|
||||
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||
|
||||
void refreshColors();
|
||||
};
|
||||
|
||||
class VolumeSlider : public AbsoluteSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
obs_fader_t *fad;
|
||||
|
||||
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||
|
||||
bool getDisplayTicks() const;
|
||||
void setDisplayTicks(bool display);
|
||||
|
||||
private:
|
||||
bool displayTicks = false;
|
||||
QColor tickColor;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||
|
||||
public:
|
||||
|
@ -1,21 +1,13 @@
|
||||
#include "window-basic-main.hpp"
|
||||
#include "moc_volume-control.cpp"
|
||||
#include "obs-app.hpp"
|
||||
#include "mute-checkbox.hpp"
|
||||
#include "absolute-slider.hpp"
|
||||
#include "source-label.hpp"
|
||||
#include "VolumeMeter.hpp"
|
||||
|
||||
#include <slider-ignorewheel.hpp>
|
||||
#include <qt-wrappers.hpp>
|
||||
#include <QFontDatabase>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <OBSApp.hpp>
|
||||
#include <utility/VolumeMeterTimer.hpp>
|
||||
|
||||
#include <QEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define FADER_PRECISION 4096.0
|
||||
#include "moc_VolumeMeter.cpp"
|
||||
|
||||
// Size of the audio indicator in pixels
|
||||
#define INDICATOR_THICKNESS 3
|
||||
@ -25,385 +17,6 @@ using namespace std;
|
||||
|
||||
std::weak_ptr<VolumeMeterTimer> VolumeMeter::updateTimer;
|
||||
|
||||
static inline Qt::CheckState GetCheckState(bool muted, bool unassigned)
|
||||
{
|
||||
if (muted)
|
||||
return Qt::Checked;
|
||||
else if (unassigned)
|
||||
return Qt::PartiallyChecked;
|
||||
else
|
||||
return Qt::Unchecked;
|
||||
}
|
||||
|
||||
static inline bool IsSourceUnassigned(obs_source_t *source)
|
||||
{
|
||||
uint32_t mixes = (obs_source_get_audio_mixers(source) & ((1 << MAX_AUDIO_MIXES) - 1));
|
||||
obs_monitoring_type mt = obs_source_get_monitoring_type(source);
|
||||
|
||||
return mixes == 0 && mt != OBS_MONITORING_TYPE_MONITOR_ONLY;
|
||||
}
|
||||
|
||||
static void ShowUnassignedWarning(const char *name)
|
||||
{
|
||||
auto msgBox = [=]() {
|
||||
QMessageBox msgbox(App()->GetMainWindow());
|
||||
msgbox.setWindowTitle(QTStr("VolControl.UnassignedWarning.Title"));
|
||||
msgbox.setText(QTStr("VolControl.UnassignedWarning.Text").arg(name));
|
||||
msgbox.setIcon(QMessageBox::Icon::Information);
|
||||
msgbox.addButton(QMessageBox::Ok);
|
||||
|
||||
QCheckBox *cb = new QCheckBox(QTStr("DoNotShowAgain"));
|
||||
msgbox.setCheckBox(cb);
|
||||
|
||||
msgbox.exec();
|
||||
|
||||
if (cb->isChecked()) {
|
||||
config_set_bool(App()->GetUserConfig(), "General", "WarnedAboutUnassignedSources", true);
|
||||
config_save_safe(App()->GetUserConfig(), "tmp", nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
QMetaObject::invokeMethod(App(), "Exec", Qt::QueuedConnection, Q_ARG(VoidFunc, msgBox));
|
||||
}
|
||||
|
||||
void VolControl::OBSVolumeChanged(void *data, float db)
|
||||
{
|
||||
Q_UNUSED(db);
|
||||
VolControl *volControl = static_cast<VolControl *>(data);
|
||||
|
||||
QMetaObject::invokeMethod(volControl, "VolumeChanged");
|
||||
}
|
||||
|
||||
void VolControl::OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS])
|
||||
{
|
||||
VolControl *volControl = static_cast<VolControl *>(data);
|
||||
|
||||
volControl->volMeter->setLevels(magnitude, peak, inputPeak);
|
||||
}
|
||||
|
||||
void VolControl::OBSVolumeMuted(void *data, calldata_t *calldata)
|
||||
{
|
||||
VolControl *volControl = static_cast<VolControl *>(data);
|
||||
bool muted = calldata_bool(calldata, "muted");
|
||||
|
||||
QMetaObject::invokeMethod(volControl, "VolumeMuted", Q_ARG(bool, muted));
|
||||
}
|
||||
|
||||
void VolControl::VolumeChanged()
|
||||
{
|
||||
slider->blockSignals(true);
|
||||
slider->setValue((int)(obs_fader_get_deflection(obs_fader) * FADER_PRECISION));
|
||||
slider->blockSignals(false);
|
||||
|
||||
updateText();
|
||||
}
|
||||
|
||||
void VolControl::VolumeMuted(bool muted)
|
||||
{
|
||||
bool unassigned = IsSourceUnassigned(source);
|
||||
|
||||
auto newState = GetCheckState(muted, unassigned);
|
||||
if (mute->checkState() != newState)
|
||||
mute->setCheckState(newState);
|
||||
|
||||
volMeter->muted = muted || unassigned;
|
||||
}
|
||||
|
||||
void VolControl::OBSMixersOrMonitoringChanged(void *data, calldata_t *)
|
||||
{
|
||||
|
||||
VolControl *volControl = static_cast<VolControl *>(data);
|
||||
QMetaObject::invokeMethod(volControl, "MixersOrMonitoringChanged", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void VolControl::MixersOrMonitoringChanged()
|
||||
{
|
||||
bool muted = obs_source_muted(source);
|
||||
bool unassigned = IsSourceUnassigned(source);
|
||||
|
||||
auto newState = GetCheckState(muted, unassigned);
|
||||
if (mute->checkState() != newState)
|
||||
mute->setCheckState(newState);
|
||||
|
||||
volMeter->muted = muted || unassigned;
|
||||
}
|
||||
|
||||
void VolControl::SetMuted(bool)
|
||||
{
|
||||
bool checked = mute->checkState() == Qt::Checked;
|
||||
bool prev = obs_source_muted(source);
|
||||
obs_source_set_muted(source, checked);
|
||||
bool unassigned = IsSourceUnassigned(source);
|
||||
|
||||
if (!checked && unassigned) {
|
||||
mute->setCheckState(Qt::PartiallyChecked);
|
||||
/* Show notice about the source no being assigned to any tracks */
|
||||
bool has_shown_warning =
|
||||
config_get_bool(App()->GetUserConfig(), "General", "WarnedAboutUnassignedSources");
|
||||
if (!has_shown_warning)
|
||||
ShowUnassignedWarning(obs_source_get_name(source));
|
||||
}
|
||||
|
||||
auto undo_redo = [](const std::string &uuid, bool val) {
|
||||
OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
|
||||
obs_source_set_muted(source, val);
|
||||
};
|
||||
|
||||
QString text = QTStr(checked ? "Undo.Volume.Mute" : "Undo.Volume.Unmute");
|
||||
|
||||
const char *name = obs_source_get_name(source);
|
||||
const char *uuid = obs_source_get_uuid(source);
|
||||
OBSBasic::Get()->undo_s.add_action(text.arg(name), std::bind(undo_redo, std::placeholders::_1, prev),
|
||||
std::bind(undo_redo, std::placeholders::_1, checked), uuid, uuid);
|
||||
}
|
||||
|
||||
void VolControl::SliderChanged(int vol)
|
||||
{
|
||||
float prev = obs_source_get_volume(source);
|
||||
|
||||
obs_fader_set_deflection(obs_fader, float(vol) / FADER_PRECISION);
|
||||
updateText();
|
||||
|
||||
auto undo_redo = [](const std::string &uuid, float val) {
|
||||
OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
|
||||
obs_source_set_volume(source, val);
|
||||
};
|
||||
|
||||
float val = obs_source_get_volume(source);
|
||||
const char *name = obs_source_get_name(source);
|
||||
const char *uuid = obs_source_get_uuid(source);
|
||||
OBSBasic::Get()->undo_s.add_action(QTStr("Undo.Volume.Change").arg(name),
|
||||
std::bind(undo_redo, std::placeholders::_1, prev),
|
||||
std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid, true);
|
||||
}
|
||||
|
||||
void VolControl::updateText()
|
||||
{
|
||||
QString text;
|
||||
float db = obs_fader_get_db(obs_fader);
|
||||
|
||||
if (db < -96.0f)
|
||||
text = "-inf dB";
|
||||
else
|
||||
text = QString::number(db, 'f', 1).append(" dB");
|
||||
|
||||
volLabel->setText(text);
|
||||
|
||||
bool muted = obs_source_muted(source);
|
||||
const char *accTextLookup = muted ? "VolControl.SliderMuted" : "VolControl.SliderUnmuted";
|
||||
|
||||
QString sourceName = obs_source_get_name(source);
|
||||
QString accText = QTStr(accTextLookup).arg(sourceName);
|
||||
|
||||
slider->setAccessibleName(accText);
|
||||
}
|
||||
|
||||
void VolControl::EmitConfigClicked()
|
||||
{
|
||||
emit ConfigClicked();
|
||||
}
|
||||
|
||||
void VolControl::SetMeterDecayRate(qreal q)
|
||||
{
|
||||
volMeter->setPeakDecayRate(q);
|
||||
}
|
||||
|
||||
void VolControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
|
||||
{
|
||||
volMeter->setPeakMeterType(peakMeterType);
|
||||
}
|
||||
|
||||
VolControl::VolControl(OBSSource source_, bool showConfig, bool vertical)
|
||||
: source(std::move(source_)),
|
||||
levelTotal(0.0f),
|
||||
levelCount(0.0f),
|
||||
obs_fader(obs_fader_create(OBS_FADER_LOG)),
|
||||
obs_volmeter(obs_volmeter_create(OBS_FADER_LOG)),
|
||||
vertical(vertical),
|
||||
contextMenu(nullptr)
|
||||
{
|
||||
nameLabel = new OBSSourceLabel(source);
|
||||
volLabel = new QLabel();
|
||||
mute = new MuteCheckBox();
|
||||
|
||||
volLabel->setObjectName("volLabel");
|
||||
volLabel->setAlignment(Qt::AlignCenter);
|
||||
|
||||
#ifdef __APPLE__
|
||||
mute->setAttribute(Qt::WA_LayoutUsesWidgetRect);
|
||||
#endif
|
||||
|
||||
QString sourceName = obs_source_get_name(source);
|
||||
setObjectName(sourceName);
|
||||
|
||||
if (showConfig) {
|
||||
config = new QPushButton(this);
|
||||
config->setProperty("class", "icon-dots-vert");
|
||||
config->setAutoDefault(false);
|
||||
|
||||
config->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
|
||||
config->setAccessibleName(QTStr("VolControl.Properties").arg(sourceName));
|
||||
|
||||
connect(config, &QAbstractButton::clicked, this, &VolControl::EmitConfigClicked);
|
||||
}
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
mainLayout->setContentsMargins(0, 0, 0, 0);
|
||||
mainLayout->setSpacing(0);
|
||||
|
||||
if (vertical) {
|
||||
QHBoxLayout *nameLayout = new QHBoxLayout;
|
||||
QHBoxLayout *controlLayout = new QHBoxLayout;
|
||||
QHBoxLayout *volLayout = new QHBoxLayout;
|
||||
QFrame *meterFrame = new QFrame;
|
||||
QHBoxLayout *meterLayout = new QHBoxLayout;
|
||||
|
||||
volMeter = new VolumeMeter(nullptr, obs_volmeter, true);
|
||||
slider = new VolumeSlider(obs_fader, Qt::Vertical);
|
||||
slider->setLayoutDirection(Qt::LeftToRight);
|
||||
slider->setDisplayTicks(true);
|
||||
|
||||
nameLayout->setAlignment(Qt::AlignCenter);
|
||||
meterLayout->setAlignment(Qt::AlignCenter);
|
||||
controlLayout->setAlignment(Qt::AlignCenter);
|
||||
volLayout->setAlignment(Qt::AlignCenter);
|
||||
|
||||
meterFrame->setObjectName("volMeterFrame");
|
||||
|
||||
nameLayout->setContentsMargins(0, 0, 0, 0);
|
||||
nameLayout->setSpacing(0);
|
||||
nameLayout->addWidget(nameLabel);
|
||||
|
||||
controlLayout->setContentsMargins(0, 0, 0, 0);
|
||||
controlLayout->setSpacing(0);
|
||||
|
||||
// Add Headphone (audio monitoring) widget here
|
||||
controlLayout->addWidget(mute);
|
||||
|
||||
if (showConfig) {
|
||||
controlLayout->addWidget(config);
|
||||
}
|
||||
|
||||
meterLayout->setContentsMargins(0, 0, 0, 0);
|
||||
meterLayout->setSpacing(0);
|
||||
meterLayout->addWidget(slider);
|
||||
meterLayout->addWidget(volMeter);
|
||||
|
||||
meterFrame->setLayout(meterLayout);
|
||||
|
||||
volLayout->setContentsMargins(0, 0, 0, 0);
|
||||
volLayout->setSpacing(0);
|
||||
volLayout->addWidget(volLabel);
|
||||
volLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Minimum));
|
||||
|
||||
mainLayout->addItem(nameLayout);
|
||||
mainLayout->addItem(volLayout);
|
||||
mainLayout->addWidget(meterFrame);
|
||||
mainLayout->addItem(controlLayout);
|
||||
|
||||
volMeter->setFocusProxy(slider);
|
||||
|
||||
// Default size can cause clipping of long names in vertical layout.
|
||||
QFont font = nameLabel->font();
|
||||
QFontInfo info(font);
|
||||
nameLabel->setFont(font);
|
||||
|
||||
setMaximumWidth(110);
|
||||
} else {
|
||||
QHBoxLayout *textLayout = new QHBoxLayout;
|
||||
QHBoxLayout *controlLayout = new QHBoxLayout;
|
||||
QFrame *meterFrame = new QFrame;
|
||||
QVBoxLayout *meterLayout = new QVBoxLayout;
|
||||
QVBoxLayout *buttonLayout = new QVBoxLayout;
|
||||
|
||||
volMeter = new VolumeMeter(nullptr, obs_volmeter, false);
|
||||
volMeter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
|
||||
|
||||
slider = new VolumeSlider(obs_fader, Qt::Horizontal);
|
||||
slider->setLayoutDirection(Qt::LeftToRight);
|
||||
slider->setDisplayTicks(true);
|
||||
|
||||
textLayout->setContentsMargins(0, 0, 0, 0);
|
||||
textLayout->addWidget(nameLabel);
|
||||
textLayout->addWidget(volLabel);
|
||||
textLayout->setAlignment(nameLabel, Qt::AlignLeft);
|
||||
textLayout->setAlignment(volLabel, Qt::AlignRight);
|
||||
|
||||
meterFrame->setObjectName("volMeterFrame");
|
||||
meterFrame->setLayout(meterLayout);
|
||||
|
||||
meterLayout->setContentsMargins(0, 0, 0, 0);
|
||||
meterLayout->setSpacing(0);
|
||||
|
||||
meterLayout->addWidget(volMeter);
|
||||
meterLayout->addWidget(slider);
|
||||
|
||||
buttonLayout->setContentsMargins(0, 0, 0, 0);
|
||||
buttonLayout->setSpacing(0);
|
||||
|
||||
if (showConfig) {
|
||||
buttonLayout->addWidget(config);
|
||||
}
|
||||
buttonLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding));
|
||||
buttonLayout->addWidget(mute);
|
||||
|
||||
controlLayout->addItem(buttonLayout);
|
||||
controlLayout->addWidget(meterFrame);
|
||||
|
||||
mainLayout->addItem(textLayout);
|
||||
mainLayout->addItem(controlLayout);
|
||||
|
||||
volMeter->setFocusProxy(slider);
|
||||
}
|
||||
|
||||
setLayout(mainLayout);
|
||||
|
||||
nameLabel->setText(sourceName);
|
||||
|
||||
slider->setMinimum(0);
|
||||
slider->setMaximum(int(FADER_PRECISION));
|
||||
|
||||
bool muted = obs_source_muted(source);
|
||||
bool unassigned = IsSourceUnassigned(source);
|
||||
mute->setCheckState(GetCheckState(muted, unassigned));
|
||||
volMeter->muted = muted || unassigned;
|
||||
mute->setAccessibleName(QTStr("VolControl.Mute").arg(sourceName));
|
||||
obs_fader_add_callback(obs_fader, OBSVolumeChanged, this);
|
||||
obs_volmeter_add_callback(obs_volmeter, OBSVolumeLevel, this);
|
||||
|
||||
sigs.emplace_back(obs_source_get_signal_handler(source), "mute", OBSVolumeMuted, this);
|
||||
sigs.emplace_back(obs_source_get_signal_handler(source), "audio_mixers", OBSMixersOrMonitoringChanged, this);
|
||||
sigs.emplace_back(obs_source_get_signal_handler(source), "audio_monitoring", OBSMixersOrMonitoringChanged,
|
||||
this);
|
||||
|
||||
QWidget::connect(slider, &VolumeSlider::valueChanged, this, &VolControl::SliderChanged);
|
||||
QWidget::connect(mute, &MuteCheckBox::clicked, this, &VolControl::SetMuted);
|
||||
|
||||
obs_fader_attach_source(obs_fader, source);
|
||||
obs_volmeter_attach_source(obs_volmeter, source);
|
||||
|
||||
/* Call volume changed once to init the slider position and label */
|
||||
VolumeChanged();
|
||||
}
|
||||
|
||||
void VolControl::EnableSlider(bool enable)
|
||||
{
|
||||
slider->setEnabled(enable);
|
||||
}
|
||||
|
||||
VolControl::~VolControl()
|
||||
{
|
||||
obs_fader_remove_callback(obs_fader, OBSVolumeChanged, this);
|
||||
obs_volmeter_remove_callback(obs_volmeter, OBSVolumeLevel, this);
|
||||
|
||||
sigs.clear();
|
||||
|
||||
if (contextMenu)
|
||||
contextMenu->close();
|
||||
}
|
||||
|
||||
static inline QColor color_from_int(long long val)
|
||||
{
|
||||
QColor color(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
|
||||
@ -636,16 +249,6 @@ void VolumeMeter::setMeterFontScaling(qreal v)
|
||||
recalculateLayout = true;
|
||||
}
|
||||
|
||||
void VolControl::refreshColors()
|
||||
{
|
||||
volMeter->setBackgroundNominalColor(volMeter->getBackgroundNominalColor());
|
||||
volMeter->setBackgroundWarningColor(volMeter->getBackgroundWarningColor());
|
||||
volMeter->setBackgroundErrorColor(volMeter->getBackgroundErrorColor());
|
||||
volMeter->setForegroundNominalColor(volMeter->getForegroundNominalColor());
|
||||
volMeter->setForegroundWarningColor(volMeter->getForegroundWarningColor());
|
||||
volMeter->setForegroundErrorColor(volMeter->getForegroundErrorColor());
|
||||
}
|
||||
|
||||
qreal VolumeMeter::getMinimumLevel() const
|
||||
{
|
||||
return minimumLevel;
|
||||
@ -1349,159 +952,3 @@ void VolumeMeter::changeEvent(QEvent *e)
|
||||
|
||||
QWidget::changeEvent(e);
|
||||
}
|
||||
|
||||
void VolumeMeterTimer::AddVolControl(VolumeMeter *meter)
|
||||
{
|
||||
volumeMeters.push_back(meter);
|
||||
}
|
||||
|
||||
void VolumeMeterTimer::RemoveVolControl(VolumeMeter *meter)
|
||||
{
|
||||
volumeMeters.removeOne(meter);
|
||||
}
|
||||
|
||||
void VolumeMeterTimer::timerEvent(QTimerEvent *)
|
||||
{
|
||||
for (VolumeMeter *meter : volumeMeters) {
|
||||
if (meter->needLayoutChange()) {
|
||||
// Tell paintEvent to update layout and paint everything
|
||||
meter->update();
|
||||
} else {
|
||||
// Tell paintEvent to paint only the bars
|
||||
meter->update(meter->getBarRect());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent) : AbsoluteSlider(parent)
|
||||
{
|
||||
fad = fader;
|
||||
}
|
||||
|
||||
VolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent)
|
||||
: AbsoluteSlider(orientation, parent)
|
||||
{
|
||||
fad = fader;
|
||||
}
|
||||
|
||||
bool VolumeSlider::getDisplayTicks() const
|
||||
{
|
||||
return displayTicks;
|
||||
}
|
||||
|
||||
void VolumeSlider::setDisplayTicks(bool display)
|
||||
{
|
||||
displayTicks = display;
|
||||
}
|
||||
|
||||
void VolumeSlider::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
if (!getDisplayTicks()) {
|
||||
QSlider::paintEvent(event);
|
||||
return;
|
||||
}
|
||||
|
||||
QPainter painter(this);
|
||||
QColor tickColor(91, 98, 115, 255);
|
||||
|
||||
obs_fader_conversion_t fader_db_to_def = obs_fader_db_to_def(fad);
|
||||
|
||||
QStyleOptionSlider opt;
|
||||
initStyleOption(&opt);
|
||||
|
||||
QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);
|
||||
QRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);
|
||||
|
||||
if (orientation() == Qt::Horizontal) {
|
||||
const int sliderWidth = groove.width() - handle.width();
|
||||
|
||||
float tickLength = groove.height() * 1.5;
|
||||
tickLength = std::max((int)tickLength + groove.height(), 8 + groove.height());
|
||||
|
||||
float yPos = groove.center().y() - (tickLength / 2) + 1;
|
||||
|
||||
for (int db = -10; db >= -90; db -= 10) {
|
||||
float tickValue = fader_db_to_def(db);
|
||||
|
||||
float xPos = groove.left() + (tickValue * sliderWidth) + (handle.width() / 2);
|
||||
painter.fillRect(xPos, yPos, 1, tickLength, tickColor);
|
||||
}
|
||||
}
|
||||
|
||||
if (orientation() == Qt::Vertical) {
|
||||
const int sliderHeight = groove.height() - handle.height();
|
||||
|
||||
float tickLength = groove.width() * 1.5;
|
||||
tickLength = std::max((int)tickLength + groove.width(), 8 + groove.width());
|
||||
|
||||
float xPos = groove.center().x() - (tickLength / 2) + 1;
|
||||
|
||||
for (int db = -10; db >= -96; db -= 10) {
|
||||
float tickValue = fader_db_to_def(db);
|
||||
|
||||
float yPos =
|
||||
groove.height() + groove.top() - (tickValue * sliderHeight) - (handle.height() / 2);
|
||||
painter.fillRect(xPos, yPos, tickLength, 1, tickColor);
|
||||
}
|
||||
}
|
||||
|
||||
QSlider::paintEvent(event);
|
||||
}
|
||||
|
||||
VolumeAccessibleInterface::VolumeAccessibleInterface(QWidget *w) : QAccessibleWidget(w) {}
|
||||
|
||||
VolumeSlider *VolumeAccessibleInterface::slider() const
|
||||
{
|
||||
return qobject_cast<VolumeSlider *>(object());
|
||||
}
|
||||
|
||||
QString VolumeAccessibleInterface::text(QAccessible::Text t) const
|
||||
{
|
||||
if (slider()->isVisible()) {
|
||||
switch (t) {
|
||||
case QAccessible::Text::Value:
|
||||
return currentValue().toString();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QAccessibleWidget::text(t);
|
||||
}
|
||||
|
||||
QVariant VolumeAccessibleInterface::currentValue() const
|
||||
{
|
||||
QString text;
|
||||
float db = obs_fader_get_db(slider()->fad);
|
||||
|
||||
if (db < -96.0f)
|
||||
text = "-inf dB";
|
||||
else
|
||||
text = QString::number(db, 'f', 1).append(" dB");
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
void VolumeAccessibleInterface::setCurrentValue(const QVariant &value)
|
||||
{
|
||||
slider()->setValue(value.toInt());
|
||||
}
|
||||
|
||||
QVariant VolumeAccessibleInterface::maximumValue() const
|
||||
{
|
||||
return slider()->maximum();
|
||||
}
|
||||
|
||||
QVariant VolumeAccessibleInterface::minimumValue() const
|
||||
{
|
||||
return slider()->minimum();
|
||||
}
|
||||
|
||||
QVariant VolumeAccessibleInterface::minimumStepSize() const
|
||||
{
|
||||
return slider()->singleStep();
|
||||
}
|
||||
|
||||
QAccessible::Role VolumeAccessibleInterface::role() const
|
||||
{
|
||||
return QAccessible::Role::Slider;
|
||||
}
|
||||
|
@ -1,18 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <QWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QTimer>
|
||||
#include <QMutex>
|
||||
#include <QList>
|
||||
#include <QMenu>
|
||||
#include <QAccessibleWidget>
|
||||
#include "absolute-slider.hpp"
|
||||
|
||||
class QPushButton;
|
||||
#include <QMutex>
|
||||
#include <QWidget>
|
||||
|
||||
#define FADER_PRECISION 4096.0
|
||||
|
||||
class VolumeMeterTimer;
|
||||
class VolumeSlider;
|
||||
|
||||
class VolumeMeter : public QWidget {
|
||||
Q_OBJECT
|
||||
@ -225,117 +220,3 @@ protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
void changeEvent(QEvent *e) override;
|
||||
};
|
||||
|
||||
class VolumeMeterTimer : public QTimer {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
inline VolumeMeterTimer() : QTimer() {}
|
||||
|
||||
void AddVolControl(VolumeMeter *meter);
|
||||
void RemoveVolControl(VolumeMeter *meter);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event) override;
|
||||
QList<VolumeMeter *> volumeMeters;
|
||||
};
|
||||
|
||||
class QLabel;
|
||||
class VolumeSlider;
|
||||
class MuteCheckBox;
|
||||
class OBSSourceLabel;
|
||||
|
||||
class VolControl : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
OBSSource source;
|
||||
std::vector<OBSSignal> sigs;
|
||||
OBSSourceLabel *nameLabel;
|
||||
QLabel *volLabel;
|
||||
VolumeMeter *volMeter;
|
||||
VolumeSlider *slider;
|
||||
MuteCheckBox *mute;
|
||||
QPushButton *config = nullptr;
|
||||
float levelTotal;
|
||||
float levelCount;
|
||||
OBSFader obs_fader;
|
||||
OBSVolMeter obs_volmeter;
|
||||
bool vertical;
|
||||
QMenu *contextMenu;
|
||||
|
||||
static void OBSVolumeChanged(void *param, float db);
|
||||
static void OBSVolumeLevel(void *data, const float magnitude[MAX_AUDIO_CHANNELS],
|
||||
const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);
|
||||
static void OBSVolumeMuted(void *data, calldata_t *calldata);
|
||||
static void OBSMixersOrMonitoringChanged(void *data, calldata_t *);
|
||||
|
||||
void EmitConfigClicked();
|
||||
|
||||
private slots:
|
||||
void VolumeChanged();
|
||||
void VolumeMuted(bool muted);
|
||||
void MixersOrMonitoringChanged();
|
||||
|
||||
void SetMuted(bool checked);
|
||||
void SliderChanged(int vol);
|
||||
void updateText();
|
||||
|
||||
signals:
|
||||
void ConfigClicked();
|
||||
|
||||
public:
|
||||
explicit VolControl(OBSSource source, bool showConfig = false, bool vertical = false);
|
||||
~VolControl();
|
||||
|
||||
inline obs_source_t *GetSource() const { return source; }
|
||||
|
||||
void SetMeterDecayRate(qreal q);
|
||||
void setPeakMeterType(enum obs_peak_meter_type peakMeterType);
|
||||
|
||||
void EnableSlider(bool enable);
|
||||
inline void SetContextMenu(QMenu *cm) { contextMenu = cm; }
|
||||
|
||||
void refreshColors();
|
||||
};
|
||||
|
||||
class VolumeSlider : public AbsoluteSlider {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
obs_fader_t *fad;
|
||||
|
||||
VolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);
|
||||
VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||
|
||||
bool getDisplayTicks() const;
|
||||
void setDisplayTicks(bool display);
|
||||
|
||||
private:
|
||||
bool displayTicks = false;
|
||||
QColor tickColor;
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
class VolumeAccessibleInterface : public QAccessibleWidget {
|
||||
|
||||
public:
|
||||
VolumeAccessibleInterface(QWidget *w);
|
||||
|
||||
QVariant currentValue() const;
|
||||
void setCurrentValue(const QVariant &value);
|
||||
|
||||
QVariant maximumValue() const;
|
||||
QVariant minimumValue() const;
|
||||
|
||||
QVariant minimumStepSize() const;
|
||||
|
||||
private:
|
||||
VolumeSlider *slider() const;
|
||||
|
||||
protected:
|
||||
virtual QAccessible::Role role() const override;
|
||||
virtual QString text(QAccessible::Text t) const override;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user