// This file is part of Desktop App Toolkit, // a set of libraries for developing nice desktop applications. // // For license and copyright information please follow this link: // https://github.com/desktop-app/legal/blob/master/LEGAL // #include "ui/gl/gl_surface.h" #include "ui/rp_widget.h" #include "ui/painter.h" #include #include #include #include namespace Ui::GL { namespace { struct SurfaceTraits : RpWidgetDefaultTraits { static constexpr bool kSetZeroGeometry = false; }; class SurfaceOpenGL final : public RpWidgetBase { public: SurfaceOpenGL(QWidget *parent, std::unique_ptr renderer); ~SurfaceOpenGL(); private: void initializeGL() override; void resizeGL(int w, int h) override; void paintEvent(QPaintEvent *e) override; void paintGL() override; void callDeInit(); const std::unique_ptr _renderer; QMetaObject::Connection _connection; QSize _deviceSize; bool _inPaintEvent = false; }; class SurfaceRaster final : public RpWidgetBase { public: SurfaceRaster(QWidget *parent, std::unique_ptr renderer); private: void paintEvent(QPaintEvent *e) override; const std::unique_ptr _renderer; }; SurfaceOpenGL::SurfaceOpenGL( QWidget *parent, std::unique_ptr renderer) : RpWidgetBase(parent) , _renderer(std::move(renderer)) { setUpdateBehavior(QOpenGLWidget::PartialUpdate); } SurfaceOpenGL::~SurfaceOpenGL() { callDeInit(); } void SurfaceOpenGL::initializeGL() { Expects(window()->windowHandle() != nullptr); if (_connection) { QObject::disconnect(base::take(_connection)); } const auto context = this->context(); _connection = QObject::connect( context, &QOpenGLContext::aboutToBeDestroyed, [=] { callDeInit(); }); _renderer->init(this, *context->functions()); } void SurfaceOpenGL::resizeGL(int w, int h) { _deviceSize = QSize(w, h) * devicePixelRatio(); _renderer->resize(this, *context()->functions(), w, h); } void SurfaceOpenGL::paintEvent(QPaintEvent *e) { if (_inPaintEvent) { return; } _inPaintEvent = true; if (_deviceSize != size() * devicePixelRatio()) { QResizeEvent event = { size(), size() }; resizeEvent(&event); } QOpenGLWidget::paintEvent(e); _inPaintEvent = false; } void SurfaceOpenGL::paintGL() { if (!updatesEnabled() || size().isEmpty() || !isValid()) { return; } const auto f = context()->functions(); if (const auto bg = _renderer->clearColor()) { f->glClearColor(bg->redF(), bg->greenF(), bg->blueF(), bg->alphaF()); f->glClear(GL_COLOR_BUFFER_BIT); } f->glDisable(GL_BLEND); _renderer->paint(this, *f); } void SurfaceOpenGL::callDeInit() { if (!_connection) { return; } QObject::disconnect(base::take(_connection)); makeCurrent(); const auto context = this->context(); _renderer->deinit( this, ((isValid() && context && QOpenGLContext::currentContext() == context) ? context->functions() : nullptr)); } SurfaceRaster::SurfaceRaster( QWidget *parent, std::unique_ptr renderer) : RpWidgetBase(parent) , _renderer(std::move(renderer)) { } void SurfaceRaster::paintEvent(QPaintEvent *e) { _renderer->paintFallback(Painter(this), e->region(), Backend::Raster); } } // namespace void Renderer::paint( not_null widget, QOpenGLFunctions &f) { paintFallback(Painter(widget.get()), widget->rect(), Backend::OpenGL); } std::unique_ptr CreateSurface( Fn chooseRenderer) { auto chosen = chooseRenderer(CheckCapabilities(nullptr)); switch (chosen.backend) { case Backend::OpenGL: return std::make_unique( nullptr, std::move(chosen.renderer)); case Backend::Raster: return std::make_unique( nullptr, std::move(chosen.renderer)); } Unexpected("Backend value in Ui::GL::CreateSurface."); } std::unique_ptr CreateSurface( QWidget *parent, ChosenRenderer chosen) { switch (chosen.backend) { case Backend::OpenGL: return std::make_unique( parent, std::move(chosen.renderer)); case Backend::Raster: return std::make_unique( parent, std::move(chosen.renderer)); } Unexpected("Backend value in Ui::GL::CreateSurface."); } } // namespace Ui::GL