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

Stellarium / stellarium / 15670918640

16 Jun 2025 02:08AM UTC coverage: 11.775% (-0.2%) from 11.931%
15670918640

push

github

alex-w
Updated data

14700 of 124846 relevant lines covered (11.77%)

18324.52 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
#ifdef Q_OS_WIN
64
        #include <QPinchGesture>
65
#endif
66
#include <QOpenGLShader>
67
#include <QOpenGLShaderProgram>
68
#include <QOpenGLFramebufferObject>
69
#include <QOpenGLPaintDevice>
70
#ifdef OPENGL_DEBUG_LOGGING
71
#include <QOpenGLDebugLogger>
72
#endif
73
#include <QLoggingCategory>
74

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

77
#include <clocale>
78

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

270
                painter->begin(paintDevice);
×
271

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

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

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

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

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

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

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

366
                setAcceptHoverEvents(true);
×
367

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

376
        void setSize(const QSize& size)
×
377
        {
378
                prepareGeometryChange();
×
379
                rect.setSize(size);
×
380
        }
×
381

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

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

388

389
protected:
390
        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
×
391
        {
392
                Q_UNUSED(option)
393
                Q_UNUSED(widget)
394

395
                //a sanity check
396
                Q_ASSERT(mainView->glContext() == QOpenGLContext::currentContext());
×
397

398
                StelApp& app = StelApp::getInstance();
×
399

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

407
                const double now = StelApp::getTotalRunTime();
×
408
                double dt = now - previousPaintTime;
×
409
                //qDebug()<<"dt"<<dt;
410
                previousPaintTime = now;
×
411

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

415
                //fix for bug LP:1628072 caused by QTBUG-56798
416
#ifndef QT_NO_DEBUG
417
                StelOpenGL::clearGLErrors();
×
418
#endif
419

420
                //update and draw
421
                app.update(dt); // may also issue GL calls
×
422
                app.draw();
×
423
                painter->endNativePainting();
×
424

425
                mainView->drawEnded();
×
426
        }
×
427

428
        QRectF boundingRect() const override
×
429
        {
430
                return rect;
×
431
        }
432

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

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

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

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

478
        void keyPressEvent(QKeyEvent *event) override
×
479
        {
480
                StelApp::getInstance().handleKeys(event);
×
481
                if(event->isAccepted())
×
482
                        mainView->thereWasAnEvent();
×
483
        }
×
484

485
        void keyReleaseEvent(QKeyEvent *event) override
×
486
        {
487
                StelApp::getInstance().handleKeys(event);
×
488
                if(event->isAccepted())
×
489
                        mainView->thereWasAnEvent();
×
490
        }
×
491

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

505
                                if (touchPoints.count() == 1)
506
                                        setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton);
507

508
                                r = true;
509
                                break;
510
                        }
511
                        case QEvent::Gesture:
512
                                setAcceptedMouseButtons(Qt::NoButton);
513
                                r = gestureEvent(static_cast<QGestureEvent*>(e));
514
                                break;
515
                        default:
516
                                r = QGraphicsObject::event(e);
517
                }
518
                return r;
519
        }
520

521
private:
522
        bool gestureEvent(QGestureEvent *event)
523
        {
524
                if (QGesture *pinch = event->gesture(Qt::PinchGesture))
525
                        pinchTriggered(static_cast<QPinchGesture *>(pinch));
526

527
                return true;
528
        }
529

530
        void pinchTriggered(QPinchGesture *gesture)
531
        {
532
                QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
533
                if (changeFlags & QPinchGesture::ScaleFactorChanged) {
534
                        qreal zoom = gesture->scaleFactor();
535

536
                        if (zoom < 2 && zoom > 0.5){
537
                                StelApp::getInstance().handlePinch(zoom, true);
538
                        }
539
                }
540
        }
541
#endif
542

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

573
                QPointF pos = event->scenePos();
×
574
                //Y needs to be inverted
575
                pos.setY(rect.height() - 1 - pos.y());
×
576
                return QMouseEvent(t,pos,event->button(),event->buttons(),event->modifiers());
×
577
        }
578

579
        QRectF rect;
580
        double previousPaintTime;
581
        StelMainView* mainView;
582
        Vec3f skyBackgroundColor;           //! color which is used to initialize the frame. Should be black, but for some applications e.g. dark blue may be preferred.
583
};
584

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

597
protected:
598
        void resizeEvent(QGraphicsSceneResizeEvent* event) override
×
599
        {
600
                if(!inited) return;
×
601

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

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

642
        StelApp::initStatic();
×
643

644
        fpsTimer = new QTimer(this);
×
645
        fpsTimer->setTimerType(Qt::PreciseTimer);
×
646
        fpsTimer->setInterval(qRound(1000.f/minfps));
×
647
        connect(fpsTimer,SIGNAL(timeout()),this,SLOT(fpsTimerUpdate()));
×
648

649
        cursorTimeoutTimer = new QTimer(this);
×
650
        cursorTimeoutTimer->setSingleShot(true);
×
651
        connect(cursorTimeoutTimer, SIGNAL(timeout()), this, SLOT(hideCursor()));
×
652

653
        // Can't create 2 StelMainView instances
654
        Q_ASSERT(!singleton);
×
655
        singleton = this;
×
656

657
        qApp->installEventFilter(this);
×
658

659
        setWindowIcon(QIcon(":/mainWindow/icon.bmp"));
×
660
        initTitleI18n();
×
661
        setObjectName("MainView");
×
662

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

671
#ifdef OPENGL_DEBUG_LOGGING
672
        if (QApplication::testAttribute(Qt::AA_UseOpenGLES))
673
        {
674
                // QOpenGLDebugLogger doesn't work with OpenGLES's GL_KHR_debug.
675
                // See Qt Bug 62070: https://bugreports.qt.io/browse/QTBUG-62070
676

677
                glLogger = Q_NULLPTR;
678
        }
679
        else
680
        {
681
                glLogger = new QOpenGLDebugLogger(this);
682
                connect(glLogger, SIGNAL(messageLogged(QOpenGLDebugMessage)), this, SLOT(logGLMessage(QOpenGLDebugMessage)));
683
        }
684
#endif
685

686
        QSurfaceFormat glFormat = getDesiredGLFormat(configuration);
×
687
        glWidget = new StelGLWidget(glFormat, this);
×
688
        setViewport(glWidget);
×
689

690
        stelScene = new StelGraphicsScene(this);
×
691
        setScene(stelScene);
×
692
        scene()->setItemIndexMethod(QGraphicsScene::NoIndex);
×
693
        rootItem = new StelRootItem(this);
×
694

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

703
        // We cannot use global mousetracking. Only if mouse is hidden!
704
        //setMouseTracking(true);
705

706
    setRenderHint(QPainter::Antialiasing);
×
707
}
×
708

709
void StelMainView::resizeEvent(QResizeEvent* event)
×
710
{
711
        if(scene())
×
712
        {
713
                const QSize& sz = event->size();
×
714
                scene()->setSceneRect(QRect(QPoint(0, 0), sz));
×
715
                rootItem->setSize(sz);
×
716
                if(guiItem)
×
717
                        guiItem->setGeometry(QRectF(0.0,0.0,sz.width(),sz.height()));
×
718
                if (StelApp::isInitialized()  && !isFullScreen())
×
719
                {
720
                        StelApp::immediateSave("video/screen_w", sz.width());
×
721
                        StelApp::immediateSave("video/screen_h", sz.height());
×
722
                }
723
                emit sizeChanged(sz);
×
724
        }
725
        QGraphicsView::resizeEvent(event);
×
726
}
×
727

728
bool StelMainView::eventFilter(QObject *obj, QEvent *event)
×
729
{
730
        if(event->type() == QEvent::FileOpen)
×
731
        {
732
                QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
×
733
                //qDebug() << "load script:" << openEvent->file();
734
                qApp->setProperty("onetime_startup_script", openEvent->file());
×
735
        }
736
        return QGraphicsView::eventFilter(obj, event);
×
737
}
738

739
void StelMainView::mouseMoveEvent(QMouseEvent *event)
×
740
{
741
        if (flagCursorTimeout) 
×
742
        {
743
                // Show the previous cursor and reset the timeout if the current is "hidden"
744
                if (QGuiApplication::overrideCursor() && (QGuiApplication::overrideCursor()->shape() == Qt::BlankCursor) )
×
745
                {
746
                        QGuiApplication::restoreOverrideCursor();
×
747
                }
748

749
                cursorTimeoutTimer->start();
×
750
        }
751
        
752
#ifdef MOUSE_TRACKING
753
        QPointF pos = event->pos();
×
754
        //Y needs to be inverted
755
        int height1 = StelApp::getInstance().mainWin->height();
×
756
        pos.setY(height1 - 1 - pos.y());
×
757
        event->setAccepted(StelApp::getInstance().handleMove(pos.x(), pos.y(), event->buttons()));
×
758
        if(event->isAccepted())
×
759
                thereWasAnEvent();
×
760
#endif
761

762
        QGraphicsView::mouseMoveEvent(event);
×
763
}
×
764

765

766
void StelMainView::focusSky() {
×
767
        //scene()->setActiveWindow(0);
768
        rootItem->setFocus();
×
769
}
×
770

771
StelMainView::~StelMainView()
×
772
{
773
        //delete the night view graphic effect here while GL context is still valid
774
        rootItem->setGraphicsEffect(Q_NULLPTR);
×
775
        StelApp::deinitStatic();
×
776
        delete guiItem;
×
777
        guiItem=Q_NULLPTR;
×
778
}
×
779

780
QSurfaceFormat StelMainView::getDesiredGLFormat(QSettings* configuration)
×
781
{
782
        //use the default format as basis
783
        QSurfaceFormat fmt = QSurfaceFormat::defaultFormat();
×
784
        qDebug() << "Default surface format: " << fmt;
×
785

786
        //if on an GLES build, do not set the format
787
        const auto openGLModuleType = QOpenGLContext::openGLModuleType();
×
788
        qInfo() << "OpenGL module type:" << (openGLModuleType==QOpenGLContext::LibGL
×
789
                                                                                  ? "desktop OpenGL"
790
                                                                                  : openGLModuleType==QOpenGLContext::LibGL
791
                                                                                        ? "OpenGL ES 2 or higher"
×
792
                                                                                        : std::to_string(openGLModuleType).c_str());
×
793
        if (openGLModuleType==QOpenGLContext::LibGL)
×
794
        {
795
                fmt.setRenderableType(QSurfaceFormat::OpenGL);
×
796
                fmt.setVersion(3, 3);
×
797
                fmt.setProfile(QSurfaceFormat::CoreProfile);
×
798

799
                if (qApp && qApp->property("onetime_opengl_compat").toBool())
×
800
                {
801
                        qInfo() << "Setting OpenGL Compatibility profile from command line...";
×
802
                        fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
×
803
                        fmt.setOption(QSurfaceFormat::DeprecatedFunctions);
×
804
                }
805
                // FIXME: temporary hook for Qt5-based macOS bundles
806
                #if defined(Q_OS_MACOS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
807
                fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
808
                #endif
809
        }
810

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

813
        //request some sane buffer formats
814
        fmt.setRedBufferSize(8);
×
815
        fmt.setGreenBufferSize(8);
×
816
        fmt.setBlueBufferSize(8);
×
817
        fmt.setDepthBufferSize(24);
×
818
        // Without stencil buffer the debug versions of Qt will repeatedly emit warnings like:
819
        // "OpenGL paint engine: attempted to use stencil test without requesting a stencil buffer."
820
        fmt.setStencilBufferSize(8);
×
821

822
        if(qApp && qApp->property("onetime_single_buffer").toBool())
×
823
                fmt.setSwapBehavior(QSurfaceFormat::SingleBuffer);
×
824

825
        // VSync control. NOTE: it must be applied to the default format (QSurfaceFormat::setDefaultFormat) to take effect.
826
#ifdef Q_OS_MACOS
827
        // FIXME: workaround for bug LP:#1705832 (https://bugs.launchpad.net/stellarium/+bug/1705832)
828
        // Qt: https://bugreports.qt.io/browse/QTBUG-53273
829
        const bool vsdef = false; // use vsync=false by default on macOS
830
#else
831
        const bool vsdef = true;
×
832
#endif
833
        if (configuration && configuration->value("video/vsync", vsdef).toBool())
×
834
                fmt.setSwapInterval(1);
×
835
        else
836
                fmt.setSwapInterval(0);
×
837

838
#ifdef OPENGL_DEBUG_LOGGING
839
        //try to enable GL debugging using GL_KHR_debug
840
        fmt.setOption(QSurfaceFormat::DebugContext);
841
#endif
842

843
        return fmt;
×
844
}
×
845

846
void StelMainView::init()
×
847
{
848
#ifdef OPENGL_DEBUG_LOGGING
849
        if (glLogger)
850
        {
851
                if(!QOpenGLContext::currentContext()->hasExtension(QByteArrayLiteral("GL_KHR_debug")))
852
                        qWarning()<<"GL_KHR_debug extension missing, OpenGL debug logger will likely not work";
853
                if(glLogger->initialize())
854
                {
855
                        qInfo()<<"OpenGL debug logger initialized";
856
                        glLogger->disableMessages(QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::AnyType,
857
                                                                          QOpenGLDebugMessage::NotificationSeverity);
858
                        glLogger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
859
                        //the internal log buffer may not be empty, so check it
860
                        for (const auto& msg : glLogger->loggedMessages())
861
                        {
862
                                logGLMessage(msg);
863
                        }
864
                }
865
                else
866
                        qWarning()<<"Failed to initialize OpenGL debug logger";
867

868
                connect(QOpenGLContext::currentContext(),SIGNAL(aboutToBeDestroyed()),this,SLOT(contextDestroyed()));
869
                //for easier debugging, print the address of the main GL context
870
                qDebug()<<"CurCtxPtr:"<<QOpenGLContext::currentContext();
871
        }
872
#endif
873

874
        qInfo() << "Initializing StelMainView";
×
875

876
        glInfo.mainContext = QOpenGLContext::currentContext();
×
877
        glInfo.surface = glInfo.mainContext->surface();
×
878
        glInfo.functions = glInfo.mainContext->functions();
×
879
        glInfo.vendor = QString(reinterpret_cast<const char*>(glInfo.functions->glGetString(GL_VENDOR)));
×
880
        glInfo.renderer = QString(reinterpret_cast<const char*>(glInfo.functions->glGetString(GL_RENDERER)));
×
881
        const auto format = glInfo.mainContext->format();
×
882
        glInfo.supportsLuminanceTextures = format.profile() == QSurfaceFormat::CompatibilityProfile ||
×
883
                                                                           format.majorVersion() < 3;
×
884
        glInfo.isGLES = format.renderableType()==QSurfaceFormat::OpenGLES;
×
885
        qInfo().nospace() << "Luminance textures are " << (glInfo.supportsLuminanceTextures ? "" : "not ") << "supported";
×
886
        glInfo.isCoreProfile = format.profile() == QSurfaceFormat::CoreProfile;
×
887
        #ifndef Q_OS_HAIKU
888
        // Haiku OS hasn't hardware acceleration and we shouldn't use High Graphics Mode here
889
        glInfo.isHighGraphicsMode = !qApp->property("onetime_force_low_graphics").toBool() && !!StelOpenGL::highGraphicsFunctions();
×
890
        #endif
891
        qInfo() << "Running in" << (glInfo.isHighGraphicsMode ? "High" : "Low") << "Graphics Mode";
×
892

893
        auto& gl = *QOpenGLContext::currentContext()->functions();
×
894
        if(format.majorVersion() * 1000 + format.minorVersion() >= 4006 ||
×
895
           glInfo.mainContext->hasExtension("GL_EXT_texture_filter_anisotropic") ||
×
896
           glInfo.mainContext->hasExtension("GL_ARB_texture_filter_anisotropic"))
×
897
        {
898
                StelOpenGL::clearGLErrors();
×
899
                gl.glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY, &glInfo.maxAnisotropy);
×
900
                const auto error = gl.glGetError();
×
901
                if(error != GL_NO_ERROR)
×
902
                {
903
                        qWarning() << "Failed to get maximum texture anisotropy:" << StelOpenGL::getGLErrorText(error);
×
904
                }
905
                else if(glInfo.maxAnisotropy > 0)
×
906
                {
907
                        qInfo() << "Maximum texture anisotropy:" << glInfo.maxAnisotropy;
×
908
                }
909
                else
910
                {
911
                        qWarning() << "Maximum texture anisotropy is not positive:" << glInfo.maxAnisotropy;
×
912
                        glInfo.maxAnisotropy = 0;
×
913
                }
914
        }
915
        else
916
        {
917
                qWarning() << "Anisotropic filtering is not supported!";
×
918
        }
919

920
        if(format.majorVersion() > 4 || glInfo.mainContext->hasExtension("GL_ARB_sample_shading"))
×
921
        {
922
                auto addr = glInfo.mainContext->getProcAddress("glMinSampleShading");
×
923
                if(!addr)
×
924
                        addr = glInfo.mainContext->getProcAddress("glMinSampleShadingARB");
×
925
                glInfo.glMinSampleShading = reinterpret_cast<PFNGLMINSAMPLESHADINGPROC>(addr);
×
926
        }
927
        gl.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glInfo.maxTextureSize);
×
928
        qInfo() << "Maximum 2D texture size:" << glInfo.maxTextureSize;
×
929

930
        gui = new StelGui();
×
931

932
        // Should be check of requirements disabled? -- NO! This is intentional here, and does no harm.
933
        if (configuration->value("main/check_requirements", true).toBool())
×
934
        {
935
                // Find out lots of debug info about supported version of OpenGL and vendor/renderer.
936
                processOpenGLdiagnosticsAndWarnings(configuration, glInfo.mainContext);
×
937
        }
938

939
        //setup StelOpenGLArray global state
940
        StelOpenGLArray::initGL();
×
941

942
        //create and initialize main app
943
        stelApp = new StelApp(this);
×
944
        stelApp->setGui(gui);
×
945
        stelApp->init(configuration);
×
946
        //this makes sure the app knows how large the window is
947
        connect(stelScene,SIGNAL(sceneRectChanged(QRectF)),stelApp,SLOT(glWindowHasBeenResized(QRectF)));
×
948
#ifdef ENABLE_SPOUT
949
        QObject::connect(stelScene, &StelGraphicsScene::sceneRectChanged, [&](const QRectF& rect)
950
        {
951
        stelApp->glPhysicalWindowHasBeenResized(getPhysicalSize(rect));
952
        });
953
#endif
954

955
        //also immediately set the current values
956
        stelApp->glWindowHasBeenResized(stelScene->sceneRect());
×
957
#ifdef ENABLE_SPOUT
958
        stelApp->glPhysicalWindowHasBeenResized(getPhysicalSize(stelScene->sceneRect()));
959
#endif
960

961
        StelActionMgr *actionMgr = stelApp->getStelActionManager();
×
962
        actionMgr->addAction("actionSave_Screenshot_Global", N_("Miscellaneous"), N_("Save screenshot"), this, "saveScreenShot()", "Ctrl+S");
×
963
        actionMgr->addAction("actionReload_Shaders", N_("Miscellaneous"), N_("Reload shaders (for development)"), this, "reloadShaders()", "Ctrl+R, P");
×
964
        actionMgr->addAction("actionSet_Full_Screen_Global", N_("Display Options"), N_("Full-screen mode"), this, "fullScreen", "F11");
×
965
        
966
        StelPainter::initGLShaders();
×
967

968
        guiItem = new StelGuiItem(size(), rootItem);
×
969
        scene()->addItem(rootItem);
×
970
        //set the default focus to the sky
971
        focusSky();
×
972
        nightModeEffect = new NightModeGraphicsEffect(this);
×
973
        updateNightModeProperty(StelApp::getInstance().getVisionModeNight());
×
974
        //install the effect on the whole view
975
        rootItem->setGraphicsEffect(nightModeEffect);
×
976

977
        flagInvertScreenShotColors = configuration->value("main/invert_screenshots_colors", false).toBool();
×
978
        setScreenshotFormat(configuration->value("main/screenshot_format", "png").toString()); // includes check for supported formats.
×
979
        flagScreenshotDateFileName=configuration->value("main/screenshot_datetime_filename", false).toBool();
×
980
        screenShotFileMask = configuration->value("main/screenshot_datetime_filemask", "yyyyMMdd-hhmmssz").toString();
×
981
        flagUseCustomScreenshotSize=configuration->value("main/screenshot_custom_size", false).toBool();
×
982
        customScreenshotWidth=configuration->value("main/screenshot_custom_width", 1024).toInt();
×
983
        customScreenshotHeight=configuration->value("main/screenshot_custom_height", 768).toInt();
×
984
        screenshotDpi=configuration->value("main/screenshot_dpi", 72).toInt();
×
985
        setFlagCursorTimeout(configuration->value("gui/flag_mouse_cursor_timeout", false).toBool());
×
986
        setCursorTimeout(configuration->value("gui/mouse_cursor_timeout", 10.f).toDouble());
×
987
        setMaxFps(configuration->value("video/maximum_fps",10000.f).toFloat());
×
988
        setMinFps(configuration->value("video/minimum_fps",10000.f).toFloat());
×
989
        setMinTimeBetweenFrames(qMax(0, configuration->value("video/min_time_between_frames",5).toInt()));
×
990
        setSkyBackgroundColor(Vec3f(configuration->value("color/sky_background_color", "0,0,0").toString()));
×
991

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

996
        // The script manager can only be fully initialized after the plugins have loaded.
997
        stelApp->initScriptMgr();
×
998

999
        // Set the global stylesheet, this is only useful for the tooltips.
1000
        StelGui* sgui = dynamic_cast<StelGui*>(stelApp->getGui());
×
1001
        if (sgui!=Q_NULLPTR)
×
1002
                setStyleSheet(sgui->getStelStyle().qtStyleSheet);
×
1003
        connect(stelApp, SIGNAL(visionNightModeChanged(bool)), this, SLOT(updateNightModeProperty(bool)));
×
1004

1005
        // I doubt this will have any effect on framerate, but may cause problems elsewhere?
1006
        QThread::currentThread()->setPriority(QThread::HighestPriority);
×
1007
#ifndef NDEBUG
1008
        // Get an overview of module callOrders
1009
        if (qApp->property("verbose")==true)
×
1010
        {
1011
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionDraw);
×
1012
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionUpdate);
×
1013
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleMouseClicks);
×
1014
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleMouseMoves);
×
1015
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleKeys);
×
1016
        }
1017
#endif
1018

1019
        // check conflicts for keyboard shortcuts...
1020
        if (configuration->childGroups().contains("shortcuts"))
×
1021
        {
1022
                QStringList defaultShortcuts =  actionMgr->getShortcutsList();
×
1023
                QStringList conflicts;
×
1024
                configuration->beginGroup("shortcuts");
×
1025
                QStringList cstActionNames = configuration->allKeys();
×
1026
                QMultiMap<QString, QString> cstActionsMap; // It is possible we have a very messed-up setup with duplicates
×
1027
                for (QStringList::const_iterator cstActionName = cstActionNames.constBegin(); cstActionName != cstActionNames.constEnd(); ++cstActionName)
×
1028
                {
1029
                        #if (QT_VERSION>=QT_VERSION_CHECK(5, 14, 0))
1030
                        QStringList singleCustomActionShortcuts = configuration->value((*cstActionName).toLocal8Bit().constData()).toString().split(" ", Qt::SkipEmptyParts);
×
1031
                        #else
1032
                        QStringList singleCustomActionShortcuts = configuration->value((*cstActionName).toLocal8Bit().constData()).toString().split(" ", QString::SkipEmptyParts);
1033
                        #endif
1034
                        singleCustomActionShortcuts.removeAll("\"\"");
×
1035

1036
                        // Add 1-2 entries per action
1037
                        for (QStringList::const_iterator cstActionShortcut = singleCustomActionShortcuts.constBegin(); cstActionShortcut != singleCustomActionShortcuts.constEnd(); ++cstActionShortcut)
×
1038
                                if (strcmp( (*cstActionShortcut).toLocal8Bit().constData(), "") )
×
1039
                                        cstActionsMap.insert((*cstActionShortcut), (*cstActionName));
×
1040
                }
×
1041
                // Now we have a QMultiMap with (customShortcut, actionName). It may contain multiple keys!
1042
                QStringList allMapKeys=cstActionsMap.keys();
×
1043
                QStringList uniqueMapKeys=cstActionsMap.uniqueKeys();
×
1044
                for (auto &key : uniqueMapKeys)
×
1045
                        allMapKeys.removeOne(key);
×
1046
                conflicts << allMapKeys; // Add the remaining (duplicate) keys
×
1047

1048
                // Check every shortcut from the Map that it is not assigned to its own correct action
1049
                for (QMultiMap<QString, QString>::const_iterator it=cstActionsMap.constBegin(); it != cstActionsMap.constEnd(); ++it)
×
1050
                {
1051
                        QString customKey(it.key());
×
1052
                        QString actionName=cstActionsMap.value(it.key());
×
1053
                        StelAction *action = actionMgr->findAction(actionName);
×
1054
                        if (action && defaultShortcuts.contains(customKey) && actionMgr->findActionFromShortcut(customKey)->getId()!=action->getId())
×
1055
                                conflicts << customKey;
×
1056
                }
×
1057
                configuration->endGroup();
×
1058

1059
                if (!conflicts.isEmpty())
×
1060
                {
1061
                        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);
×
1062
                        qCritical() << "Conflicting keyboard shortcut assignments found. Please resolve:" << conflicts.join("; "); // Repeat in logfile for later retrieval.
×
1063
                }
1064
        }
×
1065
}
×
1066

1067
void StelMainView::updateNightModeProperty(bool b)
×
1068
{
1069
        // So that the bottom bar tooltips get properly rendered in night mode.
1070
        setProperty("nightMode", b);
×
1071
        nightModeEffect->setEnabled(b);
×
1072
}
×
1073

1074
void StelMainView::reloadShaders()
×
1075
{
1076
        //make sure GL context is bound
1077
        glContextMakeCurrent();
×
1078
        emit reloadShadersRequested();
×
1079
}
×
1080

1081
// This is a series of various diagnostics based on "bugs" reported for 0.13.0 and 0.13.1.
1082
// Almost all can be traced to insufficient driver level.
1083
// No changes of OpenGL state is done here.
1084
// If problems are detected, warn the user one time, but continue. Warning panel will be suppressed on next start.
1085
// Work in progress, as long as we get reports about bad systems or until OpenGL startup is finalized and safe.
1086
// Several tests do not apply to MacOS X.
1087
void StelMainView::processOpenGLdiagnosticsAndWarnings(QSettings *conf, QOpenGLContext *context) const
×
1088
{
1089
#ifdef Q_OS_MACOS
1090
        Q_UNUSED(conf);
1091
#endif
1092
        QSurfaceFormat format=context->format();
×
1093

1094
        // These tests are not required on MacOS X
1095
#ifndef Q_OS_MACOS
1096
        bool openGLerror=false;
×
1097
        if (format.renderableType()==QSurfaceFormat::OpenGL || format.renderableType()==QSurfaceFormat::OpenGLES)
×
1098
        {
1099
                qInfo().noquote() << "Detected:" << (format.renderableType()==QSurfaceFormat::OpenGL  ? "OpenGL" : "OpenGL ES" ) << QString("%1.%2").arg(format.majorVersion()).arg(format.minorVersion());
×
1100
        }
1101
        else
1102
        {
1103
                openGLerror=true;
×
1104
                qCritical() << "Neither OpenGL nor OpenGL ES detected: Unsupported Format!";
×
1105
        }
1106
#endif
1107
        QOpenGLFunctions* gl = context->functions();
×
1108

1109
        QString glDriver(reinterpret_cast<const char*>(gl->glGetString(GL_VERSION)));
×
1110
        qInfo().noquote() << "Driver version string:" << glDriver;
×
1111
        qInfo().noquote() << "GL vendor:" << QString(reinterpret_cast<const char*>(gl->glGetString(GL_VENDOR)));
×
1112
        QString glRenderer(reinterpret_cast<const char*>(gl->glGetString(GL_RENDERER)));
×
1113
        qInfo().noquote() << "GL renderer:" << glRenderer;
×
1114

1115
        // 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).
1116
        // 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
1117
        // detected as providing ps_3_0.
1118
        // This means, usually systems with OpenGL3 support reported in the driver will work, those with reported 2.1 only will almost certainly fail.
1119
        // If platform does not even support minimal OpenGL version for Qt5, then tell the user about troubles and quit from application.
1120
        // This test is apparently not applicable on MacOS X due to its behaving differently from all other known OSes.
1121
        // The correct way to handle driver issues on MacOS X remains however unclear for now.
1122
#ifndef Q_OS_MACOS
1123
        bool isMesa=glDriver.contains("Mesa", Qt::CaseInsensitive);
×
1124
        #if (defined Q_OS_WIN) && (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1125
        bool isANGLE=glRenderer.startsWith("ANGLE", Qt::CaseSensitive);
1126
        #endif
1127
        if ( openGLerror ||
×
1128
             ((format.renderableType()==QSurfaceFormat::OpenGL  ) && (format.version() < QPair<int, int>(2, 1)) && !isMesa) ||
×
1129
             ((format.renderableType()==QSurfaceFormat::OpenGL  ) && (format.version() < QPair<int, int>(2, 0)) &&  isMesa) || // Mesa defaults to 2.0 but works!
×
1130
             ((format.renderableType()==QSurfaceFormat::OpenGLES) && (format.version() < QPair<int, int>(2, 0)))  )
×
1131
        {
1132
        #if (defined Q_OS_WIN) && (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1133
                if ((!isANGLE) && (!isMesa))
1134
                        qCritical() << "Oops... Insufficient OpenGL version. Please update drivers, graphics hardware, or use --angle-mode (or even --mesa-mode) option.";
1135
                else if (isANGLE)
1136
                        qCritical() << "Oops... Insufficient OpenGLES version in ANGLE. Please update drivers, graphics hardware, or use --mesa-mode option.";
1137
        #elif (defined Q_OS_WIN)
1138
                if (!isMesa)
1139
                        qCritical() << "Oops... Insufficient OpenGL version. Please update drivers, graphics hardware, or use --mesa-mode option.";
1140
                else
1141
                        qCritical() << "Oops... Insufficient OpenGL version. Mesa failed! Please send a bug report.";
1142

1143
                #if (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1144
                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);
1145
                #else
1146
                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Insufficient OpenGL version. Please update drivers, graphics hardware, or use --mesa-mode option."), QMessageBox::Abort, QMessageBox::Abort);
1147
                #endif
1148
        #else
1149
                qCritical() << "Oops... Insufficient OpenGL version. Please update drivers, or graphics hardware.";
×
1150
                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Insufficient OpenGL version. Please update drivers, or graphics hardware."), QMessageBox::Abort, QMessageBox::Abort);
×
1151
        #endif
1152
                exit(1);
×
1153
        }
1154
#endif
1155
        // This call requires OpenGL2+.
1156
        QString glslString(reinterpret_cast<const char*>(gl->glGetString(GL_SHADING_LANGUAGE_VERSION)));
×
1157
        qInfo().noquote() << "GL Shading Language version:" << glslString;
×
1158

1159
        // Only give extended info if called on command line, for diagnostic.
1160
        if (qApp->property("dump_OpenGL_details").toBool())
×
1161
                dumpOpenGLdiagnostics();
×
1162

1163
#if (defined Q_OS_WIN) && (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1164
        // If we have ANGLE, check esp. for insufficient ps_2 level.
1165
        if (isANGLE)
1166
        {
1167
                static const QRegularExpression angleVsPsRegExp(" vs_(\\d)_(\\d) ps_(\\d)_(\\d)");
1168
                int angleVSPSpos=glRenderer.indexOf(angleVsPsRegExp);
1169

1170
                if (angleVSPSpos >-1)
1171
                {
1172
                        QRegularExpressionMatch match=angleVsPsRegExp.match(glRenderer);
1173
                        float vsVersion=match.captured(1).toFloat() + 0.1f*match.captured(2).toFloat();
1174
                        float psVersion=match.captured(3).toFloat() + 0.1f*match.captured(4).toFloat();
1175
                        qInfo() << "VS Version Number detected:" << vsVersion;
1176
                        qInfo() << "PS Version Number detected:" << psVersion;
1177
                        if ((vsVersion<2.0f) || (psVersion<3.0f))
1178
                        {
1179
                                openGLerror=true;
1180
                                qCritical() << "This is not enough: we need DirectX9 with vs_2_0 and ps_3_0 or later.";
1181
                                qCritical() << "You should update graphics drivers, graphics hardware, or use the --mesa-mode option.";
1182
                                qCritical() << "Else, please try to use an older version like 0.12.9, and try with --safe-mode";
1183

1184
                                if (conf->value("main/ignore_opengl_warning", false).toBool())
1185
                                {
1186
                                        qWarning() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
1187
                                }
1188
                                else
1189
                                {
1190
                                        qInfo() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
1191
                                        qInfo() << "But more than likely problems will persist.";
1192
                                        QMessageBox::StandardButton answerButton=
1193
                                        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?"),
1194
                                                              QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
1195
                                        if (answerButton == QMessageBox::Abort)
1196
                                        {
1197
                                                qCritical() << "Aborting due to ANGLE OpenGL ES / DirectX vs or ps version problems.";
1198
                                                exit(1);
1199
                                        }
1200
                                        else
1201
                                        {
1202
                                                qWarning() << "Ignoring all warnings, continuing without further question.";
1203
                                                conf->setValue("main/ignore_opengl_warning", true);
1204
                                        }
1205
                                }
1206
                        }
1207
                        else
1208
                                qInfo() << "vs/ps version is fine, we should not see a graphics problem.";
1209
                }
1210
                else
1211
                {
1212
                        qCritical() << "Cannot parse ANGLE shader version string. This may indicate future problems.";
1213
                        qCritical() << "Please send a bug report that includes this log file and states if Stellarium runs or has problems.";
1214
                }
1215
        }
1216
#endif
1217
#ifndef Q_OS_MACOS
1218
        // 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.
1219
        if (isMesa)
×
1220
        {
1221
                static const QRegularExpression mesaRegExp("Mesa (\\d+\\.\\d+)"); // we need only major version. Minor should always be here. Test?
×
1222
                int mesaPos=glDriver.indexOf(mesaRegExp);
×
1223

1224
                if (mesaPos >-1)
×
1225
                {
1226
                        float mesaVersion=mesaRegExp.match(glDriver).captured(1).toFloat();
×
1227
                        qInfo() << "MESA version number detected:" << mesaVersion;
×
1228
                        if ((mesaVersion<10.0f))
×
1229
                        {
1230
                                openGLerror=true;
×
1231
                                qCritical() << "This is not enough: we need Mesa 10.0 or later.";
×
1232
                                qCritical() << "You should update graphics drivers or graphics hardware.";
×
1233
                                qCritical() << "Else, please try to use an older version like 0.12.9, and try there with --safe-mode";
×
1234

1235
                                if (conf->value("main/ignore_opengl_warning", false).toBool())
×
1236
                                {
1237
                                        qWarning() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
×
1238
                                }
1239
                                else
1240
                                {
1241
                                        qInfo() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
×
1242
                                        qInfo() << "But more than likely problems will persist.";
×
1243
                                        QMessageBox::StandardButton answerButton=
1244
                                        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?"),
×
1245
                                                              QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
1246
                                        if (answerButton == QMessageBox::Abort)
×
1247
                                        {
1248
                                                qCritical() << "Aborting due to OpenGL/Mesa insufficient version problems.";
×
1249
                                                exit(1);
×
1250
                                        }
1251
                                        else
1252
                                        {
1253
                                                qWarning() << "Ignoring all warnings, continuing without further question.";
×
1254
                                                conf->setValue("main/ignore_opengl_warning", true);
×
1255
                                        }
1256
                                }
1257
                        }
1258
                        else
1259
                                qInfo() << "Mesa version is fine, we should not see a graphics problem.";
×
1260
                }
1261
                else
1262
                {
1263
                        qCritical() << "Cannot parse Mesa Driver version string. This may indicate future problems.";
×
1264
                        qCritical() << "Please send a bug report that includes this log file and states if Stellarium runs or has problems.";
×
1265
                }
1266
        }
1267
#endif
1268

1269
        // Although our shaders are only GLSL1.10, there are frequent problems with systems just at this level of programmable shaders.
1270
        // If GLSL version is less than 1.30 or GLSL ES 1.00, Stellarium usually does run properly on Windows or various Linux flavours.
1271
        // Depending on whatever driver/implementation details, Stellarium may crash or show only minor graphical errors.
1272
        // On these systems, we show a warning panel that can be suppressed by a config option which is automatically added on first run.
1273
        // Again, based on a sample size of one, Macs have been reported already to always work in this case.
1274
#ifndef Q_OS_MACOS
1275
        static const QRegularExpression glslRegExp("^(\\d\\.\\d\\d)");
×
1276
        int pos=glslString.indexOf(glslRegExp);
×
1277
        // VC4 drivers on Raspberry Pi reports ES 1.0.16 or so, we must step down to one cipher after decimal.
1278
        static const QRegularExpression glslesRegExp("ES (\\d\\.\\d)");
×
1279
        int posES=glslString.indexOf(glslesRegExp);
×
1280
        if (pos >-1)
×
1281
        {
1282
                float glslVersion=glslRegExp.match(glslString).captured(1).toFloat();
×
1283
                qInfo() << "GLSL Version Number detected:" << glslVersion;
×
1284
                if (glslVersion<1.3f)
×
1285
                {
1286
                        openGLerror=true;
×
1287
                        qCritical() << "This is not enough: we need GLSL1.30 or later.";
×
1288
                        #ifdef Q_OS_WIN
1289
                        qCritical() << "You should update graphics drivers, graphics hardware, or use the --mesa-mode option.";
1290
                        #else
1291
                        qCritical() << "You should update graphics drivers or graphics hardware.";
×
1292
                        #endif
1293
                        qCritical() << "Else, please try to use an older version like 0.12.9, and try there with --safe-mode";
×
1294

1295
                        if (conf->value("main/ignore_opengl_warning", false).toBool())
×
1296
                        {
1297
                                qWarning() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
×
1298
                        }
1299
                        else
1300
                        {
1301
                                qInfo() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
×
1302
                                qInfo() << "But more than likely problems will persist.";
×
1303
                                QMessageBox::StandardButton answerButton=
1304
                                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?"),
×
1305
                                                      QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
1306
                                if (answerButton == QMessageBox::Abort)
×
1307
                                {
1308
                                        qCritical() << "Aborting due to OpenGL/GLSL version problems.";
×
1309
                                        exit(1);
×
1310
                                }
1311
                                else
1312
                                {
1313
                                        qWarning() << "Ignoring all warnings, continuing without further question.";
×
1314
                                        conf->setValue("main/ignore_opengl_warning", true);
×
1315
                                }
1316
                        }
1317
                }
1318
                else
1319
                        qInfo() << "GLSL version is fine, we should not see a graphics problem.";
×
1320
        }
1321
        else if (posES >-1)
×
1322
        {
1323
                float glslesVersion=glslesRegExp.match(glslString).captured(1).toFloat();
×
1324
                qInfo() << "GLSL ES Version Number detected:" << glslesVersion;
×
1325
                if (glslesVersion<1.0f) // TBD: is this possible at all?
×
1326
                {
1327
                        openGLerror=true;
×
1328
                        qCritical() << "This is not enough: we need GLSL ES 1.00 or later.";
×
1329
#ifdef Q_OS_WIN
1330
                        qCritical() << "You should update graphics drivers, graphics hardware, or use the --mesa-mode option.";
1331
#else
1332
                        qCritical() << "You should update graphics drivers or graphics hardware.";
×
1333
#endif
1334
                        qCritical() << "Else, please try to use an older version like 0.12.9, and try there with --safe-mode";
×
1335

1336
                        if (conf->value("main/ignore_opengl_warning", false).toBool())
×
1337
                        {
1338
                                qWarning() << "Config option main/ignore_opengl_warning found, continuing. Expect problems.";
×
1339
                        }
1340
                        else
1341
                        {
1342
                                qInfo() << "You can try to run in an unsupported degraded mode by ignoring the warning and continuing.";
×
1343
                                qInfo() << "But more than likely problems will persist.";
×
1344
                                QMessageBox::StandardButton answerButton=
1345
                                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?"),
×
1346
                                                      QMessageBox::Ignore|QMessageBox::Abort, QMessageBox::Abort);
1347
                                if (answerButton == QMessageBox::Abort)
×
1348
                                {
1349
                                        qCritical() << "Aborting due to OpenGL ES/GLSL ES version problems.";
×
1350
                                        exit(1);
×
1351
                                }
1352
                                else
1353
                                {
1354
                                        qWarning() << "Ignoring all warnings, continuing without further question.";
×
1355
                                        conf->setValue("main/ignore_opengl_warning", true);
×
1356
                                }
1357
                        }
1358
                }
1359
                else
1360
                {
1361
                        if (openGLerror)
×
1362
                                qWarning() << "GLSL ES version is OK, but there were previous errors, expect problems.";
×
1363
                        else
1364
                                qInfo() << "GLSL ES version is fine, we should not see a graphics problem.";
×
1365
                }
1366
        }
1367
        else
1368
        {
1369
                qCritical() << "Cannot parse GLSL (ES) version string. This may indicate future problems.";
×
1370
                qCritical() << "Please send a bug report that includes this log file and states if Stellarium works or has problems.";
×
1371
        }
1372
#endif
1373
}
×
1374

1375
// Get physical dimensions given the virtual dimensions for the screen where this window is located.
1376
QRectF StelMainView::getPhysicalSize(const QRectF& virtualRect) const
×
1377
{
1378
        auto window = this->window();
×
1379
        if(window)
×
1380
        {
1381
                auto pixelRatio = window->devicePixelRatio();
×
1382
                QRectF newRect(virtualRect.x(), virtualRect.y(), virtualRect.width() * pixelRatio, virtualRect.height() * pixelRatio);
×
1383
                return newRect;
×
1384
        }
1385

1386
        // If the platform does not support getting the QWindow backing instance of this QWidget
1387
        return virtualRect;
×
1388
}
1389

1390
// Debug info about OpenGL capabilities.
1391
void StelMainView::dumpOpenGLdiagnostics() const
×
1392
{
1393
        QOpenGLContext *context = QOpenGLContext::currentContext();
×
1394
        if (context)
×
1395
        {
1396
                context->functions()->initializeOpenGLFunctions();
×
1397
                qDebug() << "initializeOpenGLFunctions()...";
×
1398
                QOpenGLFunctions::OpenGLFeatures oglFeatures=context->functions()->openGLFeatures();
×
1399
                qInfo() << "OpenGL Features:";
×
1400
                qInfo() << " - glActiveTexture() function" << ((oglFeatures&QOpenGLFunctions::Multitexture) ? "is" : "is NOT") << "available.";
×
1401
                qInfo() << " - Shader functions" << ((oglFeatures&QOpenGLFunctions::Shaders) ? "are" : "are NOT ") << "available.";
×
1402
                qInfo() << " - Vertex and index buffer functions" << ((oglFeatures&QOpenGLFunctions::Buffers) ? "are" : "are NOT") << "available.";
×
1403
                qInfo() << " - Framebuffer object functions" << ((oglFeatures&QOpenGLFunctions::Framebuffers) ? "are" : "are NOT") << "available.";
×
1404
                qInfo() << " - glBlendColor()" << ((oglFeatures&QOpenGLFunctions::BlendColor) ? "is" : "is NOT") << "available.";
×
1405
                qInfo() << " - glBlendEquation()" << ((oglFeatures&QOpenGLFunctions::BlendEquation) ? "is" : "is NOT") << "available.";
×
1406
                qInfo() << " - glBlendEquationSeparate()" << ((oglFeatures&QOpenGLFunctions::BlendEquationSeparate) ? "is" : "is NOT") << "available.";
×
1407
                qInfo() << " - glBlendFuncSeparate()" << ((oglFeatures&QOpenGLFunctions::BlendFuncSeparate) ? "is" : "is NOT") << "available.";
×
1408
                qInfo() << " - Blend subtract mode" << ((oglFeatures&QOpenGLFunctions::BlendSubtract) ? "is" : "is NOT") << "available.";
×
1409
                qInfo() << " - Compressed texture functions" << ((oglFeatures&QOpenGLFunctions::CompressedTextures) ? "are" : "are NOT") << "available.";
×
1410
                qInfo() << " - glSampleCoverage() function" << ((oglFeatures&QOpenGLFunctions::Multisample) ? "is" : "is NOT") << "available.";
×
1411
                qInfo() << " - Separate stencil functions" << ((oglFeatures&QOpenGLFunctions::StencilSeparate) ? "are" : "are NOT") << "available.";
×
1412
                qInfo() << " - Non power of two textures" << ((oglFeatures&QOpenGLFunctions::NPOTTextures) ? "are" : "are NOT") << "available.";
×
1413
                qInfo() << " - Non power of two textures" << ((oglFeatures&QOpenGLFunctions::NPOTTextureRepeat) ? "can" : "CANNOT") << "use GL_REPEAT as wrap parameter.";
×
1414
                qInfo() << " - The fixed function pipeline" << ((oglFeatures&QOpenGLFunctions::FixedFunctionPipeline) ? "is" : "is NOT") << "available.";
×
1415
                GLfloat lineWidthRange[2];
1416
                context->functions()->glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, lineWidthRange);
×
1417
                qInfo() << "Line widths available from" << lineWidthRange[0] << "to" << lineWidthRange[1];
×
1418

1419
                qInfo() << "OpenGL shader capabilities and details:";
×
1420
                qInfo() << " - Vertex Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Vertex, context) ? "YES" : "NO");
×
1421
                qInfo() << " - Fragment Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Fragment, context) ? "YES" : "NO");
×
1422
                qInfo() << " - Geometry Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Geometry, context) ? "YES" : "NO");
×
1423
                qInfo() << " - TessellationControl Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::TessellationControl, context) ? "YES" : "NO");
×
1424
                qInfo() << " - TessellationEvaluation Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::TessellationEvaluation, context) ? "YES" : "NO");
×
1425
                qInfo() << " - Compute Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Compute, context) ? "YES" : "NO");
×
1426
                
1427
                // List available extensions. Not sure if this is in any way useful?
1428
                QSet<QByteArray> extensionSet=context->extensions();
×
1429
                qInfo() << "We have" << extensionSet.count() << "OpenGL extensions:";
×
1430
                QMap<QString, QString> extensionMap;
×
1431
                QSetIterator<QByteArray> iter(extensionSet);
×
1432
                while (iter.hasNext())
×
1433
                {
1434
                        if (!iter.peekNext().isEmpty()) {// Don't insert empty lines
×
1435
                                extensionMap.insert(QString(iter.peekNext()), QString(iter.peekNext()));
×
1436
                        }
1437
                        iter.next();
×
1438
                }
1439
                QMapIterator<QString, QString> iter2(extensionMap);
×
1440
                while (iter2.hasNext()) {
×
1441
                        qInfo().noquote() << " -" << iter2.next().key();
×
1442
                }
1443

1444
                QFunctionPointer programParameterPtr =context->getProcAddress("glProgramParameteri");
×
1445
                if (programParameterPtr == Q_NULLPTR) {
×
1446
                        qWarning() << "glProgramParameteri cannot be resolved here. BAD!";
×
1447
                }
1448
                programParameterPtr =context->getProcAddress("glProgramParameteriEXT");
×
1449
                if (programParameterPtr == Q_NULLPTR) {
×
1450
                        qWarning() << "glProgramParameteriEXT cannot be resolved here. BAD!";
×
1451
                }
1452
        }
×
1453
        else
1454
        {
1455
                qCritical() << "dumpOpenGLdiagnostics(): No OpenGL context";
×
1456
        }
1457
}
×
1458

1459
void StelMainView::deinit()
×
1460
{
1461
        glContextMakeCurrent();
×
1462
        deinitGL();
×
1463
        delete stelApp;
×
1464
        stelApp = Q_NULLPTR;
×
1465
}
×
1466

1467
// Update the translated title
1468
void StelMainView::initTitleI18n()
×
1469
{
1470
        setWindowTitle(StelUtils::getApplicationName());
×
1471
}
×
1472

1473
void StelMainView::setFullScreen(bool b)
×
1474
{
1475
        if (b)
×
1476
                showFullScreen();
×
1477
        else
1478
        {
1479
                showNormal();
×
1480
                // 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.
1481
                // Therefore moving is not possible. We must move to the stored position or at least defaults.
1482
                if ( (x()<0)  && (y()<0))
×
1483
                {
1484
                        QSettings *conf = stelApp->getSettings();
×
1485
                        int screen = conf->value("video/screen_number", 0).toInt();
×
1486
                        if (screen < 0 || screen >= qApp->screens().count())
×
1487
                        {
1488
                                qWarning() << "Screen" << screen << "not found";
×
1489
                                screen = 0;
×
1490
                        }
1491
                        QRect screenGeom = qApp->screens().at(screen)->geometry();
×
1492
                        int x = conf->value("video/screen_x", 0).toInt();
×
1493
                        int y = conf->value("video/screen_y", 0).toInt();
×
1494
                        move(x + screenGeom.x(), y + screenGeom.y());
×
1495
                }
1496
        }
1497
        StelApp::immediateSave("video/fullscreen", b);
×
1498
        emit fullScreenChanged(b);
×
1499
}
×
1500

1501
void StelMainView::drawEnded()
×
1502
{
1503
        updateQueued = false;
×
1504

1505
#ifndef Q_OS_MACOS
1506
        int requiredFpsInterval = qRound(needsMaxFPS()?1000.f/maxfps:1000.f/minfps);
×
1507
        if(fpsTimer->interval() != requiredFpsInterval)
×
1508
                fpsTimer->setInterval(requiredFpsInterval);
×
1509
#else
1510
        // FIXME: workaround for https://github.com/Stellarium/stellarium/issues/2778, in which touchpad-based
1511
        // view manipulation can be very laggy on Macs in circumstances where frame rendering more time than
1512
        // the trackpad move update rate from the OS. This is perhaps a bug in Qt; see the discussion around
1513
        // https://github.com/Stellarium/stellarium/issues/2778#issuecomment-1722766935 and below for details.
1514
        fpsTimer->setInterval(qMax(minTimeBetweenFrames, qRound(needsMaxFPS()?1000.f/maxfps:1000.f/minfps)));
1515
#endif
1516

1517
        if(!fpsTimer->isActive())
×
1518
                fpsTimer->start();
×
1519

1520
        emit frameFinished();
×
1521
}
×
1522

1523
void StelMainView::setFlagCursorTimeout(bool b)
×
1524
{
1525
        if (b == flagCursorTimeout) return;
×
1526

1527
        flagCursorTimeout = b;
×
1528
        if (b)         // enable timer
×
1529
        {
1530
                cursorTimeoutTimer->start();
×
1531
        }
1532
        else        // disable timer
1533
        {
1534
                // Show the previous cursor if the current is "hidden" 
1535
                if (QGuiApplication::overrideCursor() && (QGuiApplication::overrideCursor()->shape() == Qt::BlankCursor) )
×
1536
                {
1537
                        // pop the blank cursor
1538
                        QGuiApplication::restoreOverrideCursor();
×
1539
                }
1540
                // and stop the timer 
1541
                cursorTimeoutTimer->stop();
×
1542
        }
1543
        
1544
        StelApp::immediateSave("gui/flag_mouse_cursor_timeout", b);
×
1545
        emit flagCursorTimeoutChanged(b);
×
1546
}
1547

1548
void StelMainView::hideCursor()
×
1549
{
1550
        // timeout fired...
1551
        // if the feature is not asked, do nothing
1552
        if (!flagCursorTimeout) return;
×
1553

1554
        // "hide" the current cursor by pushing a Blank cursor
1555
        QGuiApplication::setOverrideCursor(Qt::BlankCursor);
×
1556
}
1557

1558
void StelMainView::fpsTimerUpdate()
×
1559
{
1560
        if(!updateQueued)
×
1561
        {
1562
                updateQueued = true;
×
1563
                QTimer::singleShot(0, glWidget, SLOT(update()));
×
1564
        }
1565
}
×
1566

1567
#ifdef OPENGL_DEBUG_LOGGING
1568
void StelMainView::logGLMessage(const QOpenGLDebugMessage &debugMessage)
1569
{
1570
        qDebug()<<debugMessage;
1571
}
1572

1573
void StelMainView::contextDestroyed()
1574
{
1575
        qDebug()<<"Main OpenGL context destroyed";
1576
}
1577
#endif
1578

1579
void StelMainView::thereWasAnEvent()
×
1580
{
1581
        lastEventTimeSec = StelApp::getTotalRunTime();
×
1582
}
×
1583

1584
bool StelMainView::needsMaxFPS() const
×
1585
{
1586
        const double now = StelApp::getTotalRunTime();
×
1587

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

1596
void StelMainView::moveEvent(QMoveEvent * event)
×
1597
{
1598
        const QPoint &pos=event->pos();
×
1599

1600
        // We use the glWidget instead of the event, as we want the screen that shows most of the widget.
1601
        QWindow* win = glWidget->windowHandle();
×
1602
        if(win)
×
1603
        {
1604
                stelApp->setDevicePixelsPerPixel(win->devicePixelRatio());
×
1605
        }
1606

1607
        if (StelApp::isInitialized())
×
1608
        {
1609
                StelApp::immediateSave("video/screen_x", pos.x());
×
1610
                StelApp::immediateSave("video/screen_y", pos.y());
×
1611
        }
1612
}
×
1613

1614
void StelMainView::closeEvent(QCloseEvent* event)
×
1615
{
1616
        Q_UNUSED(event)
1617
        stelApp->quit();
×
1618
}
×
1619

1620
//! Delete openGL textures (to call before the GLContext disappears)
1621
void StelMainView::deinitGL()
×
1622
{
1623
        //fix for bug 1628072 caused by QTBUG-56798
1624
#ifndef QT_NO_DEBUG
1625
        StelOpenGL::clearGLErrors();
×
1626
#endif
1627

1628
        stelApp->deinit();
×
1629
        delete gui;
×
1630
        gui = Q_NULLPTR;
×
1631
}
×
1632

1633
void StelMainView::setScreenshotFormat(const QString filetype)
×
1634
{
1635
        const QString candidate=filetype.toLower();
×
1636
        const QByteArray candBA=candidate.toUtf8();
×
1637

1638
        // Make sure format is supported by Qt, but restrict some useless formats.
1639
        QList<QByteArray> formats = QImageWriter::supportedImageFormats();
×
1640
        formats.removeOne("icns");
×
1641
        formats.removeOne("wbmp");
×
1642
        formats.removeOne("cur");
×
1643
        if (formats.contains(candBA))
×
1644
        {
1645
                screenShotFormat=candidate;
×
1646
                // apply setting immediately
1647
                configuration->setValue("main/screenshot_format", candidate);
×
1648
                emit screenshotFormatChanged(candidate);
×
1649
        }
1650
        else
1651
        {
1652
                qCritical() << "Invalid filetype for screenshot: " << filetype;
×
1653
        }
1654
}
×
1655

1656
void StelMainView::setFlagScreenshotDateFileName(bool b)
×
1657
{
1658
        flagScreenshotDateFileName=b;
×
1659
        StelApp::getInstance().getSettings()->setValue("main/screenshot_datetime_filename", b);
×
1660
        emit flagScreenshotDateFileNameChanged(b);
×
1661
}
×
1662

1663
void StelMainView::setScreenshotFileMask(const QString filemask)
×
1664
{
1665
        screenShotFileMask = filemask;
×
1666
        StelApp::getInstance().getSettings()->setValue("main/screenshot_datetime_filemask", filemask);
×
1667
        emit screenshotFileMaskChanged(filemask);
×
1668
}
×
1669

1670
void StelMainView::setScreenshotDpi(int dpi)
×
1671
{
1672
        screenshotDpi=dpi;
×
1673
        StelApp::getInstance().getSettings()->setValue("main/screenshot_dpi", dpi);
×
1674
        emit screenshotDpiChanged(dpi);
×
1675
}
×
1676

1677
void StelMainView::saveScreenShot(const QString& filePrefix, const QString& saveDir, const bool overwrite)
×
1678
{
1679
        screenShotPrefix = QFileInfo(filePrefix).fileName(); // Strip away any path elements (Security issue!)
×
1680
        if (screenShotPrefix.isEmpty())
×
1681
                        screenShotPrefix = "stellarium-";
×
1682
        screenShotDir = saveDir;
×
1683
        flagOverwriteScreenshots=overwrite;
×
1684
        emit screenshotRequested();
×
1685
}
×
1686

1687
void StelMainView::doScreenshot(void)
×
1688
{
1689
        QFileInfo shotDir;
×
1690
        // Make a screenshot which may be larger than the current window. This is harder than you would think:
1691
        // fbObj the framebuffer governs size of the target image, that's the easy part, but it also has its limits.
1692
        // However, the GUI parts need to be placed properly,
1693
        // HiDPI screens interfere, and the viewing angle has to be maintained.
1694
        // First, image size:
1695
        glWidget->makeCurrent();
×
1696
        const auto screen = QOpenGLContext::currentContext()->screen();
×
1697
        const auto pixelRatio = screen->devicePixelRatio();
×
1698
        int physImgWidth  = std::lround(stelScene->width() * pixelRatio);
×
1699
        int physImgHeight = std::lround(stelScene->height() * pixelRatio);
×
1700
        bool nightModeWasEnabled=nightModeEffect->isEnabled();
×
1701
        nightModeEffect->setEnabled(false);
×
1702
        if (flagUseCustomScreenshotSize)
×
1703
        {
1704
                // Borrowed from Scenery3d renderer: determine maximum framebuffer size as minimum of texture, viewport and renderbuffer size
1705
                QOpenGLContext *context = QOpenGLContext::currentContext();
×
1706
                if (context)
×
1707
                {
1708
                        context->functions()->initializeOpenGLFunctions();
×
1709
                        //qDebug() << "initializeOpenGLFunctions()...";
1710
                        // TODO: Investigate this further when GL memory issues should appear.
1711
                        // Make sure we have enough free GPU memory!
1712
#ifndef NDEBUG
1713
#ifdef GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX
1714
                        GLint freeGLmemory;
1715
                        context->functions()->glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &freeGLmemory);
×
1716
                        qCDebug(mainview)<<"Free GPU memory:" << freeGLmemory << "kB -- we ask for " << customScreenshotWidth*customScreenshotHeight*8 / 1024 <<"kB";
×
1717
#endif
1718
#ifdef GL_RENDERBUFFER_FREE_MEMORY_ATI
1719
                        GLint freeGLmemoryAMD[4];
1720
                        context->functions()->glGetIntegerv(GL_RENDERBUFFER_FREE_MEMORY_ATI, freeGLmemoryAMD);
×
1721
                        qCDebug(mainview)<<"Free GPU memory (AMD version):" << static_cast<uint>(freeGLmemoryAMD[1])/1024 << "+"
×
1722
                                          << static_cast<uint>(freeGLmemoryAMD[3])/1024 << " of "
×
1723
                                          << static_cast<uint>(freeGLmemoryAMD[0])/1024 << "+"
×
1724
                                          << static_cast<uint>(freeGLmemoryAMD[2])/1024 << "kB -- we ask for "
×
1725
                                          << customScreenshotWidth*customScreenshotHeight*8 / 1024 <<"kB";
×
1726
#endif
1727
#endif
1728
                        GLint texSize,viewportSize[2],rbSize;
1729
                        context->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
×
1730
                        context->functions()->glGetIntegerv(GL_MAX_VIEWPORT_DIMS, viewportSize);
×
1731
                        context->functions()->glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &rbSize);
×
1732
                        qCDebug(mainview)<<"Maximum texture size:"<<texSize;
×
1733
                        qCDebug(mainview)<<"Maximum viewport dims:"<<viewportSize[0]<<viewportSize[1];
×
1734
                        qCDebug(mainview)<<"Maximum renderbuffer size:"<<rbSize;
×
1735
                        int maximumFramebufferSize = qMin(texSize,qMin(rbSize,qMin(viewportSize[0],viewportSize[1])));
×
1736
                        qCDebug(mainview)<<"Maximum framebuffer size:"<<maximumFramebufferSize;
×
1737

1738
                        physImgWidth =qMin(maximumFramebufferSize, customScreenshotWidth);
×
1739
                        physImgHeight=qMin(maximumFramebufferSize, customScreenshotHeight);
×
1740
                }
1741
                else
1742
                {
1743
                        qCWarning(mainview) << "No GL context for screenshot! Aborting.";
×
1744
                        return;
×
1745
                }
1746
        }
1747
        // 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.
1748
        bool isGLES=(QOpenGLContext::currentContext()->format().renderableType() == QSurfaceFormat::OpenGLES);
×
1749

1750
        QOpenGLFramebufferObjectFormat fbFormat;
×
1751
        fbFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
×
1752
        fbFormat.setInternalTextureFormat(isGLES ? GL_RGBA : GL_RGB); // try to avoid transparent background!
×
1753
        QOpenGLFramebufferObject * fbObj = new QOpenGLFramebufferObject(physImgWidth, physImgHeight, fbFormat);
×
1754
        fbObj->bind();
×
1755
        // Now the painter has to be convinced to paint to the potentially larger image frame.
1756
        QOpenGLPaintDevice fbObjPaintDev(physImgWidth, physImgHeight);
×
1757

1758
        // It seems the projector has its own knowledge about image size. We must adjust fov and image size, but reset afterwards.
1759
        StelCore *core=StelApp::getInstance().getCore();
×
1760
        StelProjector::StelProjectorParams pParams=core->getCurrentStelProjectorParams();
×
1761
        StelProjector::StelProjectorParams sParams=pParams;
×
1762
        //qCDebug(mainview) << "Screenshot Viewport: x" << pParams.viewportXywh[0] << "/y" << pParams.viewportXywh[1] << "/w" << pParams.viewportXywh[2] << "/h" << pParams.viewportXywh[3];
1763
        const auto virtImgWidth  = physImgWidth  / pixelRatio;
×
1764
        const auto virtImgHeight = physImgHeight / pixelRatio;
×
1765
        sParams.viewportXywh[2] = virtImgWidth;
×
1766
        sParams.viewportXywh[3] = virtImgHeight;
×
1767

1768
        sParams.viewportCenter.set(0.0+(0.5+pParams.viewportCenterOffset.v[0])*virtImgWidth,
×
1769
                                                           0.0+(0.5+pParams.viewportCenterOffset.v[1])*virtImgHeight);
×
1770
        sParams.viewportFovDiameter = qMin(virtImgWidth,virtImgHeight);
×
1771
        core->setCurrentStelProjectorParams(sParams);
×
1772

1773
        QPainter painter;
×
1774
        painter.begin(&fbObjPaintDev);
×
1775
        // next line was above begin(), but caused a complaint. Maybe use after begin()?
1776
        painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
×
1777
        stelScene->setSceneRect(0, 0, virtImgWidth, virtImgHeight);
×
1778

1779
        // push the button bars back to the sides where they belong, and fix root item clipping its children.
1780
        dynamic_cast<StelGui*>(gui)->getSkyGui()->setGeometry(0, 0, virtImgWidth, virtImgHeight);
×
1781
        rootItem->setSize(QSize(virtImgWidth, virtImgHeight));
×
1782
        dynamic_cast<StelGui*>(gui)->forceRefreshGui(); // refresh bar position.
×
1783

1784
        stelScene->render(&painter, QRectF(), QRectF(0,0,virtImgWidth,virtImgHeight) , Qt::KeepAspectRatio);
×
1785
        painter.end();
×
1786

1787
        QImage im;
×
1788
        if (isGLES)
×
1789
        {
1790
                // We have RGBA texture with possibly empty spots when atmosphere was off.
1791
                // See toImage() help entry why to create wrapper here.
1792
                QImage fboImage(fbObj->toImage());
×
1793
                //qDebug() << "FBOimage format:" << fboImage.format(); // returns Format_RGBA8888_Premultiplied
1794
                QImage im2(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_RGBX8888);
×
1795
                im=im2.copy();
×
1796
        }
×
1797
        else
1798
                im=fbObj->toImage();
×
1799
        fbObj->release();
×
1800
        delete fbObj;
×
1801
        // reset viewport and GUI
1802
        core->setCurrentStelProjectorParams(pParams);
×
1803
        nightModeEffect->setEnabled(nightModeWasEnabled);
×
1804
        stelScene->setSceneRect(0, 0, pParams.viewportXywh[2], pParams.viewportXywh[3]);
×
1805
        rootItem->setSize(QSize(pParams.viewportXywh[2], pParams.viewportXywh[3]));
×
1806
        StelGui* stelGui = dynamic_cast<StelGui*>(gui);
×
1807
        if (stelGui)
×
1808
        {
1809
                stelGui->getSkyGui()->setGeometry(0, 0, pParams.viewportXywh[2], pParams.viewportXywh[3]);
×
1810
                stelGui->forceRefreshGui();
×
1811
        }
1812

1813
        if (nightModeWasEnabled)
×
1814
        {
1815
                for (int row=0; row<im.height(); ++row)
×
1816
                        for (int col=0; col<im.width(); ++col)
×
1817
                        {
1818
                                QRgb rgb=im.pixel(col, row);
×
1819
                                int gray=qGray(rgb);
×
1820
                                im.setPixel(col, row, qRgb(gray, 0, 0));
×
1821
                        }
1822
        }
1823
        if (flagInvertScreenShotColors)
×
1824
                im.invertPixels();
×
1825

1826
        if (StelFileMgr::getScreenshotDir().isEmpty())
×
1827
        {
1828
                qWarning() << "Oops, the directory for screenshots is not set! Let's try create and set it...";
×
1829
                // Create a directory for screenshots if main/screenshot_dir option is unset and user do screenshot at the moment!
1830
                QString screenshotDirSuffix = "/Stellarium";
×
1831
                QString screenshotDir;
×
1832
                if (!QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).isEmpty())
×
1833
                {
1834
                        screenshotDir = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation).constFirst();
×
1835
                        screenshotDir.append(screenshotDirSuffix);
×
1836
                }
1837
                else
1838
                        screenshotDir = StelFileMgr::getUserDir().append(screenshotDirSuffix);
×
1839

1840
                try
1841
                {
1842
                        StelFileMgr::setScreenshotDir(screenshotDir);
×
1843
                        StelApp::getInstance().getSettings()->setValue("main/screenshot_dir", screenshotDir);
×
1844
                }
1845
                catch (std::runtime_error &e)
×
1846
                {
1847
                        qCritical() << "Error: cannot create screenshot directory:" << e.what();
×
1848
                }
×
1849
        }
×
1850

1851
        if (screenShotDir == "")
×
1852
                shotDir = QFileInfo(StelFileMgr::getScreenshotDir());
×
1853
        else
1854
                shotDir = QFileInfo(screenShotDir);
×
1855

1856
        if (!shotDir.isDir())
×
1857
        {
1858
                qCritical() << "Requested screenshot directory is not a directory: " << QDir::toNativeSeparators(shotDir.filePath());
×
1859
                return;
×
1860
        }
1861
        else if (!shotDir.isWritable())
×
1862
        {
1863
                qCritical() << "Requested screenshot directory is not writable: " << QDir::toNativeSeparators(shotDir.filePath());
×
1864
                return;
×
1865
        }
1866

1867
        QFileInfo shotPath;
×
1868
        if (flagOverwriteScreenshots)
×
1869
        {
1870
                shotPath = QFileInfo(shotDir.filePath() + "/" + screenShotPrefix + "." + screenShotFormat);
×
1871
        }
1872
        else
1873
        {
1874
                QString shotPathString;
×
1875
                if (flagScreenshotDateFileName)
×
1876
                {
1877
                        // name screenshot files with current time
1878
                        QString currentTime = QDateTime::currentDateTime().toString(screenShotFileMask);
×
1879
                        shotPathString = QString("%1/%2%3.%4").arg(shotDir.filePath(), screenShotPrefix, currentTime, screenShotFormat);
×
1880
                        shotPath = QFileInfo(shotPathString);
×
1881
                }
×
1882
                else
1883
                {
1884
                        // build filter for file list, so we only select Stellarium screenshot files (prefix*.format)
1885
                        QString shotFilePattern = QString("%1*.%2").arg(screenShotPrefix, screenShotFormat);
×
1886
                        QStringList fileNameFilters(shotFilePattern);
×
1887
                        // get highest-numbered file in screenshot directory
1888
                        QDir dir(shotDir.filePath());
×
1889
                        QStringList existingFiles = dir.entryList(fileNameFilters);
×
1890

1891
                        // screenshot number - default to 1 for empty directory
1892
                        int shotNum = 1;
×
1893
                        if (!existingFiles.empty())
×
1894
                        {
1895
                                // already have screenshots, find largest number
1896
                                QString lastFileName = existingFiles[existingFiles.size() - 1];
×
1897

1898
                                // extract number from highest-numbered file name
1899
                                QString lastShotNumString = lastFileName.replace(screenShotPrefix, "").replace("." + screenShotFormat, "");
×
1900
                                // new screenshot number = start at highest number
1901
                                shotNum = lastShotNumString.toInt() + 1;
×
1902
                        }
×
1903

1904
                        // build new screenshot path: "path/prefix-num.format"
1905
                        // num is at least 3 characters
1906
                        QString shotNumString = QString::number(shotNum).rightJustified(3, '0');
×
1907
                        shotPathString = QString("%1/%2%3.%4").arg(shotDir.filePath(), screenShotPrefix, shotNumString, screenShotFormat);
×
1908
                        shotPath = QFileInfo(shotPathString);
×
1909
                        // validate if new screenshot number is valid (non-existent)
1910
                        while (shotPath.exists()) {
×
1911
                                shotNum++;
×
1912
                                shotNumString = QString::number(shotNum).rightJustified(3, '0');
×
1913
                                shotPathString = QString("%1/%2%3.%4").arg(shotDir.filePath(), screenShotPrefix, shotNumString, screenShotFormat);
×
1914
                                shotPath = QFileInfo(shotPathString);
×
1915
                        }
1916
                }
×
1917
        }
×
1918
        /*
1919
        // OPTIONAL: Determine free space and reject storing. This should avoid filling user space.
1920
        QStorageInfo storageInfo(shotPath.filePath());
1921
        if (storageInfo.bytesAvailable() < 50*1024*1024)
1922
        {
1923
                qWarning() << "Less than 50MB free. Not storing screenshot to" << shotPath.filePath();
1924
                qWarning() << "You must clean up your system to free disk space!";
1925
                return;
1926
        }
1927
        */
1928

1929
        // Set preferred image resolution (for some printing workflows)
1930
        im.setDotsPerMeterX(qRound(screenshotDpi*100./2.54));
×
1931
        im.setDotsPerMeterY(qRound(screenshotDpi*100./2.54));
×
1932
        qInfo() << "Saving screenshot to file: " << QDir::toNativeSeparators(shotPath.filePath());
×
1933

1934
        QImageWriter imageWriter(shotPath.filePath());
×
1935
        if (screenShotFormat=="tif")
×
1936
                imageWriter.setCompression(1); // use LZW
×
1937
        if (screenShotFormat=="jpg")
×
1938
        {
1939
                imageWriter.setQuality(75); // This is actually default
×
1940
        }
1941
        if (screenShotFormat=="jpeg")
×
1942
        {
1943
                imageWriter.setQuality(100);
×
1944
        }
1945
        if (!imageWriter.write(im))
×
1946
        {
1947
                qCritical() << "Failed to write screenshot to:" << QDir::toNativeSeparators(shotPath.filePath());
×
1948
        }
1949
}
×
1950

1951
QPoint StelMainView::getMousePos() const
×
1952
{
1953
        return glWidget->mapFromGlobal(QCursor::pos());
×
1954
}
1955

1956
QOpenGLContext* StelMainView::glContext() const
×
1957
{
1958
        return glWidget->context();
×
1959
}
1960

1961
void StelMainView::glContextMakeCurrent()
×
1962
{
1963
        glWidget->makeCurrent();
×
1964
}
×
1965

1966
void StelMainView::glContextDoneCurrent()
×
1967
{
1968
        glWidget->doneCurrent();
×
1969
}
×
1970

1971
// Set the sky background color. Everything else than black creates a work of art!
1972
void StelMainView::setSkyBackgroundColor(Vec3f color)
×
1973
{
1974
        rootItem->setSkyBackgroundColor(color);
×
1975
        StelApp::getInstance().getSettings()->setValue("color/sky_background_color", color.toStr());
×
1976
        emit skyBackgroundColorChanged(color);
×
1977
}
×
1978

1979
// Get the sky background color. Everything else than black creates a work of art!
1980
Vec3f StelMainView::getSkyBackgroundColor() const
×
1981
{
1982
        return rootItem->getSkyBackgroundColor();
×
1983
}
1984

1985
QRectF StelMainView::setWindowSize(int width, int height)
×
1986
{
1987
        // Make sure to leave fullscreen if necessary.
1988
        if (isFullScreen())
×
1989
                setFullScreen(false);
×
1990
        QRect geo=geometry();
×
1991
        geo.setWidth(width);
×
1992
        geo.setHeight(height);
×
1993
        setGeometry(geo);
×
1994

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