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