Vollbildmodus für HDMI
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
build/
|
build/
|
||||||
uartscope-git/
|
uartscope-git/
|
||||||
|
*.tar.gz
|
||||||
@@ -12,6 +12,33 @@
|
|||||||
|
|
||||||
#include "v4l2worker.h"
|
#include "v4l2worker.h"
|
||||||
|
|
||||||
|
// A borderless top-level window that mirrors the live video frame at full
|
||||||
|
// screen size. Opened from VideoWidget's "⛶" button; closes itself on
|
||||||
|
// Escape or a double-click anywhere on it.
|
||||||
|
class FullscreenVideoView : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FullscreenVideoView(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void setFrame(const QImage &frame);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Emitted from closeEvent() so VideoWidget can drop its pointer before
|
||||||
|
// the WA_DeleteOnClose deletion actually happens.
|
||||||
|
void closed();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QImage m_frame;
|
||||||
|
};
|
||||||
|
|
||||||
// VideoWidget shows the live V4L2 preview and can additionally record it
|
// VideoWidget shows the live V4L2 preview and can additionally record it
|
||||||
// to an MP4 file. Recording works by piping raw BGRA frames into an
|
// to an MP4 file. Recording works by piping raw BGRA frames into an
|
||||||
// external `ffmpeg` process (via QProcess/stdin), which encodes them to
|
// external `ffmpeg` process (via QProcess/stdin), which encodes them to
|
||||||
@@ -54,6 +81,7 @@ private slots:
|
|||||||
void onFreeze(bool frozen);
|
void onFreeze(bool frozen);
|
||||||
void onScreenshot();
|
void onScreenshot();
|
||||||
void onRecordToggled(bool checked);
|
void onRecordToggled(bool checked);
|
||||||
|
void onFullscreenToggle();
|
||||||
void refreshDeviceList();
|
void refreshDeviceList();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -83,8 +111,14 @@ private:
|
|||||||
QPushButton *m_freezeBtn = nullptr;
|
QPushButton *m_freezeBtn = nullptr;
|
||||||
QPushButton *m_screenshotBtn = nullptr;
|
QPushButton *m_screenshotBtn = nullptr;
|
||||||
QPushButton *m_recordBtn = nullptr;
|
QPushButton *m_recordBtn = nullptr;
|
||||||
|
QPushButton *m_fullscreenBtn = nullptr;
|
||||||
QLabel *m_infoLabel = nullptr;
|
QLabel *m_infoLabel = nullptr;
|
||||||
|
|
||||||
|
// Fullscreen overlay window (nullptr when not open). Owns itself via
|
||||||
|
// Qt::WA_DeleteOnClose; VideoWidget just tracks whether one currently
|
||||||
|
// exists and forwards frames to it while it's open.
|
||||||
|
FullscreenVideoView *m_fullscreenView = nullptr;
|
||||||
|
|
||||||
// Recording state. m_recordProcess is the running ffmpeg instance (or
|
// Recording state. m_recordProcess is the running ffmpeg instance (or
|
||||||
// nullptr when not recording); m_recordSize is the frame size the
|
// nullptr when not recording); m_recordSize is the frame size the
|
||||||
// recording was started with, so a mid-stream resolution change (which
|
// recording was started with, so a mid-stream resolution change (which
|
||||||
|
|||||||
@@ -9,6 +9,71 @@
|
|||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
#include <QResizeEvent>
|
#include <QResizeEvent>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QCloseEvent>
|
||||||
|
#include <QScreen>
|
||||||
|
|
||||||
|
// ── FullscreenVideoView ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
FullscreenVideoView::FullscreenVideoView(QWidget *parent)
|
||||||
|
: QWidget(parent, Qt::Window)
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("uartscope - Video (Fullscreen)"));
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
setCursor(Qt::BlankCursor);
|
||||||
|
|
||||||
|
QPalette pal = palette();
|
||||||
|
pal.setColor(QPalette::Window, Qt::black);
|
||||||
|
setPalette(pal);
|
||||||
|
setAutoFillBackground(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullscreenVideoView::setFrame(const QImage &frame)
|
||||||
|
{
|
||||||
|
m_frame = frame;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullscreenVideoView::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
QPainter p(this);
|
||||||
|
p.fillRect(rect(), Qt::black);
|
||||||
|
|
||||||
|
if (m_frame.isNull()) {
|
||||||
|
p.setPen(QColor(0x44, 0x44, 0x44));
|
||||||
|
p.setFont(QFont("Sans", 14));
|
||||||
|
p.drawText(rect(), Qt::AlignCenter, tr("No signal"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QImage scaled = m_frame.scaled(size(), Qt::KeepAspectRatio,
|
||||||
|
Qt::SmoothTransformation);
|
||||||
|
const QPoint origin((width() - scaled.width()) / 2,
|
||||||
|
(height() - scaled.height()) / 2);
|
||||||
|
p.drawImage(origin, scaled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullscreenVideoView::keyPressEvent(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
if (event->key() == Qt::Key_Escape || event->key() == Qt::Key_F11)
|
||||||
|
close();
|
||||||
|
else
|
||||||
|
QWidget::keyPressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullscreenVideoView::mouseDoubleClickEvent(QMouseEvent *)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullscreenVideoView::closeEvent(QCloseEvent *event)
|
||||||
|
{
|
||||||
|
emit closed();
|
||||||
|
QWidget::closeEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── VideoWidget ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
static QStringList enumerateVideoDevices()
|
static QStringList enumerateVideoDevices()
|
||||||
{
|
{
|
||||||
@@ -44,6 +109,8 @@ VideoWidget::VideoWidget(QWidget *parent)
|
|||||||
m_infoLabel->setText(tr("Stopped"));
|
m_infoLabel->setText(tr("Stopped"));
|
||||||
if (m_recording)
|
if (m_recording)
|
||||||
stopRecording();
|
stopRecording();
|
||||||
|
if (m_fullscreenView)
|
||||||
|
m_fullscreenView->close();
|
||||||
update();
|
update();
|
||||||
});
|
});
|
||||||
connect(m_worker, &V4l2Worker::errorOccurred, this, [this](const QString &msg) {
|
connect(m_worker, &V4l2Worker::errorOccurred, this, [this](const QString &msg) {
|
||||||
@@ -63,6 +130,9 @@ void VideoWidget::shutdown()
|
|||||||
return;
|
return;
|
||||||
m_shutdownDone = true;
|
m_shutdownDone = true;
|
||||||
|
|
||||||
|
if (m_fullscreenView)
|
||||||
|
m_fullscreenView->close();
|
||||||
|
|
||||||
if (m_recording)
|
if (m_recording)
|
||||||
stopRecording();
|
stopRecording();
|
||||||
|
|
||||||
@@ -119,12 +189,18 @@ void VideoWidget::setupUi()
|
|||||||
m_recordBtn->setToolTip(tr("Record the live preview to an MP4 file (requires ffmpeg)"));
|
m_recordBtn->setToolTip(tr("Record the live preview to an MP4 file (requires ffmpeg)"));
|
||||||
connect(m_recordBtn, &QPushButton::toggled, this, &VideoWidget::onRecordToggled);
|
connect(m_recordBtn, &QPushButton::toggled, this, &VideoWidget::onRecordToggled);
|
||||||
|
|
||||||
|
m_fullscreenBtn = new QPushButton(tr("⛶"), this);
|
||||||
|
m_fullscreenBtn->setMaximumWidth(28);
|
||||||
|
m_fullscreenBtn->setToolTip(tr("Show video fullscreen (Esc or double-click to exit)"));
|
||||||
|
connect(m_fullscreenBtn, &QPushButton::clicked, this, &VideoWidget::onFullscreenToggle);
|
||||||
|
|
||||||
ctrlRow->addWidget(m_deviceCombo, 1);
|
ctrlRow->addWidget(m_deviceCombo, 1);
|
||||||
ctrlRow->addWidget(refreshBtn);
|
ctrlRow->addWidget(refreshBtn);
|
||||||
ctrlRow->addWidget(m_startBtn);
|
ctrlRow->addWidget(m_startBtn);
|
||||||
ctrlRow->addWidget(m_freezeBtn);
|
ctrlRow->addWidget(m_freezeBtn);
|
||||||
ctrlRow->addWidget(m_screenshotBtn);
|
ctrlRow->addWidget(m_screenshotBtn);
|
||||||
ctrlRow->addWidget(m_recordBtn);
|
ctrlRow->addWidget(m_recordBtn);
|
||||||
|
ctrlRow->addWidget(m_fullscreenBtn);
|
||||||
layout->addLayout(ctrlRow);
|
layout->addLayout(ctrlRow);
|
||||||
|
|
||||||
m_infoLabel = new QLabel(tr("No device started"), this);
|
m_infoLabel = new QLabel(tr("No device started"), this);
|
||||||
@@ -229,6 +305,9 @@ void VideoWidget::onNewFrame(const QImage &frame)
|
|||||||
m_frame = frame;
|
m_frame = frame;
|
||||||
updateScaled();
|
updateScaled();
|
||||||
update();
|
update();
|
||||||
|
|
||||||
|
if (m_fullscreenView)
|
||||||
|
m_fullscreenView->setFrame(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoWidget::clearFrame()
|
void VideoWidget::clearFrame()
|
||||||
@@ -321,6 +400,28 @@ void VideoWidget::onRecordToggled(bool checked)
|
|||||||
stopRecording();
|
stopRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VideoWidget::onFullscreenToggle()
|
||||||
|
{
|
||||||
|
// Already open - a second click just closes it again.
|
||||||
|
if (m_fullscreenView) {
|
||||||
|
m_fullscreenView->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fullscreenView = new FullscreenVideoView();
|
||||||
|
connect(m_fullscreenView, &FullscreenVideoView::closed, this, [this] {
|
||||||
|
m_fullscreenView = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Put it on whichever screen the main window currently lives on
|
||||||
|
// (useful when a second/HDMI display is attached), then go fullscreen.
|
||||||
|
if (QScreen *screen = this->screen())
|
||||||
|
m_fullscreenView->setGeometry(screen->geometry());
|
||||||
|
|
||||||
|
m_fullscreenView->setFrame(m_frame);
|
||||||
|
m_fullscreenView->showFullScreen();
|
||||||
|
}
|
||||||
|
|
||||||
void VideoWidget::startRecording()
|
void VideoWidget::startRecording()
|
||||||
{
|
{
|
||||||
if (m_frame.isNull()) {
|
if (m_frame.isNull()) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Maintainer: diabolus <your@email.com>
|
# Maintainer: diabolus <your@email.com>
|
||||||
pkgname=uartscope
|
pkgname=uartscope
|
||||||
pkgver=1.1.0.r1.gef335f2
|
pkgver=1.1.0.r2.g934f32a
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Qt6-based UART serial monitor with tag monitoring, table view and auto-reconnect"
|
pkgdesc="Qt6-based UART serial monitor with tag monitoring, table view and auto-reconnect"
|
||||||
arch=('x86_64' 'aarch64')
|
arch=('x86_64' 'aarch64')
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -3,12 +3,12 @@
|
|||||||
pkgname = uartscope
|
pkgname = uartscope
|
||||||
pkgbase = uartscope
|
pkgbase = uartscope
|
||||||
xdata = pkgtype=pkg
|
xdata = pkgtype=pkg
|
||||||
pkgver = 1.1.0.r1.gef335f2-1
|
pkgver = 1.1.0.r2.g934f32a-1
|
||||||
pkgdesc = Qt6-based UART serial monitor with tag monitoring, table view and auto-reconnect
|
pkgdesc = Qt6-based UART serial monitor with tag monitoring, table view and auto-reconnect
|
||||||
url = https://git.projekt-hirnfrei.de/diabolus/uartscope
|
url = https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||||
builddate = 1787701264
|
builddate = 1788785731
|
||||||
packager = Unknown Packager
|
packager = Unknown Packager
|
||||||
size = 434929
|
size = 455537
|
||||||
arch = x86_64
|
arch = x86_64
|
||||||
license = MIT
|
license = MIT
|
||||||
conflict = uartscope
|
conflict = uartscope
|
||||||
|
|||||||
Binary file not shown.
Submodule uartscope-git/src/uartscope updated: ef335f2d9e...934f32a3bd
@@ -1,3 +1,3 @@
|
|||||||
ef335f2d9eaa98572ab305e5830a0372474163e8 not-for-merge branch 'main' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
934f32a3bd94355c2d92f37899c80b95374d9fac not-for-merge branch 'main' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||||
cc102c93eb17f7b910d8e74c3505f198bed77f10 not-for-merge tag 'v1.0.0' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
cc102c93eb17f7b910d8e74c3505f198bed77f10 not-for-merge tag 'v1.0.0' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||||
36923e16461bc15086c84187790dbcb5f0a5030f not-for-merge tag 'v1.1.0' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
36923e16461bc15086c84187790dbcb5f0a5030f not-for-merge tag 'v1.1.0' of https://git.projekt-hirnfrei.de/diabolus/uartscope
|
||||||
|
|||||||
Reference in New Issue
Block a user