From e3ec9fb124cd7a5dc3748f5bfab5ab6a557ca291 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 12 Dec 2021 17:05:58 +0000 Subject: [PATCH] Fix using wxMemoryDC without a GUI wxApp instance This used to work, at least in wxMSW, but stopped working after the (perfectly valid, on their own) changes of 2508efdd6e (Initialize wxMemoryDC with a default font, 2019-08-13), as this resulted in calling wxApp::GetTopWindow() that can only be called from the GUI code. Fix this by adding wxApp::GetGUIInstance() and using it in GetMainTopWindow(), so that we only call GetTopWindow() if we actually have a GUI wxApp object on which to call it. Implement this in terms of a new virtual IsGUI() which seems slightly better than, although roughly equivalent to, using wxDynamicCast(). Closes https://github.com/wxWidgets/wxWidgets/pull/2617 Closes #19343. --- include/wx/app.h | 18 +++++++++++++++++- interface/wx/app.h | 35 +++++++++++++++++++++++++++++++++-- src/common/appcmn.cpp | 2 +- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/include/wx/app.h b/include/wx/app.h index 45e35ef7d3..aae578e209 100644 --- a/include/wx/app.h +++ b/include/wx/app.h @@ -462,6 +462,9 @@ public: static wxAppConsole *GetInstance() { return ms_appInstance; } static void SetInstance(wxAppConsole *app) { ms_appInstance = app; } + // returns true for GUI wxApp subclasses + virtual bool IsGUI() const { return false; } + // command line arguments (public for backwards compatibility) int argc; @@ -684,6 +687,19 @@ public: // deactivated virtual void SetActive(bool isActive, wxWindow *lastFocus); + virtual bool IsGUI() const wxOVERRIDE { return true; } + + // returns non-null pointer only if we have a GUI application object: this + // is only useful in the rare cases when the same code can be used in both + // console and GUI applications, but needs to use GUI-specific functions if + // the GUI is available + static wxAppBase *GetGUIInstance() + { + return ms_appInstance && ms_appInstance->IsGUI() + ? static_cast(ms_appInstance) + : NULL; + } + protected: // override base class method to use GUI traits virtual wxAppTraits *CreateTraits() wxOVERRIDE; @@ -761,7 +777,7 @@ protected: // return the object of the correct type (i.e. MyApp and not wxApp) // // the cast is safe as in GUI build we only use wxApp, not wxAppConsole, and in -// console mode it does nothing at all +// console mode it does nothing at all (but see also wxApp::GetGUIInstance()) #define wxTheApp static_cast(wxApp::GetInstance()) // ---------------------------------------------------------------------------- diff --git a/interface/wx/app.h b/interface/wx/app.h index e9b869588a..8840be86d3 100644 --- a/interface/wx/app.h +++ b/interface/wx/app.h @@ -251,7 +251,7 @@ public: Returns the one and only global application object. Usually ::wxTheApp is used instead. - @see SetInstance() + @see SetInstance(), wxApp::GetGUIInstance() */ static wxAppConsole* GetInstance(); @@ -838,6 +838,35 @@ public: */ virtual wxVideoMode GetDisplayMode() const; + /** + Returns the current GUI wxApp object if any or @NULL otherwise. + + This function should only be used in the rare cases when the same code + needs to work in both console and GUI applications, but needs to use + GUI-specific functionality if it is available, and so just calling + wxAppConsole::GetInstance() is insufficient while using ::wxTheApp is + incorrect, as the application object is not always a GUI wxApp. + + For example: + @code + WXWidget handle = 0; + if ( wxApp* const app = wxApp::GetGUIInstance() ) { + if ( wxWindow* const w = app->GetTopWindow() ) { + handle = w->GetHandle(); + } + } + //else: no window to use + + some_native_function_taking_a_window_handle(handle); + @endcode + + Note that in this particular example, you could use GetMainTopWindow() + which already does the same thing instead of doing it yourself. + + @since 3.1.6 + */ + static wxAppConsole* GetGUIInstance(); + /** Returns @true if the application will exit when the top-level frame is deleted. @@ -1213,7 +1242,9 @@ public: /** The global pointer to the singleton wxApp object. - @see wxApp::GetInstance() + This pointer can only be used in the GUI applications. + + @see wxAppConsole::GetInstance(), wxApp::GetGUIInstance() */ wxApp *wxTheApp; diff --git a/src/common/appcmn.cpp b/src/common/appcmn.cpp index bfda967ead..0546b2ef1b 100644 --- a/src/common/appcmn.cpp +++ b/src/common/appcmn.cpp @@ -193,7 +193,7 @@ wxWindow* wxAppBase::GetTopWindow() const /* static */ wxWindow* wxAppBase::GetMainTopWindow() { - const wxAppBase* const app = static_cast(GetInstance()); + const wxAppBase* const app = GetGUIInstance(); return app ? app->GetTopWindow() : NULL; }