Choose OpenGL / Raster surface at runtime.

This commit is contained in:
John Preston 2021-05-19 16:59:31 +04:00
parent 9510ba07f7
commit a45064257a
10 changed files with 343 additions and 236 deletions

View file

@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/fade_wrap.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/platform/ui_platform_utility.h"
#include "ui/gl/gl_surface.h"
#include "ui/toast/toast.h"
#include "ui/empty_userpic.h"
#include "ui/emoji_config.h"
@ -50,35 +51,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QWindow>
namespace Calls {
namespace {
#if 1
#define USE_OPENGL_OVERLAY_WIDGET 1
#else // Q_OS_MAC && !OS_MAC_OLD
#define USE_OPENGL_OVERLAY_WIDGET 0
#endif // Q_OS_MAC && !OS_MAC_OLD
#if USE_OPENGL_OVERLAY_WIDGET
using IncomingParent = Ui::RpWidgetWrap<QOpenGLWidget>;
#else // USE_OPENGL_OVERLAY_WIDGET
using IncomingParent = Ui::RpWidget;
#endif // USE_OPENGL_OVERLAY_WIDGET
} // namespace
class Panel::Incoming final : public IncomingParent {
class Panel::Incoming final {
public:
Incoming(
not_null<QWidget*> parent,
not_null<Webrtc::VideoTrack*> track);
[[nodiscard]] not_null<QWidget*> widget() const;
[[nodiscard]] not_null<Ui::RpWidgetWrap* > rp() const;
private:
void paintEvent(QPaintEvent *e) override;
void paint(QPainter &p, const QRegion &clip, bool opengl);
void initBottomShadow();
void fillTopShadow(QPainter &p);
void fillBottomShadow(QPainter &p);
[[nodiscard]] Ui::GL::ChosenRenderer chooseRenderer(
Ui::GL::Capabilities capabilities);
const std::unique_ptr<Ui::RpWidgetWrap> _surface;
const not_null<Webrtc::VideoTrack*> _track;
QPixmap _bottomShadow;
@ -87,35 +80,76 @@ private:
Panel::Incoming::Incoming(
not_null<QWidget*> parent,
not_null<Webrtc::VideoTrack*> track)
: IncomingParent(parent)
: _surface(Ui::GL::CreateSurface(
parent,
[=](Ui::GL::Capabilities capabilities) {
return chooseRenderer(capabilities);
}))
, _track(track) {
initBottomShadow();
setAttribute(Qt::WA_OpaquePaintEvent);
setAttribute(Qt::WA_TransparentForMouseEvents);
widget()->setAttribute(Qt::WA_OpaquePaintEvent);
widget()->setAttribute(Qt::WA_TransparentForMouseEvents);
}
void Panel::Incoming::paintEvent(QPaintEvent *e) {
QPainter p(this);
not_null<QWidget*> Panel::Incoming::widget() const {
return _surface->rpWidget();
}
not_null<Ui::RpWidgetWrap*> Panel::Incoming::rp() const {
return _surface.get();
}
Ui::GL::ChosenRenderer Panel::Incoming::chooseRenderer(
Ui::GL::Capabilities capabilities) {
class Renderer : public Ui::GL::Renderer {
public:
Renderer(not_null<Panel::Incoming*> owner) : _owner(owner) {
}
void paintFallback(
QPainter &&p,
const QRegion &clip,
Ui::GL::Backend backend) override {
_owner->paint(
p,
clip.boundingRect(),
backend == Ui::GL::Backend::OpenGL);
}
private:
const not_null<Panel::Incoming*> _owner;
};
return {
.renderer = std::make_unique<Renderer>(this),
.backend = (capabilities.supported
? Ui::GL::Backend::OpenGL
: Ui::GL::Backend::Raster),
};
}
void Panel::Incoming::paint(QPainter &p, const QRegion &clip, bool opengl) {
const auto [image, rotation] = _track->frameOriginalWithRotation();
if (image.isNull()) {
p.fillRect(e->rect(), Qt::black);
p.fillRect(clip.boundingRect(), Qt::black);
} else {
const auto rect = widget()->rect();
using namespace Media::View;
auto hq = PainterHighQualityEnabler(p);
if (UsePainterRotation(rotation, USE_OPENGL_OVERLAY_WIDGET)) {
if (UsePainterRotation(rotation, opengl)) {
if (rotation) {
p.save();
p.rotate(rotation);
}
p.drawImage(RotatedRect(rect(), rotation), image);
p.drawImage(RotatedRect(rect, rotation), image);
if (rotation) {
p.restore();
}
} else if (rotation) {
p.drawImage(rect(), RotateFrameImage(image, rotation));
p.drawImage(rect, RotateFrameImage(image, rotation));
} else {
p.drawImage(rect(), image);
p.drawImage(rect, image);
}
fillBottomShadow(p);
fillTopShadow(p);
@ -146,18 +180,19 @@ void Panel::Incoming::initBottomShadow() {
void Panel::Incoming::fillTopShadow(QPainter &p) {
#ifdef Q_OS_WIN
const auto width = parentWidget()->width();
const auto width = widget()->parentWidget()->width();
const auto position = QPoint(width - st::callTitleShadow.width(), 0);
const auto shadowArea = QRect(
position,
st::callTitleShadow.size());
const auto fill = shadowArea.intersected(geometry()).translated(-pos());
const auto fill = shadowArea.intersected(
widget()->geometry()).translated(-widget()->pos());
if (fill.isEmpty()) {
return;
}
p.save();
p.setClipRect(fill);
st::callTitleShadow.paint(p, position - pos(), width);
st::callTitleShadow.paint(p, position - widget()->pos(), width);
p.restore();
#endif // Q_OS_WIN
}
@ -165,10 +200,11 @@ void Panel::Incoming::fillTopShadow(QPainter &p) {
void Panel::Incoming::fillBottomShadow(QPainter &p) {
const auto shadowArea = QRect(
0,
parentWidget()->height() - st::callBottomShadowSize,
parentWidget()->width(),
widget()->parentWidget()->height() - st::callBottomShadowSize,
widget()->parentWidget()->width(),
st::callBottomShadowSize);
const auto fill = shadowArea.intersected(geometry()).translated(-pos());
const auto fill = shadowArea.intersected(
widget()->geometry()).translated(-widget()->pos());
if (fill.isEmpty()) {
return;
}
@ -178,7 +214,8 @@ void Panel::Incoming::fillBottomShadow(QPainter &p) {
_bottomShadow,
QRect(
0,
factor * (fill.y() - shadowArea.translated(-pos()).y()),
(factor
* (fill.y() - shadowArea.translated(-widget()->pos()).y())),
factor,
factor * fill.height()));
}
@ -394,7 +431,7 @@ void Panel::refreshIncomingGeometry() {
Expects(_incoming != nullptr);
if (_incomingFrameSize.isEmpty()) {
_incoming->hide();
_incoming->widget()->hide();
return;
}
const auto to = widget()->size();
@ -411,8 +448,8 @@ void Panel::refreshIncomingGeometry() {
const auto pos = QPoint(
(to.width() - use.width()) / 2,
(to.height() - use.height()) / 2);
_incoming->setGeometry(QRect(pos, use));
_incoming->show();
_incoming->widget()->setGeometry(QRect(pos, use));
_incoming->widget()->show();
}
void Panel::reinitWithCall(Call *call) {
@ -449,7 +486,7 @@ void Panel::reinitWithCall(Call *call) {
_incoming = std::make_unique<Incoming>(
widget(),
_call->videoIncoming());
_incoming->hide();
_incoming->widget()->hide();
_call->mutedValue(
) | rpl::start_with_next([=](bool mute) {
@ -480,12 +517,12 @@ void Panel::reinitWithCall(Call *call) {
setIncomingSize((rotation == 90 || rotation == 270)
? QSize(frame.height(), frame.width())
: frame.size());
if (_incoming->isHidden()) {
if (_incoming->widget()->isHidden()) {
return;
}
const auto incoming = incomingFrameGeometry();
const auto outgoing = outgoingFrameGeometry();
_incoming->update();
_incoming->widget()->update();
if (incoming.intersects(outgoing)) {
widget()->update(outgoing);
}
@ -497,7 +534,7 @@ void Panel::reinitWithCall(Call *call) {
const auto outgoing = outgoingFrameGeometry();
widget()->update(outgoing);
if (incoming.intersects(outgoing)) {
_incoming->update();
_incoming->widget()->update();
}
}, _callLifetime);
@ -541,7 +578,7 @@ void Panel::reinitWithCall(Call *call) {
_name->setText(_user->name);
updateStatusText(_call->state());
_incoming->lower();
_incoming->widget()->lower();
}
void Panel::createRemoteAudioMute() {
@ -606,7 +643,7 @@ void Panel::showControls() {
_cancel->setVisible(_cancel->toggled());
const auto shown = !_incomingFrameSize.isEmpty();
_incoming->setVisible(shown);
_incoming->widget()->setVisible(shown);
_name->setVisible(!shown);
_status->setVisible(!shown);
_userpic->setVisible(!shown);
@ -650,9 +687,9 @@ void Panel::toggleFullScreen(bool fullscreen) {
}
QRect Panel::incomingFrameGeometry() const {
return (!_incoming || _incoming->isHidden())
return (!_incoming || _incoming->widget()->isHidden())
? QRect()
: _incoming->geometry();
: _incoming->widget()->geometry();
}
QRect Panel::outgoingFrameGeometry() const {
@ -788,13 +825,13 @@ void Panel::paint(QRect clip) {
Painter p(widget());
auto region = QRegion(clip);
if (!_incoming->isHidden()) {
region = region.subtracted(QRegion(_incoming->geometry()));
if (!_incoming->widget()->isHidden()) {
region = region.subtracted(QRegion(_incoming->widget()->geometry()));
}
for (const auto rect : region) {
p.fillRect(rect, st::callBgOpaque);
}
if (_incoming && _incoming->isHidden()) {
if (_incoming && _incoming->widget()->isHidden()) {
_call->videoIncoming()->markFrameShown();
}
}

View file

@ -13,11 +13,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "webrtc/webrtc_video_track.h"
#include "ui/painter.h"
#include "ui/abstract_button.h"
#include "ui/gl/gl_surface.h"
#include "ui/effects/animations.h"
#include "ui/effects/cross_line.h"
#include "lang/lang_keys.h"
#include "styles/style_calls.h"
#include <QtGui/QWindow>
namespace Calls::Group {
namespace {
@ -57,31 +60,64 @@ LargeVideo::LargeVideo(
bool visible,
rpl::producer<LargeVideoTrack> track,
rpl::producer<bool> pinned)
: _content(parent, [=](QRect clip) { paint(clip); })
: _content(Ui::GL::CreateSurface(
parent,
[=](Ui::GL::Capabilities capabilities) {
return chooseRenderer(capabilities);
}))
, _st(st)
, _pinButton((_st.pinPosition.x() >= 0)
? std::make_unique<PinButton>(&_content, st)
? std::make_unique<PinButton>(widget(), st)
: nullptr)
, _smallLayout(!_pinButton) {
_content.setVisible(visible);
widget()->setVisible(visible);
if (_smallLayout) {
_content.setCursor(style::cur_pointer);
widget()->setCursor(style::cur_pointer);
}
setup(std::move(track), std::move(pinned));
}
LargeVideo::~LargeVideo() = default;
Ui::GL::ChosenRenderer LargeVideo::chooseRenderer(
Ui::GL::Capabilities capabilities) {
class Renderer : public Ui::GL::Renderer {
public:
Renderer(not_null<LargeVideo*> owner) : _owner(owner) {
}
void paintFallback(
QPainter &&p,
const QRegion &clip,
Ui::GL::Backend backend) override {
_owner->paint(
clip.boundingRect(),
backend == Ui::GL::Backend::OpenGL);
}
private:
const not_null<LargeVideo*> _owner;
};
return {
.renderer = std::make_unique<Renderer>(this),
.backend = (capabilities.supported
? Ui::GL::Backend::OpenGL
: Ui::GL::Backend::Raster),
};
}
void LargeVideo::raise() {
_content.raise();
widget()->raise();
}
void LargeVideo::setVisible(bool visible) {
_content.setVisible(visible);
widget()->setVisible(visible);
}
void LargeVideo::setGeometry(int x, int y, int width, int height) {
_content.setGeometry(x, y, width, height);
widget()->setGeometry(x, y, width, height);
if (width > 0 && height > 0) {
const auto kMedium = style::ConvertScale(380);
const auto kSmall = style::ConvertScale(200);
@ -98,7 +134,7 @@ void LargeVideo::setControlsShown(float64 shown) {
return;
}
_controlsShownRatio = shown;
_content.update();
widget()->update();
updateControlsGeometry();
}
@ -119,19 +155,27 @@ rpl::producer<QSize> LargeVideo::trackSizeValue() const {
rpl::producer<VideoQuality> LargeVideo::requestedQuality() const {
using namespace rpl::mappers;
return rpl::combine(
_content.shownValue(),
_content->shownValue(),
_requestedQuality.value()
) | rpl::filter([=](bool shown, auto) {
return shown;
}) | rpl::map(_2);
}
rpl::lifetime &LargeVideo::lifetime() {
return _content->lifetime();
}
not_null<QWidget*> LargeVideo::widget() const {
return _content->rpWidget();
}
void LargeVideo::setup(
rpl::producer<LargeVideoTrack> track,
rpl::producer<bool> pinned) {
_content.setAttribute(Qt::WA_OpaquePaintEvent);
widget()->setAttribute(Qt::WA_OpaquePaintEvent);
_content.events(
_content->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) {
const auto type = e->type();
if (type == QEvent::Enter && _pinButton) {
@ -147,21 +191,21 @@ void LargeVideo::setup(
e.get())->button() == Qt::LeftButton
&& _mouseDown) {
_mouseDown = false;
if (!_content.isHidden()) {
if (!widget()->isHidden()) {
_clicks.fire({});
}
}
}, _content.lifetime());
}, _content->lifetime());
rpl::combine(
_content.shownValue(),
_content->shownValue(),
std::move(track)
) | rpl::map([=](bool shown, LargeVideoTrack track) {
return shown ? track : LargeVideoTrack();
}) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](LargeVideoTrack track) {
_track = track;
_content.update();
widget()->update();
_trackLifetime.destroy();
if (!track.track) {
@ -176,12 +220,12 @@ void LargeVideo::setup(
} else {
_trackSize = size;
}
_content.update();
widget()->update();
}, _trackLifetime);
if (const auto size = track.track->frameSize(); !size.isEmpty()) {
_trackSize = size;
}
}, _content.lifetime());
}, _content->lifetime());
setupControls(std::move(pinned));
}
@ -194,7 +238,7 @@ void LargeVideo::togglePinShown(bool shown) {
}
_pinButton->shown = shown;
_pinButton->shownAnimation.start(
[=] { updateControlsGeometry(); _content.update(); },
[=] { updateControlsGeometry(); widget()->update(); },
shown ? 0. : 1.,
shown ? 1. : 0.,
st::slideWrapDuration);
@ -211,13 +255,13 @@ void LargeVideo::setupControls(rpl::producer<bool> pinned) {
: tr::lng_pinned_pin)(tr::now));
updateControlsGeometry();
}
_content.update();
}, _content.lifetime());
widget()->update();
}, _content->lifetime());
_content.sizeValue(
_content->sizeValue(
) | rpl::start_with_next([=](QSize size) {
updateControlsGeometry();
}, _content.lifetime());
}, _content->lifetime());
}
void LargeVideo::updateControlsGeometry() {
@ -236,20 +280,20 @@ void LargeVideo::updateControlsGeometry() {
0,
_pinButton->shownAnimation.value(_pinButton->shown ? 1. : 0.));
_pinButton->rect = QRect(
_content.width() - _st.pinPosition.x() - buttonWidth,
widget()->width() - _st.pinPosition.x() - buttonWidth,
_st.pinPosition.y() - slide,
buttonWidth,
buttonHeight);
_pinButton->area.setGeometry(
_content.width() - fullWidth,
widget()->width() - fullWidth,
-slide,
fullWidth,
fullHeight);
}
}
void LargeVideo::paint(QRect clip) {
auto p = Painter(&_content);
void LargeVideo::paint(QRect clip, bool opengl) {
auto p = Painter(widget());
const auto fill = [&](QRect rect) {
if (rect.intersects(clip)) {
p.fillRect(rect.intersected(clip), st::groupCallMembersBg);
@ -264,7 +308,7 @@ void LargeVideo::paint(QRect clip) {
}
auto hq = PainterHighQualityEnabler(p);
using namespace Media::View;
const auto size = _content.size();
const auto size = widget()->size();
const auto scaled = FlipSizeByRotation(
image.size(),
rotation
@ -272,7 +316,7 @@ void LargeVideo::paint(QRect clip) {
const auto left = (size.width() - scaled.width()) / 2;
const auto top = (size.height() - scaled.height()) / 2;
const auto target = QRect(QPoint(left, top), scaled);
if (UsePainterRotation(rotation, USE_OPENGL_LARGE_VIDEO)) {
if (UsePainterRotation(rotation, opengl)) {
if (rotation) {
p.save();
p.rotate(rotation);
@ -307,8 +351,8 @@ void LargeVideo::paint(QRect clip) {
}
void LargeVideo::paintControls(Painter &p, QRect clip) {
const auto width = _content.width();
const auto height = _content.height();
const auto width = widget()->width();
const auto height = widget()->height();
// Pin.
if (_pinButton && _pinButton->rect.intersects(clip)) {

View file

@ -9,12 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/rp_widget.h"
#if 1
#define USE_OPENGL_LARGE_VIDEO 1
#else
#define USE_OPENGL_LARGE_VIDEO 0
#endif // Q_OS_MAC
namespace style {
struct GroupCallLargeVideo;
} // namespace style
@ -25,6 +19,11 @@ class VideoTrack;
namespace Ui {
class AbstractButton;
class RpWidgetWrap;
namespace GL {
struct Capabilities;
struct ChosenRenderer;
} // namespace GL
} // namespace Ui
namespace Calls::Group {
@ -77,45 +76,26 @@ public:
[[nodiscard]] rpl::producer<QSize> trackSizeValue() const;
[[nodiscard]] rpl::producer<VideoQuality> requestedQuality() const;
[[nodiscard]] rpl::lifetime &lifetime() {
return _content.lifetime();
}
[[nodiscard]] rpl::lifetime &lifetime();
private:
#if USE_OPENGL_LARGE_VIDEO
using ContentParent = Ui::RpWidgetWrap<QOpenGLWidget>;
#else // USE_OPENGL_OVERLAY_WIDGET
using ContentParent = Ui::RpWidget;
#endif // USE_OPENGL_OVERLAY_WIDGET
class Content final : public ContentParent {
public:
Content(QWidget *parent, Fn<void(QRect)> paint)
: ContentParent(parent), _paint(std::move(paint)) {
Expects(_paint != nullptr);
}
private:
void paintEvent(QPaintEvent *e) override {
_paint(e->rect());
}
Fn<void(QRect)> _paint;
};
struct PinButton;
[[nodiscard]] not_null<QWidget*> widget() const;
void setup(
rpl::producer<LargeVideoTrack> track,
rpl::producer<bool> pinned);
void setupControls(rpl::producer<bool> pinned);
void paint(QRect clip);
void paint(QRect clip, bool opengl);
void paintControls(Painter &p, QRect clip);
void updateControlsGeometry();
void togglePinShown(bool shown);
Content _content;
[[nodiscard]] Ui::GL::ChosenRenderer chooseRenderer(
Ui::GL::Capabilities capabilities);
const std::unique_ptr<Ui::RpWidgetWrap> _content;
const style::GroupCallLargeVideo &_st;
LargeVideoTrack _track;
QImage _shadow;

View file

@ -1263,11 +1263,9 @@ void Panel::setupPinnedVideo() {
raw->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::Enter) {
LOG(("Track Enter"));
Ui::Integration::Instance().registerLeaveSubscription(raw);
toggleWideControls(true);
} else if (e->type() == QEvent::Leave) {
LOG(("Track Leave"));
Ui::Integration::Instance().unregisterLeaveSubscription(raw);
toggleWideControls(false);
}
@ -1281,12 +1279,10 @@ void Panel::toggleWideControls(bool shown) {
return;
}
_showWideControls = shown;
LOG(("On Main Scheduled"));
crl::on_main(widget(), [=] {
if (_wideControlsShown == _showWideControls) {
return;
}
LOG(("On Main Fired: %1").arg(Logs::b(_showWideControls)));
_wideControlsShown = _showWideControls;
_wideControlsAnimation.start(
[=] { updateButtonsGeometry(); },
@ -1866,10 +1862,8 @@ void Panel::trackControls(bool track) {
raw->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) {
if (e->type() == QEvent::Enter) {
LOG(("Track Enter"));
toggleWideControls(true);
} else if (e->type() == QEvent::Leave) {
LOG(("Track Leave"));
toggleWideControls(false);
}
}, _trackControlsOverStateLifetime);

View file

@ -300,6 +300,8 @@ public:
[[nodiscard]] ImageLocation userpicLocation() const {
return _userpic.location();
}
static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL);
[[nodiscard]] bool userpicPhotoUnknown() const {
return (_userpicPhotoId == kUnknownPhotoId);
}
@ -411,8 +413,6 @@ private:
void setUserpicChecked(PhotoId photoId, const ImageLocation &location);
static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL);
const not_null<Data::Session*> _owner;
mutable Data::CloudImage _userpic;

View file

@ -474,7 +474,9 @@ void OverlayWidget::updateGeometry() {
const auto useSizeHack = (USE_OPENGL_OVERLAY_WIDGET
&& Platform::IsWindows());
const auto use = available.marginsAdded({ 0, 0, 0, 1 });
const auto mask = useSizeHack ? QRegion(available) : QRegion();
const auto mask = useSizeHack
? QRegion(QRect(QPoint(), available.size()))
: QRegion();
if ((geometry() == use)
&& (!useSizeHack || (window && window->mask() == mask))) {
return;

View file

@ -63,9 +63,9 @@ struct OverlayParentTraits : Ui::RpWidgetDefaultTraits {
};
#if USE_OPENGL_OVERLAY_WIDGET
using OverlayParent = Ui::RpWidgetWrap<QOpenGLWidget, OverlayParentTraits>;
using OverlayParent = Ui::RpWidgetBase<QOpenGLWidget, OverlayParentTraits>;
#else // USE_OPENGL_OVERLAY_WIDGET
using OverlayParent = Ui::RpWidgetWrap<QWidget, OverlayParentTraits>;
using OverlayParent = Ui::RpWidgetBase<QWidget, OverlayParentTraits>;
#endif // USE_OPENGL_OVERLAY_WIDGET
class OverlayWidget final

View file

@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/wrap/fade_wrap.h"
#include "ui/widgets/shadow.h"
#include "ui/text/format_values.h"
#include "ui/gl/gl_surface.h"
#include "window/window_controller.h"
#include "styles/style_widgets.h"
#include "styles/style_window.h"
@ -372,35 +373,77 @@ QImage RotateFrameImage(QImage image, int rotation) {
PipPanel::PipPanel(
QWidget *parent,
Fn<void(QPainter&, FrameRequest)> paint)
: _parent(parent)
Fn<void(QPainter&, FrameRequest, bool)> paint)
: _content(Ui::GL::CreateSurface(
parent,
[=](Ui::GL::Capabilities capabilities) {
return chooseRenderer(capabilities);
}))
, _parent(parent)
, _paint(std::move(paint)) {
}
Ui::GL::ChosenRenderer PipPanel::chooseRenderer(
Ui::GL::Capabilities capabilities) {
class Renderer : public Ui::GL::Renderer {
public:
Renderer(not_null<PipPanel*> owner) : _owner(owner) {
}
void paintFallback(
QPainter &&p,
const QRegion &clip,
Ui::GL::Backend backend) override {
_owner->paint(
p,
clip,
backend == Ui::GL::Backend::OpenGL);
}
private:
const not_null<PipPanel*> _owner;
};
return {
.renderer = std::make_unique<Renderer>(this),
.backend = (capabilities.supported
? Ui::GL::Backend::OpenGL
: Ui::GL::Backend::Raster),
};
}
void PipPanel::init() {
setWindowFlags(Qt::Tool
widget()->setWindowFlags(Qt::Tool
| Qt::WindowStaysOnTopHint
| Qt::FramelessWindowHint
| Qt::WindowDoesNotAcceptFocus);
setAttribute(Qt::WA_ShowWithoutActivating);
setAttribute(Qt::WA_MacAlwaysShowToolWindow);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TranslucentBackground);
Ui::Platform::IgnoreAllActivation(this);
Ui::Platform::InitOnTopPanel(this);
setMouseTracking(true);
resize(0, 0);
hide();
createWinId();
widget()->setAttribute(Qt::WA_ShowWithoutActivating);
widget()->setAttribute(Qt::WA_MacAlwaysShowToolWindow);
widget()->setAttribute(Qt::WA_NoSystemBackground);
widget()->setAttribute(Qt::WA_TranslucentBackground);
Ui::Platform::IgnoreAllActivation(widget());
Ui::Platform::InitOnTopPanel(widget());
widget()->setMouseTracking(true);
widget()->resize(0, 0);
widget()->hide();
widget()->createWinId();
rp()->shownValue(
) | rpl::filter([=](bool shown) {
return shown;
}) | rpl::start_with_next([=] {
// Workaround Qt's forced transient parent.
Ui::Platform::ClearTransientParent(widget());
}, rp()->lifetime());
}
void PipPanel::setVisibleHook(bool visible) {
PipParent::setVisibleHook(visible);
not_null<QWidget*> PipPanel::widget() const {
return _content->rpWidget();
}
// workaround Qt's forced transient parent
if (visible) {
Ui::Platform::ClearTransientParent(this);
}
not_null<Ui::RpWidgetWrap*> PipPanel::rp() const {
return _content.get();
}
void PipPanel::setAspectRatio(QSize ratio) {
@ -411,7 +454,7 @@ void PipPanel::setAspectRatio(QSize ratio) {
if (_ratio.isEmpty()) {
_ratio = QSize(1, 1);
}
if (!size().isEmpty()) {
if (!widget()->size().isEmpty()) {
setPosition(countPosition());
}
}
@ -429,7 +472,7 @@ void PipPanel::setPosition(Position position) {
}
QRect PipPanel::inner() const {
return rect().marginsRemoved(_padding);
return widget()->rect().marginsRemoved(_padding);
}
RectParts PipPanel::attached() const {
@ -452,7 +495,10 @@ rpl::producer<> PipPanel::saveGeometryRequests() const {
}
QScreen *PipPanel::myScreen() const {
return windowHandle() ? windowHandle()->screen() : nullptr;
if (const auto window = widget()->windowHandle()) {
return window->screen();
}
return nullptr;
}
PipPanel::Position PipPanel::countPosition() const {
@ -462,7 +508,7 @@ PipPanel::Position PipPanel::countPosition() const {
}
auto result = Position();
result.screen = screen->geometry();
result.geometry = geometry().marginsRemoved(_padding);
result.geometry = widget()->geometry().marginsRemoved(_padding);
const auto available = screen->availableGeometry();
const auto skip = st::pipBorderSkip;
const auto left = result.geometry.x();
@ -501,9 +547,9 @@ void PipPanel::setPositionDefault() {
return nullptr;
};
const auto parentScreen = widgetScreen(_parent);
const auto myScreen = widgetScreen(this);
const auto myScreen = widgetScreen(widget());
if (parentScreen && myScreen && myScreen != parentScreen) {
windowHandle()->setScreen(parentScreen);
widget()->windowHandle()->setScreen(parentScreen);
}
const auto screen = parentScreen
? parentScreen
@ -583,21 +629,19 @@ void PipPanel::setPositionOnScreen(Position position, QRect available) {
geometry += _padding;
setGeometry(geometry);
setMinimumSize(minimalSize);
setMaximumSize(
widget()->setGeometry(geometry);
widget()->setMinimumSize(minimalSize);
widget()->setMaximumSize(
std::max(minimalSize.width(), maximalSize.width()),
std::max(minimalSize.height(), maximalSize.height()));
updateDecorations();
update();
widget()->update();
}
void PipPanel::paintEvent(QPaintEvent *e) {
QPainter p(this);
if (_useTransparency && USE_OPENGL_PIP_WIDGET) {
void PipPanel::paint(QPainter &p, const QRegion &clip, bool opengl) {
if (_useTransparency && opengl) {
p.setCompositionMode(QPainter::CompositionMode_Source);
for (const auto rect : e->region()) {
for (const auto rect : clip) {
p.fillRect(rect, Qt::transparent);
}
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
@ -622,27 +666,27 @@ void PipPanel::paintEvent(QPaintEvent *e) {
request.radius = ImageRoundRadius::Large;
if (_useTransparency) {
const auto sides = RectPart::AllSides & ~_attached;
Ui::Shadow::paint(p, inner, width(), st::callShadow);
Ui::Shadow::paint(p, inner, widget()->width(), st::callShadow);
}
_paint(p, request);
_paint(p, request, opengl);
}
void PipPanel::mousePressEvent(QMouseEvent *e) {
if (e->button() != Qt::LeftButton) {
void PipPanel::handleMousePress(QPoint position, Qt::MouseButton button) {
if (button != Qt::LeftButton) {
return;
}
updateOverState(e->pos());
updateOverState(position);
_pressState = _overState;
_pressPoint = e->globalPos();
_pressPoint = QCursor::pos();
}
void PipPanel::mouseReleaseEvent(QMouseEvent *e) {
if (e->button() != Qt::LeftButton || !base::take(_pressState)) {
void PipPanel::handleMouseRelease(QPoint position, Qt::MouseButton button) {
if (button != Qt::LeftButton || !base::take(_pressState)) {
return;
} else if (!base::take(_dragState)) {
//playbackPauseResume();
} else {
finishDrag(e->globalPos());
finishDrag(QCursor::pos());
}
}
@ -656,26 +700,28 @@ void PipPanel::updateOverState(QPoint point) {
const auto top = count(RectPart::Top, _padding.top());
const auto right = count(RectPart::Right, _padding.right());
const auto bottom = count(RectPart::Bottom, _padding.bottom());
const auto width = widget()->width();
const auto height = widget()->height();
const auto overState = [&] {
if (point.x() < left) {
if (point.y() < top) {
return RectPart::TopLeft;
} else if (point.y() >= height() - bottom) {
} else if (point.y() >= height - bottom) {
return RectPart::BottomLeft;
} else {
return RectPart::Left;
}
} else if (point.x() >= width() - right) {
} else if (point.x() >= width - right) {
if (point.y() < top) {
return RectPart::TopRight;
} else if (point.y() >= height() - bottom) {
} else if (point.y() >= height - bottom) {
return RectPart::BottomRight;
} else {
return RectPart::Right;
}
} else if (point.y() < top) {
return RectPart::Top;
} else if (point.y() >= height() - bottom) {
} else if (point.y() >= height - bottom) {
return RectPart::Bottom;
} else {
return RectPart::Center;
@ -683,7 +729,7 @@ void PipPanel::updateOverState(QPoint point) {
}();
if (_overState != overState) {
_overState = overState;
setCursor([&] {
widget()->setCursor([&] {
switch (_overState) {
case RectPart::Center:
return style::cur_pointer;
@ -705,19 +751,19 @@ void PipPanel::updateOverState(QPoint point) {
}
}
void PipPanel::mouseMoveEvent(QMouseEvent *e) {
void PipPanel::handleMouseMove(QPoint position) {
if (!_pressState) {
updateOverState(e->pos());
updateOverState(position);
return;
}
const auto point = e->globalPos();
const auto point = QCursor::pos();
const auto distance = QApplication::startDragDistance();
if (!_dragState
&& (point - _pressPoint).manhattanLength() > distance
&& !_dragDisabled) {
_dragState = _pressState;
updateDecorations();
_dragStartGeometry = geometry().marginsRemoved(_padding);
_dragStartGeometry = widget()->geometry().marginsRemoved(_padding);
}
if (_dragState) {
if (Platform::IsWayland()) {
@ -733,9 +779,9 @@ void PipPanel::startSystemDrag() {
const auto stateEdges = RectPartToQtEdges(*_dragState);
if (stateEdges) {
windowHandle()->startSystemResize(stateEdges);
widget()->windowHandle()->startSystemResize(stateEdges);
} else {
windowHandle()->startSystemMove();
widget()->windowHandle()->startSystemMove();
}
}
@ -780,14 +826,14 @@ void PipPanel::processDrag(QPoint point) {
} else {
const auto newGeometry = valid.marginsAdded(_padding);
_positionAnimation.stop();
setGeometry(newGeometry);
widget()->setGeometry(newGeometry);
}
}
void PipPanel::finishDrag(QPoint point) {
const auto screen = ScreenFromPosition(point);
const auto inner = geometry().marginsRemoved(_padding);
const auto position = pos();
const auto inner = widget()->geometry().marginsRemoved(_padding);
const auto position = widget()->pos();
const auto clamped = [&] {
auto result = position;
if (Platform::IsWayland()) {
@ -818,7 +864,8 @@ void PipPanel::finishDrag(QPoint point) {
void PipPanel::updatePositionAnimated() {
const auto progress = _positionAnimation.value(1.);
if (!_positionAnimation.animating()) {
move(_positionAnimationTo - QPoint(_padding.left(), _padding.top()));
widget()->move(_positionAnimationTo
- QPoint(_padding.left(), _padding.top()));
if (!_dragState) {
updateDecorations();
}
@ -826,7 +873,7 @@ void PipPanel::updatePositionAnimated() {
}
const auto from = QPointF(_positionAnimationFrom);
const auto to = QPointF(_positionAnimationTo);
move((from + (to - from) * progress).toPoint()
widget()->move((from + (to - from) * progress).toPoint()
- QPoint(_padding.left(), _padding.top()));
}
@ -835,7 +882,8 @@ void PipPanel::moveAnimated(QPoint to) {
return;
}
_positionAnimationTo = to;
_positionAnimationFrom = pos() + QPoint(_padding.left(), _padding.top());
_positionAnimationFrom = widget()->pos()
+ QPoint(_padding.left(), _padding.top());
_positionAnimation.stop();
_positionAnimation.start(
[=] { updatePositionAnimated(); },
@ -868,9 +916,9 @@ void PipPanel::updateDecorations() {
_attached = position.attached;
_padding = padding;
_useTransparency = use;
setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);
setGeometry(newGeometry);
update();
widget()->setAttribute(Qt::WA_OpaquePaintEvent, !_useTransparency);
widget()->setGeometry(newGeometry);
widget()->update();
}
Pip::Pip(
@ -886,7 +934,9 @@ Pip::Pip(
, _instance(std::move(shared), [=] { waitingAnimationCallback(); })
, _panel(
_delegate->pipParentWidget(),
[=](QPainter &p, const FrameRequest &request) { paint(p, request); })
[=](QPainter &p, const FrameRequest &request, bool opengl) {
paint(p, request, opengl);
})
, _playbackProgress(std::make_unique<PlaybackProgress>())
, _rotation(data->owner().mediaRotation().get(data))
, _roundRect(ImageRoundRadius::Large, st::radialBg)
@ -899,7 +949,7 @@ Pip::Pip(
_data->session().account().sessionChanges(
) | rpl::start_with_next([=] {
_destroy();
}, _panel.lifetime());
}, _panel.rp()->lifetime());
}
Pip::~Pip() = default;
@ -920,14 +970,14 @@ void Pip::setupPanel() {
}();
_panel.setAspectRatio(FlipSizeByRotation(size, _rotation));
_panel.setPosition(Deserialize(_delegate->pipLoadGeometry()));
_panel.show();
_panel.widget()->show();
_panel.saveGeometryRequests(
) | rpl::start_with_next([=] {
saveGeometry();
}, _panel.lifetime());
}, _panel.rp()->lifetime());
_panel.events(
_panel.rp()->events(
) | rpl::start_with_next([=](not_null<QEvent*> e) {
const auto mousePosition = [&] {
return static_cast<QMouseEvent*>(e.get())->pos();
@ -951,11 +1001,11 @@ void Pip::setupPanel() {
handleDoubleClick(mouseButton());
break;
}
}, _panel.lifetime());
}, _panel.rp()->lifetime());
}
void Pip::handleClose() {
crl::on_main(&_panel, [=] {
crl::on_main(_panel.widget(), [=] {
_destroy();
});
}
@ -965,6 +1015,7 @@ void Pip::handleLeave() {
}
void Pip::handleMouseMove(QPoint position) {
_panel.handleMouseMove(position);
setOverState(computeState(position));
seekUpdate(position);
}
@ -978,7 +1029,7 @@ void Pip::setOverState(OverState state) {
const auto nowShown = (_over != OverState::None);
if ((was != OverState::None) != nowShown) {
_controlsShown.start(
[=] { _panel.update(); },
[=] { _panel.widget()->update(); },
nowShown ? 0. : 1.,
nowShown ? 1. : 0.,
st::fadeWrapDuration,
@ -987,7 +1038,7 @@ void Pip::setOverState(OverState state) {
if (!_pressed) {
updateActiveState(was);
}
_panel.update();
_panel.widget()->update();
}
void Pip::setPressedState(std::optional<OverState> state) {
@ -1012,7 +1063,7 @@ void Pip::updateActiveState(OverState was) {
const auto now = (activeState() == button.state);
if ((was == button.state) != now) {
button.active.start(
[=, &button] { _panel.update(button.icon); },
[=, &button] { _panel.widget()->update(button.icon); },
now ? 0. : 1.,
now ? 1. : 0.,
st::fadeWrapDuration,
@ -1026,6 +1077,7 @@ void Pip::updateActiveState(OverState was) {
}
void Pip::handleMousePress(QPoint position, Qt::MouseButton button) {
_panel.handleMousePress(position, button);
if (button != Qt::LeftButton) {
return;
}
@ -1037,6 +1089,7 @@ void Pip::handleMousePress(QPoint position, Qt::MouseButton button) {
}
void Pip::handleMouseRelease(QPoint position, Qt::MouseButton button) {
_panel.handleMouseRelease(position, button);
if (button != Qt::LeftButton) {
return;
}
@ -1053,7 +1106,7 @@ void Pip::handleMouseRelease(QPoint position, Qt::MouseButton button) {
_lastHandledPress = _over;
switch (_over) {
case OverState::Close: _panel.close(); break;
case OverState::Close: _panel.widget()->close(); break;
case OverState::Enlarge: _closeAndContinue(); break;
case OverState::Other: playbackPauseResume(); break;
}
@ -1120,7 +1173,7 @@ void Pip::setupButtons() {
_enlarge.state = OverState::Enlarge;
_playback.state = OverState::Playback;
_play.state = OverState::Other;
_panel.sizeValue(
_panel.rp()->sizeValue(
) | rpl::map([=] {
return _panel.inner();
}) | rpl::start_with_next([=](QRect rect) {
@ -1162,12 +1215,12 @@ void Pip::setupButtons() {
playbackHeight);
_playback.icon = _playback.area.marginsRemoved(
{ playbackSkip, playbackSkip, playbackSkip, playbackSkip });
}, _panel.lifetime());
}, _panel.rp()->lifetime());
_playbackProgress->setValueChangedCallback([=](
float64 value,
float64 receivedTill) {
_panel.update(_playback.area);
_panel.widget()->update(_playback.area);
});
}
@ -1179,7 +1232,7 @@ void Pip::updatePlayPauseResumeState(const Player::TrackState &state) {
auto showPause = Player::ShowPauseIcon(state.state);
if (showPause != _showPause) {
_showPause = showPause;
_panel.update();
_panel.widget()->update();
}
}
@ -1196,7 +1249,7 @@ void Pip::setupStreaming() {
updatePlaybackState();
}
void Pip::paint(QPainter &p, FrameRequest request) {
void Pip::paint(QPainter &p, FrameRequest request, bool opengl) {
const auto image = videoFrameForDirectPaint(
UnrotateRequest(request, _rotation));
const auto inner = _panel.inner();
@ -1204,7 +1257,7 @@ void Pip::paint(QPainter &p, FrameRequest request) {
inner.topLeft(),
request.outer / style::DevicePixelRatio()
};
if (UsePainterRotation(_rotation, USE_OPENGL_PIP_WIDGET)) {
if (UsePainterRotation(_rotation, opengl)) {
if (_rotation) {
p.save();
p.rotate(_rotation);
@ -1257,7 +1310,7 @@ void Pip::paintFade(QPainter &p) const {
void Pip::paintButtons(QPainter &p) const {
const auto opacity = p.opacity();
const auto outer = _panel.width();
const auto outer = _panel.widget()->width();
const auto drawOne = [&](
const Button &button,
const style::icon &icon,
@ -1344,7 +1397,7 @@ void Pip::handleStreamingUpdate(Streaming::Update &&update) {
}, [&](const PreloadedVideo &update) {
updatePlaybackState();
}, [&](const UpdateVideo &update) {
_panel.update();
_panel.widget()->update();
Core::App().updateNonIdle();
updatePlaybackState();
}, [&](const PreloadedAudio &update) {
@ -1399,7 +1452,7 @@ void Pip::updatePlaybackTexts(
_timeAlready = already;
_timeLeft = left;
_timeLeftWidth = st::pipPlaybackFont->width(_timeLeft);
_panel.update(QRect(
_panel.widget()->update(QRect(
_playback.area.x(),
_playback.icon.y() - st::pipPlaybackFont->height,
_playback.area.width(),
@ -1407,12 +1460,12 @@ void Pip::updatePlaybackTexts(
}
void Pip::handleStreamingError(Streaming::Error &&error) {
_panel.close();
_panel.widget()->close();
}
void Pip::playbackPauseResume() {
if (_instance.player().failed()) {
_panel.close();
_panel.widget()->close();
} else if (_instance.player().finished()
|| !_instance.player().active()) {
_startPaused = false;
@ -1602,7 +1655,7 @@ void Pip::paintRadialLoadingContent(QPainter &p, const QRect &inner) const {
_instance.waitingState(),
arc.topLeft(),
arc.size(),
_panel.width(),
_panel.widget()->width(),
st::radialFg,
st::radialLine);
}
@ -1632,7 +1685,7 @@ Pip::OverState Pip::computeState(QPoint position) const {
}
void Pip::waitingAnimationCallback() {
_panel.update(countRadialRect());
_panel.widget()->update(countRadialRect());
}
} // namespace View

View file

@ -22,6 +22,10 @@ namespace Ui {
class IconButton;
template <typename Widget>
class FadeWrap;
namespace GL {
struct ChosenRenderer;
struct Capabilities;
} // namespace GL
} // namespace Ui
namespace Media {
@ -38,19 +42,7 @@ class PlaybackProgress;
[[nodiscard]] QSize FlipSizeByRotation(QSize size, int rotation);
[[nodiscard]] QImage RotateFrameImage(QImage image, int rotation);
#if 1
#define USE_OPENGL_PIP_WIDGET 1
#else
#define USE_OPENGL_PIP_WIDGET 0
#endif // Q_OS_MAC && !OS_MAC_OLD
#if USE_OPENGL_PIP_WIDGET
using PipParent = Ui::RpWidgetWrap<QOpenGLWidget>;
#else // USE_OPENGL_PIP_WIDGET
using PipParent = Ui::RpWidget;
#endif // USE_OPENGL_PIP_WIDGET
class PipPanel final : public PipParent {
class PipPanel final {
public:
struct Position {
RectParts attached = RectPart(0);
@ -62,9 +54,12 @@ public:
PipPanel(
QWidget *parent,
Fn<void(QPainter&, FrameRequest)> paint);
Fn<void(QPainter&, FrameRequest, bool)> paint);
void init();
[[nodiscard]] not_null<QWidget*> widget() const;
[[nodiscard]] not_null<Ui::RpWidgetWrap*> rp() const;
void setAspectRatio(QSize ratio);
[[nodiscard]] Position countPosition() const;
void setPosition(Position position);
@ -73,17 +68,15 @@ public:
void setDragDisabled(bool disabled);
[[nodiscard]] bool dragging() const;
void handleMousePress(QPoint position, Qt::MouseButton button);
void handleMouseRelease(QPoint position, Qt::MouseButton button);
void handleMouseMove(QPoint position);
[[nodiscard]] rpl::producer<> saveGeometryRequests() const;
protected:
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void setVisibleHook(bool visible) override;
private:
void paint(QPainter &p, const QRegion &clip, bool opengl);
void setPositionDefault();
void setPositionOnScreen(Position position, QRect available);
@ -96,8 +89,12 @@ private:
void moveAnimated(QPoint to);
void updateDecorations();
[[nodiscard]] Ui::GL::ChosenRenderer chooseRenderer(
Ui::GL::Capabilities capabilities);
std::unique_ptr<Ui::RpWidgetWrap> _content;
QPointer<QWidget> _parent;
Fn<void(QPainter&, FrameRequest)> _paint;
Fn<void(QPainter&, FrameRequest, bool)> _paint;
RectParts _attached = RectParts();
RectParts _snapped = RectParts();
QSize _ratio;
@ -164,7 +161,7 @@ private:
void setupPanel();
void setupButtons();
void setupStreaming();
void paint(QPainter &p, FrameRequest request);
void paint(QPainter &p, FrameRequest request, bool opengl);
void playbackPauseResume();
void waitingAnimationCallback();
void handleStreamingUpdate(Streaming::Update &&update);

@ -1 +1 @@
Subproject commit e9fcbfcbacfe9f2f454a7bd34f1a3c5403245523
Subproject commit 95ee92088e62dfa20eb11d6fe59b0fb1834a1207