1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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/processfactory.hxx>
31 #include <comphelper/solarmutex.hxx>
32 #include <comphelper/windowserrorstring.hxx>
33 #include <com/sun/star/uno/Reference.h>
34 #include <o3tl/char16_t2wchar_t.hxx>
36 #include <dndhelper.hxx>
37 #include <vcl/inputtypes.hxx>
38 #include <vcl/opengl/OpenGLContext.hxx>
39 #include <vcl/sysdata.hxx>
40 #include <vcl/timer.hxx>
41 #include <vclpluginapi.h>
43 #include <win/dnd_source.hxx>
44 #include <win/dnd_target.hxx>
45 #include <win/wincomp.hxx>
46 #include <win/salids.hrc>
47 #include <win/saldata.hxx>
48 #include <win/salinst.h>
49 #include <win/salframe.h>
50 #include <win/salobj.h>
51 #include <win/saltimer.h>
52 #include <win/salbmp.h>
53 #include <win/winlayout.hxx>
55 #include <config_features.h>
56 #include <vcl/skia/SkiaHelper.hxx>
58 #include <config_skia.h>
59 #include <skia/salbmp.hxx>
60 #include <skia/win/gdiimpl.hxx>
65 #include <desktop/crashreport.hxx>
74 static LRESULT CALLBACK
SalComWndProcW( HWND hWnd
, UINT nMsg
, WPARAM wParam
, LPARAM lParam
);
76 class SalYieldMutex
: public comphelper::SolarMutex
78 public: // for ImplSalYield() and ImplSalYieldMutexAcquireWithWait()
79 osl::Condition m_condition
; /// for MsgWaitForMultipleObjects()
82 virtual void doAcquire( sal_uInt32 nLockCount
) override
;
83 virtual sal_uInt32
doRelease( bool bUnlockAll
) override
;
85 static void BeforeReleaseHandler();
88 explicit SalYieldMutex();
90 virtual bool IsCurrentThread() const override
;
91 virtual bool tryToAcquire() override
;
94 SalYieldMutex::SalYieldMutex()
96 SetBeforeReleaseHandler( &SalYieldMutex::BeforeReleaseHandler
);
99 void SalYieldMutex::BeforeReleaseHandler()
101 OpenGLContext::prepareForYield();
103 if ( GetSalData()->mnAppThreadId
!= GetCurrentThreadId() )
105 // If we don't call these message, the Output from the
106 // Java clients doesn't come in the right order
111 /// note: while VCL is fully up and running (other threads started and
112 /// before shutdown), the main thread must acquire SolarMutex only via
113 /// this function to avoid deadlock
114 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount
)
116 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
117 if ( pInst
&& pInst
->IsMainThread() )
119 if ( pInst
->m_nNoYieldLock
)
121 // tdf#96887 If this is the main thread, then we must wait for two things:
122 // - the yield mutex being unlocked
123 // - SendMessage() being triggered
124 // This can nicely be done using MsgWaitForMultipleObjects, which is called in
125 // m_condition.wait(). The 2nd one is
126 // needed because if we don't reschedule, then we create deadlocks if a
127 // Window's create/destroy is called via SendMessage() from another thread.
128 // Have a look at the osl_waitCondition implementation for more info.
130 // Calling Condition::reset frequently turns out to be a little expensive,
131 // and the vast majority of the time there is no contention, so first
132 // try just acquiring the mutex.
133 if (m_aMutex
.tryToAcquire())
135 // reset condition *before* acquiring!
137 if (m_aMutex
.tryToAcquire())
139 // wait for SalYieldMutex::release() to set the condition
140 osl::Condition::Result res
= m_condition
.wait();
141 assert(osl::Condition::Result::result_ok
== res
);
151 comphelper::SolarMutex::doAcquire( nLockCount
);
154 sal_uInt32
SalYieldMutex::doRelease( const bool bUnlockAll
)
156 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
157 if ( pInst
&& pInst
->m_nNoYieldLock
&& pInst
->IsMainThread() )
160 sal_uInt32 nCount
= comphelper::SolarMutex::doRelease( bUnlockAll
);
161 // wake up ImplSalYieldMutexAcquireWithWait() after release
167 bool SalYieldMutex::tryToAcquire()
169 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
172 if ( pInst
->m_nNoYieldLock
&& pInst
->IsMainThread() )
175 return comphelper::SolarMutex::tryToAcquire();
181 void ImplSalYieldMutexAcquireWithWait( sal_uInt32 nCount
)
183 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
185 pInst
->GetYieldMutex()->acquire( nCount
);
188 bool ImplSalYieldMutexTryToAcquire()
190 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
191 return pInst
&& pInst
->GetYieldMutex()->tryToAcquire();
194 void ImplSalYieldMutexRelease()
196 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
200 pInst
->GetYieldMutex()->release();
204 bool SalYieldMutex::IsCurrentThread() const
206 if ( !GetSalData()->mpInstance
->m_nNoYieldLock
)
207 return SolarMutex::IsCurrentThread();
209 return GetSalData()->mpInstance
->IsMainThread();
212 void SalData::initKeyCodeMap()
215 #define initKey( a, b )\
216 nKey = LOWORD( VkKeyScanW( a ) );\
222 initKey( L
'+', KEY_ADD
);
223 initKey( L
'-', KEY_SUBTRACT
);
224 initKey( L
'*', KEY_MULTIPLY
);
225 initKey( L
'/', KEY_DIVIDE
);
226 initKey( L
'.', KEY_POINT
);
227 initKey( L
',', KEY_COMMA
);
228 initKey( L
'<', KEY_LESS
);
229 initKey( L
'>', KEY_GREATER
);
230 initKey( L
'=', KEY_EQUAL
);
231 initKey( L
'~', KEY_TILDE
);
232 initKey( L
'`', KEY_QUOTELEFT
);
233 initKey( L
'[', KEY_BRACKETLEFT
);
234 initKey( L
']', KEY_BRACKETRIGHT
);
235 initKey( L
';', KEY_SEMICOLON
);
236 initKey( L
'\'', KEY_QUOTERIGHT
);
237 initKey( L
'}', KEY_RIGHTCURLYBRACKET
);
238 initKey( L
'#', KEY_NUMBERSIGN
);
239 initKey( L
':', KEY_COLON
);
245 : sal::systools::CoInitializeGuard(COINIT_APARTMENTTHREADED
, false,
246 sal::systools::CoInitializeGuard::WhenFailed::NoThrow
)
247 // put main thread in Single Threaded Apartment (STA)
249 mhInst
= nullptr; // default instance handle
250 mnCmdShow
= 0; // default frame show style
251 mhDitherPal
= nullptr; // dither palette
252 mhDitherDIB
= nullptr; // dither memory handle
253 mpDitherDIB
= nullptr; // dither memory
254 mpDitherDIBData
= nullptr; // beginning of DIB data
255 mpDitherDiff
= nullptr; // Dither mapping table
256 mpDitherLow
= nullptr; // Dither mapping table
257 mpDitherHigh
= nullptr; // Dither mapping table
258 mhSalObjMsgHook
= nullptr; // hook to get interesting msg for SalObject
259 mhWantLeaveMsg
= nullptr; // window handle, that want a MOUSELEAVE message
260 mpMouseLeaveTimer
= nullptr; // Timer for MouseLeave Test
261 mpInstance
= nullptr; // pointer of first instance
262 mpFirstFrame
= nullptr; // pointer of first frame
263 mpFirstObject
= nullptr; // pointer of first object window
264 mpFirstVD
= nullptr; // first VirDev
265 mpFirstPrinter
= nullptr; // first printing printer
266 mpHDCCache
= nullptr; // Cache for three DC's
267 mh50Bmp
= nullptr; // 50% Bitmap
268 mh50Brush
= nullptr; // 50% Brush
270 for(i
=0; i
<MAX_STOCKPEN
; i
++)
272 maStockPenColorAry
[i
] = 0;
273 mhStockPenAry
[i
] = nullptr;
275 for(i
=0; i
<MAX_STOCKBRUSH
; i
++)
277 maStockBrushColorAry
[i
] = 0;
278 mhStockBrushAry
[i
] = nullptr;
280 mnStockPenCount
= 0; // count of static pens
281 mnStockBrushCount
= 0; // count of static brushes
282 mnSalObjWantKeyEvt
= 0; // KeyEvent for the SalObj hook
283 mnCacheDCInUse
= 0; // count of CacheDC in use
284 mbObjClassInit
= false; // is SALOBJECTCLASS initialised
285 mbInPalChange
= false; // is in WM_QUERYNEWPALETTE
286 mnAppThreadId
= 0; // Id from Application-Thread
287 mpFirstIcon
= nullptr; // icon cache, points to first icon, NULL if none
288 mpSharedTempFontItem
= nullptr;
289 mpOtherTempFontItem
= nullptr;
290 mbThemeChanged
= false; // true if visual theme was changed: throw away theme handles
291 mbThemeMenuSupport
= false;
301 static Gdiplus::GdiplusStartupInput gdiplusStartupInput
;
302 Gdiplus::GdiplusStartup(&gdiplusToken
, &gdiplusStartupInput
, nullptr);
308 SetSalData( nullptr );
311 Gdiplus::GdiplusShutdown(gdiplusToken
);
314 bool OSSupportsDarkMode()
317 if (HMODULE h_ntdll
= GetModuleHandleW(L
"ntdll.dll"))
319 typedef LONG(WINAPI
* RtlGetVersion_t
)(PRTL_OSVERSIONINFOW
);
320 if (auto RtlGetVersion
321 = reinterpret_cast<RtlGetVersion_t
>(GetProcAddress(h_ntdll
, "RtlGetVersion")))
323 RTL_OSVERSIONINFOW vi2
{};
324 vi2
.dwOSVersionInfoSize
= sizeof(vi2
);
325 if (RtlGetVersion(&vi2
) == 0)
327 if (vi2
.dwMajorVersion
> 10)
329 else if (vi2
.dwMajorVersion
== 10)
331 if (vi2
.dwMinorVersion
> 0)
333 else if (vi2
.dwBuildNumber
>= 18362)
344 enum PreferredAppMode
356 VCLPLUG_WIN_PUBLIC SalInstance
* create_SalInstance()
358 SalData
* pSalData
= new SalData();
361 aSI
.cb
= sizeof( aSI
);
362 GetStartupInfoW( &aSI
);
363 pSalData
->mhInst
= GetModuleHandleW( nullptr );
364 pSalData
->mnCmdShow
= aSI
.wShowWindow
;
366 pSalData
->mnAppThreadId
= GetCurrentThreadId();
368 static bool bSetAllowDarkMode
= OSSupportsDarkMode(); // too early to additionally check LibreOffice's config
369 if (bSetAllowDarkMode
)
371 typedef PreferredAppMode(WINAPI
* SetPreferredAppMode_t
)(PreferredAppMode
);
372 if (HINSTANCE hUxthemeLib
= LoadLibraryExW(L
"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32
))
374 if (auto SetPreferredAppMode
= reinterpret_cast<SetPreferredAppMode_t
>(GetProcAddress(hUxthemeLib
, MAKEINTRESOURCEA(135))))
375 SetPreferredAppMode(AllowDark
);
376 FreeLibrary(hUxthemeLib
);
380 // register frame class
381 WNDCLASSEXW aWndClassEx
;
382 aWndClassEx
.cbSize
= sizeof( aWndClassEx
);
383 aWndClassEx
.style
= CS_OWNDC
;
384 aWndClassEx
.lpfnWndProc
= SalFrameWndProcW
;
385 aWndClassEx
.cbClsExtra
= 0;
386 aWndClassEx
.cbWndExtra
= SAL_FRAME_WNDEXTRA
;
387 aWndClassEx
.hInstance
= pSalData
->mhInst
;
388 aWndClassEx
.hCursor
= nullptr;
389 aWndClassEx
.hbrBackground
= nullptr;
390 aWndClassEx
.lpszMenuName
= nullptr;
391 aWndClassEx
.lpszClassName
= SAL_FRAME_CLASSNAMEW
;
392 ImplLoadSalIcon( SAL_RESID_ICON_DEFAULT
, aWndClassEx
.hIcon
, aWndClassEx
.hIconSm
);
393 if ( !RegisterClassExW( &aWndClassEx
) )
396 aWndClassEx
.hIcon
= nullptr;
397 aWndClassEx
.hIconSm
= nullptr;
398 aWndClassEx
.style
|= CS_SAVEBITS
;
399 aWndClassEx
.lpszClassName
= SAL_SUBFRAME_CLASSNAMEW
;
400 if ( !RegisterClassExW( &aWndClassEx
) )
403 // shadow effect for popups on XP
404 aWndClassEx
.style
|= CS_DROPSHADOW
;
405 aWndClassEx
.lpszClassName
= SAL_TMPSUBFRAME_CLASSNAMEW
;
406 if ( !RegisterClassExW( &aWndClassEx
) )
409 aWndClassEx
.style
= 0;
410 aWndClassEx
.lpfnWndProc
= SalComWndProcW
;
411 aWndClassEx
.cbWndExtra
= 0;
412 aWndClassEx
.lpszClassName
= SAL_COM_CLASSNAMEW
;
413 if ( !RegisterClassExW( &aWndClassEx
) )
416 HWND hComWnd
= CreateWindowExW( WS_EX_TOOLWINDOW
, SAL_COM_CLASSNAMEW
,
417 L
"", WS_POPUP
, 0, 0, 0, 0, nullptr, nullptr,
418 pSalData
->mhInst
, nullptr );
422 WinSalInstance
* pInst
= new WinSalInstance
;
424 // init instance (only one instance in this version !!!)
425 pSalData
->mpInstance
= pInst
;
426 pInst
->mhInst
= pSalData
->mhInst
;
427 pInst
->mhComWnd
= hComWnd
;
429 // init static GDI Data
436 WinSalInstance::WinSalInstance()
437 : SalInstance(std::make_unique
<SalYieldMutex
>())
439 , mhComWnd( nullptr )
440 , m_nNoYieldLock( 0 )
442 ImplSVData
* pSVData
= ImplGetSVData();
443 pSVData
->maAppData
.mxToolkitName
= OUString("win");
444 m_bSupportsOpenGL
= true;
445 #if HAVE_FEATURE_SKIA
446 WinSkiaSalGraphicsImpl::prepareSkia();
447 #if SKIA_USE_BITMAP32
448 if (SkiaHelper::isVCLSkiaEnabled())
449 m_bSupportsBitmap32
= true;
454 WinSalInstance::~WinSalInstance()
457 DestroyWindow( mhComWnd
);
458 #if HAVE_FEATURE_SKIA
459 SkiaHelper::cleanup();
463 void WinSalInstance::AfterAppInit()
465 // (1) Ideally this would be done at the place that creates the thread, but since this thread is normally
466 // just the default/main thread, that is not possible.
467 // (2) Don't do this on unix, where it causes tools like pstree on Linux to
468 // confusingly report soffice.bin as VCL Main instead.
469 osl_setThreadName("VCL Main");
472 static LRESULT
ImplSalDispatchMessage( const MSG
* pMsg
)
474 SalData
* pSalData
= GetSalData();
475 if ( pSalData
->mpFirstObject
&& ImplSalPreDispatchMsg( pMsg
) )
477 LRESULT lResult
= DispatchMessageW( pMsg
);
478 if ( pSalData
->mpFirstObject
)
479 ImplSalPostDispatchMsg( pMsg
);
483 // probably can't be static, because of SalTimer friend? (static gives C4211)
484 bool ImplSalYield(const bool bWait
, const bool bHandleAllCurrentEvents
)
486 // used to abort further message processing on tick count wraps
487 static sal_uInt32 nLastTicks
= 0;
489 // we should never yield in m_nNoYieldLock mode!
490 const bool bNoYieldLock
= (GetSalData()->mpInstance
->m_nNoYieldLock
> 0);
491 assert(!bNoYieldLock
);
496 bool bWasMsg
= false, bWasTimeoutMsg
= false;
497 WinSalTimer
* pTimer
= static_cast<WinSalTimer
*>(ImplGetSVData()->maSchedCtx
.mpSalTimer
);
499 sal_uInt32 nCurTicks
= GetTickCount();
503 if (!PeekMessageW(&aMsg
, nullptr, 0, 0, PM_REMOVE
))
507 TranslateMessage(&aMsg
);
508 LRESULT nRet
= ImplSalDispatchMessage(&aMsg
);
510 bWasTimeoutMsg
|= (SAL_MSG_TIMER_CALLBACK
== aMsg
.message
) && static_cast<bool>(nRet
);
512 if (!bHandleAllCurrentEvents
)
515 if ((aMsg
.time
> nCurTicks
) && (nLastTicks
<= nCurTicks
|| aMsg
.time
< nLastTicks
))
520 // 0ms timeouts are handled out-of-bounds to prevent busy-locking the
521 // event loop with timeout messages.
522 // We ensure we never handle more than one timeout per call.
523 // This way we'll always process a normal system message.
524 if ( !bWasTimeoutMsg
&& pTimer
&& pTimer
->IsDirectTimeout() )
526 pTimer
->ImplHandleElapsedTimer();
530 nLastTicks
= nCurTicks
;
532 if ( bWait
&& !bWasMsg
)
534 switch (GetMessageW(&aMsg
, nullptr, 0, 0))
537 SAL_WARN("vcl.schedule", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
538 // should we std::abort() / SalAbort here?
541 SAL_INFO("vcl.schedule", "GetMessageW received WM_QUIT while waiting");
545 TranslateMessage(&aMsg
);
546 ImplSalDispatchMessage(&aMsg
);
551 // If we enabled ForceRealTimer mode skipping our direct timeout processing,
552 // mainly because some Windows API call spawns its own nested message loop,
553 // switch back to our own processing (like after window resize or move)
555 pTimer
->SetForceRealTimer( false );
560 bool WinSalInstance::IsMainThread() const
562 const SalData
* pSalData
= GetSalData();
563 return pSalData
->mnAppThreadId
== GetCurrentThreadId();
566 bool WinSalInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
568 bool bDidWork
= false;
569 SolarMutexReleaser aReleaser
;
570 if ( !IsMainThread() )
572 bDidWork
= SendMessageW( mhComWnd
, SAL_MSG_THREADYIELD
,
573 WPARAM(false), static_cast<LPARAM
>(bHandleAllCurrentEvents
) );
574 if ( !bDidWork
&& bWait
)
576 maWaitingYieldCond
.reset();
577 maWaitingYieldCond
.wait();
583 bDidWork
= ImplSalYield( bWait
, bHandleAllCurrentEvents
);
585 maWaitingYieldCond
.set();
591 #define CASE_NOYIELDLOCK( salmsg, function ) \
593 if (bIsOtherThreadMessage) \
595 ++pInst->m_nNoYieldLock; \
597 --pInst->m_nNoYieldLock; \
601 DBG_TESTSOLARMUTEX(); \
606 #define CASE_NOYIELDLOCK_RESULT( salmsg, function ) \
608 if (bIsOtherThreadMessage) \
610 ++pInst->m_nNoYieldLock; \
611 nRet = reinterpret_cast<LRESULT>( function ); \
612 --pInst->m_nNoYieldLock; \
616 DBG_TESTSOLARMUTEX(); \
617 nRet = reinterpret_cast<LRESULT>( function ); \
621 LRESULT CALLBACK
SalComWndProc( HWND
, UINT nMsg
, WPARAM wParam
, LPARAM lParam
, bool& rDef
)
623 const bool bIsOtherThreadMessage
= InSendMessage();
625 WinSalInstance
*pInst
= GetSalData()->mpInstance
;
626 WinSalTimer
*const pTimer
= static_cast<WinSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
628 SAL_INFO("vcl.gdi.wndproc", "SalComWndProc(nMsg=" << nMsg
<< ", wParam=" << wParam
629 << ", lParam=" << lParam
<< "); inSendMsg: " << bIsOtherThreadMessage
);
631 if (ImplGetSVData()->mbDeInit
)
633 SAL_WARN("vcl.gdi.wndproc", "ignoring timer event because we are shutting down");
639 case SAL_MSG_THREADYIELD
:
640 assert( !static_cast<bool>(wParam
) );
641 nRet
= static_cast<LRESULT
>(ImplSalYield(
642 false, static_cast<bool>( lParam
) ));
645 case SAL_MSG_STARTTIMER
:
647 auto const nParam
= static_cast<sal_uInt64
>( lParam
);
648 sal_uInt64 nTime
= tools::Time::GetSystemTicks();
649 if ( nTime
< nParam
)
650 nTime
= nParam
- nTime
;
653 assert( pTimer
!= nullptr );
654 pTimer
->ImplStart( nTime
);
658 case SAL_MSG_STOPTIMER
:
659 assert( pTimer
!= nullptr );
663 CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEFRAME
, ImplSalCreateFrame( GetSalData()->mpInstance
,
664 reinterpret_cast<HWND
>(lParam
), static_cast<SalFrameStyleFlags
>(wParam
)) )
665 CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATEHWND
, ImplSalReCreateHWND(
666 reinterpret_cast<HWND
>(wParam
), reinterpret_cast<HWND
>(lParam
), false) )
667 CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATECHILDHWND
, ImplSalReCreateHWND(
668 reinterpret_cast<HWND
>(wParam
), reinterpret_cast<HWND
>(lParam
), true) )
669 CASE_NOYIELDLOCK( SAL_MSG_DESTROYFRAME
, delete reinterpret_cast<SalFrame
*>(lParam
) )
671 case SAL_MSG_DESTROYHWND
:
672 // We only destroy the native window here. We do NOT destroy the SalFrame contained
673 // in the structure (GetWindowPtr()).
674 if (DestroyWindow(reinterpret_cast<HWND
>(lParam
)) == 0)
676 OSL_FAIL("DestroyWindow failed!");
677 // Failure: We remove the SalFrame from the window structure. So we avoid that
678 // the window structure may contain an invalid pointer, once the SalFrame is deleted.
679 SetWindowPtr(reinterpret_cast<HWND
>(lParam
), nullptr);
683 CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEOBJECT
, ImplSalCreateObject(
684 GetSalData()->mpInstance
, reinterpret_cast<WinSalFrame
*>(lParam
)) )
685 CASE_NOYIELDLOCK( SAL_MSG_DESTROYOBJECT
, delete reinterpret_cast<SalObject
*>(lParam
) )
686 CASE_NOYIELDLOCK_RESULT( SAL_MSG_GETCACHEDDC
, GetDCEx(
687 reinterpret_cast<HWND
>(wParam
), nullptr, DCX_CACHE
) )
688 CASE_NOYIELDLOCK( SAL_MSG_RELEASEDC
, ReleaseDC(
689 reinterpret_cast<HWND
>(wParam
), reinterpret_cast<HDC
>(lParam
)) )
691 case SAL_MSG_TIMER_CALLBACK
:
692 assert( pTimer
!= nullptr );
693 pTimer
->ImplHandleTimerEvent( wParam
);
697 assert( pTimer
!= nullptr );
698 pTimer
->ImplHandle_WM_TIMER( wParam
);
701 case SAL_MSG_FORCE_REAL_TIMER
:
702 assert(pTimer
!= nullptr);
703 pTimer
->SetForceRealTimer(true);
717 #undef CASE_NOYIELDLOCK
718 #undef CASE_NOYIELDLOCK_RESULT
720 LRESULT CALLBACK
SalComWndProcW( HWND hWnd
, UINT nMsg
, WPARAM wParam
, LPARAM lParam
)
726 nRet
= SalComWndProc( hWnd
, nMsg
, wParam
, lParam
, bDef
);
728 __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
733 if ( !ImplHandleGlobalMsg( hWnd
, nMsg
, wParam
, lParam
, nRet
) )
734 nRet
= DefWindowProcW( hWnd
, nMsg
, wParam
, lParam
);
739 bool WinSalInstance::AnyInput( VclInputFlags nType
)
741 if ( nType
& VclInputFlags::TIMER
)
743 const WinSalTimer
* pTimer
= static_cast<WinSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
744 if ( pTimer
&& pTimer
->HasTimerElapsed() )
748 // Note: Do not use PeekMessage(), despite the name it may dispatch events,
749 // even with PM_NOREMOVE specified, which may lead to unwanted recursion.
751 if ( (nType
& VCL_INPUT_ANY
) == VCL_INPUT_ANY
)
753 // revert bugfix for #108919# which never reported timeouts when called from the timer handler
754 // which made the application completely unresponsive during background formatting
755 if ( GetQueueStatus( QS_ALLEVENTS
))
762 // This code previously considered modifier keys as OTHER,
763 // but that makes this hard to do without PeekMessage,
764 // is inconsistent with the X11 backend, and I see no good reason.
765 if ( nType
& VclInputFlags::KEYBOARD
)
768 if ( nType
& VclInputFlags::MOUSE
)
771 if ( nType
& VclInputFlags::PAINT
)
774 if ( nType
& VclInputFlags::TIMER
)
777 if( nType
& VclInputFlags::OTHER
)
778 flags
|= QS_ALLEVENTS
& ~QS_KEY
& ~QS_MOUSE
& ~QS_PAINT
& ~QS_TIMER
;
780 if( GetQueueStatus( flags
))
787 SalFrame
* WinSalInstance::CreateChildFrame( SystemParentData
* pSystemParentData
, SalFrameStyleFlags nSalFrameStyle
)
789 // to switch to Main-Thread
790 return reinterpret_cast<SalFrame
*>(static_cast<sal_IntPtr
>(SendMessageW( mhComWnd
, SAL_MSG_CREATEFRAME
, static_cast<WPARAM
>(nSalFrameStyle
), reinterpret_cast<LPARAM
>(pSystemParentData
->hWnd
) )));
793 SalFrame
* WinSalInstance::CreateFrame( SalFrame
* pParent
, SalFrameStyleFlags nSalFrameStyle
)
795 // to switch to Main-Thread
798 hWndParent
= static_cast<WinSalFrame
*>(pParent
)->mhWnd
;
800 hWndParent
= nullptr;
801 return reinterpret_cast<SalFrame
*>(static_cast<sal_IntPtr
>(SendMessageW( mhComWnd
, SAL_MSG_CREATEFRAME
, static_cast<WPARAM
>(nSalFrameStyle
), reinterpret_cast<LPARAM
>(hWndParent
) )));
804 void WinSalInstance::DestroyFrame( SalFrame
* pFrame
)
806 OpenGLContext::prepareForYield();
807 SendMessageW( mhComWnd
, SAL_MSG_DESTROYFRAME
, 0, reinterpret_cast<LPARAM
>(pFrame
) );
810 SalObject
* WinSalInstance::CreateObject( SalFrame
* pParent
,
811 SystemWindowData
* /*pWindowData*/, // SystemWindowData meaningless on Windows
814 // to switch to Main-Thread
815 return reinterpret_cast<SalObject
*>(static_cast<sal_IntPtr
>(SendMessageW( mhComWnd
, SAL_MSG_CREATEOBJECT
, 0, reinterpret_cast<LPARAM
>(static_cast<WinSalFrame
*>(pParent
)) )));
818 void WinSalInstance::DestroyObject( SalObject
* pObject
)
820 SendMessageW( mhComWnd
, SAL_MSG_DESTROYOBJECT
, 0, reinterpret_cast<LPARAM
>(pObject
) );
823 OUString
WinSalInstance::GetConnectionIdentifier()
828 /** Add a file to the system shells recent document list if there is any.
829 This function may have no effect under Unix because there is no
830 standard API among the different desktop managers.
833 The file url of the document.
835 void WinSalInstance::AddToRecentDocumentList(const OUString
& rFileUrl
, const OUString
& /*rMimeType*/, const OUString
& rDocumentService
)
837 if (Application::IsHeadlessModeEnabled())
840 OUString system_path
;
841 osl::FileBase::RC rc
= osl::FileBase::getSystemPathFromFileURL(rFileUrl
, system_path
);
843 OSL_ENSURE(osl::FileBase::E_None
== rc
, "Invalid file url");
845 if (osl::FileBase::E_None
== rc
)
847 IShellItem
* pShellItem
= nullptr;
849 HRESULT hr
= SHCreateItemFromParsingName(o3tl::toW(system_path
.getStr()), nullptr, IID_PPV_ARGS(&pShellItem
));
851 if ( SUCCEEDED(hr
) && pShellItem
)
853 OUString sApplicationName
;
855 if ( rDocumentService
== "com.sun.star.text.TextDocument" ||
856 rDocumentService
== "com.sun.star.text.GlobalDocument" ||
857 rDocumentService
== "com.sun.star.text.WebDocument" ||
858 rDocumentService
== "com.sun.star.xforms.XMLFormDocument" )
859 sApplicationName
= "Writer";
860 else if ( rDocumentService
== "com.sun.star.sheet.SpreadsheetDocument" ||
861 rDocumentService
== "com.sun.star.chart2.ChartDocument" )
862 sApplicationName
= "Calc";
863 else if ( rDocumentService
== "com.sun.star.presentation.PresentationDocument" )
864 sApplicationName
= "Impress";
865 else if ( rDocumentService
== "com.sun.star.drawing.DrawingDocument" )
866 sApplicationName
= "Draw";
867 else if ( rDocumentService
== "com.sun.star.formula.FormulaProperties" )
868 sApplicationName
= "Math";
869 else if ( rDocumentService
== "com.sun.star.sdb.DatabaseDocument" ||
870 rDocumentService
== "com.sun.star.sdb.OfficeDatabaseDocument" ||
871 rDocumentService
== "com.sun.star.sdb.RelationDesign" ||
872 rDocumentService
== "com.sun.star.sdb.QueryDesign" ||
873 rDocumentService
== "com.sun.star.sdb.TableDesign" ||
874 rDocumentService
== "com.sun.star.sdb.DataSourceBrowser" )
875 sApplicationName
= "Base";
877 if ( !sApplicationName
.isEmpty() )
879 OUString
sApplicationID("Collabora.CollaboraOffice." + sApplicationName
);
882 info
.psi
= pShellItem
;
883 info
.pszAppID
= o3tl::toW(sApplicationID
.getStr());
885 SHAddToRecentDocs ( SHARD_APPIDINFO
, &info
);
889 // For whatever reason, we could not use the SHARD_APPIDINFO semantics
890 SHAddToRecentDocs(SHARD_PATHW
, system_path
.getStr());
894 SalTimer
* WinSalInstance::CreateSalTimer()
896 return new WinSalTimer();
899 std::shared_ptr
<SalBitmap
> WinSalInstance::CreateSalBitmap()
901 #if HAVE_FEATURE_SKIA
902 if (SkiaHelper::isVCLSkiaEnabled())
903 return std::make_shared
<SkiaSalBitmap
>();
906 return std::make_shared
<WinSalBitmap
>();
909 int WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(int, LPEXCEPTION_POINTERS pExceptionInfo
)
911 // Decide if an exception is a c++ (mostly UNO) exception or a process violation.
912 // Depending on this information we pass process violations directly to our signal handler ...
913 // and c++ (UNO) exceptions are sended to the following code on the current stack.
914 // Problem behind: user32.dll sometime consumes exceptions/process violations .-)
917 static const DWORD EXCEPTION_MSC_CPP_EXCEPTION
= 0xE06D7363;
919 if (pExceptionInfo
->ExceptionRecord
->ExceptionCode
== EXCEPTION_MSC_CPP_EXCEPTION
)
920 return EXCEPTION_CONTINUE_SEARCH
;
922 return UnhandledExceptionFilter( pExceptionInfo
);
925 typedef LONG NTSTATUS
;
926 typedef NTSTATUS(WINAPI
* RtlGetVersion_t
)(PRTL_OSVERSIONINFOW
);
927 constexpr NTSTATUS STATUS_SUCCESS
= 0x00000000;
929 OUString
WinSalInstance::getOSVersion()
931 OUStringBuffer
aVer(50); // capacity for string like "Windows 6.1 Service Pack 1 build 7601"
932 aVer
.append("Windows ");
933 // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
934 // subject to manifest-based behavior since Windows 8.1, so give wrong results.
935 // Another approach would be to use NetWkstaGetInfo, but that has some small
936 // reported delays (some milliseconds), and might get slower in domains with
937 // poor network connections.
938 // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
939 bool bHaveVerFromKernel32
= false;
940 if (HMODULE h_kernel32
= GetModuleHandleW(L
"kernel32.dll"))
942 wchar_t szPath
[MAX_PATH
];
943 DWORD dwCount
= GetModuleFileNameW(h_kernel32
, szPath
, SAL_N_ELEMENTS(szPath
));
944 if (dwCount
!= 0 && dwCount
< SAL_N_ELEMENTS(szPath
))
946 dwCount
= GetFileVersionInfoSizeW(szPath
, nullptr);
949 std::unique_ptr
<char[]> ver(new char[dwCount
]);
950 if (GetFileVersionInfoW(szPath
, 0, dwCount
, ver
.get()) != FALSE
)
952 void* pBlock
= nullptr;
954 if (VerQueryValueW(ver
.get(), L
"\\", &pBlock
, &dwBlockSz
) != FALSE
&& dwBlockSz
>= sizeof(VS_FIXEDFILEINFO
))
956 VS_FIXEDFILEINFO
* vi1
= static_cast<VS_FIXEDFILEINFO
*>(pBlock
);
957 aVer
.append(OUString::number(HIWORD(vi1
->dwProductVersionMS
)) + "."
958 + OUString::number(LOWORD(vi1
->dwProductVersionMS
)));
959 bHaveVerFromKernel32
= true;
965 // Now use RtlGetVersion (which is not subject to deprecation for GetVersion(Ex) API)
966 // to get build number and SP info
967 bool bHaveVerFromRtlGetVersion
= false;
968 if (HMODULE h_ntdll
= GetModuleHandleW(L
"ntdll.dll"))
970 if (auto RtlGetVersion
971 = reinterpret_cast<RtlGetVersion_t
>(GetProcAddress(h_ntdll
, "RtlGetVersion")))
973 RTL_OSVERSIONINFOW vi2
{}; // initialize with zeroes - a better alternative to memset
974 vi2
.dwOSVersionInfoSize
= sizeof(vi2
);
975 if (STATUS_SUCCESS
== RtlGetVersion(&vi2
))
977 if (!bHaveVerFromKernel32
) // we failed above; let's hope this would be useful
978 aVer
.append(OUString::number(vi2
.dwMajorVersion
) + "."
979 + OUString::number(vi2
.dwMinorVersion
));
981 if (vi2
.szCSDVersion
[0])
982 aVer
.append(OUString::Concat(o3tl::toU(vi2
.szCSDVersion
)) + " ");
983 aVer
.append("Build " + OUString::number(vi2
.dwBuildNumber
));
984 bHaveVerFromRtlGetVersion
= true;
988 if (!bHaveVerFromKernel32
&& !bHaveVerFromRtlGetVersion
)
989 aVer
.append("unknown");
990 return aVer
.makeStringAndClear();
993 void WinSalInstance::BeforeAbort(const OUString
&, bool)
998 css::uno::Reference
<css::uno::XInterface
> WinSalInstance::ImplCreateDragSource(const SystemEnvData
* pSysEnv
)
1000 return vcl::OleDnDHelper(new DragSource(comphelper::getProcessComponentContext()),
1001 reinterpret_cast<sal_IntPtr
>(pSysEnv
->hWnd
), vcl::DragOrDrop::Drag
);
1004 css::uno::Reference
<css::uno::XInterface
> WinSalInstance::ImplCreateDropTarget(const SystemEnvData
* pSysEnv
)
1006 return vcl::OleDnDHelper(new DropTarget(comphelper::getProcessComponentContext()),
1007 reinterpret_cast<sal_IntPtr
>(pSysEnv
->hWnd
), vcl::DragOrDrop::Drop
);
1010 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */