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

Stellarium / stellarium / 15793453064

21 Jun 2025 06:37AM UTC coverage: 11.767% (-0.002%) from 11.769%
15793453064

push

github

10110111
Add support for touch clicks and moves

0 of 30 new or added lines in 1 file covered. (0.0%)

1336 existing lines in 5 files now uncovered.

14700 of 124924 relevant lines covered (11.77%)

18313.08 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
#include "TextureAverageComputer.hpp"
37

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

73
Q_LOGGING_CATEGORY(mainview, "stel.MainView")
×
74

75
#include <clocale>
76

77
#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY
78
# define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF
79
#endif
80

81
// Initialize static variables
82
StelMainView* StelMainView::singleton = Q_NULLPTR;
83

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

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

104
        ~StelGLWidget() override
×
105
        {
×
106
                qDebug()<<"StelGLWidget destroyed";
×
107
        }
×
108

109
        void initializeGL() override
×
110
        {
111
                if(initialized)
×
112
                {
113
                        qWarning()<<"Double initialization, should not happen";
×
114
                        Q_ASSERT(false);
×
115
                        return;
116
                }
117

118
                //This seems to be the correct place to initialize all
119
                //GL related stuff of the application
120
                //this includes all the init() calls of the modules
121

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

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

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

137
                parent->init();
×
138
                initialized = true;
×
139
        }
140

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

157
private:
158
        StelMainView* parent;
159
        bool initialized;
160
};
161

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

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

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

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

232
                QPaintDevice* paintDevice = painter->device();
×
233

234
                int mainFBO;
235
                gl->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mainFBO);
×
236

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

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

256
                // stop drawing to the old paint device to make sure state is reset correctly
257
                painter->end();
×
258

259
                // create our paint device
260
                QOpenGLPaintDevice fboPaintDevice(size);
×
261
                fboPaintDevice.setDevicePixelRatio(pixelRatio);
×
262

263
                fbo->bind();
×
264
                painter->begin(&fboPaintDevice);
×
265
                drawSource(painter);
×
266
                painter->end();
×
267

268
                painter->begin(paintDevice);
×
269

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

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

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

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

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

350
private:
351
        StelMainView* parent;
352
};
353

354
class StelRootItem : public QGraphicsObject
355
{
356
public:
357
        StelRootItem(StelMainView* mainView, QGraphicsItem* parent = Q_NULLPTR)
×
358
                : QGraphicsObject(parent),
×
359
                  mainView(mainView),
×
360
                  skyBackgroundColor(0.f,0.f,0.f)
×
361
        {
362
                setFlags(QGraphicsItem::ItemClipsToShape | QGraphicsItem::ItemClipsChildrenToShape | QGraphicsItem::ItemIsFocusable);
×
363

364
                setAcceptHoverEvents(true);
×
365

366
                setAcceptTouchEvents(true);
×
367
                grabGesture(Qt::PinchGesture);
×
368
                setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton);
×
369
                previousPaintTime = StelApp::getTotalRunTime();
×
370
        }
×
371

372
        void setSize(const QSize& size)
×
373
        {
374
                prepareGeometryChange();
×
375
                rect.setSize(size);
×
376
        }
×
377

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

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

384

385
protected:
386
        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
×
387
        {
388
                Q_UNUSED(option)
389
                Q_UNUSED(widget)
390

391
                //a sanity check
392
                Q_ASSERT(mainView->glContext() == QOpenGLContext::currentContext());
×
393

394
                StelApp& app = StelApp::getInstance();
×
395

396
                // This can change even on the screen even without actual system settings change.
397
                // E.g. in KWin 6.1.5 with Wayland backend, if we set 150% scale in System Settings,
398
                // the app first gets device pixel ratio of 200%, then the widgets are rescaled to 150%
399
                // while the screen still remains at 200%. This is ugly, and shouldn't behave like this,
400
                // but the following call seems to be enough to get things working right.
401
                app.setDevicePixelsPerPixel(mainView->devicePixelRatioF());
×
402

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

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

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

416
                //update and draw
417
                app.update(dt); // may also issue GL calls
×
418
                app.draw();
×
419
                painter->endNativePainting();
×
420

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

424
        QRectF boundingRect() const override
×
425
        {
426
                return rect;
×
427
        }
428

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

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

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

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

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

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

488
        //*** Gesture and touch support, currently only for Windows
489
        bool event(QEvent * e) override
×
490
        {
491
                bool r = false;
×
NEW
492
                const auto t = e->type();
×
NEW
493
                const bool isTouch = t == QEvent::TouchBegin ||
×
NEW
494
                                     t == QEvent::TouchUpdate ||
×
495
                                     t == QEvent::TouchEnd;
496
                QTouchEvent *touchEvent;
NEW
497
                QList<QTouchEvent::TouchPoint> touchPoints;
×
NEW
498
                QPointF mousePos;
×
NEW
499
                if (isTouch)
×
500
                {
NEW
501
                        touchEvent = static_cast<QTouchEvent *>(e);
×
NEW
502
                        touchPoints = touchEvent->touchPoints();
×
NEW
503
                        r = true;
×
NEW
504
                        if (touchPoints.count() == 1)
×
NEW
505
                                setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton);
×
506
                        else
NEW
507
                                return r;
×
NEW
508
                        const auto pos = touchPoints[0].pos();
×
NEW
509
                        mousePos = QPoint(pos.x(), mainView->height()-1-pos.y());
×
510
                }
NEW
511
                std::unique_ptr<QMouseEvent> mouseEvent;
×
NEW
512
                switch (t)
×
513
                {
514
                        case QEvent::TouchBegin:
×
515
                        {
NEW
516
                                mouseEvent.reset(new QMouseEvent(QEvent::MouseButtonPress, mousePos,
×
NEW
517
                                                                 Qt::LeftButton, Qt::LeftButton, touchEvent->modifiers()));
×
NEW
518
                                break;
×
519
                        }
UNCOV
520
                        case QEvent::TouchUpdate:
×
521
                        {
NEW
522
                                mouseEvent.reset(new QMouseEvent(QEvent::MouseMove, mousePos,
×
NEW
523
                                                                 Qt::LeftButton, Qt::LeftButton, touchEvent->modifiers()));
×
NEW
524
                                break;
×
525
                        }
UNCOV
526
                        case QEvent::TouchEnd:
×
527
                        {
NEW
528
                                mouseEvent.reset(new QMouseEvent(QEvent::MouseButtonRelease, mousePos,
×
NEW
529
                                                                 Qt::LeftButton, Qt::LeftButton, touchEvent->modifiers()));
×
530
                                break;
×
531
                        }
UNCOV
532
                        case QEvent::Gesture:
×
533
                                setAcceptedMouseButtons(Qt::NoButton);
×
534
                                r = gestureEvent(static_cast<QGestureEvent*>(e));
×
535
                                break;
×
UNCOV
536
                        default:
×
537
                                r = QGraphicsObject::event(e);
×
538
                }
539

NEW
540
                if (isTouch)
×
541
                {
NEW
542
                        if (mouseEvent->type() == QEvent::MouseMove)
×
543
                        {
NEW
544
                                r = StelApp::getInstance().handleMove(mouseEvent->pos().x(), mouseEvent->pos().y(), mouseEvent->buttons());
×
545
                        }
546
                        else
547
                        {
NEW
548
                                StelApp::getInstance().handleClick(mouseEvent.get());
×
NEW
549
                                r = mouseEvent->isAccepted();
×
550
                        }
NEW
551
                        if(r) mainView->thereWasAnEvent();
×
552
                }
553

554
                return r;
×
555
        }
×
556

557
private:
558
        bool gestureEvent(QGestureEvent *event)
×
559
        {
UNCOV
560
                if (QGesture *pinch = event->gesture(Qt::PinchGesture))
×
561
                        pinchTriggered(static_cast<QPinchGesture *>(pinch));
×
562

563
                return true;
×
564
        }
565

UNCOV
566
        void pinchTriggered(QPinchGesture *gesture)
×
567
        {
UNCOV
568
                QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
×
569
                if (changeFlags & QPinchGesture::ScaleFactorChanged) {
×
570
                        qreal zoom = gesture->scaleFactor();
×
571

UNCOV
572
                        if (zoom < 2 && zoom > 0.5){
×
573
                                StelApp::getInstance().handlePinch(zoom, true);
×
574
                        }
575
                }
576
        }
×
577

578
private:
579
        //! Helper function to convert a QGraphicsSceneMouseEvent to a QMouseEvent suitable for StelApp consumption
UNCOV
580
        QMouseEvent convertMouseEvent(QGraphicsSceneMouseEvent *event) const
×
581
        {
582
                //convert graphics scene mouse event to widget style mouse event
583
                QEvent::Type t = QEvent::None;
×
UNCOV
584
                switch(event->type())
×
585
                {
UNCOV
586
                        case QEvent::GraphicsSceneMousePress:
×
UNCOV
587
                                t = QEvent::MouseButtonPress;
×
UNCOV
588
                                break;
×
589
                        case QEvent::GraphicsSceneMouseRelease:
×
590
                                t = QEvent::MouseButtonRelease;
×
UNCOV
591
                                break;
×
592
                        case QEvent::GraphicsSceneMouseMove:
×
UNCOV
593
                                t = QEvent::MouseMove;
×
UNCOV
594
                                break;
×
595
                        case QEvent::GraphicsSceneMouseDoubleClick:
×
596
                                //note: the old code seems to have ignored double clicks
597
                                // and handled them the same as normal mouse presses
598
                                //if we ever want to handle double clicks, switch out these lines
599
                                t = QEvent::MouseButtonDblClick;
×
600
                                //t = QEvent::MouseButtonPress;
601
                                break;
×
602
                        default:
×
603
                                //warn in release and assert in debug
604
                                qWarning("Unhandled mouse event type %d",event->type());
×
UNCOV
605
                                Q_ASSERT(false);
×
606
                }
607

UNCOV
608
                QPointF pos = event->scenePos();
×
609
                //Y needs to be inverted
610
                pos.setY(rect.height() - 1 - pos.y());
×
611
                return QMouseEvent(t,pos,event->button(),event->buttons(),event->modifiers());
×
612
        }
613

614
        QRectF rect;
615
        double previousPaintTime;
616
        StelMainView* mainView;
617
        Vec3f skyBackgroundColor;           //! color which is used to initialize the frame. Should be black, but for some applications e.g. dark blue may be preferred.
618
};
619

620
//! Initialize and render Stellarium gui.
621
class StelGuiItem : public QGraphicsWidget
622
{
623
public:
624
        StelGuiItem(const QSize& size, QGraphicsItem* parent = Q_NULLPTR)
×
625
                : QGraphicsWidget(parent)
×
626
        {
627
                resize(size);
×
628
                StelApp::getInstance().getGui()->init(this);
×
629
                inited=true;
×
630
        }
×
631

632
protected:
633
        void resizeEvent(QGraphicsSceneResizeEvent* event) override
×
634
        {
635
                if(!inited) return;
×
636

637
                Q_UNUSED(event)
638
                //widget->setGeometry(0, 0, size().width(), size().height());
UNCOV
639
                StelApp::getInstance().getGui()->forceRefreshGui();
×
640
        }
641
private:
642
        //QGraphicsWidget *widget;
643
        // void onSizeChanged();
644
        bool inited = false; // guards resize during construction
645
};
646

UNCOV
647
StelMainView::StelMainView(QSettings* settings)
×
648
        : QGraphicsView(),
649
          configuration(settings),
×
UNCOV
650
          guiItem(Q_NULLPTR),
×
651
          gui(Q_NULLPTR),
×
652
          stelApp(Q_NULLPTR),
×
UNCOV
653
          updateQueued(false),
×
UNCOV
654
          flagInvertScreenShotColors(false),
×
UNCOV
655
          flagScreenshotDateFileName(false),
×
UNCOV
656
          flagOverwriteScreenshots(false),
×
UNCOV
657
          flagUseCustomScreenshotSize(false),
×
UNCOV
658
          customScreenshotWidth(1024),
×
UNCOV
659
          customScreenshotHeight(768),
×
UNCOV
660
          screenshotDpi(72),
×
UNCOV
661
          screenShotPrefix("stellarium-"),
×
UNCOV
662
          screenShotFormat("png"),
×
UNCOV
663
          screenShotFileMask("yyyyMMdd-hhmmssz"),
×
UNCOV
664
          screenShotDir(""),
×
665
          flagCursorTimeout(false),
×
666
          lastEventTimeSec(0.0),
×
UNCOV
667
          minfps(1.f),
×
668
          maxfps(10000.f),
×
669
          minTimeBetweenFrames(5)
×
670
{
671
        setAttribute(Qt::WA_OpaquePaintEvent);
×
UNCOV
672
        setAttribute(Qt::WA_AcceptTouchEvents);
×
UNCOV
673
        setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
×
674
        setAutoFillBackground(false);
×
UNCOV
675
        setMouseTracking(true);
×
676

UNCOV
677
        StelApp::initStatic();
×
678

UNCOV
679
        fpsTimer = new QTimer(this);
×
680
        fpsTimer->setTimerType(Qt::PreciseTimer);
×
UNCOV
681
        fpsTimer->setInterval(qRound(1000.f/minfps));
×
UNCOV
682
        connect(fpsTimer,SIGNAL(timeout()),this,SLOT(fpsTimerUpdate()));
×
683

UNCOV
684
        cursorTimeoutTimer = new QTimer(this);
×
UNCOV
685
        cursorTimeoutTimer->setSingleShot(true);
×
UNCOV
686
        connect(cursorTimeoutTimer, SIGNAL(timeout()), this, SLOT(hideCursor()));
×
687

688
        // Can't create 2 StelMainView instances
UNCOV
689
        Q_ASSERT(!singleton);
×
690
        singleton = this;
×
691

692
        qApp->installEventFilter(this);
×
693

694
        setWindowIcon(QIcon(":/mainWindow/icon.bmp"));
×
695
        initTitleI18n();
×
696
        setObjectName("MainView");
×
697

698
        setViewportUpdateMode(QGraphicsView::NoViewportUpdate);
×
699
        setFrameShape(QFrame::NoFrame);
×
700
        setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
×
701
        setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
×
702
        //because we only want child elements to have focus, we turn it off here
703
        setFocusPolicy(Qt::NoFocus);
×
704
        connect(this, SIGNAL(screenshotRequested()), this, SLOT(doScreenshot()));
×
705

706
#ifdef OPENGL_DEBUG_LOGGING
707
        if (QApplication::testAttribute(Qt::AA_UseOpenGLES))
708
        {
709
                // QOpenGLDebugLogger doesn't work with OpenGLES's GL_KHR_debug.
710
                // See Qt Bug 62070: https://bugreports.qt.io/browse/QTBUG-62070
711

712
                glLogger = Q_NULLPTR;
713
        }
714
        else
715
        {
716
                glLogger = new QOpenGLDebugLogger(this);
717
                connect(glLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this, SLOT(logGLMessage(QOpenGLDebugMessage)));
718
        }
719
#endif
720

721
        QSurfaceFormat glFormat = getDesiredGLFormat(configuration);
×
722
        glWidget = new StelGLWidget(glFormat, this);
×
723
        setViewport(glWidget);
×
724

725
        stelScene = new StelGraphicsScene(this);
×
726
        setScene(stelScene);
×
727
        scene()->setItemIndexMethod(QGraphicsScene::NoIndex);
×
UNCOV
728
        rootItem = new StelRootItem(this);
×
729

730
        // Workaround (see Bug #940638) Although we have already explicitly set
731
        // LC_NUMERIC to "C" in main.cpp there seems to be a bug in OpenGL where
732
        // it will silently reset LC_NUMERIC to the value of LC_ALL during OpenGL
733
        // initialization. This has been observed on Ubuntu 11.10 under certain
734
        // circumstances, so here we set it again just to be on the safe side.
735
        setlocale(LC_NUMERIC, "C");
×
736
        // End workaround
737

738
        // We cannot use global mousetracking. Only if mouse is hidden!
739
        //setMouseTracking(true);
740

741
    setRenderHint(QPainter::Antialiasing);
×
742
}
×
743

744
void StelMainView::resizeEvent(QResizeEvent* event)
×
745
{
UNCOV
746
        if(scene())
×
747
        {
UNCOV
748
                const QSize& sz = event->size();
×
UNCOV
749
                scene()->setSceneRect(QRect(QPoint(0, 0), sz));
×
UNCOV
750
                rootItem->setSize(sz);
×
UNCOV
751
                if(guiItem)
×
UNCOV
752
                        guiItem->setGeometry(QRectF(0.0,0.0,sz.width(),sz.height()));
×
UNCOV
753
                if (StelApp::isInitialized()  && !isFullScreen())
×
754
                {
UNCOV
755
                        StelApp::immediateSave("video/screen_w", sz.width());
×
UNCOV
756
                        StelApp::immediateSave("video/screen_h", sz.height());
×
757
                }
UNCOV
758
                emit sizeChanged(sz);
×
759
        }
UNCOV
760
        QGraphicsView::resizeEvent(event);
×
UNCOV
761
}
×
762

763
bool StelMainView::eventFilter(QObject *obj, QEvent *event)
×
764
{
UNCOV
765
        if(event->type() == QEvent::FileOpen)
×
766
        {
767
                QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
×
768
                //qDebug() << "load script:" << openEvent->file();
769
                qApp->setProperty("onetime_startup_script", openEvent->file());
×
770
        }
UNCOV
771
        return QGraphicsView::eventFilter(obj, event);
×
772
}
773

UNCOV
774
void StelMainView::mouseMoveEvent(QMouseEvent *event)
×
775
{
776
        if (flagCursorTimeout) 
×
777
        {
778
                // Show the previous cursor and reset the timeout if the current is "hidden"
UNCOV
779
                if (QGuiApplication::overrideCursor() && (QGuiApplication::overrideCursor()->shape() == Qt::BlankCursor) )
×
780
                {
UNCOV
781
                        QGuiApplication::restoreOverrideCursor();
×
782
                }
783

UNCOV
784
                cursorTimeoutTimer->start();
×
785
        }
786
        
787
#ifdef MOUSE_TRACKING
UNCOV
788
        QPointF pos = event->pos();
×
789
        //Y needs to be inverted
790
        int height1 = StelApp::getInstance().mainWin->height();
×
791
        pos.setY(height1 - 1 - pos.y());
×
792
        event->setAccepted(StelApp::getInstance().handleMove(pos.x(), pos.y(), event->buttons()));
×
793
        if(event->isAccepted())
×
794
                thereWasAnEvent();
×
795
#endif
796

797
        QGraphicsView::mouseMoveEvent(event);
×
UNCOV
798
}
×
799

800

801
void StelMainView::focusSky() {
×
802
        //scene()->setActiveWindow(0);
UNCOV
803
        rootItem->setFocus();
×
804
}
×
805

806
StelMainView::~StelMainView()
×
807
{
808
        //delete the night view graphic effect here while GL context is still valid
UNCOV
809
        rootItem->setGraphicsEffect(Q_NULLPTR);
×
810
        StelApp::deinitStatic();
×
UNCOV
811
        delete guiItem;
×
812
        guiItem=Q_NULLPTR;
×
UNCOV
813
}
×
814

815
QSurfaceFormat StelMainView::getDesiredGLFormat(QSettings* configuration)
×
816
{
817
        //use the default format as basis
UNCOV
818
        QSurfaceFormat fmt = QSurfaceFormat::defaultFormat();
×
UNCOV
819
        qDebug() << "Default surface format: " << fmt;
×
820

821
        //if on an GLES build, do not set the format
822
        const auto openGLModuleType = QOpenGLContext::openGLModuleType();
×
UNCOV
823
        qInfo() << "OpenGL module type:" << (openGLModuleType==QOpenGLContext::LibGL
×
824
                                                                                  ? "desktop OpenGL"
825
                                                                                  : openGLModuleType==QOpenGLContext::LibGL
UNCOV
826
                                                                                        ? "OpenGL ES 2 or higher"
×
UNCOV
827
                                                                                        : std::to_string(openGLModuleType).c_str());
×
UNCOV
828
        if (openGLModuleType==QOpenGLContext::LibGL)
×
829
        {
UNCOV
830
                fmt.setRenderableType(QSurfaceFormat::OpenGL);
×
831
                fmt.setVersion(3, 3);
×
832
                fmt.setProfile(QSurfaceFormat::CoreProfile);
×
833

834
                if (qApp && qApp->property("onetime_opengl_compat").toBool())
×
835
                {
UNCOV
836
                        qInfo() << "Setting OpenGL Compatibility profile from command line...";
×
UNCOV
837
                        fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
×
838
                        fmt.setOption(QSurfaceFormat::DeprecatedFunctions);
×
839
                }
840
                // FIXME: temporary hook for Qt5-based macOS bundles
841
                #if defined(Q_OS_MACOS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
842
                fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
843
                #endif
844
        }
845

846
        // NOTE: multisampling is implemented via FBO now, so it's not requested here.
847

848
        //request some sane buffer formats
UNCOV
849
        fmt.setRedBufferSize(8);
×
850
        fmt.setGreenBufferSize(8);
×
851
        fmt.setBlueBufferSize(8);
×
852
        fmt.setDepthBufferSize(24);
×
853
        // Without stencil buffer the debug versions of Qt will repeatedly emit warnings like:
854
        // "OpenGL paint engine: attempted to use stencil test without requesting a stencil buffer."
UNCOV
855
        fmt.setStencilBufferSize(8);
×
856

UNCOV
857
        if(qApp && qApp->property("onetime_single_buffer").toBool())
×
UNCOV
858
                fmt.setSwapBehavior(QSurfaceFormat::SingleBuffer);
×
859

860
        // VSync control. NOTE: it must be applied to the default format (QSurfaceFormat::setDefaultFormat) to take effect.
861
#ifdef Q_OS_MACOS
862
        // FIXME: workaround for bug LP:#1705832 (https://bugs.launchpad.net/stellarium/+bug/1705832)
863
        // Qt: https://bugreports.qt.io/browse/QTBUG-53273
864
        const bool vsdef = false; // use vsync=false by default on macOS
865
#else
UNCOV
866
        const bool vsdef = true;
×
867
#endif
868
        if (configuration && configuration->value("video/vsync", vsdef).toBool())
×
869
                fmt.setSwapInterval(1);
×
870
        else
871
                fmt.setSwapInterval(0);
×
872

873
#ifdef OPENGL_DEBUG_LOGGING
874
        //try to enable GL debugging using GL_KHR_debug
875
        fmt.setOption(QSurfaceFormat::DebugContext);
876
#endif
877

878
        return fmt;
×
879
}
×
880

UNCOV
881
void StelMainView::init()
×
882
{
883
#ifdef OPENGL_DEBUG_LOGGING
884
        if (glLogger)
885
        {
886
                if(!QOpenGLContext::currentContext()->hasExtension(QByteArrayLiteral("GL_KHR_debug")))
887
                        qWarning()<<"GL_KHR_debug extension missing, OpenGL debug logger will likely not work";
888
                if(glLogger->initialize())
889
                {
890
                        qInfo()<<"OpenGL debug logger initialized";
891
                        glLogger->disableMessages(QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::AnyType,
892
                                                                          QOpenGLDebugMessage::NotificationSeverity);
893
                        glLogger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
894
                        //the internal log buffer may not be empty, so check it
895
                        for (const auto& msg : glLogger->loggedMessages())
896
                        {
897
                                logGLMessage(msg);
898
                        }
899
                }
900
                else
901
                        qWarning()<<"Failed to initialize OpenGL debug logger";
902

903
                connect(QOpenGLContext::currentContext(),SIGNAL(aboutToBeDestroyed()),this,SLOT(contextDestroyed()));
904
                //for easier debugging, print the address of the main GL context
905
                qDebug()<<"CurCtxPtr:"<<QOpenGLContext::currentContext();
906
        }
907
#endif
908

909
        qInfo() << "Initializing StelMainView";
×
910

UNCOV
911
        glInfo.mainContext = QOpenGLContext::currentContext();
×
912
        glInfo.surface = glInfo.mainContext->surface();
×
UNCOV
913
        glInfo.functions = glInfo.mainContext->functions();
×
UNCOV
914
        glInfo.vendor = QString(reinterpret_cast<const char*>(glInfo.functions->glGetString(GL_VENDOR)));
×
UNCOV
915
        glInfo.renderer = QString(reinterpret_cast<const char*>(glInfo.functions->glGetString(GL_RENDERER)));
×
UNCOV
916
        const auto format = glInfo.mainContext->format();
×
UNCOV
917
        glInfo.supportsLuminanceTextures = format.profile() == QSurfaceFormat::CompatibilityProfile ||
×
UNCOV
918
                                                                           format.majorVersion() < 3;
×
919
        glInfo.isGLES = format.renderableType()==QSurfaceFormat::OpenGLES;
×
920
        qInfo().nospace() << "Luminance textures are " << (glInfo.supportsLuminanceTextures ? "" : "not ") << "supported";
×
UNCOV
921
        glInfo.isCoreProfile = format.profile() == QSurfaceFormat::CoreProfile;
×
922
        #if defined Q_OS_HAIKU || defined Q_OS_NETBSD || defined Q_OS_OPENBSD || defined Q_OS_SOLARIS
923
        // Haiku OS/NetBSD/OpenBSD/Solaris hasn't hardware acceleration and we shouldn't use High Graphics Mode here
924
        glInfo.isHighGraphicsMode = false;
925
        #else
UNCOV
926
        glInfo.isHighGraphicsMode = !qApp->property("onetime_force_low_graphics").toBool() && !!StelOpenGL::highGraphicsFunctions();
×
927
        #endif
UNCOV
928
        qInfo() << "Running in" << (glInfo.isHighGraphicsMode ? "High" : "Low") << "Graphics Mode";
×
929

UNCOV
930
        auto& gl = *QOpenGLContext::currentContext()->functions();
×
UNCOV
931
        if(format.majorVersion() * 1000 + format.minorVersion() >= 4006 ||
×
UNCOV
932
           glInfo.mainContext->hasExtension("GL_EXT_texture_filter_anisotropic") ||
×
UNCOV
933
           glInfo.mainContext->hasExtension("GL_ARB_texture_filter_anisotropic"))
×
934
        {
UNCOV
935
                StelOpenGL::clearGLErrors();
×
UNCOV
936
                gl.glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &glInfo.maxAnisotropy);
×
UNCOV
937
                const auto error = gl.glGetError();
×
UNCOV
938
                if(error != GL_NO_ERROR)
×
939
                {
UNCOV
940
                        qWarning() << "Failed to get maximum texture anisotropy:" << StelOpenGL::getGLErrorText(error);
×
941
                }
UNCOV
942
                else if(glInfo.maxAnisotropy > 0)
×
943
                {
UNCOV
944
                        qInfo() << "Maximum texture anisotropy:" << glInfo.maxAnisotropy;
×
945
                }
946
                else
947
                {
UNCOV
948
                        qWarning() << "Maximum texture anisotropy is not positive:" << glInfo.maxAnisotropy;
×
UNCOV
949
                        glInfo.maxAnisotropy = 0;
×
950
                }
951
        }
952
        else
953
        {
954
                qWarning() << "Anisotropic filtering is not supported!";
×
955
        }
956

957
        if(format.majorVersion() > 4 || glInfo.mainContext->hasExtension("GL_ARB_sample_shading"))
×
958
        {
959
                auto addr = glInfo.mainContext->getProcAddress("glMinSampleShading");
×
960
                if(!addr)
×
961
                        addr = glInfo.mainContext->getProcAddress("glMinSampleShadingARB");
×
962
                glInfo.glMinSampleShading = reinterpret_cast<PFNGLMINSAMPLESHADINGPROC>(addr);
×
963
        }
UNCOV
964
        gl.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glInfo.maxTextureSize);
×
UNCOV
965
        qInfo() << "Maximum 2D texture size:" << glInfo.maxTextureSize;
×
966

967
        gui = new StelGui();
×
968

969
        // Should be check of requirements disabled? -- NO! This is intentional here, and does no harm.
UNCOV
970
        if (configuration->value("main/check_requirements", true).toBool())
×
971
        {
972
                // Find out lots of debug info about supported version of OpenGL and vendor/renderer.
973
                processOpenGLdiagnosticsAndWarnings(configuration, glInfo.mainContext);
×
974
        }
975

976
        //setup StelOpenGLArray global state
977
        StelOpenGLArray::initGL();
×
978

979
        //create and initialize main app
UNCOV
980
        stelApp = new StelApp(this);
×
981
        stelApp->setGui(gui);
×
UNCOV
982
        stelApp->init(configuration);
×
983
        //this makes sure the app knows how large the window is
UNCOV
984
        connect(stelScene,SIGNAL(sceneRectChanged(QRectF)),stelApp,SLOT(glWindowHasBeenResized(QRectF)));
×
985
#ifdef ENABLE_SPOUT
986
        QObject::connect(stelScene, &StelGraphicsScene::sceneRectChanged, [&](const QRectF& rect)
987
        {
988
        stelApp->glPhysicalWindowHasBeenResized(getPhysicalSize(rect));
989
        });
990
#endif
991

992
        //also immediately set the current values
UNCOV
993
        stelApp->glWindowHasBeenResized(stelScene->sceneRect());
×
994
#ifdef ENABLE_SPOUT
995
        stelApp->glPhysicalWindowHasBeenResized(getPhysicalSize(stelScene->sceneRect()));
996
#endif
997

998
        StelActionMgr *actionMgr = stelApp->getStelActionManager();
×
UNCOV
999
        actionMgr->addAction("actionSave_Screenshot_Global", N_("Miscellaneous"), N_("Save screenshot"), this, "saveScreenShot()", "Ctrl+S");
×
1000
        actionMgr->addAction("actionReload_Shaders", N_("Miscellaneous"), N_("Reload shaders (for development)"), this, "reloadShaders()", "Ctrl+R, P");
×
1001
        actionMgr->addAction("actionSet_Full_Screen_Global", N_("Display Options"), N_("Full-screen mode"), this, "fullScreen", "F11");
×
1002
        
1003
        StelPainter::initGLShaders();
×
1004

1005
        guiItem = new StelGuiItem(size(), rootItem);
×
1006
        scene()->addItem(rootItem);
×
1007
        //set the default focus to the sky
1008
        focusSky();
×
UNCOV
1009
        nightModeEffect = new NightModeGraphicsEffect(this);
×
UNCOV
1010
        updateNightModeProperty(StelApp::getInstance().getVisionModeNight());
×
1011
        //install the effect on the whole view
UNCOV
1012
        rootItem->setGraphicsEffect(nightModeEffect);
×
1013

1014
        flagInvertScreenShotColors = configuration->value("main/invert_screenshots_colors", false).toBool();
×
UNCOV
1015
        setScreenshotFormat(configuration->value("main/screenshot_format", "png").toString()); // includes check for supported formats.
×
UNCOV
1016
        flagScreenshotDateFileName=configuration->value("main/screenshot_datetime_filename", false).toBool();
×
UNCOV
1017
        screenShotFileMask = configuration->value("main/screenshot_datetime_filemask", "yyyyMMdd-hhmmssz").toString();
×
1018
        flagUseCustomScreenshotSize=configuration->value("main/screenshot_custom_size", false).toBool();
×
UNCOV
1019
        customScreenshotWidth=configuration->value("main/screenshot_custom_width", 1024).toInt();
×
UNCOV
1020
        customScreenshotHeight=configuration->value("main/screenshot_custom_height", 768).toInt();
×
1021
        screenshotDpi=configuration->value("main/screenshot_dpi", 72).toInt();
×
1022
        setFlagCursorTimeout(configuration->value("gui/flag_mouse_cursor_timeout", false).toBool());
×
1023
        setCursorTimeout(configuration->value("gui/mouse_cursor_timeout", 10.f).toDouble());
×
UNCOV
1024
        setMaxFps(configuration->value("video/maximum_fps",10000.f).toFloat());
×
1025
        setMinFps(configuration->value("video/minimum_fps",10000.f).toFloat());
×
UNCOV
1026
        setMinTimeBetweenFrames(qMax(0, configuration->value("video/min_time_between_frames",5).toInt()));
×
UNCOV
1027
        setSkyBackgroundColor(Vec3f(configuration->value("color/sky_background_color", "0,0,0").toString()));
×
1028

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

1033
        // The script manager can only be fully initialized after the plugins have loaded.
1034
        stelApp->initScriptMgr();
×
1035

1036
        // Set the global stylesheet, this is only useful for the tooltips.
UNCOV
1037
        StelGui* sgui = dynamic_cast<StelGui*>(stelApp->getGui());
×
UNCOV
1038
        if (sgui!=Q_NULLPTR)
×
1039
                setStyleSheet(sgui->getStelStyle().qtStyleSheet);
×
1040
        connect(stelApp, SIGNAL(visionNightModeChanged(bool)), this, SLOT(updateNightModeProperty(bool)));
×
1041

1042
        // I doubt this will have any effect on framerate, but may cause problems elsewhere?
UNCOV
1043
        QThread::currentThread()->setPriority(QThread::HighestPriority);
×
1044
#ifndef NDEBUG
1045
        // Get an overview of module callOrders
1046
        if (qApp->property("verbose")==true)
×
1047
        {
UNCOV
1048
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionDraw);
×
1049
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionUpdate);
×
1050
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleMouseClicks);
×
1051
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleMouseMoves);
×
UNCOV
1052
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleKeys);
×
1053
        }
1054
#endif
1055

1056
        // check conflicts for keyboard shortcuts...
1057
        if (configuration->childGroups().contains("shortcuts"))
×
1058
        {
1059
                QStringList defaultShortcuts =  actionMgr->getShortcutsList();
×
1060
                QStringList conflicts;
×
1061
                configuration->beginGroup("shortcuts");
×
1062
                QStringList cstActionNames = configuration->allKeys();
×
1063
                QMultiMap<QString, QString> cstActionsMap; // It is possible we have a very messed-up setup with duplicates
×
1064
                for (QStringList::const_iterator cstActionName = cstActionNames.constBegin(); cstActionName != cstActionNames.constEnd(); ++cstActionName)
×
1065
                {
1066
                        #if (QT_VERSION>=QT_VERSION_CHECK(5, 14, 0))
1067
                        QStringList singleCustomActionShortcuts = configuration->value((*cstActionName).toLocal8Bit().constData()).toString().split(" ", Qt::SkipEmptyParts);
×
1068
                        #else
1069
                        QStringList singleCustomActionShortcuts = configuration->value((*cstActionName).toLocal8Bit().constData()).toString().split(" ", QString::SkipEmptyParts);
1070
                        #endif
UNCOV
1071
                        singleCustomActionShortcuts.removeAll("\"\"");
×
1072

1073
                        // Add 1-2 entries per action
UNCOV
1074
                        for (QStringList::const_iterator cstActionShortcut = singleCustomActionShortcuts.constBegin(); cstActionShortcut != singleCustomActionShortcuts.constEnd(); ++cstActionShortcut)
×
1075
                                if (strcmp( (*cstActionShortcut).toLocal8Bit().constData(), "") )
×
UNCOV
1076
                                        cstActionsMap.insert((*cstActionShortcut), (*cstActionName));
×
UNCOV
1077
                }
×
1078
                // Now we have a QMultiMap with (customShortcut, actionName). It may contain multiple keys!
1079
                QStringList allMapKeys=cstActionsMap.keys();
×
1080
                QStringList uniqueMapKeys=cstActionsMap.uniqueKeys();
×
1081
                for (auto &key : uniqueMapKeys)
×
UNCOV
1082
                        allMapKeys.removeOne(key);
×
UNCOV
1083
                conflicts << allMapKeys; // Add the remaining (duplicate) keys
×
1084

1085
                // Check every shortcut from the Map that it is not assigned to its own correct action
UNCOV
1086
                for (QMultiMap<QString, QString>::const_iterator it=cstActionsMap.constBegin(); it != cstActionsMap.constEnd(); ++it)
×
1087
                {
UNCOV
1088
                        QString customKey(it.key());
×
1089
                        QString actionName=cstActionsMap.value(it.key());
×
1090
                        StelAction *action = actionMgr->findAction(actionName);
×
1091
                        if (action && defaultShortcuts.contains(customKey) && actionMgr->findActionFromShortcut(customKey)->getId()!=action->getId())
×
1092
                                conflicts << customKey;
×
1093
                }
×
UNCOV
1094
                configuration->endGroup();
×
1095

UNCOV
1096
                if (!conflicts.isEmpty())
×
1097
                {
1098
                        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);
×
UNCOV
1099
                        qCritical() << "Conflicting keyboard shortcut assignments found. Please resolve:" << conflicts.join("; "); // Repeat in logfile for later retrieval.
×
1100
                }
1101
        }
×
1102
}
×
1103

1104
void StelMainView::updateNightModeProperty(bool b)
×
1105
{
1106
        // So that the bottom bar tooltips get properly rendered in night mode.
UNCOV
1107
        setProperty("nightMode", b);
×
1108
        nightModeEffect->setEnabled(b);
×
UNCOV
1109
}
×
1110

UNCOV
1111
void StelMainView::reloadShaders()
×
1112
{
1113
        //make sure GL context is bound
UNCOV
1114
        glContextMakeCurrent();
×
1115
        emit reloadShadersRequested();
×
1116
}
×
1117

1118
// This is a series of various diagnostics based on "bugs" reported for 0.13.0 and 0.13.1.
1119
// Almost all can be traced to insufficient driver level.
1120
// No changes of OpenGL state is done here.
1121
// If problems are detected, warn the user one time, but continue. Warning panel will be suppressed on next start.
1122
// Work in progress, as long as we get reports about bad systems or until OpenGL startup is finalized and safe.
1123
// Several tests do not apply to MacOS X.
1124
void StelMainView::processOpenGLdiagnosticsAndWarnings(QSettings *conf, QOpenGLContext *context) const
×
1125
{
1126
#ifdef Q_OS_MACOS
1127
        Q_UNUSED(conf);
1128
#endif
1129
        QSurfaceFormat format=context->format();
×
1130

1131
        // These tests are not required on MacOS X
1132
#ifndef Q_OS_MACOS
1133
        bool openGLerror=false;
×
1134
        if (format.renderableType()==QSurfaceFormat::OpenGL || format.renderableType()==QSurfaceFormat::OpenGLES)
×
1135
        {
UNCOV
1136
                qInfo().noquote() << "Detected:" << (format.renderableType()==QSurfaceFormat::OpenGL  ? "OpenGL" : "OpenGL ES" ) << QString("%1.%2").arg(format.majorVersion()).arg(format.minorVersion());
×
1137
        }
1138
        else
1139
        {
1140
                openGLerror=true;
×
UNCOV
1141
                qCritical() << "Neither OpenGL nor OpenGL ES detected: Unsupported Format!";
×
1142
        }
1143
#endif
UNCOV
1144
        QOpenGLFunctions* gl = context->functions();
×
1145

UNCOV
1146
        QString glDriver(reinterpret_cast<const char*>(gl->glGetString(GL_VERSION)));
×
UNCOV
1147
        qInfo().noquote() << "Driver version string:" << glDriver;
×
1148
        qInfo().noquote() << "GL vendor:" << QString(reinterpret_cast<const char*>(gl->glGetString(GL_VENDOR)));
×
1149
        QString glRenderer(reinterpret_cast<const char*>(gl->glGetString(GL_RENDERER)));
×
1150
        qInfo().noquote() << "GL renderer:" << glRenderer;
×
1151

1152
        // 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).
1153
        // 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
1154
        // detected as providing ps_3_0.
1155
        // This means, usually systems with OpenGL3 support reported in the driver will work, those with reported 2.1 only will almost certainly fail.
1156
        // If platform does not even support minimal OpenGL version for Qt5, then tell the user about troubles and quit from application.
1157
        // This test is apparently not applicable on MacOS X due to its behaving differently from all other known OSes.
1158
        // The correct way to handle driver issues on MacOS X remains however unclear for now.
1159
#ifndef Q_OS_MACOS
UNCOV
1160
        bool isMesa=glDriver.contains("Mesa", Qt::CaseInsensitive);
×
1161
        #if (defined Q_OS_WIN) && (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1162
        bool isANGLE=glRenderer.startsWith("ANGLE", Qt::CaseSensitive);
1163
        #endif
UNCOV
1164
        if ( openGLerror ||
×
1165
             ((format.renderableType()==QSurfaceFormat::OpenGL  ) && (format.version() < QPair<int, int>(2, 1)) && !isMesa) ||
×
UNCOV
1166
             ((format.renderableType()==QSurfaceFormat::OpenGL  ) && (format.version() < QPair<int, int>(2, 0)) &&  isMesa) || // Mesa defaults to 2.0 but works!
×
UNCOV
1167
             ((format.renderableType()==QSurfaceFormat::OpenGLES) && (format.version() < QPair<int, int>(2, 0)))  )
×
1168
        {
1169
        #if (defined Q_OS_WIN) && (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1170
                if ((!isANGLE) && (!isMesa))
1171
                        qCritical() << "Oops... Insufficient OpenGL version. Please update drivers, graphics hardware, or use --angle-mode (or even --mesa-mode) option.";
1172
                else if (isANGLE)
1173
                        qCritical() << "Oops... Insufficient OpenGLES version in ANGLE. Please update drivers, graphics hardware, or use --mesa-mode option.";
1174
        #elif (defined Q_OS_WIN)
1175
                if (!isMesa)
1176
                        qCritical() << "Oops... Insufficient OpenGL version. Please update drivers, graphics hardware, or use --mesa-mode option.";
1177
                else
1178
                        qCritical() << "Oops... Insufficient OpenGL version. Mesa failed! Please send a bug report.";
1179

1180
                #if (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1181
                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);
1182
                #else
1183
                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Insufficient OpenGL version. Please update drivers, graphics hardware, or use --mesa-mode option."), QMessageBox::Abort, QMessageBox::Abort);
1184
                #endif
1185
        #else
UNCOV
1186
                qCritical() << "Oops... Insufficient OpenGL version. Please update drivers, or graphics hardware.";
×
1187
                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Insufficient OpenGL version. Please update drivers, or graphics hardware."), QMessageBox::Abort, QMessageBox::Abort);
×
1188
        #endif
1189
                exit(1);
×
1190
        }
1191
#endif
1192
        // This call requires OpenGL2+.
UNCOV
1193
        QString glslString(reinterpret_cast<const char*>(gl->glGetString(GL_SHADING_LANGUAGE_VERSION)));
×
UNCOV
1194
        qInfo().noquote() << "GL Shading Language version:" << glslString;
×
1195

1196
        // Only give extended info if called on command line, for diagnostic.
UNCOV
1197
        if (qApp->property("dump_OpenGL_details").toBool())
×
UNCOV
1198
                dumpOpenGLdiagnostics();
×
1199

1200
#if (defined Q_OS_WIN) && (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1201
        // If we have ANGLE, check esp. for insufficient ps_2 level.
1202
        if (isANGLE)
1203
        {
1204
                static const QRegularExpression angleVsPsRegExp(" vs_(\\d)_(\\d) ps_(\\d)_(\\d)");
1205
                int angleVSPSpos=glRenderer.indexOf(angleVsPsRegExp);
1206

1207
                if (angleVSPSpos >-1)
1208
                {
1209
                        QRegularExpressionMatch match=angleVsPsRegExp.match(glRenderer);
1210
                        float vsVersion=match.captured(1).toFloat() + 0.1f*match.captured(2).toFloat();
1211
                        float psVersion=match.captured(3).toFloat() + 0.1f*match.captured(4).toFloat();
1212
                        qInfo() << "VS Version Number detected:" << vsVersion;
1213
                        qInfo() << "PS Version Number detected:" << psVersion;
1214
                        if ((vsVersion<2.0f) || (psVersion<3.0f))
1215
                        {
1216
                                openGLerror=true;
1217
                                qCritical() << "This is not enough: we need DirectX9 with vs_2_0 and ps_3_0 or later.";
1218
                                qCritical() << "You should update graphics drivers, graphics hardware, or use the --mesa-mode option.";
1219
                                qCritical() << "Else, please try to use an older version like 0.12.9, and try with --safe-mode";
1220

1221
                                if (conf->value("main/ignore_opengl_warning", false).toBool())
1222
                                {
1223
                                        qWarning() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
1224
                                }
1225
                                else
1226
                                {
1227
                                        qInfo() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
1228
                                        qInfo() << "But more than likely problems will persist.";
1229
                                        QMessageBox::StandardButton answerButton=
1230
                                        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?"),
1231
                                                              QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
1232
                                        if (answerButton == QMessageBox::Abort)
1233
                                        {
1234
                                                qCritical() << "Aborting due to ANGLE OpenGL ES / DirectX vs or ps version problems.";
1235
                                                exit(1);
1236
                                        }
1237
                                        else
1238
                                        {
1239
                                                qWarning() << "Ignoring all warnings, continuing without further question.";
1240
                                                conf->setValue("main/ignore_opengl_warning", true);
1241
                                        }
1242
                                }
1243
                        }
1244
                        else
1245
                                qInfo() << "vs/ps version is fine, we should not see a graphics problem.";
1246
                }
1247
                else
1248
                {
1249
                        qCritical() << "Cannot parse ANGLE shader version string. This may indicate future problems.";
1250
                        qCritical() << "Please send a bug report that includes this log file and states if Stellarium runs or has problems.";
1251
                }
1252
        }
1253
#endif
1254
#ifndef Q_OS_MACOS
1255
        // 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.
UNCOV
1256
        if (isMesa)
×
1257
        {
UNCOV
1258
                static const QRegularExpression mesaRegExp("Mesa (\\d+\\.\\d+)"); // we need only major version. Minor should always be here. Test?
×
UNCOV
1259
                int mesaPos=glDriver.indexOf(mesaRegExp);
×
1260

UNCOV
1261
                if (mesaPos >-1)
×
1262
                {
UNCOV
1263
                        float mesaVersion=mesaRegExp.match(glDriver).captured(1).toFloat();
×
UNCOV
1264
                        qInfo() << "MESA version number detected:" << mesaVersion;
×
UNCOV
1265
                        if ((mesaVersion<10.0f))
×
1266
                        {
UNCOV
1267
                                openGLerror=true;
×
UNCOV
1268
                                qCritical() << "This is not enough: we need Mesa 10.0 or later.";
×
UNCOV
1269
                                qCritical() << "You should update graphics drivers or graphics hardware.";
×
UNCOV
1270
                                qCritical() << "Else, please try to use an older version like 0.12.9, and try there with --safe-mode";
×
1271

UNCOV
1272
                                if (conf->value("main/ignore_opengl_warning", false).toBool())
×
1273
                                {
UNCOV
1274
                                        qWarning() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
×
1275
                                }
1276
                                else
1277
                                {
UNCOV
1278
                                        qInfo() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
×
UNCOV
1279
                                        qInfo() << "But more than likely problems will persist.";
×
1280
                                        QMessageBox::StandardButton answerButton=
UNCOV
1281
                                        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?"),
×
1282
                                                              QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
UNCOV
1283
                                        if (answerButton == QMessageBox::Abort)
×
1284
                                        {
UNCOV
1285
                                                qCritical() << "Aborting due to OpenGL/Mesa insufficient version problems.";
×
UNCOV
1286
                                                exit(1);
×
1287
                                        }
1288
                                        else
1289
                                        {
UNCOV
1290
                                                qWarning() << "Ignoring all warnings, continuing without further question.";
×
UNCOV
1291
                                                conf->setValue("main/ignore_opengl_warning", true);
×
1292
                                        }
1293
                                }
1294
                        }
1295
                        else
UNCOV
1296
                                qInfo() << "Mesa version is fine, we should not see a graphics problem.";
×
1297
                }
1298
                else
1299
                {
1300
                        qCritical() << "Cannot parse Mesa Driver version string. This may indicate future problems.";
×
UNCOV
1301
                        qCritical() << "Please send a bug report that includes this log file and states if Stellarium runs or has problems.";
×
1302
                }
1303
        }
1304
#endif
1305

1306
        // Although our shaders are only GLSL1.10, there are frequent problems with systems just at this level of programmable shaders.
1307
        // If GLSL version is less than 1.30 or GLSL ES 1.00, Stellarium usually does run properly on Windows or various Linux flavours.
1308
        // Depending on whatever driver/implementation details, Stellarium may crash or show only minor graphical errors.
1309
        // On these systems, we show a warning panel that can be suppressed by a config option which is automatically added on first run.
1310
        // Again, based on a sample size of one, Macs have been reported already to always work in this case.
1311
#ifndef Q_OS_MACOS
UNCOV
1312
        static const QRegularExpression glslRegExp("^(\\d\\.\\d\\d)");
×
1313
        int pos=glslString.indexOf(glslRegExp);
×
1314
        // VC4 drivers on Raspberry Pi reports ES 1.0.16 or so, we must step down to one cipher after decimal.
1315
        static const QRegularExpression glslesRegExp("ES (\\d\\.\\d)");
×
UNCOV
1316
        int posES=glslString.indexOf(glslesRegExp);
×
UNCOV
1317
        if (pos >-1)
×
1318
        {
1319
                float glslVersion=glslRegExp.match(glslString).captured(1).toFloat();
×
1320
                qInfo() << "GLSL Version Number detected:" << glslVersion;
×
UNCOV
1321
                if (glslVersion<1.3f)
×
1322
                {
UNCOV
1323
                        openGLerror=true;
×
1324
                        qCritical() << "This is not enough: we need GLSL1.30 or later.";
×
1325
                        #ifdef Q_OS_WIN
1326
                        qCritical() << "You should update graphics drivers, graphics hardware, or use the --mesa-mode option.";
1327
                        #else
UNCOV
1328
                        qCritical() << "You should update graphics drivers or graphics hardware.";
×
1329
                        #endif
UNCOV
1330
                        qCritical() << "Else, please try to use an older version like 0.12.9, and try there with --safe-mode";
×
1331

1332
                        if (conf->value("main/ignore_opengl_warning", false).toBool())
×
1333
                        {
UNCOV
1334
                                qWarning() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
×
1335
                        }
1336
                        else
1337
                        {
UNCOV
1338
                                qInfo() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
×
UNCOV
1339
                                qInfo() << "But more than likely problems will persist.";
×
1340
                                QMessageBox::StandardButton answerButton=
1341
                                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?"),
×
1342
                                                      QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
UNCOV
1343
                                if (answerButton == QMessageBox::Abort)
×
1344
                                {
UNCOV
1345
                                        qCritical() << "Aborting due to OpenGL/GLSL version problems.";
×
UNCOV
1346
                                        exit(1);
×
1347
                                }
1348
                                else
1349
                                {
UNCOV
1350
                                        qWarning() << "Ignoring all warnings, continuing without further question.";
×
UNCOV
1351
                                        conf->setValue("main/ignore_opengl_warning", true);
×
1352
                                }
1353
                        }
1354
                }
1355
                else
1356
                        qInfo() << "GLSL version is fine, we should not see a graphics problem.";
×
1357
        }
1358
        else if (posES >-1)
×
1359
        {
1360
                float glslesVersion=glslesRegExp.match(glslString).captured(1).toFloat();
×
1361
                qInfo() << "GLSL ES Version Number detected:" << glslesVersion;
×
1362
                if (glslesVersion<1.0f) // TBD: is this possible at all?
×
1363
                {
1364
                        openGLerror=true;
×
1365
                        qCritical() << "This is not enough: we need GLSL ES 1.00 or later.";
×
1366
#ifdef Q_OS_WIN
1367
                        qCritical() << "You should update graphics drivers, graphics hardware, or use the --mesa-mode option.";
1368
#else
1369
                        qCritical() << "You should update graphics drivers or graphics hardware.";
×
1370
#endif
1371
                        qCritical() << "Else, please try to use an older version like 0.12.9, and try there with --safe-mode";
×
1372

1373
                        if (conf->value("main/ignore_opengl_warning", false).toBool())
×
1374
                        {
1375
                                qWarning() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
×
1376
                        }
1377
                        else
1378
                        {
1379
                                qInfo() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
×
1380
                                qInfo() << "But more than likely problems will persist.";
×
1381
                                QMessageBox::StandardButton answerButton=
1382
                                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?"),
×
1383
                                                      QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
1384
                                if (answerButton == QMessageBox::Abort)
×
1385
                                {
1386
                                        qCritical() << "Aborting due to OpenGL ES/GLSL ES version problems.";
×
1387
                                        exit(1);
×
1388
                                }
1389
                                else
1390
                                {
1391
                                        qWarning() << "Ignoring all warnings, continuing without further question.";
×
1392
                                        conf->setValue("main/ignore_opengl_warning", true);
×
1393
                                }
1394
                        }
1395
                }
1396
                else
1397
                {
UNCOV
1398
                        if (openGLerror)
×
1399
                                qWarning() << "GLSL ES version is OK, but there were previous errors, expect problems.";
×
1400
                        else
1401
                                qInfo() << "GLSL ES version is fine, we should not see a graphics problem.";
×
1402
                }
1403
        }
1404
        else
1405
        {
1406
                qCritical() << "Cannot parse GLSL (ES) version string. This may indicate future problems.";
×
UNCOV
1407
                qCritical() << "Please send a bug report that includes this log file and states if Stellarium works or has problems.";
×
1408
        }
1409
#endif
1410
}
×
1411

1412
// Get physical dimensions given the virtual dimensions for the screen where this window is located.
UNCOV
1413
QRectF StelMainView::getPhysicalSize(const QRectF& virtualRect) const
×
1414
{
UNCOV
1415
        auto window = this->window();
×
1416
        if(window)
×
1417
        {
UNCOV
1418
                auto pixelRatio = window->devicePixelRatio();
×
UNCOV
1419
                QRectF newRect(virtualRect.x(), virtualRect.y(), virtualRect.width() * pixelRatio, virtualRect.height() * pixelRatio);
×
1420
                return newRect;
×
1421
        }
1422

1423
        // If the platform does not support getting the QWindow backing instance of this QWidget
UNCOV
1424
        return virtualRect;
×
1425
}
1426

1427
// Debug info about OpenGL capabilities.
1428
void StelMainView::dumpOpenGLdiagnostics() const
×
1429
{
UNCOV
1430
        QOpenGLContext *context = QOpenGLContext::currentContext();
×
UNCOV
1431
        if (context)
×
1432
        {
1433
                context->functions()->initializeOpenGLFunctions();
×
UNCOV
1434
                qDebug() << "initializeOpenGLFunctions()...";
×
UNCOV
1435
                QOpenGLFunctions::OpenGLFeatures oglFeatures=context->functions()->openGLFeatures();
×
UNCOV
1436
                qInfo() << "OpenGL Features:";
×
UNCOV
1437
                qInfo() << " - glActiveTexture() function" << ((oglFeatures&QOpenGLFunctions::Multitexture) ? "is" : "is NOT") << "available.";
×
UNCOV
1438
                qInfo() << " - Shader functions" << ((oglFeatures&QOpenGLFunctions::Shaders) ? "are" : "are NOT ") << "available.";
×
1439
                qInfo() << " - Vertex and index buffer functions" << ((oglFeatures&QOpenGLFunctions::Buffers) ? "are" : "are NOT") << "available.";
×
1440
                qInfo() << " - Framebuffer object functions" << ((oglFeatures&QOpenGLFunctions::Framebuffers) ? "are" : "are NOT") << "available.";
×
UNCOV
1441
                qInfo() << " - glBlendColor()" << ((oglFeatures&QOpenGLFunctions::BlendColor) ? "is" : "is NOT") << "available.";
×
1442
                qInfo() << " - glBlendEquation()" << ((oglFeatures&QOpenGLFunctions::BlendEquation) ? "is" : "is NOT") << "available.";
×
UNCOV
1443
                qInfo() << " - glBlendEquationSeparate()" << ((oglFeatures&QOpenGLFunctions::BlendEquationSeparate) ? "is" : "is NOT") << "available.";
×
UNCOV
1444
                qInfo() << " - glBlendFuncSeparate()" << ((oglFeatures&QOpenGLFunctions::BlendFuncSeparate) ? "is" : "is NOT") << "available.";
×
UNCOV
1445
                qInfo() << " - Blend subtract mode" << ((oglFeatures&QOpenGLFunctions::BlendSubtract) ? "is" : "is NOT") << "available.";
×
UNCOV
1446
                qInfo() << " - Compressed texture functions" << ((oglFeatures&QOpenGLFunctions::CompressedTextures) ? "are" : "are NOT") << "available.";
×
1447
                qInfo() << " - glSampleCoverage() function" << ((oglFeatures&QOpenGLFunctions::Multisample) ? "is" : "is NOT") << "available.";
×
1448
                qInfo() << " - Separate stencil functions" << ((oglFeatures&QOpenGLFunctions::StencilSeparate) ? "are" : "are NOT") << "available.";
×
UNCOV
1449
                qInfo() << " - Non power of two textures" << ((oglFeatures&QOpenGLFunctions::NPOTTextures) ? "are" : "are NOT") << "available.";
×
UNCOV
1450
                qInfo() << " - Non power of two textures" << ((oglFeatures&QOpenGLFunctions::NPOTTextureRepeat) ? "can" : "CANNOT") << "use GL_REPEAT as wrap parameter.";
×
1451
                qInfo() << " - The fixed function pipeline" << ((oglFeatures&QOpenGLFunctions::FixedFunctionPipeline) ? "is" : "is NOT") << "available.";
×
1452
                GLfloat lineWidthRange[2];
UNCOV
1453
                context->functions()->glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange);
×
1454
                qInfo() << "Line widths available from" << lineWidthRange[0] << "to" << lineWidthRange[1];
×
1455

1456
                qInfo() << "OpenGL shader capabilities and details:";
×
1457
                qInfo() << " - Vertex Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Vertex, context) ? "YES" : "NO");
×
UNCOV
1458
                qInfo() << " - Fragment Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Fragment, context) ? "YES" : "NO");
×
1459
                qInfo() << " - Geometry Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Geometry, context) ? "YES" : "NO");
×
1460
                qInfo() << " - TessellationControl Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::TessellationControl, context) ? "YES" : "NO");
×
1461
                qInfo() << " - TessellationEvaluation Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::TessellationEvaluation, context) ? "YES" : "NO");
×
UNCOV
1462
                qInfo() << " - Compute Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Compute, context) ? "YES" : "NO");
×
1463
                
1464
                // List available extensions. Not sure if this is in any way useful?
1465
                QSet<QByteArray> extensionSet=context->extensions();
×
UNCOV
1466
                qInfo() << "We have" << extensionSet.count() << "OpenGL extensions:";
×
UNCOV
1467
                QMap<QString, QString> extensionMap;
×
UNCOV
1468
                QSetIterator<QByteArray> iter(extensionSet);
×
1469
                while (iter.hasNext())
×
1470
                {
1471
                        if (!iter.peekNext().isEmpty()) {// Don't insert empty lines
×
1472
                                extensionMap.insert(QString(iter.peekNext()), QString(iter.peekNext()));
×
1473
                        }
1474
                        iter.next();
×
1475
                }
1476
                QMapIterator<QString, QString> iter2(extensionMap);
×
1477
                while (iter2.hasNext()) {
×
1478
                        qInfo().noquote() << " -" << iter2.next().key();
×
1479
                }
1480

1481
                QFunctionPointer programParameterPtr =context->getProcAddress("glProgramParameteri");
×
1482
                if (programParameterPtr == Q_NULLPTR) {
×
1483
                        qWarning() << "glProgramParameteri cannot be resolved here. BAD!";
×
1484
                }
1485
                programParameterPtr =context->getProcAddress("glProgramParameteriEXT");
×
1486
                if (programParameterPtr == Q_NULLPTR) {
×
1487
                        qWarning() << "glProgramParameteriEXT cannot be resolved here. BAD!";
×
1488
                }
1489
        }
×
1490
        else
1491
        {
1492
                qCritical() << "dumpOpenGLdiagnostics(): No OpenGL context";
×
1493
        }
1494
}
×
1495

UNCOV
1496
void StelMainView::deinit()
×
1497
{
1498
        glContextMakeCurrent();
×
1499
        deinitGL();
×
1500
        delete stelApp;
×
1501
        stelApp = Q_NULLPTR;
×
1502
}
×
1503

1504
// Update the translated title
UNCOV
1505
void StelMainView::initTitleI18n()
×
1506
{
1507
        setWindowTitle(StelUtils::getApplicationName());
×
1508
}
×
1509

1510
void StelMainView::setFullScreen(bool b)
×
1511
{
1512
        if (b)
×
1513
                showFullScreen();
×
1514
        else
1515
        {
UNCOV
1516
                showNormal();
×
1517
                // 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.
1518
                // Therefore moving is not possible. We must move to the stored position or at least defaults.
1519
                if ( (x()<0)  && (y()<0))
×
1520
                {
UNCOV
1521
                        QSettings *conf = stelApp->getSettings();
×
1522
                        int screen = conf->value("video/screen_number", 0).toInt();
×
1523
                        if (screen < 0 || screen >= qApp->screens().count())
×
1524
                        {
UNCOV
1525
                                qWarning() << "Screen" << screen << "not found";
×
1526
                                screen = 0;
×
1527
                        }
1528
                        QRect screenGeom = qApp->screens().at(screen)->geometry();
×
UNCOV
1529
                        int x = conf->value("video/screen_x", 0).toInt();
×
1530
                        int y = conf->value("video/screen_y", 0).toInt();
×
UNCOV
1531
                        move(x + screenGeom.x(), y + screenGeom.y());
×
1532
                }
1533
        }
UNCOV
1534
        StelApp::immediateSave("video/fullscreen", b);
×
1535
        emit fullScreenChanged(b);
×
UNCOV
1536
}
×
1537

UNCOV
1538
void StelMainView::drawEnded()
×
1539
{
1540
        updateQueued = false;
×
1541

1542
#ifndef Q_OS_MACOS
1543
        int requiredFpsInterval = qRound(needsMaxFPS()?1000.f/maxfps:1000.f/minfps);
×
UNCOV
1544
        if(fpsTimer->interval() != requiredFpsInterval)
×
UNCOV
1545
                fpsTimer->setInterval(requiredFpsInterval);
×
1546
#else
1547
        // FIXME: workaround for https://github.com/Stellarium/stellarium/issues/2778, in which touchpad-based
1548
        // view manipulation can be very laggy on Macs in circumstances where frame rendering more time than
1549
        // the trackpad move update rate from the OS. This is perhaps a bug in Qt; see the discussion around
1550
        // https://github.com/Stellarium/stellarium/issues/2778#issuecomment-1722766935 and below for details.
1551
        fpsTimer->setInterval(qMax(minTimeBetweenFrames, qRound(needsMaxFPS()?1000.f/maxfps:1000.f/minfps)));
1552
#endif
1553

1554
        if(!fpsTimer->isActive())
×
UNCOV
1555
                fpsTimer->start();
×
1556

1557
        emit frameFinished();
×
UNCOV
1558
}
×
1559

1560
void StelMainView::setFlagCursorTimeout(bool b)
×
1561
{
1562
        if (b == flagCursorTimeout) return;
×
1563

1564
        flagCursorTimeout = b;
×
UNCOV
1565
        if (b)         // enable timer
×
1566
        {
1567
                cursorTimeoutTimer->start();
×
1568
        }
1569
        else        // disable timer
1570
        {
1571
                // Show the previous cursor if the current is "hidden" 
1572
                if (QGuiApplication::overrideCursor() && (QGuiApplication::overrideCursor()->shape() == Qt::BlankCursor) )
×
1573
                {
1574
                        // pop the blank cursor
1575
                        QGuiApplication::restoreOverrideCursor();
×
1576
                }
1577
                // and stop the timer 
UNCOV
1578
                cursorTimeoutTimer->stop();
×
1579
        }
1580
        
1581
        StelApp::immediateSave("gui/flag_mouse_cursor_timeout", b);
×
UNCOV
1582
        emit flagCursorTimeoutChanged(b);
×
1583
}
1584

1585
void StelMainView::hideCursor()
×
1586
{
1587
        // timeout fired...
1588
        // if the feature is not asked, do nothing
UNCOV
1589
        if (!flagCursorTimeout) return;
×
1590

1591
        // "hide" the current cursor by pushing a Blank cursor
UNCOV
1592
        QGuiApplication::setOverrideCursor(Qt::BlankCursor);
×
1593
}
1594

1595
void StelMainView::fpsTimerUpdate()
×
1596
{
UNCOV
1597
        if(!updateQueued)
×
1598
        {
1599
                updateQueued = true;
×
UNCOV
1600
                QTimer::singleShot(0, glWidget, SLOT(update()));
×
1601
        }
UNCOV
1602
}
×
1603

1604
#ifdef OPENGL_DEBUG_LOGGING
1605
void StelMainView::logGLMessage(const QOpenGLDebugMessage &debugMessage)
1606
{
1607
        qDebug()<<debugMessage;
1608
}
1609

1610
void StelMainView::contextDestroyed()
1611
{
1612
        qDebug()<<"Main OpenGL context destroyed";
1613
}
1614
#endif
1615

1616
void StelMainView::thereWasAnEvent()
×
1617
{
UNCOV
1618
        lastEventTimeSec = StelApp::getTotalRunTime();
×
1619
}
×
1620

UNCOV
1621
bool StelMainView::needsMaxFPS() const
×
1622
{
1623
        const double now = StelApp::getTotalRunTime();
×
1624

1625
        // Determines when the next display will need to be triggered
1626
        // The current policy is that after an event, the FPS is maximum for 2.5 seconds
1627
        // after that, it switches back to the default minfps value to save power.
1628
        // The fps is also kept to max if the timerate is higher than normal speed.
UNCOV
1629
        const double timeRate = stelApp->getCore()->getTimeRate();
×
1630
        return (now - lastEventTimeSec < 2.5) || fabs(timeRate) > StelCore::JD_SECOND;
×
1631
}
1632

1633
void StelMainView::moveEvent(QMoveEvent * event)
×
1634
{
UNCOV
1635
        const QPoint &pos=event->pos();
×
1636

1637
        // We use the glWidget instead of the event, as we want the screen that shows most of the widget.
1638
        QWindow* win = glWidget->windowHandle();
×
UNCOV
1639
        if(win)
×
1640
        {
1641
                stelApp->setDevicePixelsPerPixel(win->devicePixelRatio());
×
1642
        }
1643

UNCOV
1644
        if (StelApp::isInitialized())
×
1645
        {
UNCOV
1646
                StelApp::immediateSave("video/screen_x", pos.x());
×
UNCOV
1647
                StelApp::immediateSave("video/screen_y", pos.y());
×
1648
        }
UNCOV
1649
}
×
1650

UNCOV
1651
void StelMainView::closeEvent(QCloseEvent* event)
×
1652
{
1653
        Q_UNUSED(event)
UNCOV
1654
        stelApp->quit();
×
UNCOV
1655
}
×
1656

1657
//! Delete openGL textures (to call before the GLContext disappears)
UNCOV
1658
void StelMainView::deinitGL()
×
1659
{
1660
        //fix for bug 1628072 caused by QTBUG-56798
1661
#ifndef QT_NO_DEBUG
1662
        StelOpenGL::clearGLErrors();
×
1663
#endif
1664

UNCOV
1665
        stelApp->deinit();
×
UNCOV
1666
        delete gui;
×
UNCOV
1667
        gui = Q_NULLPTR;
×
UNCOV
1668
}
×
1669

1670
void StelMainView::setScreenshotFormat(const QString filetype)
×
1671
{
UNCOV
1672
        const QString candidate=filetype.toLower();
×
UNCOV
1673
        const QByteArray candBA=candidate.toUtf8();
×
1674

1675
        // Make sure format is supported by Qt, but restrict some useless formats.
1676
        QList<QByteArray> formats = QImageWriter::supportedImageFormats();
×
UNCOV
1677
        formats.removeOne("icns");
×
UNCOV
1678
        formats.removeOne("wbmp");
×
1679
        formats.removeOne("cur");
×
1680
        if (formats.contains(candBA))
×
1681
        {
1682
                screenShotFormat=candidate;
×
1683
                // apply setting immediately
UNCOV
1684
                configuration->setValue("main/screenshot_format", candidate);
×
1685
                emit screenshotFormatChanged(candidate);
×
1686
        }
1687
        else
1688
        {
UNCOV
1689
                qCritical() << "Invalid filetype for screenshot: " << filetype;
×
1690
        }
UNCOV
1691
}
×
1692

UNCOV
1693
void StelMainView::setFlagScreenshotDateFileName(bool b)
×
1694
{
1695
        flagScreenshotDateFileName=b;
×
1696
        StelApp::getInstance().getSettings()->setValue("main/screenshot_datetime_filename", b);
×
UNCOV
1697
        emit flagScreenshotDateFileNameChanged(b);
×
UNCOV
1698
}
×
1699

UNCOV
1700
void StelMainView::setScreenshotFileMask(const QString filemask)
×
1701
{
UNCOV
1702
        screenShotFileMask = filemask;
×
1703
        StelApp::getInstance().getSettings()->setValue("main/screenshot_datetime_filemask", filemask);
×
UNCOV
1704
        emit screenshotFileMaskChanged(filemask);
×
UNCOV
1705
}
×
1706

1707
void StelMainView::setScreenshotDpi(int dpi)
×
1708
{
1709
        screenshotDpi=dpi;
×
UNCOV
1710
        StelApp::getInstance().getSettings()->setValue("main/screenshot_dpi", dpi);
×
1711
        emit screenshotDpiChanged(dpi);
×
UNCOV
1712
}
×
1713

1714
void StelMainView::saveScreenShot(const QString& filePrefix, const QString& saveDir, const bool overwrite)
×
1715
{
UNCOV
1716
        screenShotPrefix = QFileInfo(filePrefix).fileName(); // Strip away any path elements (Security issue!)
×
1717
        if (screenShotPrefix.isEmpty())
×
1718
                        screenShotPrefix = "stellarium-";
×
1719
        screenShotDir = saveDir;
×
1720
        flagOverwriteScreenshots=overwrite;
×
1721
        emit screenshotRequested();
×
UNCOV
1722
}
×
1723

UNCOV
1724
void StelMainView::doScreenshot(void)
×
1725
{
1726
        QFileInfo shotDir;
×
1727
        // Make a screenshot which may be larger than the current window. This is harder than you would think:
1728
        // fbObj the framebuffer governs size of the target image, that's the easy part, but it also has its limits.
1729
        // However, the GUI parts need to be placed properly,
1730
        // HiDPI screens interfere, and the viewing angle has to be maintained.
1731
        // First, image size:
1732
        glWidget->makeCurrent();
×
UNCOV
1733
        const auto screen = QOpenGLContext::currentContext()->screen();
×
1734
        const auto pixelRatio = screen->devicePixelRatio();
×
UNCOV
1735
        int physImgWidth  = std::lround(stelScene->width() * pixelRatio);
×
1736
        int physImgHeight = std::lround(stelScene->height() * pixelRatio);
×
1737
        bool nightModeWasEnabled=nightModeEffect->isEnabled();
×
1738
        nightModeEffect->setEnabled(false);
×
1739
        if (flagUseCustomScreenshotSize)
×
1740
        {
1741
                // Borrowed from Scenery3d renderer: determine maximum framebuffer size as minimum of texture, viewport and renderbuffer size
UNCOV
1742
                QOpenGLContext *context = QOpenGLContext::currentContext();
×
1743
                if (context)
×
1744
                {
1745
                        context->functions()->initializeOpenGLFunctions();
×
1746
                        //qDebug() << "initializeOpenGLFunctions()...";
1747
                        // TODO: Investigate this further when GL memory issues should appear.
1748
                        // Make sure we have enough free GPU memory!
1749
#ifndef NDEBUG
1750
#ifdef GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX
1751
                        GLint freeGLmemory;
1752
                        context->functions()->glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &freeGLmemory);
×
1753
                        qCDebug(mainview)<<"Free GPU memory:" << freeGLmemory << "kB -- we ask for " << customScreenshotWidth*customScreenshotHeight*8 / 1024 <<"kB";
×
1754
#endif
1755
#ifdef GL_RENDERBUFFER_FREE_MEMORY_ATI
1756
                        GLint freeGLmemoryAMD[4];
1757
                        context->functions()->glGetIntegerv(GL_RENDERBUFFER_FREE_MEMORY_ATI, freeGLmemoryAMD);
×
1758
                        qCDebug(mainview)<<"Free GPU memory (AMD version):" << static_cast<uint>(freeGLmemoryAMD[1])/1024 << "+"
×
1759
                                          << static_cast<uint>(freeGLmemoryAMD[3])/1024 << " of "
×
1760
                                          << static_cast<uint>(freeGLmemoryAMD[0])/1024 << "+"
×
1761
                                          << static_cast<uint>(freeGLmemoryAMD[2])/1024 << "kB -- we ask for "
×
1762
                                          << customScreenshotWidth*customScreenshotHeight*8 / 1024 <<"kB";
×
1763
#endif
1764
#endif
1765
                        GLint texSize,viewportSize[2],rbSize;
UNCOV
1766
                        context->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
×
1767
                        context->functions()->glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewportSize);
×
UNCOV
1768
                        context->functions()->glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &rbSize);
×
UNCOV
1769
                        qCDebug(mainview)<<"Maximum texture size:"<<texSize;
×
UNCOV
1770
                        qCDebug(mainview)<<"Maximum viewport dims:"<<viewportSize[0]<<viewportSize[1];
×
UNCOV
1771
                        qCDebug(mainview)<<"Maximum renderbuffer size:"<<rbSize;
×
UNCOV
1772
                        int maximumFramebufferSize = qMin(texSize,qMin(rbSize,qMin(viewportSize[0],viewportSize[1])));
×
1773
                        qCDebug(mainview)<<"Maximum framebuffer size:"<<maximumFramebufferSize;
×
1774

1775
                        physImgWidth =qMin(maximumFramebufferSize, customScreenshotWidth);
×
1776
                        physImgHeight=qMin(maximumFramebufferSize, customScreenshotHeight);
×
1777
                }
1778
                else
1779
                {
1780
                        qCWarning(mainview) << "No GL context for screenshot! Aborting.";
×
UNCOV
1781
                        return;
×
1782
                }
1783
        }
1784
        // 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.
UNCOV
1785
        bool isGLES=(QOpenGLContext::currentContext()->format().renderableType() == QSurfaceFormat::OpenGLES);
×
1786

UNCOV
1787
        QOpenGLFramebufferObjectFormat fbFormat;
×
UNCOV
1788
        fbFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
×
UNCOV
1789
        fbFormat.setInternalTextureFormat(isGLES ? GL_RGBA : GL_RGB); // try to avoid transparent background!
×
UNCOV
1790
        QOpenGLFramebufferObject * fbObj = new QOpenGLFramebufferObject(physImgWidth, physImgHeight, fbFormat);
×
UNCOV
1791
        fbObj->bind();
×
1792
        // Now the painter has to be convinced to paint to the potentially larger image frame.
1793
        QOpenGLPaintDevice fbObjPaintDev(physImgWidth, physImgHeight);
×
1794

1795
        // It seems the projector has its own knowledge about image size. We must adjust fov and image size, but reset afterwards.
UNCOV
1796
        StelCore *core=StelApp::getInstance().getCore();
×
UNCOV
1797
        StelProjector::StelProjectorParams pParams=core->getCurrentStelProjectorParams();
×
1798
        StelProjector::StelProjectorParams sParams=pParams;
×
1799
        //qCDebug(mainview) << "Screenshot Viewport: x" << pParams.viewportXywh[0] << "/y" << pParams.viewportXywh[1] << "/w" << pParams.viewportXywh[2] << "/h" << pParams.viewportXywh[3];
1800
        const auto virtImgWidth  = physImgWidth  / pixelRatio;
×
1801
        const auto virtImgHeight = physImgHeight / pixelRatio;
×
1802
        sParams.viewportXywh[2] = virtImgWidth;
×
1803
        sParams.viewportXywh[3] = virtImgHeight;
×
1804

UNCOV
1805
        sParams.viewportCenter.set(0.0+(0.5+pParams.viewportCenterOffset.v[0])*virtImgWidth,
×
UNCOV
1806
                                                           0.0+(0.5+pParams.viewportCenterOffset.v[1])*virtImgHeight);
×
1807
        sParams.viewportFovDiameter = qMin(virtImgWidth,virtImgHeight);
×
1808
        core->setCurrentStelProjectorParams(sParams);
×
1809

1810
        QPainter painter;
×
1811
        painter.begin(&fbObjPaintDev);
×
1812
        // next line was above begin(), but caused a complaint. Maybe use after begin()?
1813
        painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
×
1814
        stelScene->setSceneRect(0, 0, virtImgWidth, virtImgHeight);
×
1815

1816
        // push the button bars back to the sides where they belong, and fix root item clipping its children.
1817
        dynamic_cast<StelGui*>(gui)->getSkyGui()->setGeometry(0, 0, virtImgWidth, virtImgHeight);
×
UNCOV
1818
        rootItem->setSize(QSize(virtImgWidth, virtImgHeight));
×
UNCOV
1819
        dynamic_cast<StelGui*>(gui)->forceRefreshGui(); // refresh bar position.
×
1820

1821
        stelScene->render(&painter, QRectF(), QRectF(0,0,virtImgWidth,virtImgHeight) , Qt::KeepAspectRatio);
×
1822
        painter.end();
×
1823

UNCOV
1824
        QImage im;
×
UNCOV
1825
        if (isGLES)
×
1826
        {
1827
                // We have RGBA texture with possibly empty spots when atmosphere was off.
1828
                // See toImage() help entry why to create wrapper here.
1829
                QImage fboImage(fbObj->toImage());
×
1830
                //qDebug() << "FBOimage format:" << fboImage.format(); // returns Format_RGBA8888_Premultiplied
1831
                QImage im2(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_RGBX8888);
×
1832
                im=im2.copy();
×
UNCOV
1833
        }
×
1834
        else
UNCOV
1835
                im=fbObj->toImage();
×
UNCOV
1836
        fbObj->release();
×
1837
        delete fbObj;
×
1838
        // reset viewport and GUI
1839
        core->setCurrentStelProjectorParams(pParams);
×
UNCOV
1840
        nightModeEffect->setEnabled(nightModeWasEnabled);
×
1841
        stelScene->setSceneRect(0, 0, pParams.viewportXywh[2], pParams.viewportXywh[3]);
×
1842
        rootItem->setSize(QSize(pParams.viewportXywh[2], pParams.viewportXywh[3]));
×
1843
        StelGui* stelGui = dynamic_cast<StelGui*>(gui);
×
1844
        if (stelGui)
×
1845
        {
1846
                stelGui->getSkyGui()->setGeometry(0, 0, pParams.viewportXywh[2], pParams.viewportXywh[3]);
×
1847
                stelGui->forceRefreshGui();
×
1848
        }
1849

UNCOV
1850
        if (nightModeWasEnabled)
×
1851
        {
1852
                for (int row=0; row<im.height(); ++row)
×
UNCOV
1853
                        for (int col=0; col<im.width(); ++col)
×
1854
                        {
1855
                                QRgb rgb=im.pixel(col, row);
×
UNCOV
1856
                                int gray=qGray(rgb);
×
UNCOV
1857
                                im.setPixel(col, row, qRgb(gray, 0, 0));
×
1858
                        }
1859
        }
1860
        if (flagInvertScreenShotColors)
×
UNCOV
1861
                im.invertPixels();
×
1862

1863
        if (StelFileMgr::getScreenshotDir().isEmpty())
×
1864
        {
1865
                qWarning() << "Oops, the directory for screenshots is not set! Let's try create and set it...";
×
1866
                // Create a directory for screenshots if main/screenshot_dir option is unset and user do screenshot at the moment!
UNCOV
1867
                QString screenshotDirSuffix = "/Stellarium";
×
UNCOV
1868
                QString screenshotDir;
×
UNCOV
1869
                if (!QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).isEmpty())
×
1870
                {
UNCOV
1871
                        screenshotDir = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).constFirst();
×
1872
                        screenshotDir.append(screenshotDirSuffix);
×
1873
                }
1874
                else
UNCOV
1875
                        screenshotDir = StelFileMgr::getUserDir().append(screenshotDirSuffix);
×
1876

1877
                try
1878
                {
UNCOV
1879
                        StelFileMgr::setScreenshotDir(screenshotDir);
×
1880
                        StelApp::getInstance().getSettings()->setValue("main/screenshot_dir", screenshotDir);
×
1881
                }
1882
                catch (std::runtime_error &e)
×
1883
                {
1884
                        qCritical() << "Error: cannot create screenshot directory:" << e.what();
×
1885
                }
×
UNCOV
1886
        }
×
1887

1888
        if (screenShotDir == "")
×
UNCOV
1889
                shotDir = QFileInfo(StelFileMgr::getScreenshotDir());
×
1890
        else
1891
                shotDir = QFileInfo(screenShotDir);
×
1892

1893
        if (!shotDir.isDir())
×
1894
        {
UNCOV
1895
                qCritical() << "Requested screenshot directory is not a directory: " << QDir::toNativeSeparators(shotDir.filePath());
×
1896
                return;
×
1897
        }
1898
        else if (!shotDir.isWritable())
×
1899
        {
UNCOV
1900
                qCritical() << "Requested screenshot directory is not writable: " << QDir::toNativeSeparators(shotDir.filePath());
×
1901
                return;
×
1902
        }
1903

1904
        QFileInfo shotPath;
×
UNCOV
1905
        if (flagOverwriteScreenshots)
×
1906
        {
UNCOV
1907
                shotPath = QFileInfo(shotDir.filePath() + "/" + screenShotPrefix + "." + screenShotFormat);
×
1908
        }
1909
        else
1910
        {
UNCOV
1911
                QString shotPathString;
×
1912
                if (flagScreenshotDateFileName)
×
1913
                {
1914
                        // name screenshot files with current time
UNCOV
1915
                        QString currentTime = QDateTime::currentDateTime().toString(screenShotFileMask);
×
1916
                        shotPathString = QString("%1/%2%3.%4").arg(shotDir.filePath(), screenShotPrefix, currentTime, screenShotFormat);
×
UNCOV
1917
                        shotPath = QFileInfo(shotPathString);
×
UNCOV
1918
                }
×
1919
                else
1920
                {
1921
                        // build filter for file list, so we only select Stellarium screenshot files (prefix*.format)
UNCOV
1922
                        QString shotFilePattern = QString("%1*.%2").arg(screenShotPrefix, screenShotFormat);
×
1923
                        QStringList fileNameFilters(shotFilePattern);
×
1924
                        // get highest-numbered file in screenshot directory
1925
                        QDir dir(shotDir.filePath());
×
1926
                        QStringList existingFiles = dir.entryList(fileNameFilters);
×
1927

1928
                        // screenshot number - default to 1 for empty directory
1929
                        int shotNum = 1;
×
1930
                        if (!existingFiles.empty())
×
1931
                        {
1932
                                // already have screenshots, find largest number
UNCOV
1933
                                QString lastFileName = existingFiles[existingFiles.size() - 1];
×
1934

1935
                                // extract number from highest-numbered file name
1936
                                QString lastShotNumString = lastFileName.replace(screenShotPrefix, "").replace("." + screenShotFormat, "");
×
1937
                                // new screenshot number = start at highest number
UNCOV
1938
                                shotNum = lastShotNumString.toInt() + 1;
×
1939
                        }
×
1940

1941
                        // build new screenshot path: "path/prefix-num.format"
1942
                        // num is at least 3 characters
UNCOV
1943
                        QString shotNumString = QString::number(shotNum).rightJustified(3, '0');
×
UNCOV
1944
                        shotPathString = QString("%1/%2%3.%4").arg(shotDir.filePath(), screenShotPrefix, shotNumString, screenShotFormat);
×
1945
                        shotPath = QFileInfo(shotPathString);
×
1946
                        // validate if new screenshot number is valid (non-existent)
UNCOV
1947
                        while (shotPath.exists()) {
×
1948
                                shotNum++;
×
UNCOV
1949
                                shotNumString = QString::number(shotNum).rightJustified(3, '0');
×
UNCOV
1950
                                shotPathString = QString("%1/%2%3.%4").arg(shotDir.filePath(), screenShotPrefix, shotNumString, screenShotFormat);
×
UNCOV
1951
                                shotPath = QFileInfo(shotPathString);
×
1952
                        }
1953
                }
×
UNCOV
1954
        }
×
1955
        /*
1956
        // OPTIONAL: Determine free space and reject storing. This should avoid filling user space.
1957
        QStorageInfo storageInfo(shotPath.filePath());
1958
        if (storageInfo.bytesAvailable() < 50*1024*1024)
1959
        {
1960
                qWarning() << "Less than 50MB free. Not storing screenshot to" << shotPath.filePath();
1961
                qWarning() << "You must clean up your system to free disk space!";
1962
                return;
1963
        }
1964
        */
1965

1966
        // Set preferred image resolution (for some printing workflows)
1967
        im.setDotsPerMeterX(qRound(screenshotDpi*100./2.54));
×
UNCOV
1968
        im.setDotsPerMeterY(qRound(screenshotDpi*100./2.54));
×
UNCOV
1969
        qInfo() << "Saving screenshot to file: " << QDir::toNativeSeparators(shotPath.filePath());
×
1970

1971
        QImageWriter imageWriter(shotPath.filePath());
×
UNCOV
1972
        if (screenShotFormat=="tif")
×
UNCOV
1973
                imageWriter.setCompression(1); // use LZW
×
1974
        if (screenShotFormat=="jpg")
×
1975
        {
UNCOV
1976
                imageWriter.setQuality(75); // This is actually default
×
1977
        }
UNCOV
1978
        if (screenShotFormat=="jpeg")
×
1979
        {
1980
                imageWriter.setQuality(100);
×
1981
        }
UNCOV
1982
        if (!imageWriter.write(im))
×
1983
        {
1984
                qCritical() << "Failed to write screenshot to:" << QDir::toNativeSeparators(shotPath.filePath());
×
1985
        }
1986
}
×
1987

1988
QPoint StelMainView::getMousePos() const
×
1989
{
1990
        return glWidget->mapFromGlobal(QCursor::pos());
×
1991
}
1992

UNCOV
1993
QOpenGLContext* StelMainView::glContext() const
×
1994
{
1995
        return glWidget->context();
×
1996
}
1997

UNCOV
1998
void StelMainView::glContextMakeCurrent()
×
1999
{
UNCOV
2000
        glWidget->makeCurrent();
×
UNCOV
2001
}
×
2002

UNCOV
2003
void StelMainView::glContextDoneCurrent()
×
2004
{
UNCOV
2005
        glWidget->doneCurrent();
×
UNCOV
2006
}
×
2007

2008
// Set the sky background color. Everything else than black creates a work of art!
2009
void StelMainView::setSkyBackgroundColor(Vec3f color)
×
2010
{
UNCOV
2011
        rootItem->setSkyBackgroundColor(color);
×
2012
        StelApp::getInstance().getSettings()->setValue("color/sky_background_color", color.toStr());
×
2013
        emit skyBackgroundColorChanged(color);
×
2014
}
×
2015

2016
// Get the sky background color. Everything else than black creates a work of art!
2017
Vec3f StelMainView::getSkyBackgroundColor() const
×
2018
{
2019
        return rootItem->getSkyBackgroundColor();
×
2020
}
2021

UNCOV
2022
QRectF StelMainView::setWindowSize(int width, int height)
×
2023
{
2024
        // Make sure to leave fullscreen if necessary.
2025
        if (isFullScreen())
×
UNCOV
2026
                setFullScreen(false);
×
2027
        QRect geo=geometry();
×
UNCOV
2028
        geo.setWidth(width);
×
2029
        geo.setHeight(height);
×
UNCOV
2030
        setGeometry(geo);
×
2031

UNCOV
2032
        return stelScene->sceneRect(); // retrieve what was finally available.
×
2033
}
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