Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / vcl / win / app / salinst.cxx
blobc5a0c6fec04b02489aa1c0c9452dc1db65673a06
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <string.h>
21 #include <svsys.h>
22 #include <process.h>
24 #include <osl/conditn.hxx>
25 #include <osl/file.hxx>
26 #include <rtl/ustrbuf.hxx>
27 #include <sal/log.hxx>
28 #include <tools/debug.hxx>
29 #include <tools/time.hxx>
30 #include <comphelper/solarmutex.hxx>
31 #include <o3tl/char16_t2wchar_t.hxx>
33 #include <vcl/inputtypes.hxx>
34 #include <vcl/opengl/OpenGLHelper.hxx>
35 #include <vcl/opengl/OpenGLContext.hxx>
36 #include <vcl/timer.hxx>
37 #include <vclpluginapi.h>
39 #include <opengl/salbmp.hxx>
40 #include <opengl/win/gdiimpl.hxx>
41 #include <win/wincomp.hxx>
42 #include <win/salids.hrc>
43 #include <win/saldata.hxx>
44 #include <win/salinst.h>
45 #include <win/salframe.h>
46 #include <win/salobj.h>
47 #include <win/saltimer.h>
48 #include <win/salbmp.h>
49 #include <win/winlayout.hxx>
51 #include <config_features.h>
52 #include <vcl/skia/SkiaHelper.hxx>
53 #if HAVE_FEATURE_SKIA
54 #include <config_skia.h>
55 #include <skia/salbmp.hxx>
56 #include <skia/win/gdiimpl.hxx>
57 #endif
59 #include <salsys.hxx>
61 #include <desktop/crashreport.hxx>
63 #if defined _MSC_VER
64 #ifndef min
65 #define min(a,b) (((a) < (b)) ? (a) : (b))
66 #endif
67 #ifndef max
68 #define max(a,b) (((a) > (b)) ? (a) : (b))
69 #endif
70 #endif
72 #include <prewin.h>
74 #include <gdiplus.h>
75 #include <shlobj.h>
77 #include <postwin.h>
79 void SalAbort( const OUString& rErrorText, bool )
81 ImplFreeSalGDI();
83 if ( rErrorText.isEmpty() )
85 // make sure crash reporter is triggered
86 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
87 FatalAppExitW( 0, L"Application Error" );
89 else
91 CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
92 // make sure crash reporter is triggered
93 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
94 FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) );
98 static LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam );
100 class SalYieldMutex : public comphelper::SolarMutex
102 public: // for ImplSalYield() and ImplSalYieldMutexAcquireWithWait()
103 osl::Condition m_condition; /// for MsgWaitForMultipleObjects()
105 protected:
106 virtual void doAcquire( sal_uInt32 nLockCount ) override;
107 virtual sal_uInt32 doRelease( bool bUnlockAll ) override;
109 static void BeforeReleaseHandler();
111 public:
112 explicit SalYieldMutex();
114 virtual bool IsCurrentThread() const override;
115 virtual bool tryToAcquire() override;
118 SalYieldMutex::SalYieldMutex()
120 SetBeforeReleaseHandler( &SalYieldMutex::BeforeReleaseHandler );
123 void SalYieldMutex::BeforeReleaseHandler()
125 OpenGLContext::prepareForYield();
127 if ( GetSalData()->mnAppThreadId != GetCurrentThreadId() )
129 // If we don't call these message, the Output from the
130 // Java clients doesn't come in the right order
131 GdiFlush();
135 /// note: while VCL is fully up and running (other threads started and
136 /// before shutdown), the main thread must acquire SolarMutex only via
137 /// this function to avoid deadlock
138 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
140 WinSalInstance* pInst = GetSalData()->mpInstance;
141 if ( pInst && pInst->IsMainThread() )
143 if ( pInst->m_nNoYieldLock )
144 return;
145 // tdf#96887 If this is the main thread, then we must wait for two things:
146 // - the yield mutex being unlocked
147 // - SendMessage() being triggered
148 // This can nicely be done using MsgWaitForMultipleObjects, which is called in
149 // m_condition.wait(). The 2nd one is
150 // needed because if we don't reschedule, then we create deadlocks if a
151 // Window's create/destroy is called via SendMessage() from another thread.
152 // Have a look at the osl_waitCondition implementation for more info.
153 do {
154 // reset condition *before* acquiring!
155 m_condition.reset();
156 if (m_aMutex.tryToAcquire())
157 break;
158 // wait for SalYieldMutex::release() to set the condition
159 osl::Condition::Result res = m_condition.wait();
160 assert(osl::Condition::Result::result_ok == res);
162 while ( true );
164 else
165 m_aMutex.acquire();
166 ++m_nCount;
167 --nLockCount;
169 comphelper::SolarMutex::doAcquire( nLockCount );
172 sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll )
174 WinSalInstance* pInst = GetSalData()->mpInstance;
175 if ( pInst && pInst->m_nNoYieldLock && pInst->IsMainThread() )
176 return 1;
178 sal_uInt32 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
179 // wake up ImplSalYieldMutexAcquireWithWait() after release
180 if ( 0 == m_nCount )
181 m_condition.set();
182 return nCount;
185 bool SalYieldMutex::tryToAcquire()
187 WinSalInstance* pInst = GetSalData()->mpInstance;
188 if ( pInst )
190 if ( pInst->m_nNoYieldLock && pInst->IsMainThread() )
191 return true;
192 else
193 return comphelper::SolarMutex::tryToAcquire();
195 else
196 return false;
199 void ImplSalYieldMutexAcquireWithWait( sal_uInt32 nCount )
201 WinSalInstance* pInst = GetSalData()->mpInstance;
202 if ( pInst )
203 pInst->GetYieldMutex()->acquire( nCount );
206 bool ImplSalYieldMutexTryToAcquire()
208 WinSalInstance* pInst = GetSalData()->mpInstance;
209 return pInst && pInst->GetYieldMutex()->tryToAcquire();
212 void ImplSalYieldMutexRelease()
214 WinSalInstance* pInst = GetSalData()->mpInstance;
215 if ( pInst )
217 GdiFlush();
218 pInst->GetYieldMutex()->release();
222 bool SalYieldMutex::IsCurrentThread() const
224 if ( !GetSalData()->mpInstance->m_nNoYieldLock )
225 return SolarMutex::IsCurrentThread();
226 else
227 return GetSalData()->mpInstance->IsMainThread();
230 void SalData::initKeyCodeMap()
232 UINT nKey;
233 #define initKey( a, b )\
234 nKey = LOWORD( VkKeyScanW( a ) );\
235 if( nKey < 0xffff )\
236 maVKMap[ nKey ] = b;
238 maVKMap.clear();
240 initKey( L'+', KEY_ADD );
241 initKey( L'-', KEY_SUBTRACT );
242 initKey( L'*', KEY_MULTIPLY );
243 initKey( L'/', KEY_DIVIDE );
244 initKey( L'.', KEY_POINT );
245 initKey( L',', KEY_COMMA );
246 initKey( L'<', KEY_LESS );
247 initKey( L'>', KEY_GREATER );
248 initKey( L'=', KEY_EQUAL );
249 initKey( L'~', KEY_TILDE );
250 initKey( L'`', KEY_QUOTELEFT );
251 initKey( L'[', KEY_BRACKETLEFT );
252 initKey( L']', KEY_BRACKETRIGHT );
253 initKey( L';', KEY_SEMICOLON );
254 initKey( L'\'', KEY_QUOTERIGHT );
257 // SalData
259 SalData::SalData()
261 mhInst = nullptr; // default instance handle
262 mnCmdShow = 0; // default frame show style
263 mhDitherPal = nullptr; // dither palette
264 mhDitherDIB = nullptr; // dither memory handle
265 mpDitherDIB = nullptr; // dither memory
266 mpDitherDIBData = nullptr; // beginning of DIB data
267 mpDitherDiff = nullptr; // Dither mapping table
268 mpDitherLow = nullptr; // Dither mapping table
269 mpDitherHigh = nullptr; // Dither mapping table
270 mhSalObjMsgHook = nullptr; // hook to get interesting msg for SalObject
271 mhWantLeaveMsg = nullptr; // window handle, that want a MOUSELEAVE message
272 mpMouseLeaveTimer = nullptr; // Timer for MouseLeave Test
273 mpInstance = nullptr; // pointer of first instance
274 mpFirstFrame = nullptr; // pointer of first frame
275 mpFirstObject = nullptr; // pointer of first object window
276 mpFirstVD = nullptr; // first VirDev
277 mpFirstPrinter = nullptr; // first printing printer
278 mpHDCCache = nullptr; // Cache for three DC's
279 mh50Bmp = nullptr; // 50% Bitmap
280 mh50Brush = nullptr; // 50% Brush
281 int i;
282 for(i=0; i<MAX_STOCKPEN; i++)
284 maStockPenColorAry[i] = 0;
285 mhStockPenAry[i] = nullptr;
287 for(i=0; i<MAX_STOCKBRUSH; i++)
289 maStockBrushColorAry[i] = 0;
290 mhStockBrushAry[i] = nullptr;
292 mnStockPenCount = 0; // count of static pens
293 mnStockBrushCount = 0; // count of static brushes
294 mnSalObjWantKeyEvt = 0; // KeyEvent for the SalObj hook
295 mnCacheDCInUse = 0; // count of CacheDC in use
296 mbObjClassInit = false; // is SALOBJECTCLASS initialised
297 mbInPalChange = false; // is in WM_QUERYNEWPALETTE
298 mnAppThreadId = 0; // Id from Application-Thread
299 mbScrSvrEnabled = FALSE; // ScreenSaver enabled
300 mpFirstIcon = nullptr; // icon cache, points to first icon, NULL if none
301 mpSharedTempFontItem = nullptr;
302 mpOtherTempFontItem = nullptr;
303 mbThemeChanged = false; // true if visual theme was changed: throw away theme handles
304 mbThemeMenuSupport = false;
306 // init with NULL
307 gdiplusToken = 0;
309 initKeyCodeMap();
311 SetSalData( this );
312 initNWF();
314 CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); // put main thread in Single Threaded Apartment (STA)
315 static Gdiplus::GdiplusStartupInput gdiplusStartupInput;
316 Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
319 SalData::~SalData()
321 deInitNWF();
322 SetSalData( nullptr );
324 CoUninitialize();
326 if (gdiplusToken)
327 Gdiplus::GdiplusShutdown(gdiplusToken);
330 extern "C" {
331 VCLPLUG_WIN_PUBLIC SalInstance* create_SalInstance()
333 SalData* pSalData = new SalData();
335 STARTUPINFOW aSI;
336 aSI.cb = sizeof( aSI );
337 GetStartupInfoW( &aSI );
338 pSalData->mhInst = GetModuleHandleW( nullptr );
339 pSalData->mnCmdShow = aSI.wShowWindow;
341 pSalData->mnAppThreadId = GetCurrentThreadId();
343 // register frame class
344 WNDCLASSEXW aWndClassEx;
345 aWndClassEx.cbSize = sizeof( aWndClassEx );
346 aWndClassEx.style = CS_OWNDC;
347 aWndClassEx.lpfnWndProc = SalFrameWndProcW;
348 aWndClassEx.cbClsExtra = 0;
349 aWndClassEx.cbWndExtra = SAL_FRAME_WNDEXTRA;
350 aWndClassEx.hInstance = pSalData->mhInst;
351 aWndClassEx.hCursor = nullptr;
352 aWndClassEx.hbrBackground = nullptr;
353 aWndClassEx.lpszMenuName = nullptr;
354 aWndClassEx.lpszClassName = SAL_FRAME_CLASSNAMEW;
355 ImplLoadSalIcon( SAL_RESID_ICON_DEFAULT, aWndClassEx.hIcon, aWndClassEx.hIconSm );
356 if ( !RegisterClassExW( &aWndClassEx ) )
357 return nullptr;
359 aWndClassEx.hIcon = nullptr;
360 aWndClassEx.hIconSm = nullptr;
361 aWndClassEx.style |= CS_SAVEBITS;
362 aWndClassEx.lpszClassName = SAL_SUBFRAME_CLASSNAMEW;
363 if ( !RegisterClassExW( &aWndClassEx ) )
364 return nullptr;
366 // shadow effect for popups on XP
367 aWndClassEx.style |= CS_DROPSHADOW;
368 aWndClassEx.lpszClassName = SAL_TMPSUBFRAME_CLASSNAMEW;
369 if ( !RegisterClassExW( &aWndClassEx ) )
370 return nullptr;
372 aWndClassEx.style = 0;
373 aWndClassEx.lpfnWndProc = SalComWndProcW;
374 aWndClassEx.cbWndExtra = 0;
375 aWndClassEx.lpszClassName = SAL_COM_CLASSNAMEW;
376 if ( !RegisterClassExW( &aWndClassEx ) )
377 return nullptr;
379 HWND hComWnd = CreateWindowExW( WS_EX_TOOLWINDOW, SAL_COM_CLASSNAMEW,
380 L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
381 pSalData->mhInst, nullptr );
382 if ( !hComWnd )
383 return nullptr;
385 WinSalInstance* pInst = new WinSalInstance;
387 // init instance (only one instance in this version !!!)
388 pSalData->mpInstance = pInst;
389 pInst->mhInst = pSalData->mhInst;
390 pInst->mhComWnd = hComWnd;
392 // init static GDI Data
393 ImplInitSalGDI();
395 return pInst;
399 WinSalInstance::WinSalInstance()
400 : SalInstance(std::make_unique<SalYieldMutex>())
401 , mhInst( nullptr )
402 , mhComWnd( nullptr )
403 , m_nNoYieldLock( 0 )
405 ImplSVData* pSVData = ImplGetSVData();
406 pSVData->maAppData.mxToolkitName = OUString("win");
407 #if HAVE_FEATURE_SKIA
408 WinSkiaSalGraphicsImpl::prepareSkia();
409 #endif
412 WinSalInstance::~WinSalInstance()
414 ImplFreeSalGDI();
415 DestroyWindow( mhComWnd );
416 #if HAVE_FEATURE_SKIA
417 SkiaHelper::cleanup();
418 #endif
421 static LRESULT ImplSalDispatchMessage( const MSG* pMsg )
423 SalData* pSalData = GetSalData();
424 if ( pSalData->mpFirstObject && ImplSalPreDispatchMsg( pMsg ) )
425 return 0;
426 LRESULT lResult = DispatchMessageW( pMsg );
427 if ( pSalData->mpFirstObject )
428 ImplSalPostDispatchMsg( pMsg );
429 return lResult;
432 bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
434 static sal_uInt32 nLastTicks = 0;
435 MSG aMsg;
436 bool bWasMsg = false, bOneEvent = false, bWasTimeoutMsg = false;
437 ImplSVData *const pSVData = ImplGetSVData();
438 WinSalTimer* pTimer = static_cast<WinSalTimer*>( pSVData->maSchedCtx.mpSalTimer );
439 const bool bNoYieldLock = (GetSalData()->mpInstance->m_nNoYieldLock > 0);
441 assert( !bNoYieldLock );
442 if ( bNoYieldLock )
443 return false;
445 sal_uInt32 nCurTicks = 0;
446 if ( bHandleAllCurrentEvents )
447 nCurTicks = GetTickCount();
449 bool bHadNewerEvent = false;
452 bOneEvent = PeekMessageW( &aMsg, nullptr, 0, 0, PM_REMOVE );
453 if ( bOneEvent )
455 bWasMsg = true;
456 TranslateMessage( &aMsg );
457 LRESULT nRet = ImplSalDispatchMessage( &aMsg );
459 if ( !bWasTimeoutMsg )
460 bWasTimeoutMsg = (SAL_MSG_TIMER_CALLBACK == aMsg.message)
461 && static_cast<bool>( nRet );
463 if ( bHandleAllCurrentEvents
464 && !bHadNewerEvent && aMsg.time > nCurTicks
465 && (nLastTicks <= nCurTicks || aMsg.time < nLastTicks) )
466 bHadNewerEvent = true;
467 bOneEvent = !bHadNewerEvent;
470 if ( !(bHandleAllCurrentEvents && bOneEvent) )
471 break;
473 while( true );
475 // 0ms timeouts are handled out-of-bounds to prevent busy-locking the
476 // event loop with timeout messages.
477 // We ensure we never handle more than one timeout per call.
478 // This way we'll always process a normal system message.
479 if ( !bWasTimeoutMsg && pTimer && pTimer->IsDirectTimeout() )
481 pTimer->ImplHandleElapsedTimer();
482 bWasMsg = true;
485 if ( bHandleAllCurrentEvents )
486 nLastTicks = nCurTicks;
488 if ( bWait && !bWasMsg )
490 if ( GetMessageW( &aMsg, nullptr, 0, 0 ) )
492 bWasMsg = true;
493 TranslateMessage( &aMsg );
494 ImplSalDispatchMessage( &aMsg );
498 // we're back in the main loop after resize or move
499 if ( pTimer )
500 pTimer->SetForceRealTimer( false );
502 return bWasMsg;
505 bool WinSalInstance::IsMainThread() const
507 const SalData* pSalData = GetSalData();
508 return pSalData->mnAppThreadId == GetCurrentThreadId();
511 bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
513 bool bDidWork = false;
514 SolarMutexReleaser aReleaser;
515 if ( !IsMainThread() )
517 bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD,
518 WPARAM(false), static_cast<LPARAM>(bHandleAllCurrentEvents) );
519 if ( !bDidWork && bWait )
521 maWaitingYieldCond.reset();
522 maWaitingYieldCond.wait();
523 bDidWork = true;
526 else
528 bDidWork = ImplSalYield( bWait, bHandleAllCurrentEvents );
529 if ( bDidWork )
530 maWaitingYieldCond.set();
533 return bDidWork;
536 #define CASE_NOYIELDLOCK( salmsg, function ) \
537 case salmsg: \
538 if (bIsOtherThreadMessage) \
540 ++pInst->m_nNoYieldLock; \
541 function; \
542 --pInst->m_nNoYieldLock; \
544 else \
546 DBG_TESTSOLARMUTEX(); \
547 function; \
549 break;
551 #define CASE_NOYIELDLOCK_RESULT( salmsg, function ) \
552 case salmsg: \
553 if (bIsOtherThreadMessage) \
555 ++pInst->m_nNoYieldLock; \
556 nRet = reinterpret_cast<LRESULT>( function ); \
557 --pInst->m_nNoYieldLock; \
559 else \
561 DBG_TESTSOLARMUTEX(); \
562 nRet = reinterpret_cast<LRESULT>( function ); \
564 break;
566 LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, bool& rDef )
568 const bool bIsOtherThreadMessage = InSendMessage();
569 LRESULT nRet = 0;
570 WinSalInstance *pInst = GetSalData()->mpInstance;
571 WinSalTimer *const pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
573 SAL_INFO("vcl.gdi.wndproc", "SalComWndProc(nMsg=" << nMsg << ", wParam=" << wParam
574 << ", lParam=" << lParam << "); inSendMsg: " << bIsOtherThreadMessage);
576 if (ImplGetSVData()->mbDeInit)
578 SAL_WARN("vcl.gdi.wndproc", "ignoring timer event because we are shutting down");
579 return 0;
582 switch ( nMsg )
584 case SAL_MSG_THREADYIELD:
585 assert( !static_cast<bool>(wParam) );
586 nRet = static_cast<LRESULT>(ImplSalYield(
587 false, static_cast<bool>( lParam ) ));
588 break;
590 case SAL_MSG_STARTTIMER:
592 auto const nParam = static_cast<sal_uInt64>( lParam );
593 sal_uInt64 nTime = tools::Time::GetSystemTicks();
594 if ( nTime < nParam )
595 nTime = nParam - nTime;
596 else
597 nTime = 0;
598 assert( pTimer != nullptr );
599 pTimer->ImplStart( nTime );
600 break;
603 case SAL_MSG_STOPTIMER:
604 assert( pTimer != nullptr );
605 pTimer->ImplStop();
606 break;
608 CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEFRAME, ImplSalCreateFrame( GetSalData()->mpInstance,
609 reinterpret_cast<HWND>(lParam), static_cast<SalFrameStyleFlags>(wParam)) )
610 CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATEHWND, ImplSalReCreateHWND(
611 reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), false) )
612 CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATECHILDHWND, ImplSalReCreateHWND(
613 reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), true) )
614 CASE_NOYIELDLOCK( SAL_MSG_DESTROYFRAME, delete reinterpret_cast<SalFrame*>(lParam) )
616 case SAL_MSG_DESTROYHWND:
617 // We only destroy the native window here. We do NOT destroy the SalFrame contained
618 // in the structure (GetWindowPtr()).
619 if (DestroyWindow(reinterpret_cast<HWND>(lParam)) == 0)
621 OSL_FAIL("DestroyWindow failed!");
622 // Failure: We remove the SalFrame from the window structure. So we avoid that
623 // the window structure may contain an invalid pointer, once the SalFrame is deleted.
624 SetWindowPtr(reinterpret_cast<HWND>(lParam), nullptr);
626 break;
628 CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEOBJECT, ImplSalCreateObject(
629 GetSalData()->mpInstance, reinterpret_cast<WinSalFrame*>(lParam)) )
630 CASE_NOYIELDLOCK( SAL_MSG_DESTROYOBJECT, delete reinterpret_cast<SalObject*>(lParam) )
631 CASE_NOYIELDLOCK_RESULT( SAL_MSG_GETCACHEDDC, GetDCEx(
632 reinterpret_cast<HWND>(wParam), nullptr, DCX_CACHE) )
633 CASE_NOYIELDLOCK( SAL_MSG_RELEASEDC, ReleaseDC(
634 reinterpret_cast<HWND>(wParam), reinterpret_cast<HDC>(lParam)) )
636 case SAL_MSG_TIMER_CALLBACK:
637 assert( pTimer != nullptr );
638 pTimer->ImplHandleTimerEvent( wParam );
639 break;
641 case WM_TIMER:
642 assert( pTimer != nullptr );
643 pTimer->ImplHandle_WM_TIMER( wParam );
644 break;
646 case SAL_MSG_FORCE_REAL_TIMER:
647 assert(pTimer != nullptr);
648 pTimer->SetForceRealTimer(true);
649 break;
651 case SAL_MSG_DUMMY:
652 break;
654 default:
655 rDef = true;
656 break;
659 return nRet;
662 #undef CASE_NOYIELDLOCK
663 #undef CASE_NOYIELDLOCK_RESULT
665 LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
667 bool bDef = false;
668 LRESULT nRet = 0;
669 __try
671 nRet = SalComWndProc( hWnd, nMsg, wParam, lParam, bDef );
673 __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
676 if ( bDef )
678 if ( !ImplHandleGlobalMsg( hWnd, nMsg, wParam, lParam, nRet ) )
679 nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam );
681 return nRet;
684 namespace {
686 struct MsgRange
688 UINT nStart;
689 UINT nEnd;
694 static std::vector<MsgRange> GetOtherRanges( VclInputFlags nType )
696 assert( nType != VCL_INPUT_ANY );
698 // this array must be kept sorted!
699 const UINT nExcludeMsgIds[] =
703 WM_MOVE, // 3
704 WM_SIZE, // 5
705 WM_PAINT, // 15
706 WM_KEYDOWN, // 256
707 WM_TIMER, // 275
709 WM_MOUSEFIRST, // 512
710 513,
711 514,
712 515,
713 516,
714 517,
715 518,
716 519,
717 520,
718 WM_MOUSELAST, // 521
720 SAL_MSG_POSTMOVE, // WM_USER+136
721 SAL_MSG_POSTCALLSIZE, // WM_USER+137
723 SAL_MSG_TIMER_CALLBACK, // WM_USER+162
725 UINT_MAX
727 const unsigned MAX_EXCL = SAL_N_ELEMENTS( nExcludeMsgIds );
729 bool aExcludeMsgList[ MAX_EXCL ] = { false, };
730 std::vector<MsgRange> aResult;
732 // set the excluded values
733 if ( !(nType & VclInputFlags::MOUSE) )
735 for ( unsigned i = 0; nExcludeMsgIds[ 6 + i ] <= WM_MOUSELAST; ++i )
736 aExcludeMsgList[ 6 + i ] = true;
739 if ( !(nType & VclInputFlags::KEYBOARD) )
740 aExcludeMsgList[ 4 ] = true;
742 if ( !(nType & VclInputFlags::PAINT) )
744 aExcludeMsgList[ 1 ] = true;
745 aExcludeMsgList[ 2 ] = true;
746 aExcludeMsgList[ 3 ] = true;
747 aExcludeMsgList[ 16 ] = true;
748 aExcludeMsgList[ 17 ] = true;
751 if ( !(nType & VclInputFlags::TIMER) )
753 aExcludeMsgList[ 5 ] = true;
754 aExcludeMsgList[ 18 ] = true;
757 // build the message ranges to check
758 MsgRange aRange = { 0, 0 };
759 bool doEnd = true;
760 for ( unsigned i = 1; i < MAX_EXCL; ++i )
762 if ( aExcludeMsgList[ i ] )
764 if ( !doEnd )
766 if ( nExcludeMsgIds[ i ] == aRange.nStart )
767 ++aRange.nStart;
768 else
769 doEnd = true;
771 if ( doEnd )
773 aRange.nEnd = nExcludeMsgIds[ i ] - 1;
774 aResult.push_back( aRange );
775 doEnd = false;
776 aRange.nStart = aRange.nEnd + 2;
781 if ( aRange.nStart != UINT_MAX )
783 aRange.nEnd = UINT_MAX;
784 aResult.push_back( aRange );
787 return aResult;
790 bool WinSalInstance::AnyInput( VclInputFlags nType )
792 MSG aMsg;
794 if ( nType & VclInputFlags::TIMER )
796 const WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
797 if ( pTimer && pTimer->HasTimerElapsed() )
798 return true;
801 if ( (nType & VCL_INPUT_ANY) == VCL_INPUT_ANY )
803 // revert bugfix for #108919# which never reported timeouts when called from the timer handler
804 // which made the application completely unresponsive during background formatting
805 if ( PeekMessageW( &aMsg, nullptr, 0, 0, PM_NOREMOVE | PM_NOYIELD ) )
806 return true;
808 else
810 const bool bCheck_KEYBOARD (nType & VclInputFlags::KEYBOARD);
811 const bool bCheck_OTHER (nType & VclInputFlags::OTHER);
813 // If there is a modifier key event, it counts as OTHER
814 // Previously we were simply ignoring these events...
815 if ( bCheck_KEYBOARD || bCheck_OTHER )
817 if ( PeekMessageW( &aMsg, nullptr, WM_KEYDOWN, WM_KEYDOWN,
818 PM_NOREMOVE | PM_NOYIELD ) )
820 const bool bIsModifier = ( (aMsg.wParam == VK_SHIFT) ||
821 (aMsg.wParam == VK_CONTROL) || (aMsg.wParam == VK_MENU) );
822 if ( bCheck_KEYBOARD && !bIsModifier )
823 return true;
824 if ( bCheck_OTHER && bIsModifier )
825 return true;
829 // Other checks for all messages not excluded.
830 // The less we exclude, the less ranges have to be checked!
831 if ( bCheck_OTHER )
833 // TIMER and KEYBOARD are already handled, so always exclude them!
834 VclInputFlags nOtherType = nType &
835 ~VclInputFlags(VclInputFlags::KEYBOARD | VclInputFlags::TIMER);
837 std::vector<MsgRange> aMsgRangeList( GetOtherRanges( nOtherType ) );
838 for ( MsgRange const & aRange : aMsgRangeList )
839 if ( PeekMessageW( &aMsg, nullptr, aRange.nStart,
840 aRange.nEnd, PM_NOREMOVE | PM_NOYIELD ) )
841 return true;
843 // MOUSE and PAINT already handled, so skip further checks
844 return false;
847 if ( nType & VclInputFlags::MOUSE )
849 if ( PeekMessageW( &aMsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST,
850 PM_NOREMOVE | PM_NOYIELD ) )
851 return true;
854 if ( nType & VclInputFlags::PAINT )
856 if ( PeekMessageW( &aMsg, nullptr, WM_PAINT, WM_PAINT,
857 PM_NOREMOVE | PM_NOYIELD ) )
858 return true;
860 if ( PeekMessageW( &aMsg, nullptr, WM_SIZE, WM_SIZE,
861 PM_NOREMOVE | PM_NOYIELD ) )
862 return true;
864 if ( PeekMessageW( &aMsg, nullptr, SAL_MSG_POSTCALLSIZE, SAL_MSG_POSTCALLSIZE,
865 PM_NOREMOVE | PM_NOYIELD ) )
866 return true;
868 if ( PeekMessageW( &aMsg, nullptr, WM_MOVE, WM_MOVE,
869 PM_NOREMOVE | PM_NOYIELD ) )
870 return true;
872 if ( PeekMessageW( &aMsg, nullptr, SAL_MSG_POSTMOVE, SAL_MSG_POSTMOVE,
873 PM_NOREMOVE | PM_NOYIELD ) )
874 return true;
878 return false;
881 SalFrame* WinSalInstance::CreateChildFrame( SystemParentData* pSystemParentData, SalFrameStyleFlags nSalFrameStyle )
883 // to switch to Main-Thread
884 return reinterpret_cast<SalFrame*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEFRAME, static_cast<WPARAM>(nSalFrameStyle), reinterpret_cast<LPARAM>(pSystemParentData->hWnd) )));
887 SalFrame* WinSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle )
889 // to switch to Main-Thread
890 HWND hWndParent;
891 if ( pParent )
892 hWndParent = static_cast<WinSalFrame*>(pParent)->mhWnd;
893 else
894 hWndParent = nullptr;
895 return reinterpret_cast<SalFrame*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEFRAME, static_cast<WPARAM>(nSalFrameStyle), reinterpret_cast<LPARAM>(hWndParent) )));
898 void WinSalInstance::DestroyFrame( SalFrame* pFrame )
900 OpenGLContext::prepareForYield();
901 SendMessageW( mhComWnd, SAL_MSG_DESTROYFRAME, 0, reinterpret_cast<LPARAM>(pFrame) );
904 SalObject* WinSalInstance::CreateObject( SalFrame* pParent,
905 SystemWindowData* /*pWindowData*/, // SystemWindowData meaningless on Windows
906 bool /*bShow*/ )
908 // to switch to Main-Thread
909 return reinterpret_cast<SalObject*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEOBJECT, 0, reinterpret_cast<LPARAM>(static_cast<WinSalFrame*>(pParent)) )));
912 void WinSalInstance::DestroyObject( SalObject* pObject )
914 SendMessageW( mhComWnd, SAL_MSG_DESTROYOBJECT, 0, reinterpret_cast<LPARAM>(pObject) );
917 OUString WinSalInstance::GetConnectionIdentifier()
919 return OUString();
922 /** Add a file to the system shells recent document list if there is any.
923 This function may have no effect under Unix because there is no
924 standard API among the different desktop managers.
926 @param aFileUrl
927 The file url of the document.
929 void WinSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& rDocumentService)
931 if (Application::IsHeadlessModeEnabled())
932 return;
934 OUString system_path;
935 osl::FileBase::RC rc = osl::FileBase::getSystemPathFromFileURL(rFileUrl, system_path);
937 OSL_ENSURE(osl::FileBase::E_None == rc, "Invalid file url");
939 if (osl::FileBase::E_None == rc)
941 IShellItem* pShellItem = nullptr;
943 HRESULT hr = SHCreateItemFromParsingName(o3tl::toW(system_path.getStr()), nullptr, IID_PPV_ARGS(&pShellItem));
945 if ( SUCCEEDED(hr) && pShellItem )
947 OUString sApplicationName;
949 if ( rDocumentService == "com.sun.star.text.TextDocument" ||
950 rDocumentService == "com.sun.star.text.GlobalDocument" ||
951 rDocumentService == "com.sun.star.text.WebDocument" ||
952 rDocumentService == "com.sun.star.xforms.XMLFormDocument" )
953 sApplicationName = "Writer";
954 else if ( rDocumentService == "com.sun.star.sheet.SpreadsheetDocument" ||
955 rDocumentService == "com.sun.star.chart2.ChartDocument" )
956 sApplicationName = "Calc";
957 else if ( rDocumentService == "com.sun.star.presentation.PresentationDocument" )
958 sApplicationName = "Impress";
959 else if ( rDocumentService == "com.sun.star.drawing.DrawingDocument" )
960 sApplicationName = "Draw";
961 else if ( rDocumentService == "com.sun.star.formula.FormulaProperties" )
962 sApplicationName = "Math";
963 else if ( rDocumentService == "com.sun.star.sdb.DatabaseDocument" ||
964 rDocumentService == "com.sun.star.sdb.OfficeDatabaseDocument" ||
965 rDocumentService == "com.sun.star.sdb.RelationDesign" ||
966 rDocumentService == "com.sun.star.sdb.QueryDesign" ||
967 rDocumentService == "com.sun.star.sdb.TableDesign" ||
968 rDocumentService == "com.sun.star.sdb.DataSourceBrowser" )
969 sApplicationName = "Base";
971 if ( !sApplicationName.isEmpty() )
973 OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplicationName);
975 SHARDAPPIDINFO info;
976 info.psi = pShellItem;
977 info.pszAppID = o3tl::toW(sApplicationID.getStr());
979 SHAddToRecentDocs ( SHARD_APPIDINFO, &info );
980 return;
983 // For whatever reason, we could not use the SHARD_APPIDINFO semantics
984 SHAddToRecentDocs(SHARD_PATHW, system_path.getStr());
988 SalTimer* WinSalInstance::CreateSalTimer()
990 return new WinSalTimer();
993 std::shared_ptr<SalBitmap> WinSalInstance::CreateSalBitmap()
995 #if HAVE_FEATURE_SKIA
996 if (SkiaHelper::isVCLSkiaEnabled())
997 return std::make_shared<SkiaSalBitmap>();
998 else
999 #endif
1000 if (OpenGLHelper::isVCLOpenGLEnabled())
1001 return std::make_shared<OpenGLSalBitmap>();
1002 else
1003 return std::make_shared<WinSalBitmap>();
1006 int WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(int, LPEXCEPTION_POINTERS pExceptionInfo)
1008 // Decide if an exception is a c++ (mostly UNO) exception or a process violation.
1009 // Depending on this information we pass process violations directly to our signal handler ...
1010 // and c++ (UNO) exceptions are sended to the following code on the current stack.
1011 // Problem behind: user32.dll sometime consumes exceptions/process violations .-)
1012 // see also #112221#
1014 static const DWORD EXCEPTION_MSC_CPP_EXCEPTION = 0xE06D7363;
1016 if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_MSC_CPP_EXCEPTION)
1017 return EXCEPTION_CONTINUE_SEARCH;
1019 return UnhandledExceptionFilter( pExceptionInfo );
1022 typedef LONG NTSTATUS;
1023 typedef NTSTATUS(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
1024 constexpr NTSTATUS STATUS_SUCCESS = 0x00000000;
1026 OUString WinSalInstance::getOSVersion()
1028 OUStringBuffer aVer(50); // capacity for string like "Windows 6.1 Service Pack 1 build 7601"
1029 aVer.append("Windows ");
1030 // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
1031 // subject to manifest-based behavior since Windows 8.1, so give wrong results.
1032 // Another approach would be to use NetWkstaGetInfo, but that has some small
1033 // reported delays (some milliseconds), and might get slower in domains with
1034 // poor network connections.
1035 // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
1036 bool bHaveVerFromKernel32 = false;
1037 if (HMODULE h_kernel32 = GetModuleHandleW(L"kernel32.dll"))
1039 wchar_t szPath[MAX_PATH];
1040 DWORD dwCount = GetModuleFileNameW(h_kernel32, szPath, SAL_N_ELEMENTS(szPath));
1041 if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath))
1043 dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
1044 if (dwCount != 0)
1046 std::unique_ptr<char[]> ver(new char[dwCount]);
1047 if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
1049 void* pBlock = nullptr;
1050 UINT dwBlockSz = 0;
1051 if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
1053 VS_FIXEDFILEINFO* vi1 = static_cast<VS_FIXEDFILEINFO*>(pBlock);
1054 aVer.append(OUString::number(HIWORD(vi1->dwProductVersionMS)) + "."
1055 + OUString::number(LOWORD(vi1->dwProductVersionMS)));
1056 bHaveVerFromKernel32 = true;
1062 // Now use RtlGetVersion (which is not subject to deprecation for GetVersion(Ex) API)
1063 // to get build number and SP info
1064 bool bHaveVerFromRtlGetVersion = false;
1065 if (HMODULE h_ntdll = GetModuleHandleW(L"ntdll.dll"))
1067 if (auto RtlGetVersion
1068 = reinterpret_cast<RtlGetVersion_t>(GetProcAddress(h_ntdll, "RtlGetVersion")))
1070 RTL_OSVERSIONINFOW vi2{}; // initialize with zeroes - a better alternative to memset
1071 vi2.dwOSVersionInfoSize = sizeof(vi2);
1072 if (STATUS_SUCCESS == RtlGetVersion(&vi2))
1074 if (!bHaveVerFromKernel32) // we failed above; let's hope this would be useful
1075 aVer.append(OUString::number(vi2.dwMajorVersion) + "."
1076 + OUString::number(vi2.dwMinorVersion));
1077 aVer.append(" ");
1078 if (vi2.szCSDVersion[0])
1079 aVer.append(o3tl::toU(vi2.szCSDVersion)).append(" ");
1080 aVer.append("Build " + OUString::number(vi2.dwBuildNumber));
1081 bHaveVerFromRtlGetVersion = true;
1085 if (!bHaveVerFromKernel32 && !bHaveVerFromRtlGetVersion)
1086 aVer.append("unknown");
1087 return aVer.makeStringAndClear();
1090 std::shared_ptr<vcl::BackendCapabilities> WinSalInstance::GetBackendCapabilities()
1092 auto pBackendCapabilities = SalInstance::GetBackendCapabilities();
1093 #if HAVE_FEATURE_SKIA
1094 #if SKIA_USE_BITMAP32
1095 if( SkiaHelper::isVCLSkiaEnabled())
1096 pBackendCapabilities->mbSupportsBitmap32 = true;
1097 #endif
1098 #endif
1099 return pBackendCapabilities;
1102 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */