diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /vcl/win/app | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | vcl/win/app/saldata.cxx | 78 | ||||
-rw-r--r-- | vcl/win/app/salinfo.cxx | 176 | ||||
-rw-r--r-- | vcl/win/app/salinst.cxx | 1095 | ||||
-rw-r--r-- | vcl/win/app/salshl.cxx | 112 | ||||
-rw-r--r-- | vcl/win/app/saltimer.cxx | 199 |
5 files changed, 1660 insertions, 0 deletions
diff --git a/vcl/win/app/saldata.cxx b/vcl/win/app/saldata.cxx new file mode 100644 index 000000000..31fa66163 --- /dev/null +++ b/vcl/win/app/saldata.cxx @@ -0,0 +1,78 @@ +/* -*- 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 <svsys.h> +#include <rtl/tencinfo.h> +#include <vcl/svapp.hxx> + +#include <win/saldata.hxx> + +rtl_TextEncoding ImplSalGetSystemEncoding() +{ + static const UINT nOldAnsiCodePage = 0; + static rtl_TextEncoding eEncoding = RTL_TEXTENCODING_MS_1252; + + UINT nAnsiCodePage = GetACP(); + if ( nAnsiCodePage != nOldAnsiCodePage ) + { + rtl_TextEncoding nEnc + = rtl_getTextEncodingFromWindowsCodePage(nAnsiCodePage); + if (nEnc != RTL_TEXTENCODING_DONTKNOW) + eEncoding = nEnc; + } + + return eEncoding; +} + +OUString ImplSalGetUniString(const char* pStr, sal_Int32 const nLen) +{ + return OUString( pStr, (-1 == nLen) ? strlen(pStr) : nLen, + ImplSalGetSystemEncoding(), + RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | + RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT ); +} + +int ImplSalWICompareAscii( const wchar_t* pStr1, const char* pStr2 ) +{ + int nRet; + char c2; + do + { + // change to LowerCase if the char is between 'A' and 'Z' + wchar_t c1 = *pStr1; + c2 = *pStr2; + if ( (c1 >= 65) && (c1 <= 90) ) + c1 += 32; + if ( (c2 >= 65) && (c2 <= 90) ) + c2 += 32; + nRet = static_cast<sal_Int32>(c1)- static_cast<sal_Int32>(static_cast<unsigned char>(c2)); + if ( nRet != 0 ) + break; + + pStr1++; + pStr2++; + } + while ( c2 ); + + return nRet; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/app/salinfo.cxx b/vcl/win/app/salinfo.cxx new file mode 100644 index 000000000..2787797a6 --- /dev/null +++ b/vcl/win/app/salinfo.cxx @@ -0,0 +1,176 @@ +/* -*- 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 <svsys.h> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <sal/log.hxx> +#include <vcl/window.hxx> + +#include <win/salsys.h> +#include <win/salframe.h> +#include <win/salinst.h> +#include <win/saldata.hxx> + +#include <svdata.hxx> + +#include <unordered_map> + +SalSystem* WinSalInstance::CreateSalSystem() +{ + return new WinSalSystem(); +} + +WinSalSystem::~WinSalSystem() +{ +} + +static BOOL CALLBACK ImplEnumMonitorProc( HMONITOR hMonitor, + HDC hDC, + LPRECT lpRect, + LPARAM dwData ) +{ + WinSalSystem* pSys = reinterpret_cast<WinSalSystem*>(dwData); + return pSys->handleMonitorCallback( reinterpret_cast<sal_IntPtr>(hMonitor), + reinterpret_cast<sal_IntPtr>(hDC), + reinterpret_cast<sal_IntPtr>(lpRect) ); +} + +bool WinSalSystem::handleMonitorCallback( sal_IntPtr hMonitor, sal_IntPtr, sal_IntPtr ) +{ + MONITORINFOEXW aInfo; + aInfo.cbSize = sizeof( aInfo ); + if( GetMonitorInfoW( reinterpret_cast<HMONITOR>(hMonitor), &aInfo ) ) + { + aInfo.szDevice[CCHDEVICENAME-1] = 0; + OUString aDeviceName( o3tl::toU(aInfo.szDevice) ); + std::map< OUString, unsigned int >::const_iterator it = + m_aDeviceNameToMonitor.find( aDeviceName ); + if( it != m_aDeviceNameToMonitor.end() ) + { + DisplayMonitor& rMon( m_aMonitors[ it->second ] ); + rMon.m_aArea = tools::Rectangle( Point( aInfo.rcMonitor.left, + aInfo.rcMonitor.top ), + Size( aInfo.rcMonitor.right - aInfo.rcMonitor.left, + aInfo.rcMonitor.bottom - aInfo.rcMonitor.top ) ); + if( (aInfo.dwFlags & MONITORINFOF_PRIMARY) != 0 ) + m_nPrimary = it->second; + } + } + return true; +} + +void WinSalSystem::clearMonitors() +{ + m_aMonitors.clear(); + m_nPrimary = 0; +} + +bool WinSalSystem::initMonitors() +{ + if( m_aMonitors.size() > 0 ) + return true; + + int nMonitors = GetSystemMetrics( SM_CMONITORS ); + if( nMonitors == 1 ) + { + int w = GetSystemMetrics( SM_CXSCREEN ); + int h = GetSystemMetrics( SM_CYSCREEN ); + m_aMonitors.push_back( DisplayMonitor( OUString(), + tools::Rectangle( Point(), Size( w, h ) ) ) ); + m_aDeviceNameToMonitor[ OUString() ] = 0; + m_nPrimary = 0; + } + else + { + DISPLAY_DEVICEW aDev; + aDev.cb = sizeof( aDev ); + DWORD nDevice = 0; + std::unordered_map< OUString, int > aDeviceStringCount; + while( EnumDisplayDevicesW( nullptr, nDevice++, &aDev, 0 ) ) + { + if( (aDev.StateFlags & DISPLAY_DEVICE_ACTIVE) + && !(aDev.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) ) // sort out non/disabled monitors + { + aDev.DeviceName[31] = 0; + aDev.DeviceString[127] = 0; + OUString aDeviceName( o3tl::toU(aDev.DeviceName) ); + OUString aDeviceString( o3tl::toU(aDev.DeviceString) ); + aDeviceStringCount[ aDeviceString ]++; + m_aDeviceNameToMonitor[ aDeviceName ] = m_aMonitors.size(); + m_aMonitors.push_back( DisplayMonitor( aDeviceString, + tools::Rectangle() ) ); + } + } + HDC aDesktopRC = GetDC( nullptr ); + EnumDisplayMonitors( aDesktopRC, nullptr, ImplEnumMonitorProc, reinterpret_cast<LPARAM>(this) ); + + // append monitor numbers to name strings + std::unordered_map< OUString, int > aDevCount( aDeviceStringCount ); + unsigned int nMonitorCount = m_aMonitors.size(); + for( unsigned int i = 0; i < nMonitorCount; i++ ) + { + const OUString& rDev( m_aMonitors[i].m_aName ); + if( aDeviceStringCount[ rDev ] > 1 ) + { + int nInstance = aDeviceStringCount[ rDev ] - (-- aDevCount[ rDev ] ); + m_aMonitors[ i ].m_aName = rDev + " (" + OUString::number( nInstance ) + ")"; + } + } + } + + return m_aMonitors.size() > 0; +} + +unsigned int WinSalSystem::GetDisplayScreenCount() +{ + initMonitors(); + return m_aMonitors.size(); +} + +unsigned int WinSalSystem::GetDisplayBuiltInScreen() +{ + initMonitors(); + return m_nPrimary; +} + +tools::Rectangle WinSalSystem::GetDisplayScreenPosSizePixel( unsigned int nScreen ) +{ + initMonitors(); + if (nScreen >= m_aMonitors.size()) + { + SAL_WARN("vcl", "Requested screen size/pos for screen #" + << nScreen << ", but only " << m_aMonitors.size() << " screens found."); + assert(false); + return tools::Rectangle(); + } + return m_aMonitors[nScreen].m_aArea; +} + +int WinSalSystem::ShowNativeMessageBox(const OUString& rTitle, const OUString& rMessage) +{ + ImplHideSplash(); + return MessageBoxW( + nullptr, + o3tl::toW(rMessage.getStr()), + o3tl::toW(rTitle.getStr()), + MB_TASKMODAL | MB_SETFOREGROUND | MB_ICONWARNING | MB_DEFBUTTON1); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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: */ diff --git a/vcl/win/app/salshl.cxx b/vcl/win/app/salshl.cxx new file mode 100644 index 000000000..5619ece1f --- /dev/null +++ b/vcl/win/app/salshl.cxx @@ -0,0 +1,112 @@ +/* -*- 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 <svsys.h> +#include <sal/log.hxx> +#include <win/saldata.hxx> + +SalShlData aSalShlData; + +extern "C" BOOL WINAPI DllMain(HINSTANCE hInst, DWORD nReason, LPVOID) +{ + if ( nReason == DLL_PROCESS_ATTACH ) + aSalShlData.mhInst = hInst; + return 1; +} + +HCURSOR ImplLoadSalCursor( int nId ) +{ + SAL_WARN_IF( !aSalShlData.mhInst, "vcl", "no DLL instance handle" ); + + HCURSOR hCursor = LoadCursorW( aSalShlData.mhInst, MAKEINTRESOURCEW( nId ) ); + + SAL_WARN_IF( !hCursor, "vcl", "cursor not found in sal resource" ); + + return hCursor; +} + +HBITMAP ImplLoadSalBitmap( int nId ) +{ + SAL_WARN_IF( !aSalShlData.mhInst, "vcl", "no DLL instance handle" ); + + HBITMAP hBitmap = LoadBitmapW( aSalShlData.mhInst, MAKEINTRESOURCEW( nId ) ); + + SAL_WARN_IF( !hBitmap, "vcl", "bitmap not found in sal resource" ); + + return hBitmap; +} + +bool ImplLoadSalIcon( int nId, HICON& rIcon, HICON& rSmallIcon ) +{ + SAL_WARN_IF( !aSalShlData.mhInst, "vcl", "no DLL instance handle" ); + + SalData* pSalData = GetSalData(); + + // check the cache first + SalIcon *pSalIcon = pSalData->mpFirstIcon; + while( pSalIcon ) + { + if( pSalIcon->nId != nId ) + pSalIcon = pSalIcon->pNext; + else + { + rIcon = pSalIcon->hIcon; + rSmallIcon = pSalIcon->hSmallIcon; + return (rSmallIcon != nullptr); + } + } + + // Try at first to load the icons from the application exe file + rIcon = static_cast<HICON>(LoadImageW( pSalData->mhInst, MAKEINTRESOURCEW( nId ), + IMAGE_ICON, GetSystemMetrics( SM_CXICON ), GetSystemMetrics( SM_CYICON ), + LR_DEFAULTCOLOR )); + if ( !rIcon ) + { + // If the application don't provide these icons, then we try + // to load the icon from the VCL resource + rIcon = static_cast<HICON>(LoadImageW( aSalShlData.mhInst, MAKEINTRESOURCEW( nId ), + IMAGE_ICON, GetSystemMetrics( SM_CXICON ), GetSystemMetrics( SM_CYICON ), + LR_DEFAULTCOLOR )); + if ( rIcon ) + { + rSmallIcon = static_cast<HICON>(LoadImageW( aSalShlData.mhInst, MAKEINTRESOURCEW( nId ), + IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), + LR_DEFAULTCOLOR )); + } + else + rSmallIcon = nullptr; + } + else + { + rSmallIcon = static_cast<HICON>(LoadImageW( pSalData->mhInst, MAKEINTRESOURCEW( nId ), + IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), + LR_DEFAULTCOLOR )); + } + + if( rIcon ) + { + // add to icon cache + pSalData->mpFirstIcon = new SalIcon{ + nId, rIcon, rSmallIcon, pSalData->mpFirstIcon}; + } + + return (rSmallIcon != nullptr); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/win/app/saltimer.cxx b/vcl/win/app/saltimer.cxx new file mode 100644 index 000000000..5a4760ad5 --- /dev/null +++ b/vcl/win/app/saltimer.cxx @@ -0,0 +1,199 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> + +#include <tools/time.hxx> + +#include <svsys.h> +#include <win/saldata.hxx> +#include <win/saltimer.h> +#include <win/salinst.h> + +void CALLBACK SalTimerProc(PVOID pParameter, BOOLEAN bTimerOrWaitFired); + +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms687003%28v=vs.85%29.aspx +// (and related pages) for details about the Timer Queues. + +// in order to prevent concurrent execution of ImplSalStartTimer and double +// deletion of timer (which is extremely likely, given that +// INVALID_HANDLE_VALUE waits for the callback to run on the main thread), +// this must run on the main thread too +void WinSalTimer::ImplStop() +{ + SalData *const pSalData = GetSalData(); + const WinSalInstance *pInst = pSalData->mpInstance; + assert( !pInst || pSalData->mnAppThreadId == GetCurrentThreadId() ); + + if ( m_bSetTimerRunning ) + { + m_bSetTimerRunning = false; + KillTimer( GetSalData()->mpInstance->mhComWnd, m_aWmTimerId ); + } + m_bDirectTimeout = false; + + const HANDLE hTimer = m_nTimerId; + if ( nullptr == hTimer ) + return; + + m_nTimerId = nullptr; + DeleteTimerQueueTimer( nullptr, hTimer, INVALID_HANDLE_VALUE ); + // Keep InvalidateEvent after DeleteTimerQueueTimer, because the event id + // is set in SalTimerProc, which DeleteTimerQueueTimer will finish or cancel. + InvalidateEvent(); +} + +void WinSalTimer::ImplStart( sal_uInt64 nMS ) +{ + SalData* pSalData = GetSalData(); + assert( !pSalData->mpInstance || pSalData->mnAppThreadId == GetCurrentThreadId() ); + + // DueTime parameter is a DWORD, which is always an unsigned 32bit + if (nMS > SAL_MAX_UINT32) + nMS = SAL_MAX_UINT32; + + // cannot change a one-shot timer, so delete it and create a new one + ImplStop(); + + // directly indicate an elapsed timer + m_bDirectTimeout = ( 0 == nMS ); + // probably WT_EXECUTEONLYONCE is not needed, but it enforces Period + // to be 0 and should not hurt; also see + // https://www.microsoft.com/msj/0499/pooling/pooling.aspx + if ( !m_bDirectTimeout ) + CreateTimerQueueTimer(&m_nTimerId, nullptr, SalTimerProc, this, + nMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE); + else if ( m_bForceRealTimer ) + { + // so we don't block the nested message queue in move and resize + // with posted 0ms SAL_MSG_TIMER_CALLBACK messages + SetTimer( GetSalData()->mpInstance->mhComWnd, m_aWmTimerId, + USER_TIMER_MINIMUM, nullptr ); + m_bSetTimerRunning = true; + } + // we don't need any wakeup message, as this code can just run in the + // main thread! +} + +WinSalTimer::WinSalTimer() + : m_nTimerId( nullptr ) + , m_bDirectTimeout( false ) + , m_bForceRealTimer( false ) + , m_bSetTimerRunning( false ) +{ +} + +WinSalTimer::~WinSalTimer() +{ + Stop(); +} + +void WinSalTimer::Start( sal_uInt64 nMS ) +{ + WinSalInstance *pInst = GetSalData()->mpInstance; + if ( pInst && !pInst->IsMainThread() ) + { + bool const ret = PostMessageW(pInst->mhComWnd, + SAL_MSG_STARTTIMER, 0, static_cast<LPARAM>(tools::Time::GetSystemTicks()) + nMS); + SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!"); + } + else + ImplStart( nMS ); +} + +void WinSalTimer::Stop() +{ + WinSalInstance *pInst = GetSalData()->mpInstance; + if ( pInst && !pInst->IsMainThread() ) + { + bool const ret = PostMessageW(pInst->mhComWnd, + SAL_MSG_STOPTIMER, 0, 0); + SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!"); + } + else + ImplStop(); +} + +/** + * This gets invoked from a Timer Queue thread. + * Don't acquire the SolarMutex to avoid deadlocks. + */ +void CALLBACK SalTimerProc(PVOID data, BOOLEAN) +{ + __try + { + WinSalTimer *pTimer = static_cast<WinSalTimer*>( data ); + bool const ret = PostMessageW( + GetSalData()->mpInstance->mhComWnd, SAL_MSG_TIMER_CALLBACK, + static_cast<WPARAM>(pTimer->GetNextEventVersion()), 0 ); +#if OSL_DEBUG_LEVEL > 0 + if (!ret) // SEH prevents using SAL_WARN here? + fputs("ERROR: PostMessage() failed!\n", stderr); +#endif + } + __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation())) + { + } +} + +void WinSalTimer::ImplHandleElapsedTimer() +{ + // Test for MouseLeave + SalTestMouseLeave(); + + m_bDirectTimeout = false; + ImplSalYieldMutexAcquireWithWait(); + CallCallback(); + ImplSalYieldMutexRelease(); +} + +void WinSalTimer::ImplHandleTimerEvent( const WPARAM aWPARAM ) +{ + assert( aWPARAM <= SAL_MAX_INT32 ); + if ( !IsValidEventVersion( static_cast<sal_Int32>( aWPARAM ) ) ) + return; + + ImplHandleElapsedTimer(); +} + +void WinSalTimer::SetForceRealTimer( const bool bVal ) +{ + if ( m_bForceRealTimer == bVal ) + return; + + m_bForceRealTimer = bVal; + + // we need a real timer, as m_bDirectTimeout won't be processed + if ( bVal && m_bDirectTimeout ) + Start( 0 ); +} + +void WinSalTimer::ImplHandle_WM_TIMER( const WPARAM aWPARAM ) +{ + assert( m_aWmTimerId == aWPARAM ); + if ( !(m_aWmTimerId == aWPARAM && m_bSetTimerRunning) ) + return; + + m_bSetTimerRunning = false; + KillTimer( GetSalData()->mpInstance->mhComWnd, m_aWmTimerId ); + ImplHandleElapsedTimer(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |