summaryrefslogtreecommitdiffstats
path: root/vcl/win/app/salinst.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/win/app/salinst.cxx')
-rw-r--r--vcl/win/app/salinst.cxx1095
1 files changed, 1095 insertions, 0 deletions
diff --git a/vcl/win/app/salinst.cxx b/vcl/win/app/salinst.cxx
new file mode 100644
index 000000000..5ebb22226
--- /dev/null
+++ b/vcl/win/app/salinst.cxx
@@ -0,0 +1,1095 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+#include <svsys.h>
+#include <process.h>
+
+#include <osl/conditn.hxx>
+#include <osl/file.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/time.hxx>
+#include <comphelper/solarmutex.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <vcl/inputtypes.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <vcl/opengl/OpenGLContext.hxx>
+#include <vcl/timer.hxx>
+#include <vclpluginapi.h>
+
+#include <opengl/salbmp.hxx>
+#include <opengl/win/gdiimpl.hxx>
+#include <win/wincomp.hxx>
+#include <win/salids.hrc>
+#include <win/saldata.hxx>
+#include <win/salinst.h>
+#include <win/salframe.h>
+#include <win/salobj.h>
+#include <win/saltimer.h>
+#include <win/salbmp.h>
+#include <win/winlayout.hxx>
+
+#include <config_features.h>
+#include <vcl/skia/SkiaHelper.hxx>
+#if HAVE_FEATURE_SKIA
+#include <config_skia.h>
+#include <skia/salbmp.hxx>
+#include <skia/win/gdiimpl.hxx>
+#endif
+
+#include <salsys.hxx>
+
+#include <desktop/crashreport.hxx>
+
+#if defined _MSC_VER
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#endif
+
+#include <prewin.h>
+
+#include <gdiplus.h>
+#include <shlobj.h>
+
+#include <postwin.h>
+
+void SalAbort( const OUString& rErrorText, bool )
+{
+ ImplFreeSalGDI();
+
+ if ( rErrorText.isEmpty() )
+ {
+ // make sure crash reporter is triggered
+ RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
+ FatalAppExitW( 0, L"Application Error" );
+ }
+ else
+ {
+ CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
+ // make sure crash reporter is triggered
+ RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
+ FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) );
+ }
+}
+
+static LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam );
+
+class SalYieldMutex : public comphelper::SolarMutex
+{
+public: // for ImplSalYield() and ImplSalYieldMutexAcquireWithWait()
+ osl::Condition m_condition; /// for MsgWaitForMultipleObjects()
+
+protected:
+ virtual void doAcquire( sal_uInt32 nLockCount ) override;
+ virtual sal_uInt32 doRelease( bool bUnlockAll ) override;
+
+ static void BeforeReleaseHandler();
+
+public:
+ explicit SalYieldMutex();
+
+ virtual bool IsCurrentThread() const override;
+ virtual bool tryToAcquire() override;
+};
+
+SalYieldMutex::SalYieldMutex()
+{
+ SetBeforeReleaseHandler( &SalYieldMutex::BeforeReleaseHandler );
+}
+
+void SalYieldMutex::BeforeReleaseHandler()
+{
+ OpenGLContext::prepareForYield();
+
+ if ( GetSalData()->mnAppThreadId != GetCurrentThreadId() )
+ {
+ // If we don't call these message, the Output from the
+ // Java clients doesn't come in the right order
+ GdiFlush();
+ }
+}
+
+/// note: while VCL is fully up and running (other threads started and
+/// before shutdown), the main thread must acquire SolarMutex only via
+/// this function to avoid deadlock
+void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst && pInst->IsMainThread() )
+ {
+ if ( pInst->m_nNoYieldLock )
+ return;
+ // tdf#96887 If this is the main thread, then we must wait for two things:
+ // - the yield mutex being unlocked
+ // - SendMessage() being triggered
+ // This can nicely be done using MsgWaitForMultipleObjects. The 2nd one is
+ // needed because if we don't reschedule, then we create deadlocks if a
+ // Window's create/destroy is called via SendMessage() from another thread.
+ // Have a look at the osl_waitCondition implementation for more info.
+ do {
+ // reset condition *before* acquiring!
+ m_condition.reset();
+ if (m_aMutex.tryToAcquire())
+ break;
+ // wait for SalYieldMutex::release() to set the condition
+ osl::Condition::Result res = m_condition.wait();
+ assert(osl::Condition::Result::result_ok == res);
+ }
+ while ( true );
+ }
+ else
+ m_aMutex.acquire();
+ ++m_nCount;
+ --nLockCount;
+
+ comphelper::SolarMutex::doAcquire( nLockCount );
+}
+
+sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll )
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst && pInst->m_nNoYieldLock && pInst->IsMainThread() )
+ return 1;
+
+ sal_uInt32 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
+ // wake up ImplSalYieldMutexAcquireWithWait() after release
+ if ( 0 == m_nCount )
+ m_condition.set();
+ return nCount;
+}
+
+bool SalYieldMutex::tryToAcquire()
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst )
+ {
+ if ( pInst->m_nNoYieldLock && pInst->IsMainThread() )
+ return true;
+ else
+ return comphelper::SolarMutex::tryToAcquire();
+ }
+ else
+ return false;
+}
+
+void ImplSalYieldMutexAcquireWithWait( sal_uInt32 nCount )
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst )
+ pInst->GetYieldMutex()->acquire( nCount );
+}
+
+bool ImplSalYieldMutexTryToAcquire()
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ return pInst && pInst->GetYieldMutex()->tryToAcquire();
+}
+
+void ImplSalYieldMutexRelease()
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst )
+ {
+ GdiFlush();
+ pInst->GetYieldMutex()->release();
+ }
+}
+
+bool SalYieldMutex::IsCurrentThread() const
+{
+ if ( !GetSalData()->mpInstance->m_nNoYieldLock )
+ return SolarMutex::IsCurrentThread();
+ else
+ return GetSalData()->mpInstance->IsMainThread();
+}
+
+void SalData::initKeyCodeMap()
+{
+ UINT nKey;
+ #define initKey( a, b )\
+ nKey = LOWORD( VkKeyScanW( a ) );\
+ if( nKey < 0xffff )\
+ maVKMap[ nKey ] = b;
+
+ maVKMap.clear();
+
+ initKey( L'+', KEY_ADD );
+ initKey( L'-', KEY_SUBTRACT );
+ initKey( L'*', KEY_MULTIPLY );
+ initKey( L'/', KEY_DIVIDE );
+ initKey( L'.', KEY_POINT );
+ initKey( L',', KEY_COMMA );
+ initKey( L'<', KEY_LESS );
+ initKey( L'>', KEY_GREATER );
+ initKey( L'=', KEY_EQUAL );
+ initKey( L'~', KEY_TILDE );
+ initKey( L'`', KEY_QUOTELEFT );
+ initKey( L'[', KEY_BRACKETLEFT );
+ initKey( L']', KEY_BRACKETRIGHT );
+ initKey( L';', KEY_SEMICOLON );
+ initKey( L'\'', KEY_QUOTERIGHT );
+}
+
+// SalData
+
+SalData::SalData()
+{
+ mhInst = nullptr; // default instance handle
+ mnCmdShow = 0; // default frame show style
+ mhDitherPal = nullptr; // dither palette
+ mhDitherDIB = nullptr; // dither memory handle
+ mpDitherDIB = nullptr; // dither memory
+ mpDitherDIBData = nullptr; // beginning of DIB data
+ mpDitherDiff = nullptr; // Dither mapping table
+ mpDitherLow = nullptr; // Dither mapping table
+ mpDitherHigh = nullptr; // Dither mapping table
+ mhSalObjMsgHook = nullptr; // hook to get interesting msg for SalObject
+ mhWantLeaveMsg = nullptr; // window handle, that want a MOUSELEAVE message
+ mpMouseLeaveTimer = nullptr; // Timer for MouseLeave Test
+ mpInstance = nullptr; // pointer of first instance
+ mpFirstFrame = nullptr; // pointer of first frame
+ mpFirstObject = nullptr; // pointer of first object window
+ mpFirstVD = nullptr; // first VirDev
+ mpFirstPrinter = nullptr; // first printing printer
+ mpHDCCache = nullptr; // Cache for three DC's
+ mh50Bmp = nullptr; // 50% Bitmap
+ mh50Brush = nullptr; // 50% Brush
+ int i;
+ for(i=0; i<MAX_STOCKPEN; i++)
+ {
+ maStockPenColorAry[i] = 0;
+ mhStockPenAry[i] = nullptr;
+ }
+ for(i=0; i<MAX_STOCKBRUSH; i++)
+ {
+ maStockBrushColorAry[i] = 0;
+ mhStockBrushAry[i] = nullptr;
+ }
+ 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
+ mbInPalChange = false; // is in WM_QUERYNEWPALETTE
+ mnAppThreadId = 0; // Id from Application-Thread
+ mbScrSvrEnabled = FALSE; // ScreenSaver enabled
+ mpFirstIcon = nullptr; // icon cache, points to first icon, NULL if none
+ mpSharedTempFontItem = nullptr;
+ mpOtherTempFontItem = nullptr;
+ mbThemeChanged = false; // true if visual theme was changed: throw away theme handles
+ mbThemeMenuSupport = false;
+
+ // init with NULL
+ gdiplusToken = 0;
+
+ initKeyCodeMap();
+
+ SetSalData( this );
+ initNWF();
+
+ CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); // put main thread in Single Threaded Apartment (STA)
+ static Gdiplus::GdiplusStartupInput gdiplusStartupInput;
+ Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
+}
+
+SalData::~SalData()
+{
+ deInitNWF();
+ SetSalData( nullptr );
+
+ CoUninitialize();
+
+ if (gdiplusToken)
+ Gdiplus::GdiplusShutdown(gdiplusToken);
+}
+
+extern "C" {
+VCLPLUG_WIN_PUBLIC SalInstance* create_SalInstance()
+{
+ SalData* pSalData = new SalData();
+
+ STARTUPINFOW aSI;
+ aSI.cb = sizeof( aSI );
+ GetStartupInfoW( &aSI );
+ pSalData->mhInst = GetModuleHandleW( nullptr );
+ pSalData->mnCmdShow = aSI.wShowWindow;
+
+ pSalData->mnAppThreadId = GetCurrentThreadId();
+
+ // register frame class
+ WNDCLASSEXW aWndClassEx;
+ aWndClassEx.cbSize = sizeof( aWndClassEx );
+ aWndClassEx.style = CS_OWNDC;
+ aWndClassEx.lpfnWndProc = SalFrameWndProcW;
+ aWndClassEx.cbClsExtra = 0;
+ aWndClassEx.cbWndExtra = SAL_FRAME_WNDEXTRA;
+ aWndClassEx.hInstance = pSalData->mhInst;
+ aWndClassEx.hCursor = nullptr;
+ aWndClassEx.hbrBackground = nullptr;
+ aWndClassEx.lpszMenuName = nullptr;
+ aWndClassEx.lpszClassName = SAL_FRAME_CLASSNAMEW;
+ ImplLoadSalIcon( SAL_RESID_ICON_DEFAULT, aWndClassEx.hIcon, aWndClassEx.hIconSm );
+ if ( !RegisterClassExW( &aWndClassEx ) )
+ return nullptr;
+
+ aWndClassEx.hIcon = nullptr;
+ aWndClassEx.hIconSm = nullptr;
+ aWndClassEx.style |= CS_SAVEBITS;
+ aWndClassEx.lpszClassName = SAL_SUBFRAME_CLASSNAMEW;
+ if ( !RegisterClassExW( &aWndClassEx ) )
+ return nullptr;
+
+ // shadow effect for popups on XP
+ aWndClassEx.style |= CS_DROPSHADOW;
+ aWndClassEx.lpszClassName = SAL_TMPSUBFRAME_CLASSNAMEW;
+ if ( !RegisterClassExW( &aWndClassEx ) )
+ return nullptr;
+
+ aWndClassEx.style = 0;
+ aWndClassEx.lpfnWndProc = SalComWndProcW;
+ aWndClassEx.cbWndExtra = 0;
+ aWndClassEx.lpszClassName = SAL_COM_CLASSNAMEW;
+ if ( !RegisterClassExW( &aWndClassEx ) )
+ return nullptr;
+
+ HWND hComWnd = CreateWindowExW( WS_EX_TOOLWINDOW, SAL_COM_CLASSNAMEW,
+ L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
+ pSalData->mhInst, nullptr );
+ if ( !hComWnd )
+ return nullptr;
+
+ WinSalInstance* pInst = new WinSalInstance;
+
+ // init instance (only one instance in this version !!!)
+ pSalData->mpInstance = pInst;
+ pInst->mhInst = pSalData->mhInst;
+ pInst->mhComWnd = hComWnd;
+
+ // init static GDI Data
+ ImplInitSalGDI();
+
+ return pInst;
+}
+}
+
+WinSalInstance::WinSalInstance()
+ : SalInstance(std::make_unique<SalYieldMutex>())
+ , mhInst( nullptr )
+ , mhComWnd( nullptr )
+ , m_nNoYieldLock( 0 )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ pSVData->maAppData.mxToolkitName = OUString("win");
+#if HAVE_FEATURE_SKIA
+ WinSkiaSalGraphicsImpl::prepareSkia();
+#endif
+}
+
+WinSalInstance::~WinSalInstance()
+{
+ ImplFreeSalGDI();
+ DestroyWindow( mhComWnd );
+#if HAVE_FEATURE_SKIA
+ SkiaHelper::cleanup();
+#endif
+}
+
+static LRESULT ImplSalDispatchMessage( const MSG* pMsg )
+{
+ SalData* pSalData = GetSalData();
+ if ( pSalData->mpFirstObject && ImplSalPreDispatchMsg( pMsg ) )
+ return 0;
+ LRESULT lResult = DispatchMessageW( pMsg );
+ if ( pSalData->mpFirstObject )
+ ImplSalPostDispatchMsg( pMsg );
+ return lResult;
+}
+
+bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
+{
+ static sal_uInt32 nLastTicks = 0;
+ MSG aMsg;
+ bool bWasMsg = false, bOneEvent = false, bWasTimeoutMsg = false;
+ ImplSVData *const pSVData = ImplGetSVData();
+ WinSalTimer* pTimer = static_cast<WinSalTimer*>( pSVData->maSchedCtx.mpSalTimer );
+ const bool bNoYieldLock = (GetSalData()->mpInstance->m_nNoYieldLock > 0);
+
+ assert( !bNoYieldLock );
+ if ( bNoYieldLock )
+ return false;
+
+ sal_uInt32 nCurTicks = 0;
+ if ( bHandleAllCurrentEvents )
+ nCurTicks = GetTickCount();
+
+ bool bHadNewerEvent = false;
+ do
+ {
+ bOneEvent = PeekMessageW( &aMsg, nullptr, 0, 0, PM_REMOVE );
+ if ( bOneEvent )
+ {
+ bWasMsg = true;
+ TranslateMessage( &aMsg );
+ LRESULT nRet = ImplSalDispatchMessage( &aMsg );
+
+ if ( !bWasTimeoutMsg )
+ bWasTimeoutMsg = (SAL_MSG_TIMER_CALLBACK == aMsg.message)
+ && static_cast<bool>( nRet );
+
+ if ( bHandleAllCurrentEvents
+ && !bHadNewerEvent && aMsg.time > nCurTicks
+ && (nLastTicks <= nCurTicks || aMsg.time < nLastTicks) )
+ bHadNewerEvent = true;
+ bOneEvent = !bHadNewerEvent;
+ }
+
+ if ( !(bHandleAllCurrentEvents && bOneEvent) )
+ break;
+ }
+ while( true );
+
+ // 0ms timeouts are handled out-of-bounds to prevent busy-locking the
+ // event loop with timeout messages.
+ // We ensure we never handle more than one timeout per call.
+ // This way we'll always process a normal system message.
+ if ( !bWasTimeoutMsg && pTimer && pTimer->IsDirectTimeout() )
+ {
+ pTimer->ImplHandleElapsedTimer();
+ bWasMsg = true;
+ }
+
+ if ( bHandleAllCurrentEvents )
+ nLastTicks = nCurTicks;
+
+ if ( bWait && !bWasMsg )
+ {
+ if ( GetMessageW( &aMsg, nullptr, 0, 0 ) )
+ {
+ bWasMsg = true;
+ TranslateMessage( &aMsg );
+ ImplSalDispatchMessage( &aMsg );
+ }
+ }
+
+ // we're back in the main loop after resize or move
+ if ( pTimer )
+ pTimer->SetForceRealTimer( false );
+
+ return bWasMsg;
+}
+
+bool WinSalInstance::IsMainThread() const
+{
+ const SalData* pSalData = GetSalData();
+ return pSalData->mnAppThreadId == GetCurrentThreadId();
+}
+
+bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
+{
+ bool bDidWork = false;
+ SolarMutexReleaser aReleaser;
+ if ( !IsMainThread() )
+ {
+ bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD,
+ WPARAM(false), static_cast<LPARAM>(bHandleAllCurrentEvents) );
+ if ( !bDidWork && bWait )
+ {
+ maWaitingYieldCond.reset();
+ maWaitingYieldCond.wait();
+ bDidWork = true;
+ }
+ }
+ else
+ {
+ bDidWork = ImplSalYield( bWait, bHandleAllCurrentEvents );
+ if ( bDidWork )
+ maWaitingYieldCond.set();
+ }
+
+ return bDidWork;
+}
+
+#define CASE_NOYIELDLOCK( salmsg, function ) \
+ case salmsg: \
+ if (bIsOtherThreadMessage) \
+ { \
+ ++pInst->m_nNoYieldLock; \
+ function; \
+ --pInst->m_nNoYieldLock; \
+ } \
+ else \
+ { \
+ DBG_TESTSOLARMUTEX(); \
+ function; \
+ } \
+ break;
+
+#define CASE_NOYIELDLOCK_RESULT( salmsg, function ) \
+ case salmsg: \
+ if (bIsOtherThreadMessage) \
+ { \
+ ++pInst->m_nNoYieldLock; \
+ nRet = reinterpret_cast<LRESULT>( function ); \
+ --pInst->m_nNoYieldLock; \
+ } \
+ else \
+ { \
+ DBG_TESTSOLARMUTEX(); \
+ nRet = reinterpret_cast<LRESULT>( function ); \
+ } \
+ break;
+
+LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, bool& rDef )
+{
+ const bool bIsOtherThreadMessage = InSendMessage();
+ LRESULT nRet = 0;
+ WinSalInstance *pInst = GetSalData()->mpInstance;
+ WinSalTimer *const pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
+
+ SAL_INFO("vcl.gdi.wndproc", "SalComWndProc(nMsg=" << nMsg << ", wParam=" << wParam
+ << ", lParam=" << lParam << "); inSendMsg: " << bIsOtherThreadMessage);
+
+ switch ( nMsg )
+ {
+ case SAL_MSG_THREADYIELD:
+ assert( !static_cast<bool>(wParam) );
+ nRet = static_cast<LRESULT>(ImplSalYield(
+ false, static_cast<bool>( lParam ) ));
+ break;
+
+ case SAL_MSG_STARTTIMER:
+ {
+ auto const nParam = static_cast<sal_uInt64>( lParam );
+ sal_uInt64 nTime = tools::Time::GetSystemTicks();
+ if ( nTime < nParam )
+ nTime = nParam - nTime;
+ else
+ nTime = 0;
+ assert( pTimer != nullptr );
+ pTimer->ImplStart( nTime );
+ break;
+ }
+
+ case SAL_MSG_STOPTIMER:
+ assert( pTimer != nullptr );
+ pTimer->ImplStop();
+ break;
+
+ CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEFRAME, ImplSalCreateFrame( GetSalData()->mpInstance,
+ reinterpret_cast<HWND>(lParam), static_cast<SalFrameStyleFlags>(wParam)) )
+ CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATEHWND, ImplSalReCreateHWND(
+ reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), false) )
+ CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATECHILDHWND, ImplSalReCreateHWND(
+ reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), true) )
+ CASE_NOYIELDLOCK( SAL_MSG_DESTROYFRAME, delete reinterpret_cast<SalFrame*>(lParam) )
+
+ case SAL_MSG_DESTROYHWND:
+ // We only destroy the native window here. We do NOT destroy the SalFrame contained
+ // in the structure (GetWindowPtr()).
+ if (DestroyWindow(reinterpret_cast<HWND>(lParam)) == 0)
+ {
+ OSL_FAIL("DestroyWindow failed!");
+ // Failure: We remove the SalFrame from the window structure. So we avoid that
+ // the window structure may contain an invalid pointer, once the SalFrame is deleted.
+ SetWindowPtr(reinterpret_cast<HWND>(lParam), nullptr);
+ }
+ break;
+
+ CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEOBJECT, ImplSalCreateObject(
+ GetSalData()->mpInstance, reinterpret_cast<WinSalFrame*>(lParam)) )
+ CASE_NOYIELDLOCK( SAL_MSG_DESTROYOBJECT, delete reinterpret_cast<SalObject*>(lParam) )
+ CASE_NOYIELDLOCK_RESULT( SAL_MSG_GETCACHEDDC, GetDCEx(
+ reinterpret_cast<HWND>(wParam), nullptr, DCX_CACHE) )
+ CASE_NOYIELDLOCK( SAL_MSG_RELEASEDC, ReleaseDC(
+ reinterpret_cast<HWND>(wParam), reinterpret_cast<HDC>(lParam)) )
+
+ case SAL_MSG_TIMER_CALLBACK:
+ assert( pTimer != nullptr );
+ pTimer->ImplHandleTimerEvent( wParam );
+ break;
+
+ case WM_TIMER:
+ assert( pTimer != nullptr );
+ pTimer->ImplHandle_WM_TIMER( wParam );
+ break;
+
+ case SAL_MSG_FORCE_REAL_TIMER:
+ assert(pTimer != nullptr);
+ pTimer->SetForceRealTimer(true);
+ break;
+
+ case SAL_MSG_DUMMY:
+ break;
+
+ default:
+ rDef = true;
+ break;
+ }
+
+ return nRet;
+}
+
+#undef CASE_NOYIELDLOCK
+#undef CASE_NOYIELDLOCK_RESULT
+
+LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
+{
+ bool bDef = false;
+ LRESULT nRet = 0;
+ __try
+ {
+ nRet = SalComWndProc( hWnd, nMsg, wParam, lParam, bDef );
+ }
+ __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
+ {
+ }
+ if ( bDef )
+ {
+ if ( !ImplHandleGlobalMsg( hWnd, nMsg, wParam, lParam, nRet ) )
+ nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam );
+ }
+ return nRet;
+}
+
+namespace {
+
+struct MsgRange
+{
+ UINT nStart;
+ UINT nEnd;
+};
+
+}
+
+static std::vector<MsgRange> GetOtherRanges( VclInputFlags nType )
+{
+ assert( nType != VCL_INPUT_ANY );
+
+ // this array must be kept sorted!
+ const UINT nExcludeMsgIds[] =
+ {
+ 0,
+
+ WM_MOVE, // 3
+ WM_SIZE, // 5
+ WM_PAINT, // 15
+ WM_KEYDOWN, // 256
+ WM_TIMER, // 275
+
+ WM_MOUSEFIRST, // 512
+ 513,
+ 514,
+ 515,
+ 516,
+ 517,
+ 518,
+ 519,
+ 520,
+ WM_MOUSELAST, // 521
+
+ SAL_MSG_POSTMOVE, // WM_USER+136
+ SAL_MSG_POSTCALLSIZE, // WM_USER+137
+
+ SAL_MSG_TIMER_CALLBACK, // WM_USER+162
+
+ UINT_MAX
+ };
+ const unsigned MAX_EXCL = SAL_N_ELEMENTS( nExcludeMsgIds );
+
+ bool aExcludeMsgList[ MAX_EXCL ] = { false, };
+ std::vector<MsgRange> aResult;
+
+ // set the excluded values
+ if ( !(nType & VclInputFlags::MOUSE) )
+ {
+ for ( unsigned i = 0; nExcludeMsgIds[ 6 + i ] <= WM_MOUSELAST; ++i )
+ aExcludeMsgList[ 6 + i ] = true;
+ }
+
+ if ( !(nType & VclInputFlags::KEYBOARD) )
+ aExcludeMsgList[ 4 ] = true;
+
+ if ( !(nType & VclInputFlags::PAINT) )
+ {
+ aExcludeMsgList[ 1 ] = true;
+ aExcludeMsgList[ 2 ] = true;
+ aExcludeMsgList[ 3 ] = true;
+ aExcludeMsgList[ 16 ] = true;
+ aExcludeMsgList[ 17 ] = true;
+ }
+
+ if ( !(nType & VclInputFlags::TIMER) )
+ {
+ aExcludeMsgList[ 5 ] = true;
+ aExcludeMsgList[ 18 ] = true;
+ }
+
+ // build the message ranges to check
+ MsgRange aRange = { 0, 0 };
+ bool doEnd = true;
+ for ( unsigned i = 1; i < MAX_EXCL; ++i )
+ {
+ if ( aExcludeMsgList[ i ] )
+ {
+ if ( !doEnd )
+ {
+ if ( nExcludeMsgIds[ i ] == aRange.nStart )
+ ++aRange.nStart;
+ else
+ doEnd = true;
+ }
+ if ( doEnd )
+ {
+ aRange.nEnd = nExcludeMsgIds[ i ] - 1;
+ aResult.push_back( aRange );
+ doEnd = false;
+ aRange.nStart = aRange.nEnd + 2;
+ }
+ }
+ }
+
+ if ( aRange.nStart != UINT_MAX )
+ {
+ aRange.nEnd = UINT_MAX;
+ aResult.push_back( aRange );
+ }
+
+ return aResult;
+}
+
+bool WinSalInstance::AnyInput( VclInputFlags nType )
+{
+ MSG aMsg;
+
+ if ( nType & VclInputFlags::TIMER )
+ {
+ const WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
+ if ( pTimer && pTimer->HasTimerElapsed() )
+ return true;
+ }
+
+ if ( (nType & VCL_INPUT_ANY) == VCL_INPUT_ANY )
+ {
+ // revert bugfix for #108919# which never reported timeouts when called from the timer handler
+ // which made the application completely unresponsive during background formatting
+ if ( PeekMessageW( &aMsg, nullptr, 0, 0, PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+ }
+ else
+ {
+ const bool bCheck_KEYBOARD (nType & VclInputFlags::KEYBOARD);
+ const bool bCheck_OTHER (nType & VclInputFlags::OTHER);
+
+ // If there is a modifier key event, it counts as OTHER
+ // Previously we were simply ignoring these events...
+ if ( bCheck_KEYBOARD || bCheck_OTHER )
+ {
+ if ( PeekMessageW( &aMsg, nullptr, WM_KEYDOWN, WM_KEYDOWN,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ {
+ const bool bIsModifier = ( (aMsg.wParam == VK_SHIFT) ||
+ (aMsg.wParam == VK_CONTROL) || (aMsg.wParam == VK_MENU) );
+ if ( bCheck_KEYBOARD && !bIsModifier )
+ return true;
+ if ( bCheck_OTHER && bIsModifier )
+ return true;
+ }
+ }
+
+ // Other checks for all messages not excluded.
+ // The less we exclude, the less ranges have to be checked!
+ if ( bCheck_OTHER )
+ {
+ // TIMER and KEYBOARD are already handled, so always exclude them!
+ VclInputFlags nOtherType = nType &
+ ~VclInputFlags(VclInputFlags::KEYBOARD | VclInputFlags::TIMER);
+
+ std::vector<MsgRange> aMsgRangeList( GetOtherRanges( nOtherType ) );
+ for ( MsgRange const & aRange : aMsgRangeList )
+ if ( PeekMessageW( &aMsg, nullptr, aRange.nStart,
+ aRange.nEnd, PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+
+ // MOUSE and PAINT already handled, so skip further checks
+ return false;
+ }
+
+ if ( nType & VclInputFlags::MOUSE )
+ {
+ if ( PeekMessageW( &aMsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+ }
+
+ if ( nType & VclInputFlags::PAINT )
+ {
+ if ( PeekMessageW( &aMsg, nullptr, WM_PAINT, WM_PAINT,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+
+ if ( PeekMessageW( &aMsg, nullptr, WM_SIZE, WM_SIZE,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+
+ if ( PeekMessageW( &aMsg, nullptr, SAL_MSG_POSTCALLSIZE, SAL_MSG_POSTCALLSIZE,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+
+ if ( PeekMessageW( &aMsg, nullptr, WM_MOVE, WM_MOVE,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+
+ if ( PeekMessageW( &aMsg, nullptr, SAL_MSG_POSTMOVE, SAL_MSG_POSTMOVE,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+SalFrame* WinSalInstance::CreateChildFrame( SystemParentData* pSystemParentData, SalFrameStyleFlags nSalFrameStyle )
+{
+ // to switch to Main-Thread
+ return reinterpret_cast<SalFrame*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEFRAME, static_cast<WPARAM>(nSalFrameStyle), reinterpret_cast<LPARAM>(pSystemParentData->hWnd) )));
+}
+
+SalFrame* WinSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle )
+{
+ // to switch to Main-Thread
+ HWND hWndParent;
+ if ( pParent )
+ hWndParent = static_cast<WinSalFrame*>(pParent)->mhWnd;
+ else
+ hWndParent = nullptr;
+ return reinterpret_cast<SalFrame*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEFRAME, static_cast<WPARAM>(nSalFrameStyle), reinterpret_cast<LPARAM>(hWndParent) )));
+}
+
+void WinSalInstance::DestroyFrame( SalFrame* pFrame )
+{
+ OpenGLContext::prepareForYield();
+ SendMessageW( mhComWnd, SAL_MSG_DESTROYFRAME, 0, reinterpret_cast<LPARAM>(pFrame) );
+}
+
+SalObject* WinSalInstance::CreateObject( SalFrame* pParent,
+ SystemWindowData* /*pWindowData*/, // SystemWindowData meaningless on Windows
+ bool /*bShow*/ )
+{
+ // to switch to Main-Thread
+ return reinterpret_cast<SalObject*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEOBJECT, 0, reinterpret_cast<LPARAM>(static_cast<WinSalFrame*>(pParent)) )));
+}
+
+void WinSalInstance::DestroyObject( SalObject* pObject )
+{
+ SendMessageW( mhComWnd, SAL_MSG_DESTROYOBJECT, 0, reinterpret_cast<LPARAM>(pObject) );
+}
+
+OUString WinSalInstance::GetConnectionIdentifier()
+{
+ return OUString();
+}
+
+/** Add a file to the system shells recent document list if there is any.
+ This function may have no effect under Unix because there is no
+ standard API among the different desktop managers.
+
+ @param aFileUrl
+ The file url of the document.
+*/
+void WinSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& rDocumentService)
+{
+ if (Application::IsHeadlessModeEnabled())
+ return;
+
+ OUString system_path;
+ osl::FileBase::RC rc = osl::FileBase::getSystemPathFromFileURL(rFileUrl, system_path);
+
+ OSL_ENSURE(osl::FileBase::E_None == rc, "Invalid file url");
+
+ if (osl::FileBase::E_None == rc)
+ {
+ IShellItem* pShellItem = nullptr;
+
+ HRESULT hr = SHCreateItemFromParsingName(o3tl::toW(system_path.getStr()), nullptr, IID_PPV_ARGS(&pShellItem));
+
+ if ( SUCCEEDED(hr) && pShellItem )
+ {
+ OUString sApplicationName;
+
+ if ( rDocumentService == "com.sun.star.text.TextDocument" ||
+ rDocumentService == "com.sun.star.text.GlobalDocument" ||
+ rDocumentService == "com.sun.star.text.WebDocument" ||
+ rDocumentService == "com.sun.star.xforms.XMLFormDocument" )
+ sApplicationName = "Writer";
+ else if ( rDocumentService == "com.sun.star.sheet.SpreadsheetDocument" ||
+ rDocumentService == "com.sun.star.chart2.ChartDocument" )
+ sApplicationName = "Calc";
+ else if ( rDocumentService == "com.sun.star.presentation.PresentationDocument" )
+ sApplicationName = "Impress";
+ else if ( rDocumentService == "com.sun.star.drawing.DrawingDocument" )
+ sApplicationName = "Draw";
+ else if ( rDocumentService == "com.sun.star.formula.FormulaProperties" )
+ sApplicationName = "Math";
+ else if ( rDocumentService == "com.sun.star.sdb.DatabaseDocument" ||
+ rDocumentService == "com.sun.star.sdb.OfficeDatabaseDocument" ||
+ rDocumentService == "com.sun.star.sdb.RelationDesign" ||
+ rDocumentService == "com.sun.star.sdb.QueryDesign" ||
+ rDocumentService == "com.sun.star.sdb.TableDesign" ||
+ rDocumentService == "com.sun.star.sdb.DataSourceBrowser" )
+ sApplicationName = "Base";
+
+ if ( !sApplicationName.isEmpty() )
+ {
+ OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplicationName);
+
+ SHARDAPPIDINFO info;
+ info.psi = pShellItem;
+ info.pszAppID = o3tl::toW(sApplicationID.getStr());
+
+ SHAddToRecentDocs ( SHARD_APPIDINFO, &info );
+ return;
+ }
+ }
+ // For whatever reason, we could not use the SHARD_APPIDINFO semantics
+ SHAddToRecentDocs(SHARD_PATHW, system_path.getStr());
+ }
+}
+
+SalTimer* WinSalInstance::CreateSalTimer()
+{
+ return new WinSalTimer();
+}
+
+std::shared_ptr<SalBitmap> WinSalInstance::CreateSalBitmap()
+{
+#if HAVE_FEATURE_SKIA
+ if (SkiaHelper::isVCLSkiaEnabled())
+ return std::make_shared<SkiaSalBitmap>();
+ else
+#endif
+ if (OpenGLHelper::isVCLOpenGLEnabled())
+ return std::make_shared<OpenGLSalBitmap>();
+ else
+ return std::make_shared<WinSalBitmap>();
+}
+
+int WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(int, LPEXCEPTION_POINTERS pExceptionInfo)
+{
+ // Decide if an exception is a c++ (mostly UNO) exception or a process violation.
+ // Depending on this information we pass process violations directly to our signal handler ...
+ // and c++ (UNO) exceptions are sended to the following code on the current stack.
+ // Problem behind: user32.dll sometime consumes exceptions/process violations .-)
+ // see also #112221#
+
+ static const DWORD EXCEPTION_MSC_CPP_EXCEPTION = 0xE06D7363;
+
+ if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_MSC_CPP_EXCEPTION)
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ return UnhandledExceptionFilter( pExceptionInfo );
+}
+
+typedef LONG NTSTATUS;
+typedef NTSTATUS(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
+constexpr NTSTATUS STATUS_SUCCESS = 0x00000000;
+
+OUString WinSalInstance::getOSVersion()
+{
+ OUStringBuffer aVer(50); // capacity for string like "Windows 6.1 Service Pack 1 build 7601"
+ aVer.append("Windows ");
+ // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
+ // subject to manifest-based behavior since Windows 8.1, so give wrong results.
+ // Another approach would be to use NetWkstaGetInfo, but that has some small
+ // reported delays (some milliseconds), and might get slower in domains with
+ // poor network connections.
+ // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
+ bool bHaveVerFromKernel32 = false;
+ if (HMODULE h_kernel32 = GetModuleHandleW(L"kernel32.dll"))
+ {
+ wchar_t szPath[MAX_PATH];
+ DWORD dwCount = GetModuleFileNameW(h_kernel32, szPath, SAL_N_ELEMENTS(szPath));
+ if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath))
+ {
+ dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
+ if (dwCount != 0)
+ {
+ std::unique_ptr<char[]> ver(new char[dwCount]);
+ if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
+ {
+ void* pBlock = nullptr;
+ UINT dwBlockSz = 0;
+ if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
+ {
+ VS_FIXEDFILEINFO* vi1 = static_cast<VS_FIXEDFILEINFO*>(pBlock);
+ aVer.append(OUString::number(HIWORD(vi1->dwProductVersionMS)) + "."
+ + OUString::number(LOWORD(vi1->dwProductVersionMS)));
+ bHaveVerFromKernel32 = true;
+ }
+ }
+ }
+ }
+ }
+ // Now use RtlGetVersion (which is not subject to deprecation for GetVersion(Ex) API)
+ // to get build number and SP info
+ bool bHaveVerFromRtlGetVersion = false;
+ if (HMODULE h_ntdll = GetModuleHandleW(L"ntdll.dll"))
+ {
+ if (auto RtlGetVersion
+ = reinterpret_cast<RtlGetVersion_t>(GetProcAddress(h_ntdll, "RtlGetVersion")))
+ {
+ RTL_OSVERSIONINFOW vi2{}; // initialize with zeroes - a better alternative to memset
+ vi2.dwOSVersionInfoSize = sizeof(vi2);
+ if (STATUS_SUCCESS == RtlGetVersion(&vi2))
+ {
+ if (!bHaveVerFromKernel32) // we failed above; let's hope this would be useful
+ aVer.append(OUString::number(vi2.dwMajorVersion) + "."
+ + OUString::number(vi2.dwMinorVersion));
+ aVer.append(" ");
+ if (vi2.szCSDVersion[0])
+ aVer.append(o3tl::toU(vi2.szCSDVersion)).append(" ");
+ aVer.append("Build " + OUString::number(vi2.dwBuildNumber));
+ bHaveVerFromRtlGetVersion = true;
+ }
+ }
+ }
+ if (!bHaveVerFromKernel32 && !bHaveVerFromRtlGetVersion)
+ aVer.append("unknown");
+ return aVer.makeStringAndClear();
+}
+
+std::shared_ptr<vcl::BackendCapabilities> WinSalInstance::GetBackendCapabilities()
+{
+ auto pBackendCapabilities = SalInstance::GetBackendCapabilities();
+#if HAVE_FEATURE_SKIA
+#if SKIA_USE_BITMAP32
+ if( SkiaHelper::isVCLSkiaEnabled())
+ pBackendCapabilities->mbSupportsBitmap32 = true;
+#endif
+#endif
+ return pBackendCapabilities;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */