• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Stellarium / stellarium / 3996069357

pending completion
3996069357

push

github

Ruslan Kabatsayev
Shorten some lines

5 of 5 new or added lines in 1 file covered. (100.0%)

14663 of 124076 relevant lines covered (11.82%)

22035.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/StelMainView.cpp
1
/*
2
 * Stellarium
3
 * Copyright (C) 2007 Fabien Chereau
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
18
 */
19

20
#define MOUSE_TRACKING
21

22
#include "StelMainView.hpp"
23
#include "StelApp.hpp"
24
#include "StelCore.hpp"
25
#include "StelFileMgr.hpp"
26
#include "StelProjector.hpp"
27
#include "StelPainter.hpp"
28
#include "StelGui.hpp"
29
#include "SkyGui.hpp"
30
#include "StelTranslator.hpp"
31
#include "StelUtils.hpp"
32
#include "StelActionMgr.hpp"
33
#include "StelOpenGL.hpp"
34
#include "StelOpenGLArray.hpp"
35
#include "StelProjector.hpp"
36

37
#include <QDebug>
38
#include <QDir>
39
#include <QOpenGLWidget>
40
#include <QApplication>
41
#if (QT_VERSION<QT_VERSION_CHECK(5,12,0))
42
#include <QDesktopWidget>
43
#endif
44
#include <QGuiApplication>
45
#include <QGraphicsSceneMouseEvent>
46
#include <QGraphicsAnchorLayout>
47
#include <QGraphicsWidget>
48
#include <QGraphicsEffect>
49
#include <QFileInfo>
50
#include <QIcon>
51
#include <QImageWriter>
52
#include <QMoveEvent>
53
#include <QPluginLoader>
54
#include <QScreen>
55
#include <QSettings>
56
#include <QRegularExpression>
57
#include <QtPlugin>
58
#include <QThread>
59
#include <QTimer>
60
#include <QWidget>
61
#include <QWindow>
62
#include <QMessageBox>
63
#include <QStandardPaths>
64
#ifdef Q_OS_WIN
65
        #include <QPinchGesture>
66
#endif
67
#include <QOpenGLShader>
68
#include <QOpenGLShaderProgram>
69
#include <QOpenGLFramebufferObject>
70
#include <QOpenGLPaintDevice>
71
#ifdef OPENGL_DEBUG_LOGGING
72
#include <QOpenGLDebugLogger>
73
#endif
74
#include <QLoggingCategory>
75

76
Q_LOGGING_CATEGORY(mainview, "stel.MainView")
×
77

78
#include <clocale>
79

80
#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY
81
# define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF
82
#endif
83

84
// Initialize static variables
85
StelMainView* StelMainView::singleton = Q_NULLPTR;
86

87
class StelGLWidget : public QOpenGLWidget
88
{
89
public:
90
        StelGLWidget(const QSurfaceFormat& fmt, StelMainView* parent)
×
91
                :
×
92
                  QOpenGLWidget(parent),
93
                  parent(parent),
×
94
                  initialized(false)
×
95
        {
96
                qDebug()<<"StelGLWidget constructor";
×
97
                setFormat(fmt);
×
98

99
                //because we always draw the full background,
100
                //lets skip drawing the system background
101
                setAttribute(Qt::WA_OpaquePaintEvent);
×
102
                setAttribute(Qt::WA_AcceptTouchEvents);
×
103
                setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
×
104
                setAutoFillBackground(false);
×
105
        }
×
106

107
        ~StelGLWidget() Q_DECL_OVERRIDE
×
108
        {
×
109
                qDebug()<<"StelGLWidget destroyed";
×
110
        }
×
111

112
        virtual void initializeGL() Q_DECL_OVERRIDE
×
113
        {
114
                if(initialized)
×
115
                {
116
                        qWarning()<<"Double initialization, should not happen";
×
117
                        Q_ASSERT(false);
×
118
                        return;
119
                }
120

121
                //This seems to be the correct place to initialize all
122
                //GL related stuff of the application
123
                //this includes all the init() calls of the modules
124

125
                QOpenGLContext* ctx = context();
×
126
                Q_ASSERT(ctx == QOpenGLContext::currentContext());
×
127
                StelOpenGL::mainContext = ctx; //throw an error when StelOpenGL functions are executed in another context
×
128

129
                qDebug().nospace() << "initializeGL(windowWidth = " << width() << ", windowHeight = " << height() << ")";
×
130
                qDebug() << "OpenGL supported version: " << QString(reinterpret_cast<const char*>(ctx->functions()->glGetString(GL_VERSION)));
×
131
                qDebug() << "Current Format: " << this->format();
×
132

133
                if (qApp->property("onetime_opengl_compat").toBool())
×
134
                {
135
                        // This may not return the version number set previously!
136
                        qDebug() << "StelGLWidget context format version:" << ctx->format().majorVersion() << "." << context()->format().minorVersion();
×
137
                        qDebug() << "StelGLWidget has CompatibilityProfile:" << (ctx->format().profile()==QSurfaceFormat::CompatibilityProfile ? "yes" : "no") << "(" <<context()->format().profile() << ")";
×
138
                }
139

140
                parent->init();
×
141
                initialized = true;
×
142
        }
143

144
protected:
145
        virtual void paintGL() Q_DECL_OVERRIDE
×
146
        {
147
                //this is actually never called because the
148
                //QGraphicsView intercepts the paint event
149
                //we have to draw in the background of the scene
150
                //or as a QGraphicsItem
151
                qDebug()<<"paintGL";
×
152
        }
×
153
        virtual void resizeGL(int w, int h) Q_DECL_OVERRIDE
×
154
        {
155
                //we probably can ignore this method,
156
                //it seems it is also never called
157
                qDebug()<<"resizeGL"<<w<<h;
×
158
        }
×
159

160
private:
161
        StelMainView* parent;
162
        bool initialized;
163
};
164

165
// A custom QGraphicsEffect to apply the night mode on top of the screen.
166
class NightModeGraphicsEffect : public QGraphicsEffect
167
{
168
public:
169
        NightModeGraphicsEffect(StelMainView* parent = Q_NULLPTR)
×
170
                : QGraphicsEffect(parent),
×
171
                  parent(parent), fbo(Q_NULLPTR),
×
172
                  vbo(QOpenGLBuffer::VertexBuffer)
×
173
        {
174
                Q_ASSERT(parent->glContext() == QOpenGLContext::currentContext());
×
175

176
                program = new QOpenGLShaderProgram(this);
×
177
                const auto vertexCode = StelOpenGL::globalShaderPrefix(StelOpenGL::VERTEX_SHADER) +
×
178
                                "ATTRIBUTE highp vec4 a_pos;\n"
179
                                "ATTRIBUTE highp vec2 a_texCoord;\n"
180
                                "VARYING highp   vec2 v_texCoord;\n"
181
                                "void main(void)\n"
182
                                "{\n"
183
                                "v_texCoord = a_texCoord;\n"
184
                                "gl_Position = a_pos;\n"
185
                                "}\n";
×
186
                const auto fragmentCode = StelOpenGL::globalShaderPrefix(StelOpenGL::FRAGMENT_SHADER) +
×
187
                                "VARYING highp vec2 v_texCoord;\n"
188
                                "uniform sampler2D  u_source;\n"
189
                                "void main(void)\n"
190
                                "{\n"
191
                                "        mediump vec3 color = texture2D(u_source, v_texCoord).rgb;\n"
192
                                "        mediump float luminance = max(max(color.r, color.g), color.b);\n"
193
                                "        FRAG_COLOR = vec4(luminance, luminance * 0.3, 0.0, 1.0);\n"
194
                                "}\n";
×
195
                program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode);
×
196
                program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode);
×
197
                program->link();
×
198
                vars.pos = program->attributeLocation("a_pos");
×
199
                vars.texCoord = program->attributeLocation("a_texCoord");
×
200
                vars.source = program->uniformLocation("u_source");
×
201

202
                vbo.create();
×
203
                struct VBOData
204
                {
205
                        const GLfloat pos[8] = {-1, -1, +1, -1, -1, +1, +1, +1};
206
                        const GLfloat texCoord[8] = {0, 0, 1, 0, 0, 1, 1, 1};
207
                } vboData;
×
208
                posOffset = offsetof(VBOData, pos);
×
209
                texCoordOffset = offsetof(VBOData, texCoord);
×
210
                vbo.bind();
×
211
                vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
×
212
                vbo.allocate(&vboData.pos, sizeof vboData);
×
213
                if(vao.create())
×
214
                {
215
                        vao.bind();
×
216
                        setupCurrentVAO();
×
217
                        vao.release();
×
218
                }
219
                vbo.release();
×
220
        }
×
221

222
        virtual ~NightModeGraphicsEffect() Q_DECL_OVERRIDE
×
223
        {
×
224
                // NOTE: Why Q_ASSERT is here?
225
                //Q_ASSERT(parent->glContext() == QOpenGLContext::currentContext());
226
                //clean up fbo
227
                delete fbo;
×
228
        }
×
229
protected:
230
        virtual void draw(QPainter* painter) Q_DECL_OVERRIDE
×
231
        {
232
                Q_ASSERT(parent->glContext() == QOpenGLContext::currentContext());
×
233
                QOpenGLFunctions* gl = QOpenGLContext::currentContext()->functions();
×
234

235
                QPaintDevice* paintDevice = painter->device();
×
236

237
                int mainFBO;
238
                gl->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mainFBO);
×
239

240
                double pixelRatio = paintDevice->devicePixelRatioF();
×
241
                QSize size(static_cast<int>(paintDevice->width() * pixelRatio), static_cast<int>(paintDevice->height() * pixelRatio));
×
242
                if (fbo && fbo->size() != size)
×
243
                {
244
                        delete fbo;
×
245
                        fbo = Q_NULLPTR;
×
246
                }
247
                if (!fbo)
×
248
                {
249
                        QOpenGLFramebufferObjectFormat format;
×
250
                        format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
×
251
                        format.setInternalTextureFormat(GL_RGBA);
×
252
                        fbo = new QOpenGLFramebufferObject(size, format);
×
253
                }
×
254

255
                // we have to use our own paint device
256
                // we need this because using the original paint device (QOpenGLWidgetPaintDevice when used with QOpenGLWidget) will rebind the default FBO randomly
257
                // but using 2 GL painters at the same time can mess up the GL state, so we should close the old one first
258

259
                // stop drawing to the old paint device to make sure state is reset correctly
260
                painter->end();
×
261

262
                // create our paint device
263
                QOpenGLPaintDevice fboPaintDevice(size);
×
264
                fboPaintDevice.setDevicePixelRatio(pixelRatio);
×
265

266
                fbo->bind();
×
267
                painter->begin(&fboPaintDevice);
×
268
                drawSource(painter);
×
269
                painter->end();
×
270

271
                painter->begin(paintDevice);
×
272

273
                bindVAO();
×
274
                //painter->beginNativePainting();
275
                program->bind();
×
276
                program->setUniformValue(vars.source, 0);
×
277
                gl->glBindTexture(GL_TEXTURE_2D, fbo->texture());
×
278
                gl->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
×
279
                program->release();
×
280
                //painter->endNativePainting();
281
                releaseVAO();
×
282
        }
×
283

284
        //! Binds actual VAO if it's supported, sets up the relevant state manually otherwise.
285
        void bindVAO()
×
286
        {
287
                if(vao.isCreated())
×
288
                        vao.bind();
×
289
                else
290
                        setupCurrentVAO();
×
291
        }
×
292
        //! Sets the vertex attribute states for the currently bound VAO so that glDraw* commands can work.
293
        void setupCurrentVAO()
×
294
        {
295
                vbo.bind();
×
296
                program->setAttributeBuffer(vars.pos, GL_FLOAT, posOffset, 2, 0);
×
297
                program->setAttributeBuffer(vars.texCoord, GL_FLOAT, texCoordOffset, 2, 0);
×
298
                vbo.release();
×
299
                program->enableAttributeArray(vars.pos);
×
300
                program->enableAttributeArray(vars.texCoord);
×
301
        }
×
302
        //! Binds zero VAO if VAO is supported, manually disables the relevant vertex attributes otherwise.
303
        void releaseVAO()
×
304
        {
305
                if(vao.isCreated())
×
306
                {
307
                        vao.release();
×
308
                }
309
                else
310
                {
311
                        program->disableAttributeArray(vars.pos);
×
312
                        program->disableAttributeArray(vars.texCoord);
×
313
                }
314
        }
×
315

316
private:
317
        StelMainView* parent;
318
        QOpenGLFramebufferObject* fbo;
319
        QOpenGLShaderProgram *program;
320
        struct {
321
                int pos;
322
                int texCoord;
323
                int source;
324
        } vars;
325
        int posOffset, texCoordOffset;
326
        QOpenGLVertexArrayObject vao;
327
        QOpenGLBuffer vbo;
328
};
329

330
class StelGraphicsScene : public QGraphicsScene
331
{
332
public:
333
        StelGraphicsScene(StelMainView* parent)
×
334
                : QGraphicsScene(parent), parent(parent)
×
335
        {
336
                qDebug()<<"StelGraphicsScene constructor";
×
337
        }
×
338

339
protected:
340
        void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE
×
341
        {
342
                // Try to trigger a global shortcut.
343
                StelActionMgr* actionMgr = StelApp::getInstance().getStelActionManager();
×
344
                if (actionMgr->pushKey(event->key() + int(event->modifiers()), true)) {
×
345
                        event->setAccepted(true);
×
346
                        parent->thereWasAnEvent(); // Refresh screen ASAP
×
347
                        return;
×
348
                }
349
                //pass event on to items otherwise
350
                QGraphicsScene::keyPressEvent(event);
×
351
        }
352

353
private:
354
        StelMainView* parent;
355
};
356

357
class StelRootItem : public QGraphicsObject
358
{
359
public:
360
        StelRootItem(StelMainView* mainView, QGraphicsItem* parent = Q_NULLPTR)
×
361
                : QGraphicsObject(parent),
×
362
                  mainView(mainView),
×
363
                  skyBackgroundColor(0.f,0.f,0.f)
×
364
        {
365
                setFlag(QGraphicsItem::ItemClipsToShape);
×
366
                setFlag(QGraphicsItem::ItemClipsChildrenToShape);
×
367
                setFlag(QGraphicsItem::ItemIsFocusable);
×
368

369
                setAcceptHoverEvents(true);
×
370

371
#ifdef Q_OS_WIN
372
                setAcceptTouchEvents(true);
373
                grabGesture(Qt::PinchGesture);
374
#endif
375
                setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton);
×
376
                previousPaintTime = StelApp::getTotalRunTime();
×
377
        }
×
378

379
        void setSize(const QSize& size)
×
380
        {
381
                prepareGeometryChange();
×
382
                rect.setSize(size);
×
383
        }
×
384

385
        //! Set the sky background color. Everything else than black creates a work of art!
386
        void setSkyBackgroundColor(Vec3f color) { skyBackgroundColor=color; }
×
387

388
        //! Get the sky background color. Everything else than black creates a work of art!
389
        Vec3f getSkyBackgroundColor() const { return skyBackgroundColor; }
×
390

391

392
protected:
393
        virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE
×
394
        {
395
                Q_UNUSED(option)
396
                Q_UNUSED(widget)
397

398
                //a sanity check
399
                Q_ASSERT(mainView->glContext() == QOpenGLContext::currentContext());
×
400

401
                const double now = StelApp::getTotalRunTime();
×
402
                double dt = now - previousPaintTime;
×
403
                //qDebug()<<"dt"<<dt;
404
                previousPaintTime = now;
×
405

406
                //important to call this, or Qt may have invalid state after we have drawn (wrong textures, etc...)
407
                painter->beginNativePainting();
×
408

409
                //fix for bug LP:1628072 caused by QTBUG-56798
410
#ifndef QT_NO_DEBUG
411
                StelOpenGL::clearGLErrors();
×
412
#endif
413

414
                //update and draw
415
                StelApp& app = StelApp::getInstance();
×
416
                app.update(dt); // may also issue GL calls
×
417
                app.draw();
×
418
                painter->endNativePainting();
×
419

420
                mainView->drawEnded();
×
421
        }
×
422

423
        virtual QRectF boundingRect() const Q_DECL_OVERRIDE
×
424
        {
425
                return rect;
×
426
        }
427

428
        //*** Main event handlers to pass on to StelApp ***//
429
        void mousePressEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE
×
430
        {
431
                QMouseEvent ev = convertMouseEvent(event);
×
432
                StelApp::getInstance().handleClick(&ev);
×
433
                event->setAccepted(ev.isAccepted());
×
434
                if(ev.isAccepted())
×
435
                        mainView->thereWasAnEvent();
×
436
        }
×
437

438
        void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE
×
439
        {
440
                QMouseEvent ev = convertMouseEvent(event);
×
441
                StelApp::getInstance().handleClick(&ev);
×
442
                event->setAccepted(ev.isAccepted());
×
443
                if(ev.isAccepted())
×
444
                        mainView->thereWasAnEvent();
×
445
        }
×
446

447
#ifndef MOUSE_TRACKING
448
        void mouseMoveEvent(QGraphicsSceneMouseEvent *event) Q_DECL_OVERRIDE
449
        {
450
                QMouseEvent ev = convertMouseEvent(event);
451
                QPointF pos = ev.pos();
452
                event->setAccepted(StelApp::getInstance().handleMove(pos.x(), pos.y(), ev.buttons()));
453
                if(event->isAccepted())
454
                        mainView->thereWasAnEvent();
455
        }
456
#endif
457

458
        void wheelEvent(QGraphicsSceneWheelEvent *event) Q_DECL_OVERRIDE
×
459
        {
460
                QPointF pos = event->scenePos();
×
461
                pos.setY(rect.height() - 1 - pos.y());
×
462
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
463
                QWheelEvent newEvent(pos, event->screenPos(), QPoint(event->delta(), 0), QPoint(event->delta(), 0), event->buttons(), event->modifiers(), Qt::ScrollUpdate, false);
×
464
#else
465
                QWheelEvent newEvent(QPoint(static_cast<int>(pos.x()),static_cast<int>(pos.y())), event->delta(), event->buttons(), event->modifiers(), event->orientation());
466
#endif
467
                StelApp::getInstance().handleWheel(&newEvent);
×
468
                event->setAccepted(newEvent.isAccepted());
×
469
                if(newEvent.isAccepted())
×
470
                        mainView->thereWasAnEvent();
×
471
        }
×
472

473
        void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE
×
474
        {
475
                StelApp::getInstance().handleKeys(event);
×
476
                if(event->isAccepted())
×
477
                        mainView->thereWasAnEvent();
×
478
        }
×
479

480
        void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE
×
481
        {
482
                StelApp::getInstance().handleKeys(event);
×
483
                if(event->isAccepted())
×
484
                        mainView->thereWasAnEvent();
×
485
        }
×
486

487
        //*** Gesture and touch support, currently only for Windows
488
#ifdef Q_OS_WIN
489
        bool event(QEvent * e) Q_DECL_OVERRIDE
490
        {
491
                bool r = false;
492
                switch (e->type()){
493
                        case QEvent::TouchBegin:
494
                        case QEvent::TouchUpdate:
495
                        case QEvent::TouchEnd:
496
                        {
497
                                QTouchEvent *touchEvent = static_cast<QTouchEvent *>(e);
498
                                QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
499

500
                                if (touchPoints.count() == 1)
501
                                        setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton);
502

503
                                r = true;
504
                                break;
505
                        }
506
                        case QEvent::Gesture:
507
                                setAcceptedMouseButtons(Qt::NoButton);
508
                                r = gestureEvent(static_cast<QGestureEvent*>(e));
509
                                break;
510
                        default:
511
                                r = QGraphicsObject::event(e);
512
                }
513
                return r;
514
        }
515

516
private:
517
        bool gestureEvent(QGestureEvent *event)
518
        {
519
                if (QGesture *pinch = event->gesture(Qt::PinchGesture))
520
                        pinchTriggered(static_cast<QPinchGesture *>(pinch));
521

522
                return true;
523
        }
524

525
        void pinchTriggered(QPinchGesture *gesture)
526
        {
527
                QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
528
                if (changeFlags & QPinchGesture::ScaleFactorChanged) {
529
                        qreal zoom = gesture->scaleFactor();
530

531
                        if (zoom < 2 && zoom > 0.5){
532
                                StelApp::getInstance().handlePinch(zoom, true);
533
                        }
534
                }
535
        }
536
#endif
537

538
private:
539
        //! Helper function to convert a QGraphicsSceneMouseEvent to a QMouseEvent suitable for StelApp consumption
540
        QMouseEvent convertMouseEvent(QGraphicsSceneMouseEvent *event) const
×
541
        {
542
                //convert graphics scene mouse event to widget style mouse event
543
                QEvent::Type t = QEvent::None;
×
544
                switch(event->type())
×
545
                {
546
                        case QEvent::GraphicsSceneMousePress:
×
547
                                t = QEvent::MouseButtonPress;
×
548
                                break;
×
549
                        case QEvent::GraphicsSceneMouseRelease:
×
550
                                t = QEvent::MouseButtonRelease;
×
551
                                break;
×
552
                        case QEvent::GraphicsSceneMouseMove:
×
553
                                t = QEvent::MouseMove;
×
554
                                break;
×
555
                        case QEvent::GraphicsSceneMouseDoubleClick:
×
556
                                //note: the old code seems to have ignored double clicks
557
                                // and handled them the same as normal mouse presses
558
                                //if we ever want to handle double clicks, switch out these lines
559
                                t = QEvent::MouseButtonDblClick;
×
560
                                //t = QEvent::MouseButtonPress;
561
                                break;
×
562
                        default:
×
563
                                //warn in release and assert in debug
564
                                qWarning("Unhandled mouse event type %d",event->type());
×
565
                                Q_ASSERT(false);
×
566
                }
567

568
                QPointF pos = event->scenePos();
×
569
                //Y needs to be inverted
570
                pos.setY(rect.height() - 1 - pos.y());
×
571
                return QMouseEvent(t,pos,event->button(),event->buttons(),event->modifiers());
×
572
        }
573

574
        QRectF rect;
575
        double previousPaintTime;
576
        StelMainView* mainView;
577
        Vec3f skyBackgroundColor;           //! color which is used to initialize the frame. Should be black, but for some applications e.g. dark blue may be preferred.
578
};
579

580
//! Initialize and render Stellarium gui.
581
class StelGuiItem : public QGraphicsWidget
582
{
583
public:
584
        StelGuiItem(const QSize& size, QGraphicsItem* parent = Q_NULLPTR)
×
585
                : QGraphicsWidget(parent)
×
586
        {
587
                resize(size);
×
588
                StelApp::getInstance().getGui()->init(this);
×
589
                inited=true;
×
590
        }
×
591

592
protected:
593
        void resizeEvent(QGraphicsSceneResizeEvent* event) Q_DECL_OVERRIDE
×
594
        {
595
                if(!inited) return;
×
596

597
                Q_UNUSED(event)
598
                //widget->setGeometry(0, 0, size().width(), size().height());
599
                StelApp::getInstance().getGui()->forceRefreshGui();
×
600
        }
601
private:
602
        //QGraphicsWidget *widget;
603
        // void onSizeChanged();
604
        bool inited = false; // guards resize during construction
605
};
606

607
StelMainView::StelMainView(QSettings* settings)
×
608
        : QGraphicsView(),
609
          configuration(settings),
×
610
          guiItem(Q_NULLPTR),
×
611
          gui(Q_NULLPTR),
×
612
          stelApp(Q_NULLPTR),
×
613
          updateQueued(false),
×
614
          flagInvertScreenShotColors(false),
×
615
          flagScreenshotDateFileName(false),
×
616
          flagOverwriteScreenshots(false),
×
617
          flagUseCustomScreenshotSize(false),
×
618
          customScreenshotWidth(1024),
×
619
          customScreenshotHeight(768),
×
620
          screenshotDpi(72),
×
621
          customScreenshotMagnification(1.0f),
×
622
          screenShotPrefix("stellarium-"),
×
623
          screenShotFormat("png"),
×
624
          screenShotFileMask("yyyyMMdd-hhmmssz"),
×
625
          screenShotDir(""),
×
626
          flagCursorTimeout(false),
×
627
          lastEventTimeSec(0.0),
×
628
          minfps(1.f),
×
629
          maxfps(10000.f)
×
630
{
631
        setAttribute(Qt::WA_OpaquePaintEvent);
×
632
        setAttribute(Qt::WA_AcceptTouchEvents);
×
633
        setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
×
634
        setAutoFillBackground(false);
×
635
        setMouseTracking(true);
×
636

637
        StelApp::initStatic();
×
638

639
        fpsTimer = new QTimer(this);
×
640
        fpsTimer->setTimerType(Qt::PreciseTimer);
×
641
        fpsTimer->setInterval(qRound(1000.f/minfps));
×
642
        connect(fpsTimer,SIGNAL(timeout()),this,SLOT(fpsTimerUpdate()));
×
643

644
        cursorTimeoutTimer = new QTimer(this);
×
645
        cursorTimeoutTimer->setSingleShot(true);
×
646
        connect(cursorTimeoutTimer, SIGNAL(timeout()), this, SLOT(hideCursor()));
×
647

648
        // Can't create 2 StelMainView instances
649
        Q_ASSERT(!singleton);
×
650
        singleton = this;
×
651

652
        qApp->installEventFilter(this);
×
653

654
        setWindowIcon(QIcon(":/mainWindow/icon.bmp"));
×
655
        initTitleI18n();
×
656
        setObjectName("MainView");
×
657

658
        setViewportUpdateMode(QGraphicsView::NoViewportUpdate);
×
659
        setFrameShape(QFrame::NoFrame);
×
660
        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
×
661
        setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
×
662
        //because we only want child elements to have focus, we turn it off here
663
        setFocusPolicy(Qt::NoFocus);
×
664
        connect(this, SIGNAL(screenshotRequested()), this, SLOT(doScreenshot()));
×
665

666
#ifdef OPENGL_DEBUG_LOGGING
667
        if (QApplication::testAttribute(Qt::AA_UseOpenGLES))
668
        {
669
                // QOpenGLDebugLogger doesn't work with OpenGLES's GL_KHR_debug.
670
                // See Qt Bug 62070: https://bugreports.qt.io/browse/QTBUG-62070
671

672
                glLogger = Q_NULLPTR;
673
        }
674
        else
675
        {
676
                glLogger = new QOpenGLDebugLogger(this);
677
                connect(glLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this, SLOT(logGLMessage(QOpenGLDebugMessage)));
678
        }
679
#endif
680

681
        QSurfaceFormat glFormat = getDesiredGLFormat(configuration);
×
682
        glWidget = new StelGLWidget(glFormat, this);
×
683
        setViewport(glWidget);
×
684

685
        stelScene = new StelGraphicsScene(this);
×
686
        setScene(stelScene);
×
687
        scene()->setItemIndexMethod(QGraphicsScene::NoIndex);
×
688
        rootItem = new StelRootItem(this);
×
689

690
        // Workaround (see Bug #940638) Although we have already explicitly set
691
        // LC_NUMERIC to "C" in main.cpp there seems to be a bug in OpenGL where
692
        // it will silently reset LC_NUMERIC to the value of LC_ALL during OpenGL
693
        // initialization. This has been observed on Ubuntu 11.10 under certain
694
        // circumstances, so here we set it again just to be on the safe side.
695
        setlocale(LC_NUMERIC, "C");
×
696
        // End workaround
697

698
        // We cannot use global mousetracking. Only if mouse is hidden!
699
        //setMouseTracking(true);
700

701
    setRenderHint(QPainter::Antialiasing);
×
702
}
×
703

704
void StelMainView::resizeEvent(QResizeEvent* event)
×
705
{
706
        if(scene())
×
707
        {
708
                const QSize& sz = event->size();
×
709
                scene()->setSceneRect(QRect(QPoint(0, 0), sz));
×
710
                rootItem->setSize(sz);
×
711
                if(guiItem)
×
712
                        guiItem->setGeometry(QRectF(0.0,0.0,sz.width(),sz.height()));
×
713
                emit sizeChanged(sz);
×
714
        }
715
        QGraphicsView::resizeEvent(event);
×
716
}
×
717

718
bool StelMainView::eventFilter(QObject *obj, QEvent *event)
×
719
{
720
        if(event->type() == QEvent::FileOpen)
×
721
        {
722
                QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
×
723
                //qDebug() << "load script:" << openEvent->file();
724
                qApp->setProperty("onetime_startup_script", openEvent->file());
×
725
        }
726
        return QGraphicsView::eventFilter(obj, event);
×
727
}
728

729
void StelMainView::mouseMoveEvent(QMouseEvent *event)
×
730
{
731
        if (flagCursorTimeout) 
×
732
        {
733
                // Show the previous cursor and reset the timeout if the current is "hidden"
734
                if (QGuiApplication::overrideCursor() && (QGuiApplication::overrideCursor()->shape() == Qt::BlankCursor) )
×
735
                {
736
                        QGuiApplication::restoreOverrideCursor();
×
737
                }
738

739
                cursorTimeoutTimer->start();
×
740
        }
741
        
742
#ifdef MOUSE_TRACKING
743
        QPointF pos = event->pos();
×
744
        //Y needs to be inverted
745
        int height1 = StelApp::getInstance().mainWin->height();
×
746
        pos.setY(height1 - 1 - pos.y());
×
747
        event->setAccepted(StelApp::getInstance().handleMove(pos.x(), pos.y(), event->buttons()));
×
748
        if(event->isAccepted())
×
749
                thereWasAnEvent();
×
750
#endif
751

752
        QGraphicsView::mouseMoveEvent(event);
×
753
}
×
754

755

756
void StelMainView::focusSky() {
×
757
        //scene()->setActiveWindow(0);
758
        rootItem->setFocus();
×
759
}
×
760

761
StelMainView::~StelMainView()
×
762
{
763
        //delete the night view graphic effect here while GL context is still valid
764
        rootItem->setGraphicsEffect(Q_NULLPTR);
×
765
        StelApp::deinitStatic();
×
766
        delete guiItem;
×
767
        guiItem=Q_NULLPTR;
×
768
}
×
769

770
QSurfaceFormat StelMainView::getDesiredGLFormat(QSettings* configuration)
×
771
{
772
        //use the default format as basis
773
        QSurfaceFormat fmt = QSurfaceFormat::defaultFormat();
×
774
        qDebug() << "Default surface format: " << fmt;
×
775

776
        //if on an GLES build, do not set the format
777
        const auto openGLModuleType = QOpenGLContext::openGLModuleType();
×
778
        qDebug() << "OpenGL module type:" << openGLModuleType;
×
779
        if (openGLModuleType==QOpenGLContext::LibGL)
×
780
        {
781
                fmt.setRenderableType(QSurfaceFormat::OpenGL);
×
782
                fmt.setMajorVersion(3);
×
783
                fmt.setMinorVersion(3);
×
784
                fmt.setProfile(QSurfaceFormat::CoreProfile);
×
785

786
                if (qApp && qApp->property("onetime_opengl_compat").toBool())
×
787
                {
788
                        qDebug() << "Setting OpenGL Compatibility profile from command line...";
×
789
                        fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
×
790
                }
791
                // FIXME: temporary hook for Qt5-based macOS bundles
792
                #if defined(Q_OS_MACOS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
793
                fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
794
                #endif
795
        }
796

797
        // Note: this only works if --mesa-mode was given on the command line. Auto-switch to Mesa or the driver name apparently cannot be detected at this early stage.
798
        const bool isMesa = QString(getenv("QT_OPENGL"))=="software";
×
799

800
        //request some sane buffer formats
801
        fmt.setRedBufferSize(8);
×
802
        fmt.setGreenBufferSize(8);
×
803
        fmt.setBlueBufferSize(8);
×
804
        fmt.setDepthBufferSize(24);
×
805

806
        if(qApp && qApp->property("onetime_single_buffer").toBool())
×
807
                fmt.setSwapBehavior(QSurfaceFormat::SingleBuffer);
×
808

809
        const int multisamplingLevel = configuration ? configuration->value("video/multisampling", 0).toInt() : 0;
×
810
        if(multisamplingLevel && qApp && qApp->property("spout").toString() == "none" && !isMesa)
×
811
                fmt.setSamples(multisamplingLevel);
×
812

813
        // VSync control. NOTE: it must be applied to the default format (QSurfaceFormat::setDefaultFormat) to take effect.
814
#ifdef Q_OS_MACOS
815
        // FIXME: workaround for bug LP:#1705832 (https://bugs.launchpad.net/stellarium/+bug/1705832)
816
        // Qt: https://bugreports.qt.io/browse/QTBUG-53273
817
        const bool vsdef = false; // use vsync=false by default on macOS
818
#else
819
        const bool vsdef = true;
×
820
#endif
821
        if (configuration->value("video/vsync", vsdef).toBool())
×
822
                fmt.setSwapInterval(1);
×
823
        else
824
                fmt.setSwapInterval(0);
×
825

826
#ifdef OPENGL_DEBUG_LOGGING
827
        //try to enable GL debugging using GL_KHR_debug
828
        fmt.setOption(QSurfaceFormat::DebugContext);
829
#endif
830

831
        return fmt;
×
832
}
×
833

834
void StelMainView::init()
×
835
{
836
#ifdef OPENGL_DEBUG_LOGGING
837
        if (glLogger)
838
        {
839
                if(!QOpenGLContext::currentContext()->hasExtension(QByteArrayLiteral("GL_KHR_debug")))
840
                        qWarning()<<"GL_KHR_debug extension missing, OpenGL debug logger will likely not work";
841
                if(glLogger->initialize())
842
                {
843
                        qDebug()<<"OpenGL debug logger initialized";
844
                        glLogger->disableMessages(QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::AnyType,
845
                                                                          QOpenGLDebugMessage::NotificationSeverity);
846
                        glLogger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
847
                        //the internal log buffer may not be empty, so check it
848
                        for (const auto& msg : glLogger->loggedMessages())
849
                        {
850
                                logGLMessage(msg);
851
                        }
852
                }
853
                else
854
                        qWarning()<<"Failed to initialize OpenGL debug logger";
855

856
                connect(QOpenGLContext::currentContext(),SIGNAL(aboutToBeDestroyed()),this,SLOT(contextDestroyed()));
857
                //for easier debugging, print the address of the main GL context
858
                qDebug()<<"CurCtxPtr:"<<QOpenGLContext::currentContext();
859
        }
860
#endif
861

862
        qDebug()<<"StelMainView::init";
×
863

864
        glInfo.mainContext = QOpenGLContext::currentContext();
×
865
        glInfo.surface = glInfo.mainContext->surface();
×
866
        glInfo.functions = glInfo.mainContext->functions();
×
867
        glInfo.vendor = QString(reinterpret_cast<const char*>(glInfo.functions->glGetString(GL_VENDOR)));
×
868
        glInfo.renderer = QString(reinterpret_cast<const char*>(glInfo.functions->glGetString(GL_RENDERER)));
×
869
        const auto format = glInfo.mainContext->format();
×
870
        glInfo.supportsLuminanceTextures = format.profile() == QSurfaceFormat::CompatibilityProfile ||
×
871
                                                                           format.majorVersion() < 3;
×
872
        glInfo.isGLES = format.renderableType()==QSurfaceFormat::OpenGLES;
×
873
        qDebug().nospace() << "Luminance textures are " << (glInfo.supportsLuminanceTextures ? "" : "not ") << "supported";
×
874
        glInfo.isCoreProfile = format.profile() == QSurfaceFormat::CoreProfile;
×
875

876
        auto& gl = *QOpenGLContext::currentContext()->functions();
×
877
        if(format.majorVersion() * 1000 + format.minorVersion() >= 4006 ||
×
878
           glInfo.mainContext->hasExtension("GL_EXT_texture_filter_anisotropic") ||
×
879
           glInfo.mainContext->hasExtension("GL_ARB_texture_filter_anisotropic"))
×
880
        {
881
                StelOpenGL::clearGLErrors();
×
882
                gl.glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &glInfo.maxAnisotropy);
×
883
                const auto error = gl.glGetError();
×
884
                if(error != GL_NO_ERROR)
×
885
                {
886
                        qDebug() << "Failed to get maximum texture anisotropy:" << StelOpenGL::getGLErrorText(error);
×
887
                }
888
                else if(glInfo.maxAnisotropy > 0)
×
889
                {
890
                        qDebug() << "Maximum texture anisotropy:" << glInfo.maxAnisotropy;
×
891
                }
892
                else
893
                {
894
                        qDebug() << "Maximum texture anisotropy is not positive:" << glInfo.maxAnisotropy;
×
895
                        glInfo.maxAnisotropy = 0;
×
896
                }
897
        }
898
        else
899
        {
900
                qDebug() << "Anisotropic filtering is not supported!";
×
901
        }
902

903
        if(format.majorVersion() > 4 || glInfo.mainContext->hasExtension("GL_ARB_sample_shading"))
×
904
        {
905
                auto addr = glInfo.mainContext->getProcAddress("glMinSampleShading");
×
906
                if(!addr)
×
907
                        addr = glInfo.mainContext->getProcAddress("glMinSampleShadingARB");
×
908
                glInfo.glMinSampleShading = reinterpret_cast<PFNGLMINSAMPLESHADINGPROC>(addr);
×
909
        }
910
        gl.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glInfo.maxTextureSize);
×
911
        qDebug() << "Maximum 2D texture size:" << glInfo.maxTextureSize;
×
912

913
        gui = new StelGui();
×
914

915
        // Should be check of requirements disabled? -- NO! This is intentional here, and does no harm.
916
        if (configuration->value("main/check_requirements", true).toBool())
×
917
        {
918
                // Find out lots of debug info about supported version of OpenGL and vendor/renderer.
919
                processOpenGLdiagnosticsAndWarnings(configuration, glInfo.mainContext);
×
920
        }
921

922
        //setup StelOpenGLArray global state
923
        StelOpenGLArray::initGL();
×
924

925
        //create and initialize main app
926
        stelApp = new StelApp(this);
×
927
        stelApp->setGui(gui);
×
928
        stelApp->init(configuration);
×
929
        //this makes sure the app knows how large the window is
930
        connect(stelScene,SIGNAL(sceneRectChanged(QRectF)),stelApp,SLOT(glWindowHasBeenResized(QRectF)));
×
931
        //also immediately set the current values
932
        stelApp->glWindowHasBeenResized(stelScene->sceneRect());
×
933

934
        StelActionMgr *actionMgr = stelApp->getStelActionManager();
×
935
        actionMgr->addAction("actionSave_Screenshot_Global", N_("Miscellaneous"), N_("Save screenshot"), this, "saveScreenShot()", "Ctrl+S");
×
936
        actionMgr->addAction("actionReload_Shaders", N_("Miscellaneous"), N_("Reload shaders (for development)"), this, "reloadShaders()", "Ctrl+R, P");
×
937
        actionMgr->addAction("actionSet_Full_Screen_Global", N_("Display Options"), N_("Full-screen mode"), this, "fullScreen", "F11");
×
938
        
939
        StelPainter::initGLShaders();
×
940

941
        guiItem = new StelGuiItem(size(), rootItem);
×
942
        scene()->addItem(rootItem);
×
943
        //set the default focus to the sky
944
        focusSky();
×
945
        nightModeEffect = new NightModeGraphicsEffect(this);
×
946
        updateNightModeProperty(StelApp::getInstance().getVisionModeNight());
×
947
        //install the effect on the whole view
948
        rootItem->setGraphicsEffect(nightModeEffect);
×
949

950
        flagInvertScreenShotColors = configuration->value("main/invert_screenshots_colors", false).toBool();
×
951
        screenShotFormat = configuration->value("main/screenshot_format", "png").toString();
×
952
        flagScreenshotDateFileName=configuration->value("main/screenshot_datetime_filename", false).toBool();
×
953
        screenShotFileMask = configuration->value("main/screenshot_datetime_filemask", "yyyyMMdd-hhmmssz").toString();
×
954
        flagUseCustomScreenshotSize=configuration->value("main/screenshot_custom_size", false).toBool();
×
955
        customScreenshotWidth=configuration->value("main/screenshot_custom_width", 1024).toInt();
×
956
        customScreenshotHeight=configuration->value("main/screenshot_custom_height", 768).toInt();
×
957
        screenshotDpi=configuration->value("main/screenshot_dpi", 72).toInt();
×
958
        setFlagCursorTimeout(configuration->value("gui/flag_mouse_cursor_timeout", false).toBool());
×
959
        setCursorTimeout(configuration->value("gui/mouse_cursor_timeout", 10.f).toDouble());
×
960
        setMaxFps(configuration->value("video/maximum_fps",10000.f).toFloat());
×
961
        setMinFps(configuration->value("video/minimum_fps",10000.f).toFloat());
×
962
        setSkyBackgroundColor(Vec3f(configuration->value("color/sky_background_color", "0,0,0").toString()));
×
963

964
        // XXX: This should be done in StelApp::init(), unfortunately for the moment we need to init the gui before the
965
        // plugins, because the gui creates the QActions needed by some plugins.
966
        stelApp->initPlugIns();
×
967

968
        // The script manager can only be fully initialized after the plugins have loaded.
969
        stelApp->initScriptMgr();
×
970

971
        // Set the global stylesheet, this is only useful for the tooltips.
972
        StelGui* sgui = dynamic_cast<StelGui*>(stelApp->getGui());
×
973
        if (sgui!=Q_NULLPTR)
×
974
                setStyleSheet(sgui->getStelStyle().qtStyleSheet);
×
975
        connect(stelApp, SIGNAL(visionNightModeChanged(bool)), this, SLOT(updateNightModeProperty(bool)));
×
976

977
        // I doubt this will have any effect on framerate, but may cause problems elsewhere?
978
        QThread::currentThread()->setPriority(QThread::HighestPriority);
×
979
#ifndef NDEBUG
980
        // Get an overview of module callOrders
981
        if (qApp->property("verbose")==true)
×
982
        {
983
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionDraw);
×
984
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionUpdate);
×
985
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleMouseClicks);
×
986
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleMouseMoves);
×
987
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleKeys);
×
988
        }
989
#endif
990

991
        // check conflicts for keyboard shortcuts...
992
        if (configuration->childGroups().contains("shortcuts"))
×
993
        {
994
                QStringList defaultShortcuts =  actionMgr->getShortcutsList();
×
995
                QStringList conflicts;
×
996
                configuration->beginGroup("shortcuts");
×
997
                QStringList cstActionNames = configuration->allKeys();
×
998
                QMultiMap<QString, QString> cstActionsMap; // It is possible we have a very messed-up setup with duplicates
×
999
                for (QStringList::const_iterator cstActionName = cstActionNames.constBegin(); cstActionName != cstActionNames.constEnd(); ++cstActionName)
×
1000
                {
1001
                        #if (QT_VERSION>=QT_VERSION_CHECK(5, 14, 0))
1002
                        QStringList singleCustomActionShortcuts = configuration->value((*cstActionName).toLocal8Bit().constData()).toString().split(" ", Qt::SkipEmptyParts);
×
1003
                        #else
1004
                        QStringList singleCustomActionShortcuts = configuration->value((*cstActionName).toLocal8Bit().constData()).toString().split(" ", QString::SkipEmptyParts);
1005
                        #endif
1006
                        singleCustomActionShortcuts.removeAll("\"\"");
×
1007

1008
                        // Add 1-2 entries per action
1009
                        for (QStringList::const_iterator cstActionShortcut = singleCustomActionShortcuts.constBegin(); cstActionShortcut != singleCustomActionShortcuts.constEnd(); ++cstActionShortcut)
×
1010
                                if (strcmp( (*cstActionShortcut).toLocal8Bit().constData(), "") )
×
1011
                                        cstActionsMap.insert((*cstActionShortcut), (*cstActionName));
×
1012
                }
×
1013
                // Now we have a QMultiMap with (customShortcut, actionName). It may contain multiple keys!
1014
                QStringList allMapKeys=cstActionsMap.keys();
×
1015
                QStringList uniqueMapKeys=cstActionsMap.uniqueKeys();
×
1016
                for (auto &key : uniqueMapKeys)
×
1017
                        allMapKeys.removeOne(key);
×
1018
                conflicts << allMapKeys; // Add the remaining (duplicate) keys
×
1019

1020
                // Check every shortcut from the Map that it is not assigned to its own correct action
1021
                for (QMultiMap<QString, QString>::const_iterator it=cstActionsMap.constBegin(); it != cstActionsMap.constEnd(); ++it)
×
1022
                {
1023
                        QString customKey(it.key());
×
1024
                        QString actionName=cstActionsMap.value(it.key());
×
1025
                        StelAction *action = actionMgr->findAction(actionName);
×
1026
                        if (action && defaultShortcuts.contains(customKey) && actionMgr->findActionFromShortcut(customKey)->getId()!=action->getId())
×
1027
                                conflicts << customKey;
×
1028
                }
×
1029
                configuration->endGroup();
×
1030

1031
                if (!conflicts.isEmpty())
×
1032
                {
1033
                        QMessageBox::warning(&getInstance(), q_("Attention!"), QString("%1: %2").arg(q_("Shortcuts have conflicts! Please press F7 after program startup and check following multiple assignments"), conflicts.join("; ")), QMessageBox::Ok);
×
1034
                        qWarning() << "Attention! Conflicting keyboard shortcut assignments found. Please resolve:" << conflicts.join("; "); // Repeat in logfile for later retrieval.
×
1035
                }
1036
        }
×
1037
}
×
1038

1039
void StelMainView::updateNightModeProperty(bool b)
×
1040
{
1041
        // So that the bottom bar tooltips get properly rendered in night mode.
1042
        setProperty("nightMode", b);
×
1043
        nightModeEffect->setEnabled(b);
×
1044
}
×
1045

1046
void StelMainView::reloadShaders()
×
1047
{
1048
        //make sure GL context is bound
1049
        glContextMakeCurrent();
×
1050
        emit reloadShadersRequested();
×
1051
}
×
1052

1053
// This is a series of various diagnostics based on "bugs" reported for 0.13.0 and 0.13.1.
1054
// Almost all can be traced to insufficient driver level.
1055
// No changes of OpenGL state is done here.
1056
// If problems are detected, warn the user one time, but continue. Warning panel will be suppressed on next start.
1057
// Work in progress, as long as we get reports about bad systems or until OpenGL startup is finalized and safe.
1058
// Several tests do not apply to MacOS X.
1059
void StelMainView::processOpenGLdiagnosticsAndWarnings(QSettings *conf, QOpenGLContext *context) const
×
1060
{
1061
#ifdef Q_OS_MACOS
1062
        Q_UNUSED(conf);
1063
#endif
1064
        QSurfaceFormat format=context->format();
×
1065

1066
        // These tests are not required on MacOS X
1067
#ifndef Q_OS_MACOS
1068
        bool openGLerror=false;
×
1069
        if (format.renderableType()==QSurfaceFormat::OpenGL || format.renderableType()==QSurfaceFormat::OpenGLES)
×
1070
        {
1071
                qDebug() << "Detected:" << (format.renderableType()==QSurfaceFormat::OpenGL  ? "OpenGL" : "OpenGL ES" ) << QString("%1.%2").arg(format.majorVersion()).arg(format.minorVersion());
×
1072
        }
1073
        else
1074
        {
1075
                openGLerror=true;
×
1076
                qDebug() << "Neither OpenGL nor OpenGL ES detected: Unsupported Format!";
×
1077
        }
1078
#endif
1079
        QOpenGLFunctions* gl = context->functions();
×
1080

1081
        QString glDriver(reinterpret_cast<const char*>(gl->glGetString(GL_VERSION)));
×
1082
        qDebug() << "Driver version string:" << glDriver;
×
1083
        qDebug() << "GL vendor is" << QString(reinterpret_cast<const char*>(gl->glGetString(GL_VENDOR)));
×
1084
        QString glRenderer(reinterpret_cast<const char*>(gl->glGetString(GL_RENDERER)));
×
1085
        qDebug() << "GL renderer is" << glRenderer;
×
1086

1087
        // Minimal required version of OpenGL for Qt5 is 2.1 and OpenGL Shading Language may be 1.20 (or OpenGL ES is 2.0 and GLSL ES is 1.0).
1088
        // As of V0.13.0..1, we use GLSL 1.10/GLSL ES 1.00 (implicitly, by omitting a #version line), but in case of using ANGLE we need hardware
1089
        // detected as providing ps_3_0.
1090
        // This means, usually systems with OpenGL3 support reported in the driver will work, those with reported 2.1 only will almost certainly fail.
1091
        // If platform does not even support minimal OpenGL version for Qt5, then tell the user about troubles and quit from application.
1092
        // This test is apparently not applicable on MacOS X due to its behaving differently from all other known OSes.
1093
        // The correct way to handle driver issues on MacOS X remains however unclear for now.
1094
#ifndef Q_OS_MACOS
1095
        bool isMesa=glDriver.contains("Mesa", Qt::CaseInsensitive);
×
1096
        #if (defined Q_OS_WIN) && (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1097
        bool isANGLE=glRenderer.startsWith("ANGLE", Qt::CaseSensitive);
1098
        #endif
1099
        if ( openGLerror ||
×
1100
             ((format.renderableType()==QSurfaceFormat::OpenGL  ) && (format.version() < QPair<int, int>(2, 1)) && !isMesa) ||
×
1101
             ((format.renderableType()==QSurfaceFormat::OpenGL  ) && (format.version() < QPair<int, int>(2, 0)) &&  isMesa) || // Mesa defaults to 2.0 but works!
×
1102
             ((format.renderableType()==QSurfaceFormat::OpenGLES) && (format.version() < QPair<int, int>(2, 0)))  )
×
1103
        {
1104
        #if (defined Q_OS_WIN) && (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1105
                if ((!isANGLE) && (!isMesa))
1106
                        qWarning() << "Oops... Insufficient OpenGL version. Please update drivers, graphics hardware, or use --angle-mode (or even --mesa-mode) option.";
1107
                else if (isANGLE)
1108
                        qWarning() << "Oops... Insufficient OpenGLES version in ANGLE. Please update drivers, graphics hardware, or use --mesa-mode option.";
1109
        #elif (defined Q_OS_WIN)
1110
                if (!isMesa)
1111
                        qWarning() << "Oops... Insufficient OpenGL version. Please update drivers, graphics hardware, or use --mesa-mode option.";
1112
                else
1113
                        qWarning() << "Oops... Insufficient OpenGL version. Mesa failed! Please send a bug report.";
1114

1115
                #if (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1116
                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Insufficient OpenGL version. Please update drivers, graphics hardware, or use --angle-mode (or --mesa-mode) option."), QMessageBox::Abort, QMessageBox::Abort);
1117
                #else
1118
                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Insufficient OpenGL version. Please update drivers, graphics hardware, or use --mesa-mode option."), QMessageBox::Abort, QMessageBox::Abort);
1119
                #endif
1120
        #else
1121
                qWarning() << "Oops... Insufficient OpenGL version. Please update drivers, or graphics hardware.";
×
1122
                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Insufficient OpenGL version. Please update drivers, or graphics hardware."), QMessageBox::Abort, QMessageBox::Abort);
×
1123
        #endif
1124
                exit(1);
×
1125
        }
1126
#endif
1127
        // This call requires OpenGL2+.
1128
        QString glslString(reinterpret_cast<const char*>(gl->glGetString(GL_SHADING_LANGUAGE_VERSION)));
×
1129
        qDebug() << "GL Shading Language version is" << glslString;
×
1130

1131
        // Only give extended info if called on command line, for diagnostic.
1132
        if (qApp->property("dump_OpenGL_details").toBool())
×
1133
                dumpOpenGLdiagnostics();
×
1134

1135
#if (defined Q_OS_WIN) && (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1136
        // If we have ANGLE, check esp. for insufficient ps_2 level.
1137
        if (isANGLE)
1138
        {
1139
                static const QRegularExpression angleVsPsRegExp(" vs_(\\d)_(\\d) ps_(\\d)_(\\d)");
1140
                int angleVSPSpos=glRenderer.indexOf(angleVsPsRegExp);
1141

1142
                if (angleVSPSpos >-1)
1143
                {
1144
                        QRegularExpressionMatch match=angleVsPsRegExp.match(glRenderer);
1145
                        float vsVersion=match.captured(1).toFloat() + 0.1f*match.captured(2).toFloat();
1146
                        float psVersion=match.captured(3).toFloat() + 0.1f*match.captured(4).toFloat();
1147
                        qDebug() << "VS Version Number detected: " << vsVersion;
1148
                        qDebug() << "PS Version Number detected: " << psVersion;
1149
                        if ((vsVersion<2.0f) || (psVersion<3.0f))
1150
                        {
1151
                                openGLerror=true;
1152
                                qDebug() << "This is not enough: we need DirectX9 with vs_2_0 and ps_3_0 or later.";
1153
                                qDebug() << "You should update graphics drivers, graphics hardware, or use the --mesa-mode option.";
1154
                                qDebug() << "Else, please try to use an older version like 0.12.9, and try with --safe-mode";
1155

1156
                                if (conf->value("main/ignore_opengl_warning", false).toBool())
1157
                                {
1158
                                        qDebug() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
1159
                                }
1160
                                else
1161
                                {
1162
                                        qDebug() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
1163
                                        qDebug() << "But more than likely problems will persist.";
1164
                                        QMessageBox::StandardButton answerButton=
1165
                                        QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Your DirectX/OpenGL ES subsystem has problems. See log for details.\nIgnore and suppress this notice in the future and try to continue in degraded mode anyway?"),
1166
                                                              QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
1167
                                        if (answerButton == QMessageBox::Abort)
1168
                                        {
1169
                                                qDebug() << "Aborting due to ANGLE OpenGL ES / DirectX vs or ps version problems.";
1170
                                                exit(1);
1171
                                        }
1172
                                        else
1173
                                        {
1174
                                                qDebug() << "Ignoring all warnings, continuing without further question.";
1175
                                                conf->setValue("main/ignore_opengl_warning", true);
1176
                                        }
1177
                                }
1178
                        }
1179
                        else
1180
                                qDebug() << "vs/ps version is fine, we should not see a graphics problem.";
1181
                }
1182
                else
1183
                {
1184
                        qDebug() << "Cannot parse ANGLE shader version string. This may indicate future problems.";
1185
                        qDebug() << "Please send a bug report that includes this log file and states if Stellarium runs or has problems.";
1186
                }
1187
        }
1188
#endif
1189
#ifndef Q_OS_MACOS
1190
        // Do a similar test for MESA: Ensure we have at least Mesa 10, Mesa 9 on FreeBSD (used for hardware-acceleration of AMD IGP) was reported to lose the stars.
1191
        if (isMesa)
×
1192
        {
1193
                static const QRegularExpression mesaRegExp("Mesa (\\d+\\.\\d+)"); // we need only major version. Minor should always be here. Test?
×
1194
                int mesaPos=glDriver.indexOf(mesaRegExp);
×
1195

1196
                if (mesaPos >-1)
×
1197
                {
1198
                        float mesaVersion=mesaRegExp.match(glDriver).captured(1).toFloat();
×
1199
                        qDebug() << "MESA Version Number detected: " << mesaVersion;
×
1200
                        if ((mesaVersion<10.0f))
×
1201
                        {
1202
                                openGLerror=true;
×
1203
                                qDebug() << "This is not enough: we need Mesa 10.0 or later.";
×
1204
                                qDebug() << "You should update graphics drivers or graphics hardware.";
×
1205
                                qDebug() << "Else, please try to use an older version like 0.12.9, and try there with --safe-mode";
×
1206

1207
                                if (conf->value("main/ignore_opengl_warning", false).toBool())
×
1208
                                {
1209
                                        qDebug() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
×
1210
                                }
1211
                                else
1212
                                {
1213
                                        qDebug() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
×
1214
                                        qDebug() << "But more than likely problems will persist.";
×
1215
                                        QMessageBox::StandardButton answerButton=
1216
                                        QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Your OpenGL/Mesa subsystem has problems. See log for details.\nIgnore and suppress this notice in the future and try to continue in degraded mode anyway?"),
×
1217
                                                              QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
1218
                                        if (answerButton == QMessageBox::Abort)
×
1219
                                        {
1220
                                                qDebug() << "Aborting due to OpenGL/Mesa insufficient version problems.";
×
1221
                                                exit(1);
×
1222
                                        }
1223
                                        else
1224
                                        {
1225
                                                qDebug() << "Ignoring all warnings, continuing without further question.";
×
1226
                                                conf->setValue("main/ignore_opengl_warning", true);
×
1227
                                        }
1228
                                }
1229
                        }
1230
                        else
1231
                                qDebug() << "Mesa version is fine, we should not see a graphics problem.";
×
1232
                }
1233
                else
1234
                {
1235
                        qDebug() << "Cannot parse Mesa Driver version string. This may indicate future problems.";
×
1236
                        qDebug() << "Please send a bug report that includes this log file and states if Stellarium runs or has problems.";
×
1237
                }
1238
        }
1239
#endif
1240

1241
        // Although our shaders are only GLSL1.10, there are frequent problems with systems just at this level of programmable shaders.
1242
        // If GLSL version is less than 1.30 or GLSL ES 1.00, Stellarium usually does run properly on Windows or various Linux flavours.
1243
        // Depending on whatever driver/implementation details, Stellarium may crash or show only minor graphical errors.
1244
        // On these systems, we show a warning panel that can be suppressed by a config option which is automatically added on first run.
1245
        // Again, based on a sample size of one, Macs have been reported already to always work in this case.
1246
#ifndef Q_OS_MACOS
1247
        static const QRegularExpression glslRegExp("^(\\d\\.\\d\\d)");
×
1248
        int pos=glslString.indexOf(glslRegExp);
×
1249
        // VC4 drivers on Raspberry Pi reports ES 1.0.16 or so, we must step down to one cipher after decimal.
1250
        static const QRegularExpression glslesRegExp("ES (\\d\\.\\d)");
×
1251
        int posES=glslString.indexOf(glslesRegExp);
×
1252
        if (pos >-1)
×
1253
        {
1254
                float glslVersion=glslRegExp.match(glslString).captured(1).toFloat();
×
1255
                qDebug() << "GLSL Version Number detected: " << glslVersion;
×
1256
                if (glslVersion<1.3f)
×
1257
                {
1258
                        openGLerror=true;
×
1259
                        qDebug() << "This is not enough: we need GLSL1.30 or later.";
×
1260
                        #ifdef Q_OS_WIN
1261
                        qDebug() << "You should update graphics drivers, graphics hardware, or use the --mesa-mode option.";
1262
                        #else
1263
                        qDebug() << "You should update graphics drivers or graphics hardware.";
×
1264
                        #endif
1265
                        qDebug() << "Else, please try to use an older version like 0.12.9, and try there with --safe-mode";
×
1266

1267
                        if (conf->value("main/ignore_opengl_warning", false).toBool())
×
1268
                        {
1269
                                qDebug() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
×
1270
                        }
1271
                        else
1272
                        {
1273
                                qDebug() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
×
1274
                                qDebug() << "But more than likely problems will persist.";
×
1275
                                QMessageBox::StandardButton answerButton=
1276
                                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Your OpenGL subsystem has problems. See log for details.\nIgnore and suppress this notice in the future and try to continue in degraded mode anyway?"),
×
1277
                                                      QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
1278
                                if (answerButton == QMessageBox::Abort)
×
1279
                                {
1280
                                        qDebug() << "Aborting due to OpenGL/GLSL version problems.";
×
1281
                                        exit(1);
×
1282
                                }
1283
                                else
1284
                                {
1285
                                        qDebug() << "Ignoring all warnings, continuing without further question.";
×
1286
                                        conf->setValue("main/ignore_opengl_warning", true);
×
1287
                                }
1288
                        }
1289
                }
1290
                else
1291
                        qDebug() << "GLSL version is fine, we should not see a graphics problem.";
×
1292
        }
1293
        else if (posES >-1)
×
1294
        {
1295
                float glslesVersion=glslesRegExp.match(glslString).captured(1).toFloat();
×
1296
                qDebug() << "GLSL ES Version Number detected: " << glslesVersion;
×
1297
                if (glslesVersion<1.0f) // TBD: is this possible at all?
×
1298
                {
1299
                        openGLerror=true;
×
1300
                        qDebug() << "This is not enough: we need GLSL ES 1.00 or later.";
×
1301
#ifdef Q_OS_WIN
1302
                        qDebug() << "You should update graphics drivers, graphics hardware, or use the --mesa-mode option.";
1303
#else
1304
                        qDebug() << "You should update graphics drivers or graphics hardware.";
×
1305
#endif
1306
                        qDebug() << "Else, please try to use an older version like 0.12.5, and try there with --safe-mode";
×
1307

1308
                        if (conf->value("main/ignore_opengl_warning", false).toBool())
×
1309
                        {
1310
                                qDebug() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
×
1311
                        }
1312
                        else
1313
                        {
1314
                                qDebug() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
×
1315
                                qDebug() << "But more than likely problems will persist.";
×
1316
                                QMessageBox::StandardButton answerButton=
1317
                                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Your OpenGL ES subsystem has problems. See log for details.\nIgnore and suppress this notice in the future and try to continue in degraded mode anyway?"),
×
1318
                                                      QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
1319
                                if (answerButton == QMessageBox::Abort)
×
1320
                                {
1321
                                        qDebug() << "Aborting due to OpenGL ES/GLSL ES version problems.";
×
1322
                                        exit(1);
×
1323
                                }
1324
                                else
1325
                                {
1326
                                        qDebug() << "Ignoring all warnings, continuing without further question.";
×
1327
                                        conf->setValue("main/ignore_opengl_warning", true);
×
1328
                                }
1329
                        }
1330
                }
1331
                else
1332
                {
1333
                        if (openGLerror)
×
1334
                                qDebug() << "GLSL ES version is OK, but there were previous errors, expect problems.";
×
1335
                        else
1336
                                qDebug() << "GLSL ES version is fine, we should not see a graphics problem.";
×
1337
                }
1338
        }
1339
        else
1340
        {
1341
                qDebug() << "Cannot parse GLSL (ES) version string. This may indicate future problems.";
×
1342
                qDebug() << "Please send a bug report that includes this log file and states if Stellarium works or has problems.";
×
1343
        }
1344
#endif
1345
}
×
1346

1347
// Debug info about OpenGL capabilities.
1348
void StelMainView::dumpOpenGLdiagnostics() const
×
1349
{
1350
        QOpenGLContext *context = QOpenGLContext::currentContext();
×
1351
        if (context)
×
1352
        {
1353
                context->functions()->initializeOpenGLFunctions();
×
1354
                qDebug() << "initializeOpenGLFunctions()...";
×
1355
                QOpenGLFunctions::OpenGLFeatures oglFeatures=context->functions()->openGLFeatures();
×
1356
                qDebug() << "OpenGL Features:";
×
1357
                qDebug() << " - glActiveTexture() function" << (oglFeatures&QOpenGLFunctions::Multitexture ? "is" : "is NOT") << "available.";
×
1358
                qDebug() << " - Shader functions" << (oglFeatures&QOpenGLFunctions::Shaders ? "are" : "are NOT ") << "available.";
×
1359
                qDebug() << " - Vertex and index buffer functions" << (oglFeatures&QOpenGLFunctions::Buffers ? "are" : "are NOT") << "available.";
×
1360
                qDebug() << " - Framebuffer object functions" << (oglFeatures&QOpenGLFunctions::Framebuffers ? "are" : "are NOT") << "available.";
×
1361
                qDebug() << " - glBlendColor()" << (oglFeatures&QOpenGLFunctions::BlendColor ? "is" : "is NOT") << "available.";
×
1362
                qDebug() << " - glBlendEquation()" << (oglFeatures&QOpenGLFunctions::BlendEquation ? "is" : "is NOT") << "available.";
×
1363
                qDebug() << " - glBlendEquationSeparate()" << (oglFeatures&QOpenGLFunctions::BlendEquationSeparate ? "is" : "is NOT") << "available.";
×
1364
                qDebug() << " - glBlendFuncSeparate()" << (oglFeatures&QOpenGLFunctions::BlendFuncSeparate ? "is" : "is NOT") << "available.";
×
1365
                qDebug() << " - Blend subtract mode" << (oglFeatures&QOpenGLFunctions::BlendSubtract ? "is" : "is NOT") << "available.";
×
1366
                qDebug() << " - Compressed texture functions" << (oglFeatures&QOpenGLFunctions::CompressedTextures ? "are" : "are NOT") << "available.";
×
1367
                qDebug() << " - glSampleCoverage() function" << (oglFeatures&QOpenGLFunctions::Multisample ? "is" : "is NOT") << "available.";
×
1368
                qDebug() << " - Separate stencil functions" << (oglFeatures&QOpenGLFunctions::StencilSeparate ? "are" : "are NOT") << "available.";
×
1369
                qDebug() << " - Non power of two textures" << (oglFeatures&QOpenGLFunctions::NPOTTextures ? "are" : "are NOT") << "available.";
×
1370
                qDebug() << " - Non power of two textures" << (oglFeatures&QOpenGLFunctions::NPOTTextureRepeat ? "can" : "CANNOT") << "use GL_REPEAT as wrap parameter.";
×
1371
                qDebug() << " - The fixed function pipeline" << (oglFeatures&QOpenGLFunctions::FixedFunctionPipeline ? "is" : "is NOT") << "available.";
×
1372
                GLfloat lineWidthRange[2];
1373
                context->functions()->glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange);
×
1374
                qDebug() << "Line widths available from" << lineWidthRange[0] << "to" << lineWidthRange[1];
×
1375

1376
                qDebug() << "OpenGL shader capabilities and details:";
×
1377
                qDebug() << " - Vertex Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Vertex, context) ? "YES" : "NO");
×
1378
                qDebug() << " - Fragment Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Fragment, context) ? "YES" : "NO");
×
1379
                qDebug() << " - Geometry Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Geometry, context) ? "YES" : "NO");
×
1380
                qDebug() << " - TessellationControl Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::TessellationControl, context) ? "YES" : "NO");
×
1381
                qDebug() << " - TessellationEvaluation Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::TessellationEvaluation, context) ? "YES" : "NO");
×
1382
                qDebug() << " - Compute Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Compute, context) ? "YES" : "NO");
×
1383
                
1384
                // GZ: List available extensions. Not sure if this is in any way useful?
1385
                QSet<QByteArray> extensionSet=context->extensions();
×
1386
                qDebug() << "We have" << extensionSet.count() << "OpenGL extensions:";
×
1387
                QMap<QString, QString> extensionMap;
×
1388
                QSetIterator<QByteArray> iter(extensionSet);
×
1389
                while (iter.hasNext())
×
1390
                {
1391
                        if (!iter.peekNext().isEmpty()) {// Don't insert empty lines
×
1392
                                extensionMap.insert(QString(iter.peekNext()), QString(iter.peekNext()));
×
1393
                        }
1394
                        iter.next();
×
1395
                }
1396
                QMapIterator<QString, QString> iter2(extensionMap);
×
1397
                while (iter2.hasNext()) {
×
1398
                        qDebug() << " -" << iter2.next().key();
×
1399
                }
1400
                // Apparently EXT_gpu_shader4 is required for GLSL1.3. (http://en.wikipedia.org/wiki/OpenGL#OpenGL_3.0).
1401
                qDebug() << "EXT_gpu_shader4" << (extensionSet.contains(("EXT_gpu_shader4")) ? "present, OK." : "MISSING!");
×
1402
                
1403
                QFunctionPointer programParameterPtr =context->getProcAddress("glProgramParameteri");
×
1404
                if (programParameterPtr == Q_NULLPTR) {
×
1405
                        qDebug() << "glProgramParameteri cannot be resolved here. BAD!";
×
1406
                }
1407
                programParameterPtr =context->getProcAddress("glProgramParameteriEXT");
×
1408
                if (programParameterPtr == Q_NULLPTR) {
×
1409
                        qDebug() << "glProgramParameteriEXT cannot be resolved here. BAD!";
×
1410
                }
1411
        }
×
1412
        else
1413
        {
1414
                qDebug() << "dumpOpenGLdiagnostics(): No OpenGL context";
×
1415
        }
1416
}
×
1417

1418
void StelMainView::deinit()
×
1419
{
1420
        glContextMakeCurrent();
×
1421
        deinitGL();
×
1422
        delete stelApp;
×
1423
        stelApp = Q_NULLPTR;
×
1424
}
×
1425

1426
// Update the translated title
1427
void StelMainView::initTitleI18n()
×
1428
{
1429
        setWindowTitle(StelUtils::getApplicationName());
×
1430
}
×
1431

1432
void StelMainView::setFullScreen(bool b)
×
1433
{
1434
        if (b)
×
1435
                showFullScreen();
×
1436
        else
1437
        {
1438
                showNormal();
×
1439
                // Not enough. If we had started in fullscreen, the inner part of the window is at 0/0, with the frame extending to top/left off screen.
1440
                // Therefore moving is not possible. We must move to the stored position or at least defaults.
1441
                if ( (x()<0)  && (y()<0))
×
1442
                {
1443
                        QSettings *conf = stelApp->getSettings();
×
1444
                        int screen = conf->value("video/screen_number", 0).toInt();
×
1445
                        if (screen < 0 || screen >= qApp->screens().count())
×
1446
                        {
1447
                                qWarning() << "WARNING: screen" << screen << "not found";
×
1448
                                screen = 0;
×
1449
                        }
1450
                        QRect screenGeom = qApp->screens().at(screen)->geometry();
×
1451
                        int x = conf->value("video/screen_x", 0).toInt();
×
1452
                        int y = conf->value("video/screen_y", 0).toInt();
×
1453
                        move(x + screenGeom.x(), y + screenGeom.y());
×
1454
                }
1455
        }
1456
        emit fullScreenChanged(b);
×
1457
}
×
1458

1459
void StelMainView::drawEnded()
×
1460
{
1461
        updateQueued = false;
×
1462

1463
        int requiredFpsInterval = qRound(needsMaxFPS()?1000.f/maxfps:1000.f/minfps);
×
1464

1465
        if(fpsTimer->interval() != requiredFpsInterval)
×
1466
                fpsTimer->setInterval(requiredFpsInterval);
×
1467

1468
        if(!fpsTimer->isActive())
×
1469
                fpsTimer->start();
×
1470
}
×
1471

1472
void StelMainView::setFlagCursorTimeout(bool b)
×
1473
{
1474
        if (b == flagCursorTimeout) return;
×
1475

1476
        flagCursorTimeout = b;
×
1477
        if (b)         // enable timer
×
1478
        {
1479
                cursorTimeoutTimer->start();
×
1480
        }
1481
        else        // disable timer
1482
        {
1483
                // Show the previous cursor if the current is "hidden" 
1484
                if (QGuiApplication::overrideCursor() && (QGuiApplication::overrideCursor()->shape() == Qt::BlankCursor) )
×
1485
                {
1486
                        // pop the blank cursor
1487
                        QGuiApplication::restoreOverrideCursor();
×
1488
                }
1489
                // and stop the timer 
1490
                cursorTimeoutTimer->stop();
×
1491
        }
1492
        
1493
        emit flagCursorTimeoutChanged(b);
×
1494
}
1495

1496
void StelMainView::hideCursor()
×
1497
{
1498
        // timeout fired...
1499
        // if the feature is not asked, do nothing
1500
        if (!flagCursorTimeout) return;
×
1501

1502
        // "hide" the current cursor by pushing a Blank cursor
1503
        QGuiApplication::setOverrideCursor(Qt::BlankCursor);
×
1504
}
1505

1506
void StelMainView::fpsTimerUpdate()
×
1507
{
1508
        if(!updateQueued)
×
1509
        {
1510
                updateQueued = true;
×
1511
                QTimer::singleShot(0, glWidget, SLOT(update()));
×
1512
        }
1513
}
×
1514

1515
#ifdef OPENGL_DEBUG_LOGGING
1516
void StelMainView::logGLMessage(const QOpenGLDebugMessage &debugMessage)
1517
{
1518
        qDebug()<<debugMessage;
1519
}
1520

1521
void StelMainView::contextDestroyed()
1522
{
1523
        qDebug()<<"Main OpenGL context destroyed";
1524
}
1525
#endif
1526

1527
void StelMainView::thereWasAnEvent()
×
1528
{
1529
        lastEventTimeSec = StelApp::getTotalRunTime();
×
1530
}
×
1531

1532
bool StelMainView::needsMaxFPS() const
×
1533
{
1534
        const double now = StelApp::getTotalRunTime();
×
1535

1536
        // Determines when the next display will need to be triggered
1537
        // The current policy is that after an event, the FPS is maximum for 2.5 seconds
1538
        // after that, it switches back to the default minfps value to save power.
1539
        // The fps is also kept to max if the timerate is higher than normal speed.
1540
        const double timeRate = stelApp->getCore()->getTimeRate();
×
1541
        return (now - lastEventTimeSec < 2.5) || fabs(timeRate) > StelCore::JD_SECOND;
×
1542
}
1543

1544
void StelMainView::moveEvent(QMoveEvent * event)
×
1545
{
1546
        Q_UNUSED(event)
1547

1548
        // We use the glWidget instead of the event, as we want the screen that shows most of the widget.
1549
        QWindow* win = glWidget->windowHandle();
×
1550
        if(win)
×
1551
                stelApp->setDevicePixelsPerPixel(win->devicePixelRatio());
×
1552
}
×
1553

1554
void StelMainView::closeEvent(QCloseEvent* event)
×
1555
{
1556
        Q_UNUSED(event)
1557
        stelApp->quit();
×
1558
}
×
1559

1560
//! Delete openGL textures (to call before the GLContext disappears)
1561
void StelMainView::deinitGL()
×
1562
{
1563
        //fix for bug 1628072 caused by QTBUG-56798
1564
#ifndef QT_NO_DEBUG
1565
        StelOpenGL::clearGLErrors();
×
1566
#endif
1567

1568
        stelApp->deinit();
×
1569
        delete gui;
×
1570
        gui = Q_NULLPTR;
×
1571
}
×
1572

1573
void StelMainView::setScreenshotFormat(const QString filetype)
×
1574
{
1575
        const QString candidate=filetype.toLower();
×
1576
        const QByteArray candBA=candidate.toUtf8();
×
1577

1578
        // Make sure format is supported by Qt, but restrict some useless formats.
1579
        QList<QByteArray> formats = QImageWriter::supportedImageFormats();
×
1580
        formats.removeOne("icns");
×
1581
        formats.removeOne("wbmp");
×
1582
        formats.removeOne("cur");
×
1583
        if (formats.contains(candBA))
×
1584
        {
1585
                screenShotFormat=candidate;
×
1586
                // apply setting immediately
1587
                configuration->setValue("main/screenshot_format", candidate);
×
1588
                emit screenshotFormatChanged(candidate);
×
1589
        }
1590
        else
1591
        {
1592
                qDebug() << "Invalid filetype for screenshot: " << filetype;
×
1593
        }
1594
}
×
1595

1596
void StelMainView::setFlagScreenshotDateFileName(bool b)
×
1597
{
1598
        flagScreenshotDateFileName=b;
×
1599
        StelApp::getInstance().getSettings()->setValue("main/screenshot_datetime_filename", b);
×
1600
        emit flagScreenshotDateFileNameChanged(b);
×
1601
}
×
1602

1603
void StelMainView::setScreenshotFileMask(const QString filemask)
×
1604
{
1605
        screenShotFileMask = filemask;
×
1606
        StelApp::getInstance().getSettings()->setValue("main/screenshot_datetime_filemask", filemask);
×
1607
        emit screenshotFileMaskChanged(filemask);
×
1608
}
×
1609

1610
void StelMainView::setScreenshotDpi(int dpi)
×
1611
{
1612
        screenshotDpi=dpi;
×
1613
        StelApp::getInstance().getSettings()->setValue("main/screenshot_dpi", dpi);
×
1614
        emit screenshotDpiChanged(dpi);
×
1615
}
×
1616

1617
void StelMainView::saveScreenShot(const QString& filePrefix, const QString& saveDir, const bool overwrite)
×
1618
{
1619
        screenShotPrefix = filePrefix;
×
1620
        screenShotDir = saveDir;
×
1621
        flagOverwriteScreenshots=overwrite;
×
1622
        emit screenshotRequested();
×
1623
}
×
1624

1625
void StelMainView::doScreenshot(void)
×
1626
{
1627
        QFileInfo shotDir;
×
1628
        // Make a screenshot which may be larger than the current window. This is harder than you would think:
1629
        // fbObj the framebuffer governs size of the target image, that's the easy part, but it also has its limits.
1630
        // However, the GUI parts need to be placed properly,
1631
        // HiDPI screens interfere, and the viewing angle has to be maintained.
1632
        // First, image size:
1633
        glWidget->makeCurrent();
×
1634
        float pixelRatio = static_cast<float>(QOpenGLContext::currentContext()->screen()->devicePixelRatio());
×
1635
        int imgWidth =static_cast<int>(stelScene->width());
×
1636
        int imgHeight=static_cast<int>(stelScene->height());
×
1637
        bool nightModeWasEnabled=nightModeEffect->isEnabled();
×
1638
        nightModeEffect->setEnabled(false);
×
1639
        if (flagUseCustomScreenshotSize)
×
1640
        {
1641
                // Borrowed from Scenery3d renderer: determine maximum framebuffer size as minimum of texture, viewport and renderbuffer size
1642
                QOpenGLContext *context = QOpenGLContext::currentContext();
×
1643
                if (context)
×
1644
                {
1645
                        context->functions()->initializeOpenGLFunctions();
×
1646
                        //qDebug() << "initializeOpenGLFunctions()...";
1647
                        // TODO: Investigate this further when GL memory issues should appear.
1648
                        // Make sure we have enough free GPU memory!
1649
#ifndef NDEBUG
1650
#ifdef GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX
1651
                        GLint freeGLmemory;
1652
                        context->functions()->glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &freeGLmemory);
×
1653
                        qCDebug(mainview)<<"Free GPU memory:" << freeGLmemory << "kB -- we ask for " << customScreenshotWidth*customScreenshotHeight*8 / 1024 <<"kB";
×
1654
#endif
1655
#ifdef GL_RENDERBUFFER_FREE_MEMORY_ATI
1656
                        GLint freeGLmemoryAMD[4];
1657
                        context->functions()->glGetIntegerv(GL_RENDERBUFFER_FREE_MEMORY_ATI, freeGLmemoryAMD);
×
1658
                        qCDebug(mainview)<<"Free GPU memory (AMD version):" << static_cast<uint>(freeGLmemoryAMD[1])/1024 << "+"
×
1659
                                          << static_cast<uint>(freeGLmemoryAMD[3])/1024 << " of "
×
1660
                                          << static_cast<uint>(freeGLmemoryAMD[0])/1024 << "+"
×
1661
                                          << static_cast<uint>(freeGLmemoryAMD[2])/1024 << "kB -- we ask for "
×
1662
                                          << customScreenshotWidth*customScreenshotHeight*8 / 1024 <<"kB";
×
1663
#endif
1664
#endif
1665
                        GLint texSize,viewportSize[2],rbSize;
1666
                        context->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
×
1667
                        context->functions()->glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewportSize);
×
1668
                        context->functions()->glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &rbSize);
×
1669
                        qCDebug(mainview)<<"Maximum texture size:"<<texSize;
×
1670
                        qCDebug(mainview)<<"Maximum viewport dims:"<<viewportSize[0]<<viewportSize[1];
×
1671
                        qCDebug(mainview)<<"Maximum renderbuffer size:"<<rbSize;
×
1672
                        int maximumFramebufferSize = qMin(texSize,qMin(rbSize,qMin(viewportSize[0],viewportSize[1])));
×
1673
                        qCDebug(mainview)<<"Maximum framebuffer size:"<<maximumFramebufferSize;
×
1674

1675
                        imgWidth =qMin(maximumFramebufferSize, customScreenshotWidth);
×
1676
                        imgHeight=qMin(maximumFramebufferSize, customScreenshotHeight);
×
1677
                }
1678
                else
1679
                {
1680
                        qCWarning(mainview) << "No GL context for screenshot! Aborting.";
×
1681
                        return;
×
1682
                }
1683
        }
1684
        // The texture format depends on used GL version. RGB is fine on OpenGL. on GLES, we must use RGBA and circumvent problems with a few more steps.
1685
        bool isGLES=(QOpenGLContext::currentContext()->format().renderableType() == QSurfaceFormat::OpenGLES);
×
1686

1687
        QOpenGLFramebufferObjectFormat fbFormat;
×
1688
        fbFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
×
1689
        fbFormat.setInternalTextureFormat(isGLES ? GL_RGBA : GL_RGB); // try to avoid transparent background!
×
1690
        if(const auto multisamplingLevel = configuration->value("video/multisampling", 0).toInt())
×
1691
        fbFormat.setSamples(multisamplingLevel);
×
1692
        QOpenGLFramebufferObject * fbObj = new QOpenGLFramebufferObject(static_cast<int>(static_cast<float>(imgWidth) * pixelRatio), static_cast<int>(static_cast<float>(imgHeight) * pixelRatio), fbFormat);
×
1693
        fbObj->bind();
×
1694
        // Now the painter has to be convinced to paint to the potentially larger image frame.
1695
        QOpenGLPaintDevice fbObjPaintDev(static_cast<int>(static_cast<float>(imgWidth) * pixelRatio), static_cast<int>(static_cast<float>(imgHeight) * pixelRatio));
×
1696

1697
        // It seems the projector has its own knowledge about image size. We must adjust fov and image size, but reset afterwards.
1698
        StelCore *core=StelApp::getInstance().getCore();
×
1699
        StelProjector::StelProjectorParams pParams=core->getCurrentStelProjectorParams();
×
1700
        StelProjector::StelProjectorParams sParams=pParams;
×
1701
        //qCDebug(mainview) << "Screenshot Viewport: x" << pParams.viewportXywh[0] << "/y" << pParams.viewportXywh[1] << "/w" << pParams.viewportXywh[2] << "/h" << pParams.viewportXywh[3];
1702
        sParams.viewportXywh[2]=imgWidth;
×
1703
        sParams.viewportXywh[3]=imgHeight;
×
1704

1705
        // Configure a helper value to allow some modules to tweak their output sizes. Currently used by StarMgr, maybe solve font issues?
1706
#if (QT_VERSION>=QT_VERSION_CHECK(5,12,0))
1707
        customScreenshotMagnification=static_cast<float>(imgHeight)/static_cast<float>(qApp->screenAt(QPoint(stelScene->width()*0.5, stelScene->height()*0.5))->geometry().height());
×
1708
#else
1709
        customScreenshotMagnification=static_cast<float>(imgHeight)/static_cast<float>(qApp->screens().at(qApp->desktop()->screenNumber())->geometry().height());
1710
#endif
1711
        sParams.viewportCenter.set(0.0+(0.5+pParams.viewportCenterOffset.v[0])*imgWidth, 0.0+(0.5+pParams.viewportCenterOffset.v[1])*imgHeight);
×
1712
        sParams.viewportFovDiameter = qMin(imgWidth,imgHeight);
×
1713
        core->setCurrentStelProjectorParams(sParams);
×
1714

1715
        QPainter painter;
×
1716
        painter.begin(&fbObjPaintDev);
×
1717
        // next line was above begin(), but caused a complaint. Maybe use after begin()?
1718
        painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
×
1719
        stelScene->setSceneRect(0, 0, imgWidth, imgHeight);
×
1720

1721
        // push the button bars back to the sides where they belong, and fix root item clipping its children.
1722
        dynamic_cast<StelGui*>(gui)->getSkyGui()->setGeometry(0, 0, imgWidth, imgHeight);
×
1723
        rootItem->setSize(QSize(imgWidth, imgHeight));
×
1724
        dynamic_cast<StelGui*>(gui)->forceRefreshGui(); // refresh bar position.
×
1725

1726
        stelScene->render(&painter, QRectF(), QRectF(0,0,imgWidth,imgHeight) , Qt::KeepAspectRatio);
×
1727
        painter.end();
×
1728

1729
        QImage im;
×
1730
        if (isGLES)
×
1731
        {
1732
                // We have RGBA texture with possibly empty spots when atmosphere was off.
1733
                // See toImage() help entry why to create wrapper here.
1734
                QImage fboImage(fbObj->toImage());
×
1735
                //qDebug() << "FBOimage format:" << fboImage.format(); // returns Format_RGBA8888_Premultiplied
1736
                QImage im2(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_RGBX8888);
×
1737
                im=im2.copy();
×
1738
        }
×
1739
        else
1740
                im=fbObj->toImage();
×
1741
        fbObj->release();
×
1742
        delete fbObj;
×
1743
        // reset viewport and GUI
1744
        core->setCurrentStelProjectorParams(pParams);
×
1745
        customScreenshotMagnification=1.0f;
×
1746
        nightModeEffect->setEnabled(nightModeWasEnabled);
×
1747
        stelScene->setSceneRect(0, 0, pParams.viewportXywh[2], pParams.viewportXywh[3]);
×
1748
        rootItem->setSize(QSize(pParams.viewportXywh[2], pParams.viewportXywh[3]));
×
1749
        StelGui* stelGui = dynamic_cast<StelGui*>(gui);
×
1750
        if (stelGui)
×
1751
        {
1752
                stelGui->getSkyGui()->setGeometry(0, 0, pParams.viewportXywh[2], pParams.viewportXywh[3]);
×
1753
                stelGui->forceRefreshGui();
×
1754
        }
1755

1756
        if (nightModeWasEnabled)
×
1757
        {
1758
                for (int row=0; row<im.height(); ++row)
×
1759
                        for (int col=0; col<im.width(); ++col)
×
1760
                        {
1761
                                QRgb rgb=im.pixel(col, row);
×
1762
                                int gray=qGray(rgb);
×
1763
                                im.setPixel(col, row, qRgb(gray, 0, 0));
×
1764
                        }
1765
        }
1766
        if (flagInvertScreenShotColors)
×
1767
                im.invertPixels();
×
1768

1769
        if (StelFileMgr::getScreenshotDir().isEmpty())
×
1770
        {
1771
                qWarning() << "Oops, the directory for screenshots is not set! Let's try create and set it...";
×
1772
                // Create a directory for screenshots if main/screenshot_dir option is unset and user do screenshot at the moment!
1773
                QString screenshotDirSuffix = "/Stellarium";
×
1774
                QString screenshotDir;
×
1775
                if (!QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).isEmpty())
×
1776
                {
1777
                        screenshotDir = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).constFirst();
×
1778
                        screenshotDir.append(screenshotDirSuffix);
×
1779
                }
1780
                else
1781
                        screenshotDir = StelFileMgr::getUserDir().append(screenshotDirSuffix);
×
1782

1783
                try
1784
                {
1785
                        StelFileMgr::setScreenshotDir(screenshotDir);
×
1786
                        StelApp::getInstance().getSettings()->setValue("main/screenshot_dir", screenshotDir);
×
1787
                }
1788
                catch (std::runtime_error &e)
×
1789
                {
1790
                        qDebug("Error: cannot create screenshot directory: %s", e.what());
×
1791
                }
×
1792
        }
×
1793

1794
        if (screenShotDir == "")
×
1795
                shotDir = QFileInfo(StelFileMgr::getScreenshotDir());
×
1796
        else
1797
                shotDir = QFileInfo(screenShotDir);
×
1798

1799
        if (!shotDir.isDir())
×
1800
        {
1801
                qWarning() << "ERROR requested screenshot directory is not a directory: " << QDir::toNativeSeparators(shotDir.filePath());
×
1802
                return;
×
1803
        }
1804
        else if (!shotDir.isWritable())
×
1805
        {
1806
                qWarning() << "ERROR requested screenshot directory is not writable: " << QDir::toNativeSeparators(shotDir.filePath());
×
1807
                return;
×
1808
        }
1809

1810
        QFileInfo shotPath;
×
1811
        if (flagOverwriteScreenshots)
×
1812
        {
1813
                shotPath = QFileInfo(shotDir.filePath() + "/" + screenShotPrefix + "." + screenShotFormat);
×
1814
        }
1815
        else
1816
        {
1817
                QString shotPathString;
×
1818
                if (flagScreenshotDateFileName)
×
1819
                {
1820
                        // name screenshot files with current time
1821
                        QString currentTime = QDateTime::currentDateTime().toString(screenShotFileMask);
×
1822
                        shotPathString = QString("%1/%2%3.%4").arg(shotDir.filePath(), screenShotPrefix, currentTime, screenShotFormat);
×
1823
                        shotPath = QFileInfo(shotPathString);
×
1824
                }
×
1825
                else
1826
                {
1827
                        // build filter for file list, so we only select Stellarium screenshot files (prefix*.format)
1828
                        QString shotFilePattern = QString("%1*.%2").arg(screenShotPrefix, screenShotFormat);
×
1829
                        QStringList fileNameFilters(shotFilePattern);
×
1830
                        // get highest-numbered file in screenshot directory
1831
                        QDir dir(shotDir.filePath());
×
1832
                        QStringList existingFiles = dir.entryList(fileNameFilters);
×
1833

1834
                        // screenshot number - default to 1 for empty directory
1835
                        int shotNum = 1;
×
1836
                        if (!existingFiles.empty())
×
1837
                        {
1838
                                // already have screenshots, find largest number
1839
                                QString lastFileName = existingFiles[existingFiles.size() - 1];
×
1840

1841
                                // extract number from highest-numbered file name
1842
                                QString lastShotNumString = lastFileName.replace(screenShotPrefix, "").replace("." + screenShotFormat, "");
×
1843
                                // new screenshot number = start at highest number
1844
                                shotNum = lastShotNumString.toInt() + 1;
×
1845
                        }
×
1846

1847
                        // build new screenshot path: "path/prefix-num.format"
1848
                        // num is at least 3 characters
1849
                        QString shotNumString = QString::number(shotNum).rightJustified(3, '0');
×
1850
                        shotPathString = QString("%1/%2%3.%4").arg(shotDir.filePath(), screenShotPrefix, shotNumString, screenShotFormat);
×
1851
                        shotPath = QFileInfo(shotPathString);
×
1852
                        // validate if new screenshot number is valid (non-existent)
1853
                        while (shotPath.exists()) {
×
1854
                                shotNum++;
×
1855
                                shotNumString = QString::number(shotNum).rightJustified(3, '0');
×
1856
                                shotPathString = QString("%1/%2%3.%4").arg(shotDir.filePath(), screenShotPrefix, shotNumString, screenShotFormat);
×
1857
                                shotPath = QFileInfo(shotPathString);
×
1858
                        }
1859
                }
×
1860
        }
×
1861

1862
        // Set preferred image resolution (for some printing workflows)
1863
        im.setDotsPerMeterX(qRound(screenshotDpi*100./2.54));
×
1864
        im.setDotsPerMeterY(qRound(screenshotDpi*100./2.54));
×
1865
        qDebug() << "INFO Saving screenshot in file: " << QDir::toNativeSeparators(shotPath.filePath());
×
1866
        QImageWriter imageWriter(shotPath.filePath());
×
1867
        if (screenShotFormat=="tif")
×
1868
                imageWriter.setCompression(1); // use LZW
×
1869
        if (screenShotFormat=="jpg")
×
1870
        {
1871
                imageWriter.setQuality(75); // This is actually default
×
1872
        }
1873
        if (screenShotFormat=="jpeg")
×
1874
        {
1875
                imageWriter.setQuality(100);
×
1876
        }
1877
        if (!imageWriter.write(im))
×
1878
        {
1879
                qWarning() << "WARNING failed to write screenshot to: " << QDir::toNativeSeparators(shotPath.filePath());
×
1880
        }
1881
}
×
1882

1883
QPoint StelMainView::getMousePos() const
×
1884
{
1885
        return glWidget->mapFromGlobal(QCursor::pos());
×
1886
}
1887

1888
QOpenGLContext* StelMainView::glContext() const
×
1889
{
1890
        return glWidget->context();
×
1891
}
1892

1893
void StelMainView::glContextMakeCurrent()
×
1894
{
1895
        glWidget->makeCurrent();
×
1896
}
×
1897

1898
void StelMainView::glContextDoneCurrent()
×
1899
{
1900
        glWidget->doneCurrent();
×
1901
}
×
1902

1903
// Set the sky background color. Everything else than black creates a work of art!
1904
void StelMainView::setSkyBackgroundColor(Vec3f color)
×
1905
{
1906
        rootItem->setSkyBackgroundColor(color);
×
1907
        StelApp::getInstance().getSettings()->setValue("color/sky_background_color", color.toStr());
×
1908
        emit skyBackgroundColorChanged(color);
×
1909
}
×
1910

1911
// Get the sky background color. Everything else than black creates a work of art!
1912
Vec3f StelMainView::getSkyBackgroundColor() const
×
1913
{
1914
        return rootItem->getSkyBackgroundColor();
×
1915
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc