1 // This file is part of Desktop App Toolkit,
2 // a set of libraries for developing nice desktop applications.
3 //
4 // For license and copyright information please follow this link:
5 // https://github.com/desktop-app/legal/blob/master/LEGAL
6 //
7 #include "ui/gl/gl_surface.h"
8
9 #include "ui/rp_widget.h"
10 #include "ui/painter.h"
11
12 #include <QtGui/QtEvents>
13 #include <QtGui/QOpenGLContext>
14 #include <QtGui/QWindow>
15 #include <QtGui/QPaintEngine>
16 #include <QOpenGLWidget>
17
18 namespace Ui::GL {
19 namespace {
20
21 struct SurfaceTraits : RpWidgetDefaultTraits {
22 static constexpr bool kSetZeroGeometry = false;
23 };
24
25 class SurfaceOpenGL final
26 : public RpWidgetBase<QOpenGLWidget, SurfaceTraits> {
27 public:
28 SurfaceOpenGL(QWidget *parent, std::unique_ptr<Renderer> renderer);
29 ~SurfaceOpenGL();
30
31 private:
32 void initializeGL() override;
33 void resizeGL(int w, int h) override;
34 void paintEvent(QPaintEvent *e) override;
35 void callDeInit();
36
37 const std::unique_ptr<Renderer> _renderer;
38 QMetaObject::Connection _connection;
39
40 };
41
42 class SurfaceRaster final : public RpWidgetBase<QWidget, SurfaceTraits> {
43 public:
44 SurfaceRaster(QWidget *parent, std::unique_ptr<Renderer> renderer);
45
46 private:
47 void paintEvent(QPaintEvent *e) override;
48
49 const std::unique_ptr<Renderer> _renderer;
50
51 };
52
SurfaceOpenGL(QWidget * parent,std::unique_ptr<Renderer> renderer)53 SurfaceOpenGL::SurfaceOpenGL(
54 QWidget *parent,
55 std::unique_ptr<Renderer> renderer)
56 : RpWidgetBase<QOpenGLWidget, SurfaceTraits>(parent)
57 , _renderer(std::move(renderer)) {
58 }
59
~SurfaceOpenGL()60 SurfaceOpenGL::~SurfaceOpenGL() {
61 callDeInit();
62 }
63
initializeGL()64 void SurfaceOpenGL::initializeGL() {
65 Expects(window()->windowHandle() != nullptr);
66
67 if (_connection) {
68 QObject::disconnect(base::take(_connection));
69 }
70 const auto context = this->context();
71 _connection = QObject::connect(
72 context,
73 &QOpenGLContext::aboutToBeDestroyed,
74 [=] { callDeInit(); });
75 _renderer->init(this, *context->functions());
76 }
77
resizeGL(int w,int h)78 void SurfaceOpenGL::resizeGL(int w, int h) {
79 _renderer->resize(this, *context()->functions(), w, h);
80 }
81
paintEvent(QPaintEvent * e)82 void SurfaceOpenGL::paintEvent(QPaintEvent *e) {
83 if (!updatesEnabled() || size().isEmpty() || !isValid()) {
84 return;
85 }
86 auto redirectOffset = QPoint();
87 const auto rpd = redirected(&redirectOffset);
88 const auto device = rpd ? rpd : static_cast<QPaintDevice*>(this);
89 const auto engine = device->paintEngine();
90 if (!engine) {
91 return;
92 }
93 engine->begin(device);
94 if (!isValid()) { // The call above could lose the context.
95 return;
96 }
97 const auto f = context()->functions();
98 if (const auto bg = _renderer->clearColor()) {
99 f->glClearColor(bg->redF(), bg->greenF(), bg->blueF(), bg->alphaF());
100 f->glClear(GL_COLOR_BUFFER_BIT);
101 }
102 f->glDisable(GL_BLEND);
103 f->glViewport(0, 0, device->width(), device->height());
104 _renderer->paint(this, *f);
105 engine->end();
106 }
107
callDeInit()108 void SurfaceOpenGL::callDeInit() {
109 if (!_connection) {
110 return;
111 }
112 QObject::disconnect(base::take(_connection));
113 makeCurrent();
114 const auto context = this->context();
115 _renderer->deinit(
116 this,
117 ((isValid() && context && QOpenGLContext::currentContext() == context)
118 ? context->functions()
119 : nullptr));
120 }
121
SurfaceRaster(QWidget * parent,std::unique_ptr<Renderer> renderer)122 SurfaceRaster::SurfaceRaster(
123 QWidget *parent,
124 std::unique_ptr<Renderer> renderer)
125 : RpWidgetBase<QWidget, SurfaceTraits>(parent)
126 , _renderer(std::move(renderer)) {
127 }
128
paintEvent(QPaintEvent * e)129 void SurfaceRaster::paintEvent(QPaintEvent *e) {
130 _renderer->paintFallback(Painter(this), e->region(), Backend::Raster);
131 }
132
133 } // namespace
134
paint(not_null<QOpenGLWidget * > widget,QOpenGLFunctions & f)135 void Renderer::paint(
136 not_null<QOpenGLWidget*> widget,
137 QOpenGLFunctions &f) {
138 paintFallback(Painter(widget.get()), widget->rect(), Backend::OpenGL);
139 }
140
CreateSurface(Fn<ChosenRenderer (Capabilities)> chooseRenderer)141 std::unique_ptr<RpWidgetWrap> CreateSurface(
142 Fn<ChosenRenderer(Capabilities)> chooseRenderer) {
143 auto chosen = chooseRenderer(CheckCapabilities(nullptr));
144 switch (chosen.backend) {
145 case Backend::OpenGL:
146 return std::make_unique<SurfaceOpenGL>(
147 nullptr,
148 std::move(chosen.renderer));
149 case Backend::Raster:
150 return std::make_unique<SurfaceRaster>(
151 nullptr,
152 std::move(chosen.renderer));
153 }
154 Unexpected("Backend value in Ui::GL::CreateSurface.");
155 }
156
CreateSurface(QWidget * parent,ChosenRenderer chosen)157 std::unique_ptr<RpWidgetWrap> CreateSurface(
158 QWidget *parent,
159 ChosenRenderer chosen) {
160 switch (chosen.backend) {
161 case Backend::OpenGL:
162 return std::make_unique<SurfaceOpenGL>(
163 parent,
164 std::move(chosen.renderer));
165 case Backend::Raster:
166 return std::make_unique<SurfaceRaster>(
167 parent,
168 std::move(chosen.renderer));
169 }
170 Unexpected("Backend value in Ui::GL::CreateSurface.");
171 }
172
173 } // namespace Ui::GL
174