From 966b6e94608124f5d9aecbbfa0397b8b98f3da4f Mon Sep 17 00:00:00 2001 From: Alex Shvartzkop Date: Wed, 10 Jan 2024 02:41:59 +0300 Subject: [PATCH] Improve wxQt OpenGL canvas implementation: - Support wxGLContext::SetCurrent. - Pass GL canvas attributes via ParseAttribList to handle defaults. - Remove OpenGL not implemented message. - Prevent QGLWidget from changing OpenGL state before we paint. - Set Qt::StrongFocus for wxQtGLWidget to allow keyboard input. - Use QtCanPaintWithoutActivePainter to fix wxPaintDCImpl assert when using wxGLCanvas. --- include/wx/qt/glcanvas.h | 7 +- include/wx/qt/window.h | 1 + src/qt/dcclient.cpp | 2 +- src/qt/glcanvas.cpp | 238 ++++++++++++++++++++++++++++----------- src/qt/window.cpp | 5 + 5 files changed, 184 insertions(+), 69 deletions(-) diff --git a/include/wx/qt/glcanvas.h b/include/wx/qt/glcanvas.h index 378813cc95..bc798a8156 100644 --- a/include/wx/qt/glcanvas.h +++ b/include/wx/qt/glcanvas.h @@ -24,6 +24,9 @@ public: virtual bool SetCurrent(const wxGLCanvas& win) const override; +private: + QGLContext* m_glContext = nullptr; + wxDECLARE_CLASS(wxGLContext); }; @@ -78,7 +81,9 @@ public: virtual bool SwapBuffers() override; - static bool ConvertWXAttrsToQtGL(const int *wxattrs, QGLFormat &format); + virtual bool QtCanPaintWithoutActivePainter() const override; + + static bool ConvertWXAttrsToQtGL(const wxGLAttributes &glattrs, const wxGLContextAttrs ctxAttrs, QGLFormat &format); private: wxDECLARE_CLASS(wxGLCanvas); diff --git a/include/wx/qt/window.h b/include/wx/qt/window.h index adcaf6b48d..a44d3fe111 100644 --- a/include/wx/qt/window.h +++ b/include/wx/qt/window.h @@ -151,6 +151,7 @@ public: void QtSetPicture( QPicture* pict ); QPainter *QtGetPainter(); + virtual bool QtCanPaintWithoutActivePainter() const; virtual bool QtHandlePaintEvent ( QWidget *handler, QPaintEvent *event ); virtual bool QtHandleResizeEvent ( QWidget *handler, QResizeEvent *event ); diff --git a/src/qt/dcclient.cpp b/src/qt/dcclient.cpp index 4bd150c0ef..25485dbd3f 100644 --- a/src/qt/dcclient.cpp +++ b/src/qt/dcclient.cpp @@ -126,7 +126,7 @@ wxPaintDCImpl::wxPaintDCImpl( wxDC *owner ) wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *win ) : wxWindowDCImpl( owner, win ) { - wxCHECK_RET( m_isWindowPainter, + wxCHECK_RET( m_isWindowPainter || win->QtCanPaintWithoutActivePainter(), "wxPaintDC can't be created outside wxEVT_PAINT handler" ); } diff --git a/src/qt/glcanvas.cpp b/src/qt/glcanvas.cpp index a5cc62c1c0..7af1fb1991 100644 --- a/src/qt/glcanvas.cpp +++ b/src/qt/glcanvas.cpp @@ -16,26 +16,20 @@ #include #include -#if defined(__VISUALC__) - #pragma message("OpenGL support is not implemented in wxQt") -#else - #warning "OpenGL support is not implemented in wxQt" -#endif wxGCC_WARNING_SUPPRESS(unused-parameter) class wxQtGLWidget : public wxQtEventSignalHandler< QGLWidget, wxGLCanvas > { public: wxQtGLWidget(wxWindow *parent, wxGLCanvas *handler, QGLFormat format) - : wxQtEventSignalHandler(parent, handler) - { - setFormat(format); - setAutoBufferSwap( false ); - } + : wxQtEventSignalHandler(parent, handler) + { + setFormat(format); + setAutoBufferSwap(false); + setFocusPolicy(Qt::StrongFocus); + } protected: - virtual void showEvent ( QShowEvent * event ) override; - virtual void hideEvent ( QHideEvent * event ) override; virtual void resizeEvent ( QResizeEvent * event ) override; virtual void paintEvent ( QPaintEvent * event ) override; @@ -43,16 +37,6 @@ protected: virtual void paintGL() override; }; -void wxQtGLWidget::showEvent ( QShowEvent * event ) -{ - QGLWidget::showEvent( event ); -} - -void wxQtGLWidget::hideEvent ( QHideEvent * event ) -{ - QGLWidget::hideEvent( event ); -} - void wxQtGLWidget::resizeEvent ( QResizeEvent * event ) { QGLWidget::resizeEvent(event); @@ -84,6 +68,7 @@ wxGLContextAttrs& wxGLContextAttrs::CoreProfile() { // AddAttribBits(GLX_CONTEXT_PROFILE_MASK_ARB, // GLX_CONTEXT_CORE_PROFILE_BIT_ARB); + AddAttribute(wx_GL_COMPAT_PROFILE); SetNeedsARB(); return *this; } @@ -92,6 +77,9 @@ wxGLContextAttrs& wxGLContextAttrs::MajorVersion(int val) { if ( val > 0 ) { + AddAttribute(WX_GL_MAJOR_VERSION); + AddAttribute(val); + if ( val >= 3 ) SetNeedsARB(); } @@ -102,12 +90,15 @@ wxGLContextAttrs& wxGLContextAttrs::MinorVersion(int val) { if ( val >= 0 ) { + AddAttribute(WX_GL_MINOR_VERSION); + AddAttribute(val); } return *this; } wxGLContextAttrs& wxGLContextAttrs::CompatibilityProfile() { + AddAttribute(wx_GL_COMPAT_PROFILE); SetNeedsARB(); return *this; } @@ -168,7 +159,7 @@ wxGLContextAttrs& wxGLContextAttrs::PlatformDefaults() void wxGLContextAttrs::EndList() { -// AddAttribute(None); + AddAttribute(0); } // ---------------------------------------------------------------------------- @@ -190,6 +181,7 @@ void wxGLContextAttrs::EndList() wxGLAttributes& wxGLAttributes::RGBA() { + AddAttribute(WX_GL_RGBA); return *this; } @@ -197,24 +189,28 @@ wxGLAttributes& wxGLAttributes::BufferSize(int val) { if ( val >= 0 ) { + AddAttribute(WX_GL_BUFFER_SIZE); + AddAttribute(val); } return *this; } wxGLAttributes& wxGLAttributes::Level(int val) { -// AddAttribute(GLX_LEVEL); + AddAttribute(WX_GL_LEVEL); AddAttribute(val); return *this; } wxGLAttributes& wxGLAttributes::DoubleBuffer() { + AddAttribute(WX_GL_DOUBLEBUFFER); return *this; } wxGLAttributes& wxGLAttributes::Stereo() { + AddAttribute(WX_GL_STEREO); return *this; } @@ -222,6 +218,8 @@ wxGLAttributes& wxGLAttributes::AuxBuffers(int val) { if ( val >= 0 ) { + AddAttribute(WX_GL_AUX_BUFFERS); + AddAttribute(val); } return *this; } @@ -230,15 +228,23 @@ wxGLAttributes& wxGLAttributes::MinRGBA(int mRed, int mGreen, int mBlue, int mAl { if ( mRed >= 0) { + AddAttribute(WX_GL_MIN_RED); + AddAttribute(mRed); } if ( mGreen >= 0) { + AddAttribute(WX_GL_MIN_GREEN); + AddAttribute(mGreen); } if ( mBlue >= 0) { + AddAttribute(WX_GL_MIN_BLUE); + AddAttribute(mBlue); } if ( mAlpha >= 0) { + AddAttribute(WX_GL_MIN_ALPHA); + AddAttribute(mAlpha); } return *this; } @@ -247,6 +253,8 @@ wxGLAttributes& wxGLAttributes::Depth(int val) { if ( val >= 0 ) { + AddAttribute(WX_GL_DEPTH_SIZE); + AddAttribute(val); } return *this; } @@ -255,6 +263,8 @@ wxGLAttributes& wxGLAttributes::Stencil(int val) { if ( val >= 0 ) { + AddAttribute(WX_GL_DEPTH_SIZE); + AddAttribute(val); } return *this; } @@ -263,40 +273,44 @@ wxGLAttributes& wxGLAttributes::MinAcumRGBA(int mRed, int mGreen, int mBlue, int { if ( mRed >= 0) { + AddAttribute(WX_GL_MIN_ACCUM_RED); + AddAttribute(mRed); } if ( mGreen >= 0) { + AddAttribute(WX_GL_MIN_ACCUM_GREEN); + AddAttribute(mGreen); } if ( mBlue >= 0) { + AddAttribute(WX_GL_MIN_ACCUM_BLUE); + AddAttribute(mBlue); } if ( mAlpha >= 0) { + AddAttribute(WX_GL_MIN_ACCUM_ALPHA); + AddAttribute(mAlpha); } return *this; } wxGLAttributes& wxGLAttributes::SampleBuffers(int val) { -#ifdef GLX_SAMPLE_BUFFERS_ARB - if ( val >= 0 && wxGLCanvasX11::IsGLXMultiSampleAvailable() ) + if ( val >= 0 ) { - AddAttribute(GLX_SAMPLE_BUFFERS_ARB); + AddAttribute(WX_GL_SAMPLE_BUFFERS); AddAttribute(val); } -#endif return *this; } wxGLAttributes& wxGLAttributes::Samplers(int val) { -#ifdef GLX_SAMPLES_ARB - if ( val >= 0 && wxGLCanvasX11::IsGLXMultiSampleAvailable() ) + if ( val >= 0 ) { - AddAttribute(GLX_SAMPLES_ARB); + AddAttribute(WX_GL_SAMPLES); AddAttribute(val); } -#endif return *this; } @@ -309,11 +323,12 @@ wxGLAttributes& wxGLAttributes::FrameBuffersRGB() void wxGLAttributes::EndList() { + AddAttribute(0); } wxGLAttributes& wxGLAttributes::PlatformDefaults() { - // No GLX specific values + // No Qt specific values return *this; } @@ -324,15 +339,33 @@ wxGLAttributes& wxGLAttributes::PlatformDefaults() wxIMPLEMENT_CLASS(wxGLContext, wxWindow); -wxGLContext::wxGLContext(wxGLCanvas *WXUNUSED(win), const wxGLContext* WXUNUSED(other), const wxGLContextAttrs *WXUNUSED(ctxAttrs)) +wxGLContext::wxGLContext(wxGLCanvas *win, + const wxGLContext *other, + const wxGLContextAttrs *ctxAttrs) { + QGLWidget *qglWidget = static_cast(win->GetHandle()); + m_glContext = qglWidget->context(); + + if (m_glContext != nullptr) + { + m_isOk = true; + } } -bool wxGLContext::SetCurrent(const wxGLCanvas&) const +bool wxGLContext::SetCurrent(const wxGLCanvas& win) const { -// I think I must destroy and recreate the QGLWidget to change the context? -// win->GetHandle()->makeCurrent(); - return false; + QGLWidget *qglWidget = static_cast(win.GetHandle()); + QGLContext *context = qglWidget->context(); + + if (context != m_glContext) + { + // I think I must destroy and recreate the QGLWidget to change the context? + wxLogDebug("Calling wxGLContext::SetCurrent with a different canvas is not supported in wxQt"); + return false; + } + + qglWidget->makeCurrent(); + return true; } //--------------------------------------------------------------------------- @@ -403,8 +436,11 @@ bool wxGLCanvas::Create(wxWindow *parent, const wxString& name, const wxPalette& palette) { - wxLogError("Missing implementation of " + wxString(__FUNCTION__)); - return false; + const int* attrsList = dispAttrs.GetGLAttrs(); + + wxCHECK_MSG(attrsList, false, "wxGLAttributes object is empty."); + + return Create(parent, id, pos, size, style, name, attrsList, palette); } bool wxGLCanvas::Create(wxWindow *parent, @@ -421,17 +457,35 @@ bool wxGLCanvas::Create(wxWindow *parent, #endif // wxUSE_PALETTE wxUnusedVar(palette); // Unused when wxDEBUG_LEVEL==0 - QGLFormat format; - if (!wxGLCanvas::ConvertWXAttrsToQtGL(attribList, format)) + // Separate display/context attributes, set defaults. + wxGLAttributes dispAttrs; + wxGLContextAttrs ctxAttrs; + if (!ParseAttribList(attribList, dispAttrs, &ctxAttrs)) return false; + QGLFormat format; + if (!wxGLCanvas::ConvertWXAttrsToQtGL(dispAttrs, ctxAttrs, format)) + return false; + + // Return false if any attribute is unsupported + if ( !IsDisplaySupported(attribList) ) + { + wxFAIL_MSG("Can't find a pixel format for the requested attributes"); + return false; + } + m_qtWindow = new wxQtGLWidget(parent, this, format); // Create and register a custom pan recognizer, available to all instances of this class. QGestureRecognizer* pPanRecognizer = new PanGestureRecognizer(); QGestureRecognizer::registerRecognizer(pPanRecognizer); - return wxWindow::Create( parent, id, pos, size, style, name ); + if ( !wxWindow::Create( parent, id, pos, size, style, name ) ) + return false; + + SetBackgroundStyle(wxBG_STYLE_PAINT); + + return true; } bool wxGLCanvas::SwapBuffers() @@ -440,11 +494,16 @@ bool wxGLCanvas::SwapBuffers() return true; } -/* static */ -bool wxGLCanvas::ConvertWXAttrsToQtGL(const int *wxattrs, QGLFormat &format) +bool wxGLCanvas::QtCanPaintWithoutActivePainter() const { - if (!wxattrs) - return true; + return true; +} + +/* static */ +bool wxGLCanvas::ConvertWXAttrsToQtGL(const wxGLAttributes &wxGLAttrs, const wxGLContextAttrs wxCtxAttrs, QGLFormat &format) +{ + const int *glattrs = wxGLAttrs.GetGLAttrs(); + const int *ctxattrs = wxCtxAttrs.GetGLAttrs(); // set default parameters to false format.setDoubleBuffer(false); @@ -452,14 +511,16 @@ bool wxGLCanvas::ConvertWXAttrsToQtGL(const int *wxattrs, QGLFormat &format) format.setAlpha(false); format.setStencil(false); - for ( int arg = 0; wxattrs[arg] != 0; arg++ ) + for (int arg = 0; glattrs && glattrs[arg] != 0; arg++) { // indicates whether we have a boolean attribute bool isBoolAttr = false; - int v = wxattrs[arg+1]; - switch ( wxattrs[arg] ) + int v = glattrs[arg+1]; + switch ( glattrs[arg] ) { + // Pixel format attributes + case WX_GL_BUFFER_SIZE: format.setRgba(false); // I do not know how to set the buffer size, so fail @@ -489,7 +550,7 @@ bool wxGLCanvas::ConvertWXAttrsToQtGL(const int *wxattrs, QGLFormat &format) return false; case WX_GL_MIN_RED: - format.setRedBufferSize(v*8); + format.setRedBufferSize(v); break; case WX_GL_MIN_GREEN: @@ -532,17 +593,54 @@ bool wxGLCanvas::ConvertWXAttrsToQtGL(const int *wxattrs, QGLFormat &format) // can we somehow indicate if it's not supported? break; - case WX_GL_MAJOR_VERSION: - format.setVersion ( v,0 ); - break; - default: wxLogDebug(wxT("Unsupported OpenGL attribute %d"), - wxattrs[arg]); + glattrs[arg]); continue; } - if ( !isBoolAttr ) { + if ( !isBoolAttr ) + { + if ( !v ) + return false; // zero parameter + arg++; + } + } + + for (int arg = 0; ctxattrs && ctxattrs[arg] != 0; arg++) + { + // indicates whether we have a boolean attribute + bool isBoolAttr = false; + + int v = ctxattrs[arg+1]; + switch ( ctxattrs[arg] ) + { + // Context attributes + + case WX_GL_MAJOR_VERSION: + format.setVersion ( v, format.minorVersion() ); + break; + + case WX_GL_MINOR_VERSION: + format.setVersion ( format.majorVersion(), v ); + break; + + case WX_GL_CORE_PROFILE: + format.setProfile(QGLFormat::CoreProfile); + break; + + case wx_GL_COMPAT_PROFILE: + format.setProfile(QGLFormat::CompatibilityProfile); + break; + + default: + wxLogDebug(wxT("Unsupported OpenGL attribute %d"), + ctxattrs[arg]); + continue; + } + + if ( !isBoolAttr ) + { if ( !v ) return false; // zero parameter arg++; @@ -553,23 +651,29 @@ bool wxGLCanvas::ConvertWXAttrsToQtGL(const int *wxattrs, QGLFormat &format) } /* static */ -bool -wxGLCanvasBase::IsDisplaySupported(const int *attribList) +bool wxGLCanvasBase::IsDisplaySupported(const wxGLAttributes& dispAttrs) { - QGLFormat format; + const int* attrsList = dispAttrs.GetGLAttrs(); - if (!wxGLCanvas::ConvertWXAttrsToQtGL(attribList, format)) - return false; + wxCHECK_MSG(attrsList, false, "wxGLAttributes object is empty."); - return QGLWidget(format).isValid(); + return IsDisplaySupported(attrsList); } /* static */ -bool -wxGLCanvasBase::IsDisplaySupported(const wxGLAttributes& dispAttrs) +bool wxGLCanvasBase::IsDisplaySupported(const int *attribList) { - wxLogError("Missing implementation of " + wxString(__FUNCTION__)); - return false; + // Separate display/context attributes, set defaults. + wxGLAttributes dispAttrs; + wxGLContextAttrs ctxAttrs; + if (!ParseAttribList(attribList, dispAttrs, &ctxAttrs)) + return false; + + QGLFormat format; + if (!wxGLCanvas::ConvertWXAttrsToQtGL(dispAttrs, ctxAttrs, format)) + return false; + + return QGLWidget(format).isValid(); } // ---------------------------------------------------------------------------- diff --git a/src/qt/window.cpp b/src/qt/window.cpp index 085fbcf99b..673948b7d8 100644 --- a/src/qt/window.cpp +++ b/src/qt/window.cpp @@ -1830,6 +1830,11 @@ QPainter *wxWindowQt::QtGetPainter() return m_qtPainter.get(); } +bool wxWindowQt::QtCanPaintWithoutActivePainter() const +{ + return false; +} + bool wxWindowQt::EnableTouchEvents(int eventsMask) { wxCHECK_MSG( GetHandle(), false, "can't be called before creating the window" );