vcl/inc/win/saldata.hxx     |    3 +
 vcl/inc/win/salframe.h      |    3 +
 vcl/win/app/salinst.cxx     |    8 +++
 vcl/win/window/salframe.cxx |  101 +++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 104 insertions(+), 11 deletions(-)

New commits:
commit fa5b29052dc5768048d7098a4829aa2a14ec35c7
Author:     Noel Grandin <noelgran...@gmail.com>
AuthorDate: Sun Jul 13 11:58:00 2025 +0200
Commit:     Noel Grandin <noelgran...@gmail.com>
CommitDate: Sun Jul 13 20:14:07 2025 +0200

    Revert "remove mpThreadGraphics from GDI backend"
    
    This reverts commit b5510122fd68e3d16117684f51062dfb03961b85.
    
    I was wrong, we need this for when out of process extensions
    call into vcl, which they will do not-the-main-thread.
    
    Unfortunately, the only unit test we have that exercises
    this is JunitTest_sfx2_complex and that test is not run
    on the Windows Jenkins boxes.
    
    Change-Id: I55582418c229a7064e27ebf408a9e719ef6dd8a4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187795
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>

diff --git a/vcl/inc/win/saldata.hxx b/vcl/inc/win/saldata.hxx
index 46b94ec22dd5..9ad1a925ced3 100644
--- a/vcl/inc/win/saldata.hxx
+++ b/vcl/inc/win/saldata.hxx
@@ -113,6 +113,7 @@ public:
     sal_uInt16              mnStockPenCount;        // count of static pens
     sal_uInt16              mnStockBrushCount;      // count of static brushes
     WPARAM                  mnSalObjWantKeyEvt;     // KeyEvent that should be 
processed by SalObj-Hook
+    BYTE                    mnCacheDCInUse;         // count of CacheDC in use
     bool                    mbObjClassInit;         // is SALOBJECTCLASS 
initialised
     DWORD                   mnAppThreadId;          // Id from 
Application-Thread
     SalIcon*                mpFirstIcon;            // icon cache, points to 
first icon, NULL if none
@@ -199,6 +200,8 @@ OUString ImplSalGetUniString(const char* pStr, sal_Int32 
nLen = -1);
 #define SAL_MSG_CREATEOBJECT        (WM_USER+116)
 // wParam == 0; lParam == pObject;
 #define SAL_MSG_DESTROYOBJECT       (WM_USER+117)
+// wParam == hWnd; lParam == 0; lResult == hDC
+#define SAL_MSG_GETCACHEDDC         (WM_USER+120)
 // wParam == hWnd; lParam == 0
 #define SAL_MSG_RELEASEDC           (WM_USER+121)
 // wParam == newParentHwnd; lParam == oldHwnd; lResult == newhWnd
diff --git a/vcl/inc/win/salframe.h b/vcl/inc/win/salframe.h
index 31f6b88a8cc9..9d356a11bc45 100644
--- a/vcl/inc/win/salframe.h
+++ b/vcl/inc/win/salframe.h
@@ -41,6 +41,9 @@ public:
     HCURSOR                 mhCursor;               // cursor handle
     HIMC                    mhDefIMEContext;        // default IME-Context
     WinSalGraphics*         mpLocalGraphics;        // current main thread 
frame graphics
+    /// Some threads will call vcl functions, and even though they hold the 
SolarMutex, the GDI
+    /// library will error, so we need to keep a separate graphics and DC for 
them.
+    WinSalGraphics*         mpThreadGraphics;       // current frame graphics 
for other threads (DCX_CACHE)
     WinSalFrame*            mpNextFrame;            // pointer to next frame
     HMENU                   mSelectedhMenu;         // the menu where 
highlighting is currently going on
     HMENU                   mLastActivatedhMenu;    // the menu that was most 
recently opened
diff --git a/vcl/win/app/salinst.cxx b/vcl/win/app/salinst.cxx
index dc7f93dccd3c..b0e2de5db00b 100644
--- a/vcl/win/app/salinst.cxx
+++ b/vcl/win/app/salinst.cxx
@@ -269,6 +269,7 @@ SalData::SalData()
     mnStockPenCount = 0;        // count of static pens
     mnStockBrushCount = 0;      // count of static brushes
     mnSalObjWantKeyEvt = 0;     // KeyEvent for the SalObj hook
+    mnCacheDCInUse = 0;         // count of CacheDC in use
     mbObjClassInit = false;     // is SALOBJECTCLASS initialised
     mnAppThreadId = 0;          // Id from Application-Thread
     mpFirstIcon = nullptr;      // icon cache, points to first icon, NULL if 
none
@@ -661,6 +662,13 @@ LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM 
wParam, LPARAM lParam, b
                 delete reinterpret_cast<SalObject*>(lParam);
             }
             break;
+        case (SAL_MSG_GETCACHEDDC):
+            {
+                NoYieldLockGuard g;
+                nRet = reinterpret_cast<LRESULT>(
+                    GetDCEx(reinterpret_cast<HWND>(wParam), nullptr, 
0x00000002L));
+            }
+            break;
         case (SAL_MSG_RELEASEDC):
             {
                 NoYieldLockGuard g;
diff --git a/vcl/win/window/salframe.cxx b/vcl/win/window/salframe.cxx
index 342fffbefe46..ddbce3e9e9ce 100644
--- a/vcl/win/window/salframe.cxx
+++ b/vcl/win/window/salframe.cxx
@@ -861,6 +861,7 @@ WinSalFrame::WinSalFrame()
     mhCursor            = LoadCursor( nullptr, IDC_ARROW );
     mhDefIMEContext     = nullptr;
     mpLocalGraphics     = nullptr;
+    mpThreadGraphics    = nullptr;
     m_eState = vcl::WindowState::Normal;
     mnShowState         = SW_SHOWNORMAL;
     mnMinWidth          = 0;
@@ -946,6 +947,8 @@ bool WinSalFrame::ReleaseFrameGraphicsDC( WinSalGraphics* 
pGraphics )
     pGraphics->setHDC(nullptr);
     SendMessageW( pSalData->mpInstance->mhComWnd, SAL_MSG_RELEASEDC,
         reinterpret_cast<WPARAM>(mhWnd), reinterpret_cast<LPARAM>(hDC) );
+    if ( pGraphics == mpThreadGraphics )
+        pSalData->mnCacheDCInUse--;
     return true;
 }
 
@@ -963,6 +966,14 @@ WinSalFrame::~WinSalFrame()
         *ppFrame = mpNextFrame;
     mpNextFrame = nullptr;
 
+    // destroy the thread SalGraphics
+    if ( mpThreadGraphics )
+    {
+        ReleaseFrameGraphicsDC( mpThreadGraphics );
+        delete mpThreadGraphics;
+        mpThreadGraphics = nullptr;
+    }
+
     // destroy the local SalGraphics
     if ( mpLocalGraphics )
     {
@@ -998,6 +1009,7 @@ WinSalFrame::~WinSalFrame()
 
 bool WinSalFrame::InitFrameGraphicsDC( WinSalGraphics *pGraphics, HDC hDC, 
HWND hWnd )
 {
+    SalData* pSalData = GetSalData();
     assert( pGraphics );
     pGraphics->setHWND( hWnd );
 
@@ -1010,6 +1022,8 @@ bool WinSalFrame::InitFrameGraphicsDC( WinSalGraphics 
*pGraphics, HDC hDC, HWND
     if ( !hDC )
         return false;
 
+    if ( pGraphics == mpThreadGraphics )
+        pSalData->mnCacheDCInUse++;
     return true;
 }
 
@@ -1019,24 +1033,44 @@ SalGraphics* WinSalFrame::AcquireGraphics()
         return nullptr;
 
     SalData* pSalData = GetSalData();
-    assert(pSalData->mpInstance->IsMainThread());
-    SAL_WARN_IF(!pSalData->mpInstance->IsMainThread(), "vcl", "ERROR: 
pSalData->mpInstance is not main thread!");
+    WinSalGraphics* pGraphics;
+    HDC hDC;
 
-    if ( !mpLocalGraphics )
+    // Other threads get an own DC, because Windows modify in the
+    // other case our DC (changing clip region), when they send a
+    // WM_ERASEBACKGROUND message
+    if ( !pSalData->mpInstance->IsMainThread() )
     {
-        HDC hDC = GetDC( mhWnd );
-        if (!hDC)
+        // We use only three CacheDC's for all threads, because W9x is limited
+        // to max. 5 Cache DC's per thread
+        if ( pSalData->mnCacheDCInUse >= 3 )
             return nullptr;
 
-        mpLocalGraphics = new WinSalGraphics(WinSalGraphics::WINDOW, true, 
mhWnd, this);
-        mpLocalGraphics->setHDC( hDC );
+        if ( !mpThreadGraphics )
+            mpThreadGraphics = new WinSalGraphics(WinSalGraphics::WINDOW, 
true, mhWnd, this);
+        pGraphics = mpThreadGraphics;
+        assert( !pGraphics->getHDC() );
+        hDC = reinterpret_cast<HDC>(static_cast<sal_IntPtr>(SendMessageW( 
pSalData->mpInstance->mhComWnd,
+                                    SAL_MSG_GETCACHEDDC, 
reinterpret_cast<WPARAM>(mhWnd), 0 )));
     }
-    mbGraphicsAcquired = true;
-    return mpLocalGraphics;
+    else
+    {
+        if ( !mpLocalGraphics )
+            mpLocalGraphics = new WinSalGraphics(WinSalGraphics::WINDOW, true, 
mhWnd, this);
+        pGraphics = mpLocalGraphics;
+        hDC = pGraphics->getHDC();
+        if ( !hDC )
+            hDC = GetDC( mhWnd );
+    }
+
+    mbGraphicsAcquired = InitFrameGraphicsDC( pGraphics, hDC, mhWnd );
+    return mbGraphicsAcquired ? pGraphics : nullptr;
 }
 
-void WinSalFrame::ReleaseGraphics( SalGraphics* /*pGraphics*/ )
+void WinSalFrame::ReleaseGraphics( SalGraphics* pGraphics )
 {
+    if ( mpThreadGraphics == pGraphics )
+        ReleaseFrameGraphicsDC( mpThreadGraphics );
     mbGraphicsAcquired = false;
 }
 
@@ -1462,7 +1496,29 @@ void WinSalFrame::ImplSetParentFrame( HWND 
hNewParentWnd, bool bAsChild )
     }
 
     // to recreate the DCs, if they were destroyed
-    bool bHadLocalGraphics = false;
+    bool bHadLocalGraphics = false, bHadThreadGraphics = false;
+
+    HFONT   hFont   = nullptr;
+    HPEN    hPen    = nullptr;
+    HBRUSH  hBrush  = nullptr;
+
+    int oldCount = pSalData->mnCacheDCInUse;
+
+    // release the thread DC
+    if ( mpThreadGraphics )
+    {
+        // save current gdi objects before hdc is gone
+        HDC hDC = mpThreadGraphics->getHDC();
+        if ( hDC )
+        {
+            hFont  = static_cast<HFONT>(GetCurrentObject( hDC, OBJ_FONT ));
+            hPen   = static_cast<HPEN>(GetCurrentObject( hDC, OBJ_PEN ));
+            hBrush = static_cast<HBRUSH>(GetCurrentObject( hDC, OBJ_BRUSH ));
+        }
+
+        bHadThreadGraphics = ReleaseFrameGraphicsDC( mpThreadGraphics );
+        assert( (bHadThreadGraphics && hDC) || (!bHadThreadGraphics && !hDC) );
+    }
 
     // release the local DC
     if ( mpLocalGraphics )
@@ -1478,6 +1534,27 @@ void WinSalFrame::ImplSetParentFrame( HWND 
hNewParentWnd, bool bAsChild )
     // succeeded ?
     SAL_WARN_IF( !IsWindow( hWnd ), "vcl", "WinSalFrame::SetParent not 
successful");
 
+    // re-create thread DC
+    if( bHadThreadGraphics )
+    {
+        HDC hDC = reinterpret_cast<HDC>(static_cast<sal_IntPtr>(
+                    SendMessageW( pSalData->mpInstance->mhComWnd,
+                        SAL_MSG_GETCACHEDDC, reinterpret_cast<WPARAM>(hWnd), 0 
)));
+        InitFrameGraphicsDC( mpThreadGraphics, hDC, hWnd );
+        if ( hDC )
+        {
+            // re-select saved gdi objects
+            if( hFont )
+                SelectObject( hDC, hFont );
+            if( hPen )
+                SelectObject( hDC, hPen );
+            if( hBrush )
+                SelectObject( hDC, hBrush );
+
+            SAL_WARN_IF( oldCount != pSalData->mnCacheDCInUse, "vcl", 
"WinSalFrame::SetParent() hDC count corrupted");
+        }
+    }
+
     // re-create local DC
     if( bHadLocalGraphics )
         InitFrameGraphicsDC( mpLocalGraphics, GetDC( hWnd ), hWnd );
@@ -2111,6 +2188,8 @@ void WinSalFrame::Flush()
 {
     if(mpLocalGraphics)
         mpLocalGraphics->Flush();
+    if(mpThreadGraphics)
+        mpThreadGraphics->Flush();
     GdiFlush();
 }
 

Reply via email to