wxOverlay broken by use of `WS_EX_COMPOSITED` (Issue #23047)

51 views
Skip to first unread message

VZ

unread,
Dec 18, 2022, 4:01:50 PM12/18/22
to wx-...@googlegroups.com, Subscribed

As can be seen in the drawing sample, nothing drawn using wxOverlay can be seen any longer by default but setting wx_msw_window_no_composited=1 allows the expected selection rectangle to be seen.

This is actually not due so much to wxOverlay itself as to using wxClientDC in the sample but then, of course, this is the way it's going to be used in 99% (or maybe even 100%) of the existing code, so we must find some way to make this work. In wxMSW we could use wxScreenDC which should still work even with composited windows, but it would be great to have a better solution.

@AliKet You've worked on this functionality for GTK 3, where wxClientDC can't be used neither, would you know of any way to make this work for composited windows under MSW by chance?


Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/23047@github.com>

AliKet

unread,
Dec 18, 2022, 7:44:30 PM12/18/22
to wx-...@googlegroups.com, Subscribed

Since anything drawn using wxClientDC will simply be lost if the window has the WS_EX_COMPOSITED flag set, how about blittering the drawing result into a real layered window (created with the WS_EX_LAYERED flag )?

A quick test of this seems to work fine on my machine:

diff --git a/include/wx/private/overlay.h b/include/wx/private/overlay.h
index 60f272ff43..559d191802 100644
--- a/include/wx/private/overlay.h
+++ b/include/wx/private/overlay.h
@@ -17,6 +17,8 @@
     #define wxHAS_NATIVE_OVERLAY 1
 #elif defined(__WXOSX__) && wxOSX_USE_COCOA
     #define wxHAS_NATIVE_OVERLAY 1
+#elif defined(__WXMSW__)
+    #define wxHAS_NATIVE_OVERLAY 1
 #elif defined(__WXGTK3__)
     #define wxHAS_NATIVE_OVERLAY 1
     #define wxHAS_GENERIC_OVERLAY 1
diff --git a/src/common/overlaycmn.cpp b/src/common/overlaycmn.cpp
index ff7d57b6a2..c97580aa09 100644
--- a/src/common/overlaycmn.cpp
+++ b/src/common/overlaycmn.cpp
@@ -255,6 +255,217 @@ wxOverlay::Impl* wxOverlay::Create()
 }
 #endif
 
+#else
+
+#include "wx/window.h"
+#include "wx/dcclient.h"
+
+#include "wx/nativewin.h"
+#include "wx/msw/private.h"
+
+
+namespace
+{
+static const wxChar* gs_overlayClassName = NULL;
+
+class wxOverlayWindow : public wxNativeContainerWindow
+{
+public:
+    static wxWindow*  Create(bool fullScreen = false)
+    {
+        static const wxChar* OVERLAY_WINDOW_CLASS = wxS("wxOverlayWindow");
+
+        if ( !gs_overlayClassName )
+        {
+            WNDCLASS wndclass;
+            wxZeroMemory(wndclass);
+
+            wndclass.lpfnWndProc   = ::DefWindowProc;
+            wndclass.hInstance     = wxGetInstance();
+            wndclass.lpszClassName = OVERLAY_WINDOW_CLASS;
+
+            if ( !::RegisterClass(&wndclass) )
+            {
+                wxLogLastError(wxS("RegisterClass() in wxOverlayWindow::Create()"));
+                return NULL;
+            }
+
+            gs_overlayClassName = OVERLAY_WINDOW_CLASS;
+        }
+
+        DWORD exStyle = WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE;
+
+        if ( fullScreen )
+            exStyle |= WS_EX_TOPMOST;
+
+        HWND hwnd = ::CreateWindowEx
+                      (
+                        exStyle,
+                        OVERLAY_WINDOW_CLASS,
+                        NULL,
+                        WS_POPUP, 0, 0, 0,
+                        0,
+                        (HWND) NULL,
+                        (HMENU)NULL,
+                        wxGetInstance(),
+                        (LPVOID) NULL
+                      );
+
+        if ( !hwnd )
+        {
+            wxLogLastError(wxS("CreateWindowEx() in wxOverlayWindow::Create()"));
+            return NULL;
+        }
+
+        if ( !::SetLayeredWindowAttributes(hwnd, 0, 128, LWA_COLORKEY|LWA_ALPHA) )
+        {
+            wxLogLastError(wxS("SetLayeredWindowAttributes() in wxOverlayWindow::Create()"));
+        }
+
+        return new wxOverlayWindow(hwnd);
+    }
+
+    virtual bool Destroy() wxOVERRIDE
+    {
+        HWND hwnd = GetHandle();
+
+        if ( hwnd && !::DestroyWindow(hwnd) )
+        {
+            wxLogLastError(wxS("DestroyWindow in wxOverlayWindow::Destroy()"));
+            return false;
+        }
+
+        if ( gs_overlayClassName )
+        {
+            if ( !::UnregisterClass(gs_overlayClassName, wxGetInstance()) )
+            {
+                wxLogLastError(wxS("UnregisterClass in wxOverlayWindow::Destroy()"));
+            }
+
+            gs_overlayClassName = NULL;
+        }
+
+        return true;
+    }
+
+    virtual bool Show(bool show) wxOVERRIDE
+    {
+        if ( !wxWindowBase::Show(show) || !GetHandle() )
+            return false;
+
+        if ( show )
+        {
+            ::SetWindowPos(GetHandle(), NULL, 0, 0, 0, 0,
+                           SWP_NOSIZE |
+                           SWP_NOMOVE |
+                           SWP_NOREDRAW |
+                           SWP_NOOWNERZORDER |
+                           SWP_NOACTIVATE |
+                           SWP_SHOWWINDOW);
+        }
+        else
+        {
+            ::ShowWindow(GetHandle(), SW_HIDE);
+        }
+
+        return true;
+    }
+
+private:
+    wxOverlayWindow(HWND hwnd) : wxNativeContainerWindow(hwnd) {}
+    ~wxOverlayWindow() {}
+};
+} // anonymous namespace
+
+// ----------------------------------------------------------------------------
+// wxOverlayImpl
+// ----------------------------------------------------------------------------
+class wxOverlayImpl : public wxOverlay::Impl
+{
+public:
+    wxOverlayImpl();
+    ~wxOverlayImpl();
+
+    virtual bool IsNative() const override { return true; }
+    virtual void Reset() override;
+    virtual bool IsOk() override;
+    virtual void Init(wxDC* dc, int x , int y , int width , int height) override;
+    virtual void BeginDrawing(wxDC* dc) override;
+    virtual void EndDrawing(wxDC* dc) override;
+    virtual void Clear(wxDC* dc) override;
+
+public:
+    // window the overlay is associated with
+    wxWindow* m_window;
+    // the overlay window itself
+    wxWindow* m_overlayWindow;
+
+    wxRect m_rect;
+
+    wxDECLARE_NO_COPY_CLASS(wxOverlayImpl);
+};
+
+wxOverlayImpl::wxOverlayImpl()
+{
+    m_window = NULL;
+    m_overlayWindow = NULL;
+}
+
+wxOverlayImpl::~wxOverlayImpl()
+{
+    Reset();
+}
+
+bool wxOverlayImpl::IsOk()
+{
+    return false;
+}
+
+void wxOverlayImpl::Init(wxDC* dc, int , int , int , int )
+{
+    m_window = dc->GetWindow();
+
+    if ( !m_overlayWindow )
+        m_overlayWindow = wxOverlayWindow::Create();
+
+    m_rect.SetSize(m_window->GetClientSize());
+    m_rect.SetPosition(m_window->GetScreenPosition());
+
+    m_overlayWindow->Move(m_rect.GetPosition());
+    m_overlayWindow->SetSize(m_rect.GetSize());
+    m_overlayWindow->Show();
+}
+
+void wxOverlayImpl::BeginDrawing(wxDC* WXUNUSED(dc))
+{
+}
+
+void wxOverlayImpl::EndDrawing(wxDC* dc)
+{
+    wxWindowDC winDC(m_overlayWindow);
+
+    const wxPoint pt = dc->GetDeviceOrigin();
+    winDC.Blit(0, 0, m_rect.width, m_rect.height, dc, -pt.x, -pt.y);
+}
+
+void wxOverlayImpl::Clear(wxDC* dc)
+{
+    dc->SetBackground(wxBrush(wxTransparentColour));
+    dc->Clear();
+}
+
+void wxOverlayImpl::Reset()
+{
+    if ( m_overlayWindow )
+    {
+        m_overlayWindow->Destroy();
+        m_overlayWindow = nullptr;
+        m_window = nullptr;
+    }
+}
+
+wxOverlay::Impl* wxOverlay::Create() { return new wxOverlayImpl(); }
+
 #endif // wxHAS_GENERIC_OVERLAY
 
 wxOverlay::wxOverlay()

Or something like that ?


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/23047/1356931197@github.com>

AliKet

unread,
Dec 19, 2022, 8:56:23 AM12/19/22
to wx-...@googlegroups.com, Subscribed

FYI, Qt also has this problem too as double buffering is on by default there since version 4.0

I managed to make rubber banding work in the drawing sample by implementing wxOverlayImpl in terms of QRubberBand, i.e. wxOverlayImpl just forwards to a QRubberBand object to draw the rubberband.

This makes me wonder if we should introduce a specialized class wxRubberBand for rubberbanding, and if the port doesn't have a native support for this kind of operations than wxRubberBand will be implemented in terms of wxOverlay under the hood.

what do you think ?


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/23047/1357702281@github.com>

VZ

unread,
Dec 19, 2022, 3:24:43 PM12/19/22
to wx-...@googlegroups.com, Subscribed

Using layered windows for overlays in MSW makes sense, as this is the same kind of what Cocoa uses, thanks -- please let me know if you plan on making a PR with your changes.

I have really no idea about Qt, doesn't it have some kind of layers support too?

In any case, providing a wxRubberBandSelection with generic implementation based on wxOverlay and native implementation for the ports that may do it in a better way seems worthwhile (here is an example of an animated rubber band for Cocoa), however less important than implementing working wxOverlay for MSW in the first place, so let's create another issue for this.


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/23047/1358245886@github.com>

AliKet

unread,
Dec 20, 2022, 7:13:30 AM12/20/22
to wx-...@googlegroups.com, Subscribed

Using layered windows for overlays in MSW makes sense, as this is the same kind of what Cocoa uses, thanks -- please let me know if you plan on making a PR with your changes.

done here #23058


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issues/23047/1359271564@github.com>

VZ

unread,
Feb 20, 2023, 12:34:06 PM2/20/23
to wx-...@googlegroups.com, Subscribed

Closed #23047 as completed via 91031bc.


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/issue/23047/issue_event/8563922549@github.com>

Reply all
Reply to author
Forward
0 new messages