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 <sal/log.hxx>
27 #include <tools/debug.hxx>
28 #include <tools/time.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/solarmutex.hxx>
31 #include <comphelper/windowserrorstring.hxx>
32 #include <com/sun/star/uno/Reference.h>
33 #include <o3tl/char16_t2wchar_t.hxx>
34 #include <o3tl/temporary.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()
214 auto initKey
= [this](wchar_t ch
, sal_uInt16 key
)
216 if (UINT vkey
= LOWORD(VkKeyScanW(ch
)); vkey
< 0xffff)
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 mpInstance
= nullptr; // pointer of first instance
261 mpFirstFrame
= nullptr; // pointer of first frame
262 mpFirstObject
= nullptr; // pointer of first object window
263 mpFirstVD
= nullptr; // first VirDev
264 mpFirstPrinter
= nullptr; // first printing printer
265 mh50Bmp
= nullptr; // 50% Bitmap
266 mh50Brush
= nullptr; // 50% Brush
268 for(i
=0; i
<MAX_STOCKPEN
; i
++)
270 maStockPenColorAry
[i
] = 0;
271 mhStockPenAry
[i
] = nullptr;
273 for(i
=0; i
<MAX_STOCKBRUSH
; i
++)
275 maStockBrushColorAry
[i
] = 0;
276 mhStockBrushAry
[i
] = nullptr;
278 mnStockPenCount
= 0; // count of static pens
279 mnStockBrushCount
= 0; // count of static brushes
280 mnSalObjWantKeyEvt
= 0; // KeyEvent for the SalObj hook
281 mnCacheDCInUse
= 0; // count of CacheDC in use
282 mbObjClassInit
= false; // is SALOBJECTCLASS initialised
283 mbInPalChange
= false; // is in WM_QUERYNEWPALETTE
284 mnAppThreadId
= 0; // Id from Application-Thread
285 mpFirstIcon
= nullptr; // icon cache, points to first icon, NULL if none
286 mpSharedTempFontItem
= nullptr;
287 mpOtherTempFontItem
= nullptr;
288 mbThemeChanged
= false; // true if visual theme was changed: throw away theme handles
289 mbThemeMenuSupport
= false;
299 static Gdiplus::GdiplusStartupInput gdiplusStartupInput
;
300 Gdiplus::GdiplusStartup(&gdiplusToken
, &gdiplusStartupInput
, nullptr);
306 SetSalData( nullptr );
309 Gdiplus::GdiplusShutdown(gdiplusToken
);
312 bool OSSupportsDarkMode()
314 return WinSalInstance::getWindowsBuildNumber() >= 18362;
319 enum PreferredAppMode
331 VCLPLUG_WIN_PUBLIC SalInstance
* create_SalInstance()
333 SalData
* pSalData
= new SalData();
336 aSI
.cb
= sizeof( aSI
);
337 GetStartupInfoW( &aSI
);
338 pSalData
->mhInst
= GetModuleHandleW( nullptr );
339 pSalData
->mnCmdShow
= aSI
.wShowWindow
;
341 pSalData
->mnAppThreadId
= GetCurrentThreadId();
343 static bool bSetAllowDarkMode
= OSSupportsDarkMode(); // too early to additionally check LibreOffice's config
344 if (bSetAllowDarkMode
)
346 typedef PreferredAppMode(WINAPI
* SetPreferredAppMode_t
)(PreferredAppMode
);
347 if (HINSTANCE hUxthemeLib
= LoadLibraryExW(L
"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32
))
349 if (auto SetPreferredAppMode
= reinterpret_cast<SetPreferredAppMode_t
>(GetProcAddress(hUxthemeLib
, MAKEINTRESOURCEA(135))))
350 SetPreferredAppMode(AllowDark
);
351 FreeLibrary(hUxthemeLib
);
355 // register frame class
356 WNDCLASSEXW aWndClassEx
;
357 aWndClassEx
.cbSize
= sizeof( aWndClassEx
);
358 aWndClassEx
.style
= CS_OWNDC
;
359 aWndClassEx
.lpfnWndProc
= SalFrameWndProcW
;
360 aWndClassEx
.cbClsExtra
= 0;
361 aWndClassEx
.cbWndExtra
= SAL_FRAME_WNDEXTRA
;
362 aWndClassEx
.hInstance
= pSalData
->mhInst
;
363 aWndClassEx
.hCursor
= nullptr;
364 aWndClassEx
.hbrBackground
= nullptr;
365 aWndClassEx
.lpszMenuName
= nullptr;
366 aWndClassEx
.lpszClassName
= SAL_FRAME_CLASSNAMEW
;
367 ImplLoadSalIcon( SAL_RESID_ICON_DEFAULT
, aWndClassEx
.hIcon
, aWndClassEx
.hIconSm
);
368 if ( !RegisterClassExW( &aWndClassEx
) )
371 aWndClassEx
.hIcon
= nullptr;
372 aWndClassEx
.hIconSm
= nullptr;
373 aWndClassEx
.style
|= CS_SAVEBITS
;
374 aWndClassEx
.lpszClassName
= SAL_SUBFRAME_CLASSNAMEW
;
375 if ( !RegisterClassExW( &aWndClassEx
) )
378 // shadow effect for popups on XP
379 aWndClassEx
.style
|= CS_DROPSHADOW
;
380 aWndClassEx
.lpszClassName
= SAL_TMPSUBFRAME_CLASSNAMEW
;
381 if ( !RegisterClassExW( &aWndClassEx
) )
384 aWndClassEx
.style
= 0;
385 aWndClassEx
.lpfnWndProc
= SalComWndProcW
;
386 aWndClassEx
.cbWndExtra
= 0;
387 aWndClassEx
.lpszClassName
= SAL_COM_CLASSNAMEW
;
388 if ( !RegisterClassExW( &aWndClassEx
) )
391 HWND hComWnd
= CreateWindowExW( WS_EX_TOOLWINDOW
, SAL_COM_CLASSNAMEW
,
392 L
"", WS_POPUP
, 0, 0, 0, 0, nullptr, nullptr,
393 pSalData
->mhInst
, nullptr );
397 WinSalInstance
* pInst
= new WinSalInstance
;
399 // init instance (only one instance in this version !!!)
400 pSalData
->mpInstance
= pInst
;
401 pInst
->mhInst
= pSalData
->mhInst
;
402 pInst
->mhComWnd
= hComWnd
;
404 // init static GDI Data
411 WinSalInstance::WinSalInstance()
412 : SalInstance(std::make_unique
<SalYieldMutex
>())
414 , mhComWnd( nullptr )
415 , m_nNoYieldLock( 0 )
417 ImplSVData
* pSVData
= ImplGetSVData();
418 pSVData
->maAppData
.mxToolkitName
= OUString("win");
419 m_bSupportsOpenGL
= true;
420 #if HAVE_FEATURE_SKIA
421 WinSkiaSalGraphicsImpl::prepareSkia();
422 #if SKIA_USE_BITMAP32
423 if (SkiaHelper::isVCLSkiaEnabled())
424 m_bSupportsBitmap32
= true;
429 WinSalInstance::~WinSalInstance()
432 DestroyWindow( mhComWnd
);
433 #if HAVE_FEATURE_SKIA
434 SkiaHelper::cleanup();
438 void WinSalInstance::AfterAppInit()
440 // (1) Ideally this would be done at the place that creates the thread, but since this thread is normally
441 // just the default/main thread, that is not possible.
442 // (2) Don't do this on unix, where it causes tools like pstree on Linux to
443 // confusingly report soffice.bin as VCL Main instead.
444 osl_setThreadName("VCL Main");
447 static LRESULT
ImplSalDispatchMessage( const MSG
* pMsg
)
449 SalData
* pSalData
= GetSalData();
450 if ( pSalData
->mpFirstObject
&& ImplSalPreDispatchMsg( pMsg
) )
452 LRESULT lResult
= DispatchMessageW( pMsg
);
453 if ( pSalData
->mpFirstObject
)
454 ImplSalPostDispatchMsg( pMsg
);
458 // probably can't be static, because of SalTimer friend? (static gives C4211)
459 bool ImplSalYield(const bool bWait
, const bool bHandleAllCurrentEvents
)
461 // used to abort further message processing on tick count wraps
462 static sal_uInt32 nLastTicks
= 0;
464 // we should never yield in m_nNoYieldLock mode!
465 const bool bNoYieldLock
= (GetSalData()->mpInstance
->m_nNoYieldLock
> 0);
466 assert(!bNoYieldLock
);
471 bool bWasMsg
= false, bWasTimeoutMsg
= false;
472 WinSalTimer
* pTimer
= static_cast<WinSalTimer
*>(ImplGetSVData()->maSchedCtx
.mpSalTimer
);
474 sal_uInt32 nCurTicks
= GetTickCount();
478 if (!PeekMessageW(&aMsg
, nullptr, 0, 0, PM_REMOVE
))
482 TranslateMessage(&aMsg
);
483 LRESULT nRet
= ImplSalDispatchMessage(&aMsg
);
485 bWasTimeoutMsg
|= (SAL_MSG_TIMER_CALLBACK
== aMsg
.message
) && static_cast<bool>(nRet
);
487 if (!bHandleAllCurrentEvents
)
490 if ((aMsg
.time
> nCurTicks
) && (nLastTicks
<= nCurTicks
|| aMsg
.time
< nLastTicks
))
495 // 0ms timeouts are handled out-of-bounds to prevent busy-locking the
496 // event loop with timeout messages.
497 // We ensure we never handle more than one timeout per call.
498 // This way we'll always process a normal system message.
499 if ( !bWasTimeoutMsg
&& pTimer
&& pTimer
->IsDirectTimeout() )
501 pTimer
->ImplHandleElapsedTimer();
505 nLastTicks
= nCurTicks
;
507 if ( bWait
&& !bWasMsg
)
509 switch (GetMessageW(&aMsg
, nullptr, 0, 0))
512 SAL_WARN("vcl.schedule", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
513 // should we std::abort() / SalAbort here?
516 SAL_INFO("vcl.schedule", "GetMessageW received WM_QUIT while waiting");
520 TranslateMessage(&aMsg
);
521 ImplSalDispatchMessage(&aMsg
);
526 // If we enabled ForceRealTimer mode skipping our direct timeout processing,
527 // mainly because some Windows API call spawns its own nested message loop,
528 // switch back to our own processing (like after window resize or move)
530 pTimer
->SetForceRealTimer( false );
535 bool WinSalInstance::IsMainThread() const
537 const SalData
* pSalData
= GetSalData();
538 return pSalData
->mnAppThreadId
== GetCurrentThreadId();
541 bool WinSalInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
543 bool bDidWork
= false;
544 SolarMutexReleaser aReleaser
;
545 if ( !IsMainThread() )
547 bDidWork
= SendMessageW( mhComWnd
, SAL_MSG_THREADYIELD
,
548 WPARAM(false), static_cast<LPARAM
>(bHandleAllCurrentEvents
) );
549 if ( !bDidWork
&& bWait
)
551 maWaitingYieldCond
.reset();
552 maWaitingYieldCond
.wait();
558 bDidWork
= ImplSalYield( bWait
, bHandleAllCurrentEvents
);
560 maWaitingYieldCond
.set();
568 struct NoYieldLockGuard
571 : counter(InSendMessage() ? GetSalData()->mpInstance
->m_nNoYieldLock
: dummy())
575 ~NoYieldLockGuard() { --counter
; }
576 static decltype(WinSalInstance::m_nNoYieldLock
)& dummy()
578 DBG_TESTSOLARMUTEX(); // called when !InSendMessage()
579 static decltype(WinSalInstance::m_nNoYieldLock
) n
= 0;
582 decltype(WinSalInstance::m_nNoYieldLock
)& counter
;
586 LRESULT CALLBACK
SalComWndProc( HWND
, UINT nMsg
, WPARAM wParam
, LPARAM lParam
, bool& rDef
)
589 WinSalTimer
*const pTimer
= static_cast<WinSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
591 SAL_INFO("vcl.gdi.wndproc", "SalComWndProc(nMsg=" << nMsg
<< ", wParam=" << wParam
592 << ", lParam=" << lParam
<< "); inSendMsg: " << InSendMessage());
594 if (ImplGetSVData()->mbDeInit
)
596 SAL_WARN("vcl.gdi.wndproc", "ignoring timer event because we are shutting down");
602 case SAL_MSG_THREADYIELD
:
603 assert( !static_cast<bool>(wParam
) );
604 nRet
= static_cast<LRESULT
>(ImplSalYield(
605 false, static_cast<bool>( lParam
) ));
608 case SAL_MSG_STARTTIMER
:
610 auto const nParam
= static_cast<sal_uInt64
>( lParam
);
611 sal_uInt64 nTime
= tools::Time::GetSystemTicks();
612 if ( nTime
< nParam
)
613 nTime
= nParam
- nTime
;
616 assert( pTimer
!= nullptr );
617 pTimer
->ImplStart( nTime
);
621 case SAL_MSG_STOPTIMER
:
622 assert( pTimer
!= nullptr );
626 case (SAL_MSG_CREATEFRAME
):
629 nRet
= reinterpret_cast<LRESULT
>(
630 ImplSalCreateFrame(GetSalData()->mpInstance
, reinterpret_cast<HWND
>(lParam
),
631 static_cast<SalFrameStyleFlags
>(wParam
)));
634 case (SAL_MSG_RECREATEHWND
):
637 nRet
= reinterpret_cast<LRESULT
>(ImplSalReCreateHWND(
638 reinterpret_cast<HWND
>(wParam
), reinterpret_cast<HWND
>(lParam
), false));
641 case (SAL_MSG_RECREATECHILDHWND
):
644 nRet
= reinterpret_cast<LRESULT
>(ImplSalReCreateHWND(
645 reinterpret_cast<HWND
>(wParam
), reinterpret_cast<HWND
>(lParam
), true));
648 case (SAL_MSG_DESTROYFRAME
):
651 delete reinterpret_cast<SalFrame
*>(lParam
);
655 case SAL_MSG_DESTROYHWND
:
656 // We only destroy the native window here. We do NOT destroy the SalFrame contained
657 // in the structure (GetWindowPtr()).
658 if (DestroyWindow(reinterpret_cast<HWND
>(lParam
)) == 0)
660 OSL_FAIL("DestroyWindow failed!");
661 // Failure: We remove the SalFrame from the window structure. So we avoid that
662 // the window structure may contain an invalid pointer, once the SalFrame is deleted.
663 SetWindowPtr(reinterpret_cast<HWND
>(lParam
), nullptr);
667 case (SAL_MSG_CREATEOBJECT
):
670 nRet
= reinterpret_cast<LRESULT
>(ImplSalCreateObject(
671 GetSalData()->mpInstance
, reinterpret_cast<WinSalFrame
*>(lParam
)));
674 case (SAL_MSG_DESTROYOBJECT
):
677 delete reinterpret_cast<SalObject
*>(lParam
);
680 case (SAL_MSG_GETCACHEDDC
):
683 nRet
= reinterpret_cast<LRESULT
>(
684 GetDCEx(reinterpret_cast<HWND
>(wParam
), nullptr, 0x00000002L
));
687 case (SAL_MSG_RELEASEDC
):
690 ReleaseDC(reinterpret_cast<HWND
>(wParam
), reinterpret_cast<HDC
>(lParam
));
694 case SAL_MSG_TIMER_CALLBACK
:
695 assert( pTimer
!= nullptr );
696 pTimer
->ImplHandleTimerEvent( wParam
);
700 assert( pTimer
!= nullptr );
701 pTimer
->ImplHandle_WM_TIMER( wParam
);
704 case SAL_MSG_FORCE_REAL_TIMER
:
705 assert(pTimer
!= nullptr);
706 pTimer
->SetForceRealTimer(true);
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("TheDocumentFoundation.LibreOffice." + 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 static OUString
getWinArch()
931 USHORT nNativeMachine
= IMAGE_FILE_MACHINE_UNKNOWN
;
933 using LPFN_ISWOW64PROCESS2
= BOOL(WINAPI
*)(HANDLE
, USHORT
*, USHORT
*);
934 auto fnIsWow64Process2
= reinterpret_cast<LPFN_ISWOW64PROCESS2
>(
935 GetProcAddress(GetModuleHandleW(L
"kernel32.dll"), "IsWow64Process2"));
936 if (fnIsWow64Process2
)
937 fnIsWow64Process2(GetCurrentProcess(), &o3tl::temporary(USHORT()), &nNativeMachine
);
939 if (nNativeMachine
== IMAGE_FILE_MACHINE_UNKNOWN
)
943 nNativeMachine
= IMAGE_FILE_MACHINE_AMD64
;
947 BOOL isWow64
= FALSE
;
949 IsWow64Process(GetCurrentProcess(), &isWow64
);
952 nNativeMachine
= IMAGE_FILE_MACHINE_AMD64
; // 32-bit process on 64-bit Windows
954 nNativeMachine
= IMAGE_FILE_MACHINE_I386
;
959 switch (nNativeMachine
)
961 case IMAGE_FILE_MACHINE_I386
:
962 return u
" X86_32"_ustr
;
963 case IMAGE_FILE_MACHINE_R3000
:
964 return u
" R3000"_ustr
;
965 case IMAGE_FILE_MACHINE_R4000
:
966 return u
" R4000"_ustr
;
967 case IMAGE_FILE_MACHINE_R10000
:
968 return u
" R10000"_ustr
;
969 case IMAGE_FILE_MACHINE_WCEMIPSV2
:
970 return u
" WCEMIPSV2"_ustr
;
971 case IMAGE_FILE_MACHINE_ALPHA
:
972 return u
" ALPHA"_ustr
;
973 case IMAGE_FILE_MACHINE_SH3
:
975 case IMAGE_FILE_MACHINE_SH3DSP
:
976 return u
" SH3DSP"_ustr
;
977 case IMAGE_FILE_MACHINE_SH3E
:
978 return u
" SH3E"_ustr
;
979 case IMAGE_FILE_MACHINE_SH4
:
981 case IMAGE_FILE_MACHINE_SH5
:
983 case IMAGE_FILE_MACHINE_ARM
:
985 case IMAGE_FILE_MACHINE_THUMB
:
986 return u
" THUMB"_ustr
;
987 case IMAGE_FILE_MACHINE_ARMNT
:
988 return u
" ARMNT"_ustr
;
989 case IMAGE_FILE_MACHINE_AM33
:
990 return u
" AM33"_ustr
;
991 case IMAGE_FILE_MACHINE_POWERPC
:
992 return u
" POWERPC"_ustr
;
993 case IMAGE_FILE_MACHINE_POWERPCFP
:
994 return u
" POWERPCFP"_ustr
;
995 case IMAGE_FILE_MACHINE_IA64
:
996 return u
" IA64"_ustr
;
997 case IMAGE_FILE_MACHINE_MIPS16
:
998 return u
" MIPS16"_ustr
;
999 case IMAGE_FILE_MACHINE_ALPHA64
:
1000 return u
" ALPHA64"_ustr
;
1001 case IMAGE_FILE_MACHINE_MIPSFPU
:
1002 return u
" MIPSFPU"_ustr
;
1003 case IMAGE_FILE_MACHINE_MIPSFPU16
:
1004 return u
" MIPSFPU16"_ustr
;
1005 case IMAGE_FILE_MACHINE_TRICORE
:
1006 return u
" TRICORE"_ustr
;
1007 case IMAGE_FILE_MACHINE_CEF
:
1008 return u
" CEF"_ustr
;
1009 case IMAGE_FILE_MACHINE_EBC
:
1010 return u
" EBC"_ustr
;
1011 case IMAGE_FILE_MACHINE_AMD64
:
1012 return u
" X86_64"_ustr
;
1013 case IMAGE_FILE_MACHINE_M32R
:
1014 return u
" M32R"_ustr
;
1015 case IMAGE_FILE_MACHINE_ARM64
:
1016 return u
" ARM64"_ustr
;
1017 case IMAGE_FILE_MACHINE_CEE
:
1018 return u
" CEE"_ustr
;
1020 assert(!"Yet unhandled case");
1025 static OUString
getOSVersionString(DWORD nBuildNumber
)
1027 OUStringBuffer result
= u
"Windows";
1028 if (nBuildNumber
>= 22000)
1029 result
.append(" 11");
1030 else if (nBuildNumber
> 0)
1031 result
.append(" 10");
1032 else // We don't know what Windows it is
1033 result
.append(" unknown");
1035 result
.append(getWinArch());
1038 result
.append(" (build " + OUString::number(nBuildNumber
) + ")");
1040 return result
.makeStringAndClear();
1043 DWORD
WinSalInstance::getWindowsBuildNumber()
1045 static const DWORD nResult
= []
1047 DWORD nBuildNumber
= 0;
1048 // use RtlGetVersion to get build number
1049 if (HMODULE h_ntdll
= GetModuleHandleW(L
"ntdll.dll"))
1051 if (auto RtlGetVersion
1052 = reinterpret_cast<RtlGetVersion_t
>(GetProcAddress(h_ntdll
, "RtlGetVersion")))
1054 RTL_OSVERSIONINFOW vi2
{}; // initialize with zeroes - a better alternative to memset
1055 vi2
.dwOSVersionInfoSize
= sizeof(vi2
);
1056 if (STATUS_SUCCESS
== RtlGetVersion(&vi2
))
1058 nBuildNumber
= vi2
.dwBuildNumber
;
1062 return nBuildNumber
;
1067 OUString
WinSalInstance::getOSVersion()
1069 return getOSVersionString(getWindowsBuildNumber());
1072 void WinSalInstance::BeforeAbort(const OUString
&, bool)
1077 css::uno::Reference
<css::uno::XInterface
> WinSalInstance::ImplCreateDragSource(const SystemEnvData
* pSysEnv
)
1079 return vcl::OleDnDHelper(new DragSource(comphelper::getProcessComponentContext()),
1080 reinterpret_cast<sal_IntPtr
>(pSysEnv
->hWnd
), vcl::DragOrDrop::Drag
);
1083 css::uno::Reference
<css::uno::XInterface
> WinSalInstance::ImplCreateDropTarget(const SystemEnvData
* pSysEnv
)
1085 return vcl::OleDnDHelper(new DropTarget(comphelper::getProcessComponentContext()),
1086 reinterpret_cast<sal_IntPtr
>(pSysEnv
->hWnd
), vcl::DragOrDrop::Drop
);
1089 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */