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

Stellarium / stellarium / 13260145531

11 Feb 2025 09:41AM UTC coverage: 12.127% (+0.03%) from 12.101%
13260145531

Pull #3751

github

10110111
Restore deleted additional SC files
Pull Request #3751: Switch skycultures to the new format

14613 of 120497 relevant lines covered (12.13%)

18620.19 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
                qDebug() << "OpenGL supported version: " << QString(reinterpret_cast<const char*>(ctx->functions()->glGetString(GL_VERSION)));
×
130
                qDebug() << "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
                const double now = StelApp::getTotalRunTime();
×
399
                double dt = now - previousPaintTime;
×
400
                //qDebug()<<"dt"<<dt;
401
                previousPaintTime = now;
×
402

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

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

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

417
                mainView->drawEnded();
×
418
        }
×
419

420
        QRectF boundingRect() const override
×
421
        {
422
                return rect;
×
423
        }
424

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

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

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

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

470
        void keyPressEvent(QKeyEvent *event) override
×
471
        {
472
                StelApp::getInstance().handleKeys(event);
×
473
                if(event->isAccepted())
×
474
                        mainView->thereWasAnEvent();
×
475
        }
×
476

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

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

497
                                if (touchPoints.count() == 1)
498
                                        setAcceptedMouseButtons(Qt::LeftButton | Qt::RightButton | Qt::MiddleButton);
499

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

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

519
                return true;
520
        }
521

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

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

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

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

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

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

589
protected:
590
        void resizeEvent(QGraphicsSceneResizeEvent* event) override
×
591
        {
592
                if(!inited) return;
×
593

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

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

635
        StelApp::initStatic();
×
636

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

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

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

650
        qApp->installEventFilter(this);
×
651

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

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

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

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

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

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

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

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

699
    setRenderHint(QPainter::Antialiasing);
×
700
}
×
701

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

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

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

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

755
        QGraphicsView::mouseMoveEvent(event);
×
756
}
×
757

758

759
void StelMainView::focusSky() {
×
760
        //scene()->setActiveWindow(0);
761
        rootItem->setFocus();
×
762
}
×
763

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

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

779
        //if on an GLES build, do not set the format
780
        const auto openGLModuleType = QOpenGLContext::openGLModuleType();
×
781
        qDebug() << "OpenGL module type:" << (openGLModuleType==QOpenGLContext::LibGL
×
782
                                                                                  ? "desktop OpenGL"
783
                                                                                  : openGLModuleType==QOpenGLContext::LibGL
784
                                                                                        ? "OpenGL ES 2 or higher"
×
785
                                                                                        : std::to_string(openGLModuleType).c_str());
×
786
        if (openGLModuleType==QOpenGLContext::LibGL)
×
787
        {
788
                fmt.setRenderableType(QSurfaceFormat::OpenGL);
×
789
                fmt.setVersion(3, 3);
×
790
                fmt.setProfile(QSurfaceFormat::CoreProfile);
×
791

792
                if (qApp && qApp->property("onetime_opengl_compat").toBool())
×
793
                {
794
                        qDebug() << "Setting OpenGL Compatibility profile from command line...";
×
795
                        fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
×
796
                        fmt.setOption(QSurfaceFormat::DeprecatedFunctions);
×
797
                }
798
                // FIXME: temporary hook for Qt5-based macOS bundles
799
                #if defined(Q_OS_MACOS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
800
                fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
801
                #endif
802
        }
803

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

806
        //request some sane buffer formats
807
        fmt.setRedBufferSize(8);
×
808
        fmt.setGreenBufferSize(8);
×
809
        fmt.setBlueBufferSize(8);
×
810
        fmt.setDepthBufferSize(24);
×
811
        // Without stencil buffer the debug versions of Qt will repeatedly emit warnings like:
812
        // "OpenGL paint engine: attempted to use stencil test without requesting a stencil buffer."
813
        fmt.setStencilBufferSize(8);
×
814

815
        if(qApp && qApp->property("onetime_single_buffer").toBool())
×
816
                fmt.setSwapBehavior(QSurfaceFormat::SingleBuffer);
×
817

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

831
#ifdef OPENGL_DEBUG_LOGGING
832
        //try to enable GL debugging using GL_KHR_debug
833
        fmt.setOption(QSurfaceFormat::DebugContext);
834
#endif
835

836
        return fmt;
×
837
}
×
838

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

861
                connect(QOpenGLContext::currentContext(),SIGNAL(aboutToBeDestroyed()),this,SLOT(contextDestroyed()));
862
                //for easier debugging, print the address of the main GL context
863
                qDebug()<<"CurCtxPtr:"<<QOpenGLContext::currentContext();
864
        }
865
#endif
866

867
        qDebug() << "Initialization StelMainView";
×
868

869
        glInfo.mainContext = QOpenGLContext::currentContext();
×
870
        glInfo.surface = glInfo.mainContext->surface();
×
871
        glInfo.functions = glInfo.mainContext->functions();
×
872
        glInfo.vendor = QString(reinterpret_cast<const char*>(glInfo.functions->glGetString(GL_VENDOR)));
×
873
        glInfo.renderer = QString(reinterpret_cast<const char*>(glInfo.functions->glGetString(GL_RENDERER)));
×
874
        const auto format = glInfo.mainContext->format();
×
875
        glInfo.supportsLuminanceTextures = format.profile() == QSurfaceFormat::CompatibilityProfile ||
×
876
                                                                           format.majorVersion() < 3;
×
877
        glInfo.isGLES = format.renderableType()==QSurfaceFormat::OpenGLES;
×
878
        qDebug().nospace() << "Luminance textures are " << (glInfo.supportsLuminanceTextures ? "" : "not ") << "supported";
×
879
        glInfo.isCoreProfile = format.profile() == QSurfaceFormat::CoreProfile;
×
880
        glInfo.isHighGraphicsMode = !qApp->property("onetime_force_low_graphics").toBool() &&
×
881
                                    !!StelOpenGL::highGraphicsFunctions();
×
882
        qDebug() << "Running in" << (glInfo.isHighGraphicsMode ? "High" : "Low") << "Graphics Mode";
×
883

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

911
        if(format.majorVersion() > 4 || glInfo.mainContext->hasExtension("GL_ARB_sample_shading"))
×
912
        {
913
                auto addr = glInfo.mainContext->getProcAddress("glMinSampleShading");
×
914
                if(!addr)
×
915
                        addr = glInfo.mainContext->getProcAddress("glMinSampleShadingARB");
×
916
                glInfo.glMinSampleShading = reinterpret_cast<PFNGLMINSAMPLESHADINGPROC>(addr);
×
917
        }
918
        gl.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glInfo.maxTextureSize);
×
919
        qDebug() << "Maximum 2D texture size:" << glInfo.maxTextureSize;
×
920

921
        gui = new StelGui();
×
922

923
        // Should be check of requirements disabled? -- NO! This is intentional here, and does no harm.
924
        if (configuration->value("main/check_requirements", true).toBool())
×
925
        {
926
                // Find out lots of debug info about supported version of OpenGL and vendor/renderer.
927
                processOpenGLdiagnosticsAndWarnings(configuration, glInfo.mainContext);
×
928
        }
929

930
        //setup StelOpenGLArray global state
931
        StelOpenGLArray::initGL();
×
932

933
        //create and initialize main app
934
        stelApp = new StelApp(this);
×
935
        stelApp->setGui(gui);
×
936
        stelApp->init(configuration);
×
937
        //this makes sure the app knows how large the window is
938
        connect(stelScene,SIGNAL(sceneRectChanged(QRectF)),stelApp,SLOT(glWindowHasBeenResized(QRectF)));
×
939
#ifdef ENABLE_SPOUT
940
        QObject::connect(stelScene, &StelGraphicsScene::sceneRectChanged, [&](const QRectF& rect)
941
        {
942
        stelApp->glPhysicalWindowHasBeenResized(getPhysicalSize(rect));
943
        });
944
#endif
945

946
        //also immediately set the current values
947
        stelApp->glWindowHasBeenResized(stelScene->sceneRect());
×
948
#ifdef ENABLE_SPOUT
949
        stelApp->glPhysicalWindowHasBeenResized(getPhysicalSize(stelScene->sceneRect()));
950
#endif
951

952
        StelActionMgr *actionMgr = stelApp->getStelActionManager();
×
953
        actionMgr->addAction("actionSave_Screenshot_Global", N_("Miscellaneous"), N_("Save screenshot"), this, "saveScreenShot()", "Ctrl+S");
×
954
        actionMgr->addAction("actionReload_Shaders", N_("Miscellaneous"), N_("Reload shaders (for development)"), this, "reloadShaders()", "Ctrl+R, P");
×
955
        actionMgr->addAction("actionSet_Full_Screen_Global", N_("Display Options"), N_("Full-screen mode"), this, "fullScreen", "F11");
×
956
        
957
        StelPainter::initGLShaders();
×
958

959
        guiItem = new StelGuiItem(size(), rootItem);
×
960
        scene()->addItem(rootItem);
×
961
        //set the default focus to the sky
962
        focusSky();
×
963
        nightModeEffect = new NightModeGraphicsEffect(this);
×
964
        updateNightModeProperty(StelApp::getInstance().getVisionModeNight());
×
965
        //install the effect on the whole view
966
        rootItem->setGraphicsEffect(nightModeEffect);
×
967

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

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

987
        // The script manager can only be fully initialized after the plugins have loaded.
988
        stelApp->initScriptMgr();
×
989

990
        // Set the global stylesheet, this is only useful for the tooltips.
991
        StelGui* sgui = dynamic_cast<StelGui*>(stelApp->getGui());
×
992
        if (sgui!=Q_NULLPTR)
×
993
                setStyleSheet(sgui->getStelStyle().qtStyleSheet);
×
994
        connect(stelApp, SIGNAL(visionNightModeChanged(bool)), this, SLOT(updateNightModeProperty(bool)));
×
995

996
        // I doubt this will have any effect on framerate, but may cause problems elsewhere?
997
        QThread::currentThread()->setPriority(QThread::HighestPriority);
×
998
#ifndef NDEBUG
999
        // Get an overview of module callOrders
1000
        if (qApp->property("verbose")==true)
×
1001
        {
1002
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionDraw);
×
1003
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionUpdate);
×
1004
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleMouseClicks);
×
1005
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleMouseMoves);
×
1006
                StelApp::getInstance().dumpModuleActionPriorities(StelModule::ActionHandleKeys);
×
1007
        }
1008
#endif
1009

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

1027
                        // Add 1-2 entries per action
1028
                        for (QStringList::const_iterator cstActionShortcut = singleCustomActionShortcuts.constBegin(); cstActionShortcut != singleCustomActionShortcuts.constEnd(); ++cstActionShortcut)
×
1029
                                if (strcmp( (*cstActionShortcut).toLocal8Bit().constData(), "") )
×
1030
                                        cstActionsMap.insert((*cstActionShortcut), (*cstActionName));
×
1031
                }
×
1032
                // Now we have a QMultiMap with (customShortcut, actionName). It may contain multiple keys!
1033
                QStringList allMapKeys=cstActionsMap.keys();
×
1034
                QStringList uniqueMapKeys=cstActionsMap.uniqueKeys();
×
1035
                for (auto &key : uniqueMapKeys)
×
1036
                        allMapKeys.removeOne(key);
×
1037
                conflicts << allMapKeys; // Add the remaining (duplicate) keys
×
1038

1039
                // Check every shortcut from the Map that it is not assigned to its own correct action
1040
                for (QMultiMap<QString, QString>::const_iterator it=cstActionsMap.constBegin(); it != cstActionsMap.constEnd(); ++it)
×
1041
                {
1042
                        QString customKey(it.key());
×
1043
                        QString actionName=cstActionsMap.value(it.key());
×
1044
                        StelAction *action = actionMgr->findAction(actionName);
×
1045
                        if (action && defaultShortcuts.contains(customKey) && actionMgr->findActionFromShortcut(customKey)->getId()!=action->getId())
×
1046
                                conflicts << customKey;
×
1047
                }
×
1048
                configuration->endGroup();
×
1049

1050
                if (!conflicts.isEmpty())
×
1051
                {
1052
                        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);
×
1053
                        qWarning() << "Attention! Conflicting keyboard shortcut assignments found. Please resolve:" << conflicts.join("; "); // Repeat in logfile for later retrieval.
×
1054
                }
1055
        }
×
1056
}
×
1057

1058
void StelMainView::updateNightModeProperty(bool b)
×
1059
{
1060
        // So that the bottom bar tooltips get properly rendered in night mode.
1061
        setProperty("nightMode", b);
×
1062
        nightModeEffect->setEnabled(b);
×
1063
}
×
1064

1065
void StelMainView::reloadShaders()
×
1066
{
1067
        //make sure GL context is bound
1068
        glContextMakeCurrent();
×
1069
        emit reloadShadersRequested();
×
1070
}
×
1071

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

1085
        // These tests are not required on MacOS X
1086
#ifndef Q_OS_MACOS
1087
        bool openGLerror=false;
×
1088
        if (format.renderableType()==QSurfaceFormat::OpenGL || format.renderableType()==QSurfaceFormat::OpenGLES)
×
1089
        {
1090
                qDebug().noquote() << "Detected:" << (format.renderableType()==QSurfaceFormat::OpenGL  ? "OpenGL" : "OpenGL ES" ) << QString("%1.%2").arg(format.majorVersion()).arg(format.minorVersion());
×
1091
        }
1092
        else
1093
        {
1094
                openGLerror=true;
×
1095
                qDebug() << "Neither OpenGL nor OpenGL ES detected: Unsupported Format!";
×
1096
        }
1097
#endif
1098
        QOpenGLFunctions* gl = context->functions();
×
1099

1100
        QString glDriver(reinterpret_cast<const char*>(gl->glGetString(GL_VERSION)));
×
1101
        qDebug().noquote() << "Driver version string:" << glDriver;
×
1102
        qDebug().noquote() << "GL vendor:" << QString(reinterpret_cast<const char*>(gl->glGetString(GL_VENDOR)));
×
1103
        QString glRenderer(reinterpret_cast<const char*>(gl->glGetString(GL_RENDERER)));
×
1104
        qDebug().noquote() << "GL renderer:" << glRenderer;
×
1105

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

1134
                #if (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1135
                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);
1136
                #else
1137
                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Insufficient OpenGL version. Please update drivers, graphics hardware, or use --mesa-mode option."), QMessageBox::Abort, QMessageBox::Abort);
1138
                #endif
1139
        #else
1140
                qWarning() << "Oops... Insufficient OpenGL version. Please update drivers, or graphics hardware.";
×
1141
                QMessageBox::critical(Q_NULLPTR, "Stellarium", q_("Insufficient OpenGL version. Please update drivers, or graphics hardware."), QMessageBox::Abort, QMessageBox::Abort);
×
1142
        #endif
1143
                exit(1);
×
1144
        }
1145
#endif
1146
        // This call requires OpenGL2+.
1147
        QString glslString(reinterpret_cast<const char*>(gl->glGetString(GL_SHADING_LANGUAGE_VERSION)));
×
1148
        qDebug().noquote() << "GL Shading Language version:" << glslString;
×
1149

1150
        // Only give extended info if called on command line, for diagnostic.
1151
        if (qApp->property("dump_OpenGL_details").toBool())
×
1152
                dumpOpenGLdiagnostics();
×
1153

1154
#if (defined Q_OS_WIN) && (QT_VERSION<QT_VERSION_CHECK(6,0,0))
1155
        // If we have ANGLE, check esp. for insufficient ps_2 level.
1156
        if (isANGLE)
1157
        {
1158
                static const QRegularExpression angleVsPsRegExp(" vs_(\\d)_(\\d) ps_(\\d)_(\\d)");
1159
                int angleVSPSpos=glRenderer.indexOf(angleVsPsRegExp);
1160

1161
                if (angleVSPSpos >-1)
1162
                {
1163
                        QRegularExpressionMatch match=angleVsPsRegExp.match(glRenderer);
1164
                        float vsVersion=match.captured(1).toFloat() + 0.1f*match.captured(2).toFloat();
1165
                        float psVersion=match.captured(3).toFloat() + 0.1f*match.captured(4).toFloat();
1166
                        qDebug() << "VS Version Number detected:" << vsVersion;
1167
                        qDebug() << "PS Version Number detected:" << psVersion;
1168
                        if ((vsVersion<2.0f) || (psVersion<3.0f))
1169
                        {
1170
                                openGLerror=true;
1171
                                qDebug() << "This is not enough: we need DirectX9 with vs_2_0 and ps_3_0 or later.";
1172
                                qDebug() << "You should update graphics drivers, graphics hardware, or use the --mesa-mode option.";
1173
                                qDebug() << "Else, please try to use an older version like 0.12.9, and try with --safe-mode";
1174

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

1215
                if (mesaPos >-1)
×
1216
                {
1217
                        float mesaVersion=mesaRegExp.match(glDriver).captured(1).toFloat();
×
1218
                        qDebug() << "MESA Version Number detected:" << mesaVersion;
×
1219
                        if ((mesaVersion<10.0f))
×
1220
                        {
1221
                                openGLerror=true;
×
1222
                                qDebug() << "This is not enough: we need Mesa 10.0 or later.";
×
1223
                                qDebug() << "You should update graphics drivers or graphics hardware.";
×
1224
                                qDebug() << "Else, please try to use an older version like 0.12.9, and try there with --safe-mode";
×
1225

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

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

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

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

1366
// Get physical dimensions given the virtual dimensions for the screen where this window is located.
1367
QRectF StelMainView::getPhysicalSize(const QRectF& virtualRect) const
×
1368
{
1369
        auto window = this->window();
×
1370
        if(window)
×
1371
        {
1372
                auto pixelRatio = window->devicePixelRatio();
×
1373
                QRectF newRect(virtualRect.x(), virtualRect.y(), virtualRect.width() * pixelRatio, virtualRect.height() * pixelRatio);
×
1374
                return newRect;
×
1375
        }
1376

1377
        // If the platform does not support getting the QWindow backing instance of this QWidget
1378
        return virtualRect;
×
1379
}
1380

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

1410
                qDebug() << "OpenGL shader capabilities and details:";
×
1411
                qDebug() << " - Vertex Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Vertex, context) ? "YES" : "NO");
×
1412
                qDebug() << " - Fragment Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Fragment, context) ? "YES" : "NO");
×
1413
                qDebug() << " - Geometry Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Geometry, context) ? "YES" : "NO");
×
1414
                qDebug() << " - TessellationControl Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::TessellationControl, context) ? "YES" : "NO");
×
1415
                qDebug() << " - TessellationEvaluation Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::TessellationEvaluation, context) ? "YES" : "NO");
×
1416
                qDebug() << " - Compute Shader:" << (QOpenGLShader::hasOpenGLShaders(QOpenGLShader::Compute, context) ? "YES" : "NO");
×
1417
                
1418
                // GZ: List available extensions. Not sure if this is in any way useful?
1419
                QSet<QByteArray> extensionSet=context->extensions();
×
1420
                qDebug() << "We have" << extensionSet.count() << "OpenGL extensions:";
×
1421
                QMap<QString, QString> extensionMap;
×
1422
                QSetIterator<QByteArray> iter(extensionSet);
×
1423
                while (iter.hasNext())
×
1424
                {
1425
                        if (!iter.peekNext().isEmpty()) {// Don't insert empty lines
×
1426
                                extensionMap.insert(QString(iter.peekNext()), QString(iter.peekNext()));
×
1427
                        }
1428
                        iter.next();
×
1429
                }
1430
                QMapIterator<QString, QString> iter2(extensionMap);
×
1431
                while (iter2.hasNext()) {
×
1432
                        qDebug() << " -" << iter2.next().key();
×
1433
                }
1434
                // Apparently EXT_gpu_shader4 is required for GLSL1.3. (http://en.wikipedia.org/wiki/OpenGL#OpenGL_3.0).
1435
                qDebug() << "EXT_gpu_shader4" << (extensionSet.contains(("EXT_gpu_shader4")) ? "present, OK." : "MISSING!");
×
1436
                
1437
                QFunctionPointer programParameterPtr =context->getProcAddress("glProgramParameteri");
×
1438
                if (programParameterPtr == Q_NULLPTR) {
×
1439
                        qDebug() << "glProgramParameteri cannot be resolved here. BAD!";
×
1440
                }
1441
                programParameterPtr =context->getProcAddress("glProgramParameteriEXT");
×
1442
                if (programParameterPtr == Q_NULLPTR) {
×
1443
                        qDebug() << "glProgramParameteriEXT cannot be resolved here. BAD!";
×
1444
                }
1445
        }
×
1446
        else
1447
        {
1448
                qDebug() << "dumpOpenGLdiagnostics(): No OpenGL context";
×
1449
        }
1450
}
×
1451

1452
void StelMainView::deinit()
×
1453
{
1454
        glContextMakeCurrent();
×
1455
        deinitGL();
×
1456
        delete stelApp;
×
1457
        stelApp = Q_NULLPTR;
×
1458
}
×
1459

1460
// Update the translated title
1461
void StelMainView::initTitleI18n()
×
1462
{
1463
        setWindowTitle(StelUtils::getApplicationName());
×
1464
}
×
1465

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

1494
void StelMainView::drawEnded()
×
1495
{
1496
        updateQueued = false;
×
1497

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

1510
        if(!fpsTimer->isActive())
×
1511
                fpsTimer->start();
×
1512
}
×
1513

1514
void StelMainView::setFlagCursorTimeout(bool b)
×
1515
{
1516
        if (b == flagCursorTimeout) return;
×
1517

1518
        flagCursorTimeout = b;
×
1519
        if (b)         // enable timer
×
1520
        {
1521
                cursorTimeoutTimer->start();
×
1522
        }
1523
        else        // disable timer
1524
        {
1525
                // Show the previous cursor if the current is "hidden" 
1526
                if (QGuiApplication::overrideCursor() && (QGuiApplication::overrideCursor()->shape() == Qt::BlankCursor) )
×
1527
                {
1528
                        // pop the blank cursor
1529
                        QGuiApplication::restoreOverrideCursor();
×
1530
                }
1531
                // and stop the timer 
1532
                cursorTimeoutTimer->stop();
×
1533
        }
1534
        
1535
        StelApp::immediateSave("gui/flag_mouse_cursor_timeout", b);
×
1536
        emit flagCursorTimeoutChanged(b);
×
1537
}
1538

1539
void StelMainView::hideCursor()
×
1540
{
1541
        // timeout fired...
1542
        // if the feature is not asked, do nothing
1543
        if (!flagCursorTimeout) return;
×
1544

1545
        // "hide" the current cursor by pushing a Blank cursor
1546
        QGuiApplication::setOverrideCursor(Qt::BlankCursor);
×
1547
}
1548

1549
void StelMainView::fpsTimerUpdate()
×
1550
{
1551
        if(!updateQueued)
×
1552
        {
1553
                updateQueued = true;
×
1554
                QTimer::singleShot(0, glWidget, SLOT(update()));
×
1555
        }
1556
}
×
1557

1558
#ifdef OPENGL_DEBUG_LOGGING
1559
void StelMainView::logGLMessage(const QOpenGLDebugMessage &debugMessage)
1560
{
1561
        qDebug()<<debugMessage;
1562
}
1563

1564
void StelMainView::contextDestroyed()
1565
{
1566
        qDebug()<<"Main OpenGL context destroyed";
1567
}
1568
#endif
1569

1570
void StelMainView::thereWasAnEvent()
×
1571
{
1572
        lastEventTimeSec = StelApp::getTotalRunTime();
×
1573
}
×
1574

1575
bool StelMainView::needsMaxFPS() const
×
1576
{
1577
        const double now = StelApp::getTotalRunTime();
×
1578

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

1587
void StelMainView::moveEvent(QMoveEvent * event)
×
1588
{
1589
        const QPoint &pos=event->pos();
×
1590

1591
        // We use the glWidget instead of the event, as we want the screen that shows most of the widget.
1592
        QWindow* win = glWidget->windowHandle();
×
1593
        if(win)
×
1594
        {
1595
                stelApp->setDevicePixelsPerPixel(win->devicePixelRatio());
×
1596
        }
1597

1598
        if (StelApp::isInitialized())
×
1599
        {
1600
                StelApp::immediateSave("video/screen_x", pos.x());
×
1601
                StelApp::immediateSave("video/screen_y", pos.y());
×
1602
        }
1603
}
×
1604

1605
void StelMainView::closeEvent(QCloseEvent* event)
×
1606
{
1607
        Q_UNUSED(event)
1608
        stelApp->quit();
×
1609
}
×
1610

1611
//! Delete openGL textures (to call before the GLContext disappears)
1612
void StelMainView::deinitGL()
×
1613
{
1614
        //fix for bug 1628072 caused by QTBUG-56798
1615
#ifndef QT_NO_DEBUG
1616
        StelOpenGL::clearGLErrors();
×
1617
#endif
1618

1619
        stelApp->deinit();
×
1620
        delete gui;
×
1621
        gui = Q_NULLPTR;
×
1622
}
×
1623

1624
void StelMainView::setScreenshotFormat(const QString filetype)
×
1625
{
1626
        const QString candidate=filetype.toLower();
×
1627
        const QByteArray candBA=candidate.toUtf8();
×
1628

1629
        // Make sure format is supported by Qt, but restrict some useless formats.
1630
        QList<QByteArray> formats = QImageWriter::supportedImageFormats();
×
1631
        formats.removeOne("icns");
×
1632
        formats.removeOne("wbmp");
×
1633
        formats.removeOne("cur");
×
1634
        if (formats.contains(candBA))
×
1635
        {
1636
                screenShotFormat=candidate;
×
1637
                // apply setting immediately
1638
                configuration->setValue("main/screenshot_format", candidate);
×
1639
                emit screenshotFormatChanged(candidate);
×
1640
        }
1641
        else
1642
        {
1643
                qDebug() << "Invalid filetype for screenshot: " << filetype;
×
1644
        }
1645
}
×
1646

1647
void StelMainView::setFlagScreenshotDateFileName(bool b)
×
1648
{
1649
        flagScreenshotDateFileName=b;
×
1650
        StelApp::getInstance().getSettings()->setValue("main/screenshot_datetime_filename", b);
×
1651
        emit flagScreenshotDateFileNameChanged(b);
×
1652
}
×
1653

1654
void StelMainView::setScreenshotFileMask(const QString filemask)
×
1655
{
1656
        screenShotFileMask = filemask;
×
1657
        StelApp::getInstance().getSettings()->setValue("main/screenshot_datetime_filemask", filemask);
×
1658
        emit screenshotFileMaskChanged(filemask);
×
1659
}
×
1660

1661
void StelMainView::setScreenshotDpi(int dpi)
×
1662
{
1663
        screenshotDpi=dpi;
×
1664
        StelApp::getInstance().getSettings()->setValue("main/screenshot_dpi", dpi);
×
1665
        emit screenshotDpiChanged(dpi);
×
1666
}
×
1667

1668
void StelMainView::saveScreenShot(const QString& filePrefix, const QString& saveDir, const bool overwrite)
×
1669
{
1670
        screenShotPrefix = QFileInfo(filePrefix).fileName(); // Strip away any path elements (Security issue!)
×
1671
        if (screenShotPrefix.isEmpty())
×
1672
                        screenShotPrefix = "stellarium-";
×
1673
        screenShotDir = saveDir;
×
1674
        flagOverwriteScreenshots=overwrite;
×
1675
        emit screenshotRequested();
×
1676
}
×
1677

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

1729
                        physImgWidth =qMin(maximumFramebufferSize, customScreenshotWidth);
×
1730
                        physImgHeight=qMin(maximumFramebufferSize, customScreenshotHeight);
×
1731
                }
1732
                else
1733
                {
1734
                        qCWarning(mainview) << "No GL context for screenshot! Aborting.";
×
1735
                        return;
×
1736
                }
1737
        }
1738
        // 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.
1739
        bool isGLES=(QOpenGLContext::currentContext()->format().renderableType() == QSurfaceFormat::OpenGLES);
×
1740

1741
        QOpenGLFramebufferObjectFormat fbFormat;
×
1742
        fbFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
×
1743
        fbFormat.setInternalTextureFormat(isGLES ? GL_RGBA : GL_RGB); // try to avoid transparent background!
×
1744
        QOpenGLFramebufferObject * fbObj = new QOpenGLFramebufferObject(physImgWidth, physImgHeight, fbFormat);
×
1745
        fbObj->bind();
×
1746
        // Now the painter has to be convinced to paint to the potentially larger image frame.
1747
        QOpenGLPaintDevice fbObjPaintDev(physImgWidth, physImgHeight);
×
1748

1749
        // It seems the projector has its own knowledge about image size. We must adjust fov and image size, but reset afterwards.
1750
        StelCore *core=StelApp::getInstance().getCore();
×
1751
        StelProjector::StelProjectorParams pParams=core->getCurrentStelProjectorParams();
×
1752
        StelProjector::StelProjectorParams sParams=pParams;
×
1753
        //qCDebug(mainview) << "Screenshot Viewport: x" << pParams.viewportXywh[0] << "/y" << pParams.viewportXywh[1] << "/w" << pParams.viewportXywh[2] << "/h" << pParams.viewportXywh[3];
1754
        const auto virtImgWidth  = physImgWidth  / pixelRatio;
×
1755
        const auto virtImgHeight = physImgHeight / pixelRatio;
×
1756
        sParams.viewportXywh[2] = virtImgWidth;
×
1757
        sParams.viewportXywh[3] = virtImgHeight;
×
1758

1759
        // Configure a helper value to allow some modules to tweak their output sizes. Currently used by StarMgr, maybe solve font issues?
1760
        customScreenshotMagnification=static_cast<float>(virtImgHeight)/static_cast<float>(screen->geometry().height());
×
1761
        sParams.viewportCenter.set(0.0+(0.5+pParams.viewportCenterOffset.v[0])*virtImgWidth,
×
1762
                                                           0.0+(0.5+pParams.viewportCenterOffset.v[1])*virtImgHeight);
×
1763
        sParams.viewportFovDiameter = qMin(virtImgWidth,virtImgHeight);
×
1764
        core->setCurrentStelProjectorParams(sParams);
×
1765

1766
        QPainter painter;
×
1767
        painter.begin(&fbObjPaintDev);
×
1768
        // next line was above begin(), but caused a complaint. Maybe use after begin()?
1769
        painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
×
1770
        stelScene->setSceneRect(0, 0, virtImgWidth, virtImgHeight);
×
1771

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

1777
        stelScene->render(&painter, QRectF(), QRectF(0,0,virtImgWidth,virtImgHeight) , Qt::KeepAspectRatio);
×
1778
        painter.end();
×
1779

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

1807
        if (nightModeWasEnabled)
×
1808
        {
1809
                for (int row=0; row<im.height(); ++row)
×
1810
                        for (int col=0; col<im.width(); ++col)
×
1811
                        {
1812
                                QRgb rgb=im.pixel(col, row);
×
1813
                                int gray=qGray(rgb);
×
1814
                                im.setPixel(col, row, qRgb(gray, 0, 0));
×
1815
                        }
1816
        }
1817
        if (flagInvertScreenShotColors)
×
1818
                im.invertPixels();
×
1819

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

1834
                try
1835
                {
1836
                        StelFileMgr::setScreenshotDir(screenshotDir);
×
1837
                        StelApp::getInstance().getSettings()->setValue("main/screenshot_dir", screenshotDir);
×
1838
                }
1839
                catch (std::runtime_error &e)
×
1840
                {
1841
                        qDebug("Error: cannot create screenshot directory: %s", e.what());
×
1842
                }
×
1843
        }
×
1844

1845
        if (screenShotDir == "")
×
1846
                shotDir = QFileInfo(StelFileMgr::getScreenshotDir());
×
1847
        else
1848
                shotDir = QFileInfo(screenShotDir);
×
1849

1850
        if (!shotDir.isDir())
×
1851
        {
1852
                qWarning() << "ERROR requested screenshot directory is not a directory: " << QDir::toNativeSeparators(shotDir.filePath());
×
1853
                return;
×
1854
        }
1855
        else if (!shotDir.isWritable())
×
1856
        {
1857
                qWarning() << "ERROR requested screenshot directory is not writable: " << QDir::toNativeSeparators(shotDir.filePath());
×
1858
                return;
×
1859
        }
1860

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

1885
                        // screenshot number - default to 1 for empty directory
1886
                        int shotNum = 1;
×
1887
                        if (!existingFiles.empty())
×
1888
                        {
1889
                                // already have screenshots, find largest number
1890
                                QString lastFileName = existingFiles[existingFiles.size() - 1];
×
1891

1892
                                // extract number from highest-numbered file name
1893
                                QString lastShotNumString = lastFileName.replace(screenShotPrefix, "").replace("." + screenShotFormat, "");
×
1894
                                // new screenshot number = start at highest number
1895
                                shotNum = lastShotNumString.toInt() + 1;
×
1896
                        }
×
1897

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

1923
        // Set preferred image resolution (for some printing workflows)
1924
        im.setDotsPerMeterX(qRound(screenshotDpi*100./2.54));
×
1925
        im.setDotsPerMeterY(qRound(screenshotDpi*100./2.54));
×
1926
        qDebug() << "INFO Saving screenshot in file: " << QDir::toNativeSeparators(shotPath.filePath());
×
1927

1928

1929
        QImageWriter imageWriter(shotPath.filePath());
×
1930
        if (screenShotFormat=="tif")
×
1931
                imageWriter.setCompression(1); // use LZW
×
1932
        if (screenShotFormat=="jpg")
×
1933
        {
1934
                imageWriter.setQuality(75); // This is actually default
×
1935
        }
1936
        if (screenShotFormat=="jpeg")
×
1937
        {
1938
                imageWriter.setQuality(100);
×
1939
        }
1940
        if (!imageWriter.write(im))
×
1941
        {
1942
                qWarning() << "WARNING failed to write screenshot to: " << QDir::toNativeSeparators(shotPath.filePath());
×
1943
        }
1944
}
×
1945

1946
QPoint StelMainView::getMousePos() const
×
1947
{
1948
        return glWidget->mapFromGlobal(QCursor::pos());
×
1949
}
1950

1951
QOpenGLContext* StelMainView::glContext() const
×
1952
{
1953
        return glWidget->context();
×
1954
}
1955

1956
void StelMainView::glContextMakeCurrent()
×
1957
{
1958
        glWidget->makeCurrent();
×
1959
}
×
1960

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

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

1974
// Get the sky background color. Everything else than black creates a work of art!
1975
Vec3f StelMainView::getSkyBackgroundColor() const
×
1976
{
1977
        return rootItem->getSkyBackgroundColor();
×
1978
}
1979

1980
QRectF StelMainView::setWindowSize(int width, int height)
×
1981
{
1982
        // Make sure to leave fullscreen if necessary.
1983
        if (isFullScreen())
×
1984
                setFullScreen(false);
×
1985
        QRect geo=geometry();
×
1986
        geo.setWidth(width);
×
1987
        geo.setHeight(height);
×
1988
        setGeometry(geo);
×
1989

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