From 24fd54ed31a8b09dade9d6124d88042cd322a08b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Tue, 26 Dec 2023 18:31:36 +0100 Subject: [PATCH] Add wxClientDC::CanBeUsedForDrawing() This function can be used to check if drawing on wxClientDC actually works. This has to be a run-time, rather than compile-time, check because in wxGTK3 this depends on the backend being used: wxClientDC only doesn't work with Wayland, but does work with X11 (and, less importantly, Win32) backend(s). Currently the wxWindow parameter of this function is not used but it could be useful in the future and it will be simpler to allow not specifying it (by defaulting it to nullptr) than to add it later, so it seems better to have it. --- include/wx/dc.h | 4 ++++ include/wx/dcclient.h | 2 ++ include/wx/dfb/dcclient.h | 3 +++ include/wx/gtk/dc.h | 2 ++ include/wx/gtk/dcclient.h | 3 +++ include/wx/msw/dcclient.h | 3 +++ include/wx/osx/dcclient.h | 3 +++ include/wx/qt/dcclient.h | 3 +++ include/wx/x11/dcclient.h | 3 +++ interface/wx/dcclient.h | 30 ++++++++++++++++++++++++------ src/common/dcbase.cpp | 11 +++++++++++ src/gtk/dc.cpp | 19 +++++++++++++++++++ 12 files changed, 80 insertions(+), 6 deletions(-) diff --git a/include/wx/dc.h b/include/wx/dc.h index 26402a5859..b89c38130f 100644 --- a/include/wx/dc.h +++ b/include/wx/dc.h @@ -128,6 +128,8 @@ public: virtual wxDCImpl* CreatePrinterDC( wxPrinterDC *owner, const wxPrintData &data ) = 0; #endif + virtual bool CanDrawUsingClientDC(const wxWindow* window) const = 0; + static void Set(wxDCFactory *factory); static wxDCFactory *Get(); @@ -154,6 +156,8 @@ public: #if wxUSE_PRINTING_ARCHITECTURE virtual wxDCImpl* CreatePrinterDC( wxPrinterDC *owner, const wxPrintData &data ) override; #endif + + virtual bool CanDrawUsingClientDC(const wxWindow* window) const override; }; //----------------------------------------------------------------------------- diff --git a/include/wx/dcclient.h b/include/wx/dcclient.h index 28391d1266..ced769ce34 100644 --- a/include/wx/dcclient.h +++ b/include/wx/dcclient.h @@ -36,6 +36,8 @@ class WXDLLIMPEXP_CORE wxClientDC : public wxWindowDC public: wxClientDC(wxWindow *win); + static bool CanBeUsedForDrawing(const wxWindow* window); + protected: wxClientDC(wxDCImpl *impl) : wxWindowDC(impl) { } diff --git a/include/wx/dfb/dcclient.h b/include/wx/dfb/dcclient.h index 98655e4fd3..f195829f14 100644 --- a/include/wx/dfb/dcclient.h +++ b/include/wx/dfb/dcclient.h @@ -51,6 +51,9 @@ public: wxClientDCImpl(wxDC *owner) : wxWindowDCImpl(owner) { } wxClientDCImpl(wxDC *owner, wxWindow *win); + static bool + CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return true; } + wxDECLARE_DYNAMIC_CLASS(wxClientDCImpl); wxDECLARE_NO_COPY_CLASS(wxClientDCImpl); }; diff --git a/include/wx/gtk/dc.h b/include/wx/gtk/dc.h index 472c90fe11..dd9baf9725 100644 --- a/include/wx/gtk/dc.h +++ b/include/wx/gtk/dc.h @@ -64,6 +64,8 @@ class wxClientDCImpl: public wxGTKCairoDCImpl public: wxClientDCImpl(wxClientDC* owner, wxWindow* window); + static bool CanBeUsedForDrawing(const wxWindow* window); + wxDECLARE_NO_COPY_CLASS(wxClientDCImpl); }; //----------------------------------------------------------------------------- diff --git a/include/wx/gtk/dcclient.h b/include/wx/gtk/dcclient.h index 6b42e90467..f6d6f3ed5d 100644 --- a/include/wx/gtk/dcclient.h +++ b/include/wx/gtk/dcclient.h @@ -150,6 +150,9 @@ public: virtual void DoGetSize(int *width, int *height) const override; + static bool + CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return true; } + wxDECLARE_ABSTRACT_CLASS(wxClientDCImpl); }; diff --git a/include/wx/msw/dcclient.h b/include/wx/msw/dcclient.h index d6eaffda58..01f2cca210 100644 --- a/include/wx/msw/dcclient.h +++ b/include/wx/msw/dcclient.h @@ -56,6 +56,9 @@ public: virtual void DoGetSize(int *width, int *height) const override; + static bool + CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return true; } + protected: void InitDC(); diff --git a/include/wx/osx/dcclient.h b/include/wx/osx/dcclient.h index d5840ad98a..be16886f1b 100644 --- a/include/wx/osx/dcclient.h +++ b/include/wx/osx/dcclient.h @@ -54,6 +54,9 @@ public: wxClientDCImpl( wxDC *owner, wxWindow *window ); virtual ~wxClientDCImpl(); + static bool + CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return false; } + private: wxDECLARE_CLASS(wxClientDCImpl); wxDECLARE_NO_COPY_CLASS(wxClientDCImpl); diff --git a/include/wx/qt/dcclient.h b/include/wx/qt/dcclient.h index 788c4867ba..3eafbed172 100644 --- a/include/wx/qt/dcclient.h +++ b/include/wx/qt/dcclient.h @@ -40,6 +40,9 @@ public: wxClientDCImpl( wxDC *owner ); wxClientDCImpl( wxDC *owner, wxWindow *win ); + static bool + CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return false; } + private: wxDECLARE_CLASS(wxClientDCImpl); wxDECLARE_NO_COPY_CLASS(wxClientDCImpl); diff --git a/include/wx/x11/dcclient.h b/include/wx/x11/dcclient.h index beca1247de..7c477ed34f 100644 --- a/include/wx/x11/dcclient.h +++ b/include/wx/x11/dcclient.h @@ -153,6 +153,9 @@ public: wxClientDCImpl( wxDC *owner ) : wxWindowDCImpl( owner ) { } wxClientDCImpl( wxDC *owner, wxWindow *win ); + static bool + CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) { return true; } + protected: virtual void DoGetSize(int *width, int *height) const; diff --git a/interface/wx/dcclient.h b/interface/wx/dcclient.h index 05058d016a..41997f18ed 100644 --- a/interface/wx/dcclient.h +++ b/interface/wx/dcclient.h @@ -63,12 +63,13 @@ public: window from outside an EVT_PAINT() handler in some ports, this does @em not work on most of the platforms: neither wxOSX nor wxGTK with GTK 3 Wayland backend support this at all, so drawing using wxClientDC simply doesn't - have any effect there, while wxMSW doesn't support using it for composited - windows, so wxWindow::MSWDisableComposited() must be called to allow it to - work. The only supported way of drawing on a window is via wxPaintDC. To - redraw a small part of the window, use wxWindow::RefreshRect() to - invalidate just this part and check wxWindow::GetUpdateRegion() in the - paint event handler to redraw this part only. + have any effect there. CanBeUsedForDrawing() can be used to determine + whether wxClientDC can be used for drawing in the current environment, but + it is recommended to only draw on the window using wxPaintDC, as this is + guaranteed to work everywhere. To redraw a small part of the window, use + wxWindow::RefreshRect() to invalidate just this part and check + wxWindow::GetUpdateRegion() in the paint event handler to redraw this part + only. wxClientDC objects should normally be constructed as temporary stack objects, i.e. don't store a wxClientDC object. @@ -88,6 +89,23 @@ public: Constructor. Pass a pointer to the window on which you wish to paint. */ wxClientDC(wxWindow* window); + + /** + Return true if drawing on wxClientDC actually works. + + In many environments (currently this includes wxGTK when using Wayland + backend, wxMSW when using double buffering and wxOSX in all cases), + wxClientDC can be only used for obtaining information about the device + context, but not for actually drawing on it. Portable code should avoid + using wxClientDC completely, as explained in the class documentation, + but it is also possible to optionally use it only when it does work, + i.e. when this function returns @true. + + @param window The window that would be used with wxClientDC. + + @since 3.3.0 + */ + static bool CanBeUsedForDrawing(const wxWindow* window); }; diff --git a/src/common/dcbase.cpp b/src/common/dcbase.cpp index e1060ae402..f36020179c 100644 --- a/src/common/dcbase.cpp +++ b/src/common/dcbase.cpp @@ -174,6 +174,11 @@ wxDCImpl *wxNativeDCFactory::CreatePrinterDC( wxPrinterDC *owner, const wxPrintD } #endif +bool wxNativeDCFactory::CanDrawUsingClientDC(const wxWindow* window) const +{ + return wxClientDCImpl::CanBeUsedForDrawing(window); +} + //----------------------------------------------------------------------------- // wxWindowDC //----------------------------------------------------------------------------- @@ -196,6 +201,12 @@ wxClientDC::wxClientDC(wxWindow *win) { } +/* static */ +bool wxClientDC::CanBeUsedForDrawing(const wxWindow* window) +{ + return wxDCFactory::Get()->CanDrawUsingClientDC(window); +} + //----------------------------------------------------------------------------- // wxMemoryDC //----------------------------------------------------------------------------- diff --git a/src/gtk/dc.cpp b/src/gtk/dc.cpp index 2c0e3d77c9..4cb49d135f 100644 --- a/src/gtk/dc.cpp +++ b/src/gtk/dc.cpp @@ -486,6 +486,25 @@ wxClientDCImpl::wxClientDCImpl(wxClientDC* owner, wxWindow* window) else SetGraphicsContext(wxGraphicsContext::Create()); } + +/* static */ +bool wxClientDCImpl::CanBeUsedForDrawing(const wxWindow* WXUNUSED(window)) +{ +#ifdef __UNIX__ + switch ( wxGetDisplayInfo().type ) + { + case wxDisplayNone: + case wxDisplayX11: + break; + + case wxDisplayWayland: + return false; + } +#endif // __UNIX__ + + return true; +} + //----------------------------------------------------------------------------- wxPaintDCImpl::wxPaintDCImpl(wxPaintDC* owner, wxWindow* window)