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/solarmutex.hxx>
30 #include <o3tl/char16_t2wchar_t.hxx>
32 #include <vcl/inputtypes.hxx>
33 #include <vcl/opengl/OpenGLHelper.hxx>
34 #include <vcl/opengl/OpenGLContext.hxx>
35 #include <vcl/timer.hxx>
36 #include <vclpluginapi.h>
38 #include <opengl/salbmp.hxx>
39 #include <opengl/win/gdiimpl.hxx>
40 #include <win/wincomp.hxx>
41 #include <win/salids.hrc>
42 #include <win/saldata.hxx>
43 #include <win/salinst.h>
44 #include <win/salframe.h>
45 #include <win/salobj.h>
46 #include <win/saltimer.h>
47 #include <win/salbmp.h>
48 #include <win/winlayout.hxx>
52 #include <desktop/crashreport.hxx>
56 #define min(a,b) (((a) < (b)) ? (a) : (b))
59 #define max(a,b) (((a) > (b)) ? (a) : (b))
70 void SalAbort( const OUString
& rErrorText
, bool )
74 if ( rErrorText
.isEmpty() )
76 // make sure crash reporter is triggered
77 RaiseException( 0, EXCEPTION_NONCONTINUABLE
, 0, nullptr );
78 FatalAppExitW( 0, L
"Application Error" );
82 CrashReporter::addKeyValue("AbortMessage", rErrorText
, CrashReporter::Write
);
83 // make sure crash reporter is triggered
84 RaiseException( 0, EXCEPTION_NONCONTINUABLE
, 0, nullptr );
85 FatalAppExitW( 0, o3tl::toW(rErrorText
.getStr()) );
89 static LRESULT CALLBACK
SalComWndProcW( HWND hWnd
, UINT nMsg
, WPARAM wParam
, LPARAM lParam
);
91 class SalYieldMutex
: public comphelper::SolarMutex
93 public: // for ImplSalYield() and ImplSalYieldMutexAcquireWithWait()
94 osl::Condition m_condition
; /// for MsgWaitForMultipleObjects()
97 virtual void doAcquire( sal_uInt32 nLockCount
) override
;
98 virtual sal_uInt32
doRelease( bool bUnlockAll
) override
;
100 static void BeforeReleaseHandler();
103 explicit SalYieldMutex();
105 virtual bool IsCurrentThread() const override
;
106 virtual bool tryToAcquire() override
;
109 SalYieldMutex::SalYieldMutex()
111 SetBeforeReleaseHandler( &SalYieldMutex::BeforeReleaseHandler
);
114 void SalYieldMutex::BeforeReleaseHandler()
116 OpenGLContext::prepareForYield();
118 if ( GetSalData()->mnAppThreadId
!= GetCurrentThreadId() )
120 // If we don't call these message, the Output from the
121 // Java clients doesn't come in the right order
126 /// note: while VCL is fully up and running (other threads started and
127 /// before shutdown), the main thread must acquire SolarMutex only via
128 /// this function to avoid deadlock
129 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount
)
131 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
132 if ( pInst
&& pInst
->IsMainThread() )
134 if ( pInst
->m_nNoYieldLock
)
136 // tdf#96887 If this is the main thread, then we must wait for two things:
137 // - the yield mutex being unlocked
138 // - SendMessage() being triggered
139 // This can nicely be done using MsgWaitForMultipleObjects. The 2nd one is
140 // needed because if we don't reschedule, then we create deadlocks if a
141 // Window's create/destroy is called via SendMessage() from another thread.
142 // Have a look at the osl_waitCondition implementation for more info.
144 // reset condition *before* acquiring!
146 if (m_aMutex
.tryToAcquire())
148 // wait for SalYieldMutex::release() to set the condition
149 osl::Condition::Result res
= m_condition
.wait();
150 assert(osl::Condition::Result::result_ok
== res
);
159 comphelper::SolarMutex::doAcquire( nLockCount
);
162 sal_uInt32
SalYieldMutex::doRelease( const bool bUnlockAll
)
164 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
165 if ( pInst
&& pInst
->m_nNoYieldLock
&& pInst
->IsMainThread() )
168 sal_uInt32 nCount
= comphelper::SolarMutex::doRelease( bUnlockAll
);
169 // wake up ImplSalYieldMutexAcquireWithWait() after release
175 bool SalYieldMutex::tryToAcquire()
177 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
180 if ( pInst
->m_nNoYieldLock
&& pInst
->IsMainThread() )
183 return comphelper::SolarMutex::tryToAcquire();
189 void ImplSalYieldMutexAcquireWithWait( sal_uInt32 nCount
)
191 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
193 pInst
->GetYieldMutex()->acquire( nCount
);
196 bool ImplSalYieldMutexTryToAcquire()
198 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
199 return pInst
&& pInst
->GetYieldMutex()->tryToAcquire();
202 void ImplSalYieldMutexRelease()
204 WinSalInstance
* pInst
= GetSalData()->mpInstance
;
208 pInst
->GetYieldMutex()->release();
212 bool SalYieldMutex::IsCurrentThread() const
214 if ( !GetSalData()->mpInstance
->m_nNoYieldLock
)
215 return SolarMutex::IsCurrentThread();
217 return GetSalData()->mpInstance
->IsMainThread();
220 void SalData::initKeyCodeMap()
223 #define initKey( a, b )\
224 nKey = LOWORD( VkKeyScanW( a ) );\
230 initKey( L
'+', KEY_ADD
);
231 initKey( L
'-', KEY_SUBTRACT
);
232 initKey( L
'*', KEY_MULTIPLY
);
233 initKey( L
'/', KEY_DIVIDE
);
234 initKey( L
'.', KEY_POINT
);
235 initKey( L
',', KEY_COMMA
);
236 initKey( L
'<', KEY_LESS
);
237 initKey( L
'>', KEY_GREATER
);
238 initKey( L
'=', KEY_EQUAL
);
239 initKey( L
'~', KEY_TILDE
);
240 initKey( L
'`', KEY_QUOTELEFT
);
241 initKey( L
'[', KEY_BRACKETLEFT
);
242 initKey( L
']', KEY_BRACKETRIGHT
);
243 initKey( L
';', KEY_SEMICOLON
);
244 initKey( L
'\'', KEY_QUOTERIGHT
);
251 mhInst
= nullptr; // default instance handle
252 mnCmdShow
= 0; // default frame show style
253 mhDitherPal
= nullptr; // dither palette
254 mhDitherDIB
= nullptr; // dither memory handle
255 mpDitherDIB
= nullptr; // dither memory
256 mpDitherDIBData
= nullptr; // beginning of DIB data
257 mpDitherDiff
= nullptr; // Dither mapping table
258 mpDitherLow
= nullptr; // Dither mapping table
259 mpDitherHigh
= nullptr; // Dither mapping table
260 mhSalObjMsgHook
= nullptr; // hook to get interesting msg for SalObject
261 mhWantLeaveMsg
= nullptr; // window handle, that want a MOUSELEAVE message
262 mpMouseLeaveTimer
= nullptr; // Timer for MouseLeave Test
263 mpInstance
= nullptr; // pointer of first instance
264 mpFirstFrame
= nullptr; // pointer of first frame
265 mpFirstObject
= nullptr; // pointer of first object window
266 mpFirstVD
= nullptr; // first VirDev
267 mpFirstPrinter
= nullptr; // first printing printer
268 mpHDCCache
= nullptr; // Cache for three DC's
269 mh50Bmp
= nullptr; // 50% Bitmap
270 mh50Brush
= nullptr; // 50% Brush
272 for(i
=0; i
<MAX_STOCKPEN
; i
++)
274 maStockPenColorAry
[i
] = 0;
275 mhStockPenAry
[i
] = nullptr;
277 for(i
=0; i
<MAX_STOCKBRUSH
; i
++)
279 maStockBrushColorAry
[i
] = 0;
280 mhStockBrushAry
[i
] = nullptr;
282 mnStockPenCount
= 0; // count of static pens
283 mnStockBrushCount
= 0; // count of static brushes
284 mnSalObjWantKeyEvt
= 0; // KeyEvent for the SalObj hook
285 mnCacheDCInUse
= 0; // count of CacheDC in use
286 mbObjClassInit
= false; // is SALOBJECTCLASS initialised
287 mbInPalChange
= false; // is in WM_QUERYNEWPALETTE
288 mnAppThreadId
= 0; // Id from Application-Thread
289 mbScrSvrEnabled
= FALSE
; // ScreenSaver enabled
290 mpFirstIcon
= nullptr; // icon cache, points to first icon, NULL if none
291 mpSharedTempFontItem
= nullptr;
292 mpOtherTempFontItem
= nullptr;
293 mbThemeChanged
= false; // true if visual theme was changed: throw away theme handles
294 mbThemeMenuSupport
= false;
304 CoInitialize(nullptr); // put main thread in Single Threaded Apartment (STA)
305 static Gdiplus::GdiplusStartupInput gdiplusStartupInput
;
306 Gdiplus::GdiplusStartup(&gdiplusToken
, &gdiplusStartupInput
, nullptr);
312 SetSalData( nullptr );
317 Gdiplus::GdiplusShutdown(gdiplusToken
);
321 VCLPLUG_WIN_PUBLIC SalInstance
* create_SalInstance()
323 SalData
* pSalData
= new SalData();
326 aSI
.cb
= sizeof( aSI
);
327 GetStartupInfoW( &aSI
);
328 pSalData
->mhInst
= GetModuleHandleW( nullptr );
329 pSalData
->mnCmdShow
= aSI
.wShowWindow
;
331 pSalData
->mnAppThreadId
= GetCurrentThreadId();
333 // register frame class
334 WNDCLASSEXW aWndClassEx
;
335 aWndClassEx
.cbSize
= sizeof( aWndClassEx
);
336 aWndClassEx
.style
= CS_OWNDC
;
337 aWndClassEx
.lpfnWndProc
= SalFrameWndProcW
;
338 aWndClassEx
.cbClsExtra
= 0;
339 aWndClassEx
.cbWndExtra
= SAL_FRAME_WNDEXTRA
;
340 aWndClassEx
.hInstance
= pSalData
->mhInst
;
341 aWndClassEx
.hCursor
= nullptr;
342 aWndClassEx
.hbrBackground
= nullptr;
343 aWndClassEx
.lpszMenuName
= nullptr;
344 aWndClassEx
.lpszClassName
= SAL_FRAME_CLASSNAMEW
;
345 ImplLoadSalIcon( SAL_RESID_ICON_DEFAULT
, aWndClassEx
.hIcon
, aWndClassEx
.hIconSm
);
346 if ( !RegisterClassExW( &aWndClassEx
) )
349 aWndClassEx
.hIcon
= nullptr;
350 aWndClassEx
.hIconSm
= nullptr;
351 aWndClassEx
.style
|= CS_SAVEBITS
;
352 aWndClassEx
.lpszClassName
= SAL_SUBFRAME_CLASSNAMEW
;
353 if ( !RegisterClassExW( &aWndClassEx
) )
356 // shadow effect for popups on XP
357 aWndClassEx
.style
|= CS_DROPSHADOW
;
358 aWndClassEx
.lpszClassName
= SAL_TMPSUBFRAME_CLASSNAMEW
;
359 if ( !RegisterClassExW( &aWndClassEx
) )
362 aWndClassEx
.style
= 0;
363 aWndClassEx
.lpfnWndProc
= SalComWndProcW
;
364 aWndClassEx
.cbWndExtra
= 0;
365 aWndClassEx
.lpszClassName
= SAL_COM_CLASSNAMEW
;
366 if ( !RegisterClassExW( &aWndClassEx
) )
369 HWND hComWnd
= CreateWindowExW( WS_EX_TOOLWINDOW
, SAL_COM_CLASSNAMEW
,
370 L
"", WS_POPUP
, 0, 0, 0, 0, nullptr, nullptr,
371 pSalData
->mhInst
, nullptr );
375 WinSalInstance
* pInst
= new WinSalInstance
;
377 // init instance (only one instance in this version !!!)
378 pSalData
->mpInstance
= pInst
;
379 pInst
->mhInst
= pSalData
->mhInst
;
380 pInst
->mhComWnd
= hComWnd
;
382 // init static GDI Data
389 WinSalInstance::WinSalInstance()
390 : SalInstance(std::make_unique
<SalYieldMutex
>())
392 , mhComWnd( nullptr )
393 , m_nNoYieldLock( 0 )
395 ImplSVData
* pSVData
= ImplGetSVData();
396 pSVData
->maAppData
.mxToolkitName
= OUString("win");
399 WinSalInstance::~WinSalInstance()
402 DestroyWindow( mhComWnd
);
405 static LRESULT
ImplSalDispatchMessage( const MSG
* pMsg
)
407 SalData
* pSalData
= GetSalData();
408 if ( pSalData
->mpFirstObject
&& ImplSalPreDispatchMsg( pMsg
) )
410 LRESULT lResult
= DispatchMessageW( pMsg
);
411 if ( pSalData
->mpFirstObject
)
412 ImplSalPostDispatchMsg( pMsg
);
416 bool ImplSalYield( bool bWait
, bool bHandleAllCurrentEvents
)
418 static sal_uInt32 nLastTicks
= 0;
420 bool bWasMsg
= false, bOneEvent
= false, bWasTimeoutMsg
= false;
421 ImplSVData
*const pSVData
= ImplGetSVData();
422 WinSalTimer
* pTimer
= static_cast<WinSalTimer
*>( pSVData
->maSchedCtx
.mpSalTimer
);
423 const bool bNoYieldLock
= (GetSalData()->mpInstance
->m_nNoYieldLock
> 0);
425 assert( !bNoYieldLock
);
429 sal_uInt32 nCurTicks
= 0;
430 if ( bHandleAllCurrentEvents
)
431 nCurTicks
= GetTickCount();
433 bool bHadNewerEvent
= false;
436 bOneEvent
= PeekMessageW( &aMsg
, nullptr, 0, 0, PM_REMOVE
);
440 TranslateMessage( &aMsg
);
441 LRESULT nRet
= ImplSalDispatchMessage( &aMsg
);
443 if ( !bWasTimeoutMsg
)
444 bWasTimeoutMsg
= (SAL_MSG_TIMER_CALLBACK
== aMsg
.message
)
445 && static_cast<bool>( nRet
);
447 if ( bHandleAllCurrentEvents
448 && !bHadNewerEvent
&& aMsg
.time
> nCurTicks
449 && (nLastTicks
<= nCurTicks
|| aMsg
.time
< nLastTicks
) )
450 bHadNewerEvent
= true;
451 bOneEvent
= !bHadNewerEvent
;
454 if ( !(bHandleAllCurrentEvents
&& bOneEvent
) )
459 // 0ms timeouts are handled out-of-bounds to prevent busy-locking the
460 // event loop with timeout messages.
461 // We ensure we never handle more than one timeout per call.
462 // This way we'll always process a normal system message.
463 if ( !bWasTimeoutMsg
&& pTimer
&& pTimer
->IsDirectTimeout() )
465 pTimer
->ImplHandleElapsedTimer();
469 if ( bHandleAllCurrentEvents
)
470 nLastTicks
= nCurTicks
;
472 if ( bWait
&& !bWasMsg
)
474 if ( GetMessageW( &aMsg
, nullptr, 0, 0 ) )
477 TranslateMessage( &aMsg
);
478 ImplSalDispatchMessage( &aMsg
);
482 // we're back in the main loop after resize or move
484 pTimer
->SetForceRealTimer( false );
489 bool WinSalInstance::IsMainThread() const
491 const SalData
* pSalData
= GetSalData();
492 return pSalData
->mnAppThreadId
== GetCurrentThreadId();
495 bool WinSalInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
497 bool bDidWork
= false;
498 SolarMutexReleaser aReleaser
;
499 if ( !IsMainThread() )
501 bDidWork
= SendMessageW( mhComWnd
, SAL_MSG_THREADYIELD
,
502 WPARAM(false), static_cast<LPARAM
>(bHandleAllCurrentEvents
) );
503 if ( !bDidWork
&& bWait
)
505 maWaitingYieldCond
.reset();
506 maWaitingYieldCond
.wait();
512 bDidWork
= ImplSalYield( bWait
, bHandleAllCurrentEvents
);
514 maWaitingYieldCond
.set();
520 #define CASE_NOYIELDLOCK( salmsg, function ) \
522 if (bIsOtherThreadMessage) \
524 ++pInst->m_nNoYieldLock; \
526 --pInst->m_nNoYieldLock; \
530 DBG_TESTSOLARMUTEX(); \
535 #define CASE_NOYIELDLOCK_RESULT( salmsg, function ) \
537 if (bIsOtherThreadMessage) \
539 ++pInst->m_nNoYieldLock; \
540 nRet = reinterpret_cast<LRESULT>( function ); \
541 --pInst->m_nNoYieldLock; \
545 DBG_TESTSOLARMUTEX(); \
546 nRet = reinterpret_cast<LRESULT>( function ); \
550 LRESULT CALLBACK
SalComWndProc( HWND
, UINT nMsg
, WPARAM wParam
, LPARAM lParam
, bool& rDef
)
552 const BOOL bIsOtherThreadMessage
= InSendMessage();
554 WinSalInstance
*pInst
= GetSalData()->mpInstance
;
555 WinSalTimer
*const pTimer
= static_cast<WinSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
557 SAL_INFO("vcl.gdi.wndproc", "SalComWndProc(nMsg=" << nMsg
<< ", wParam=" << wParam
558 << ", lParam=" << lParam
<< "); inSendMsg: " << bIsOtherThreadMessage
);
562 case SAL_MSG_THREADYIELD
:
563 assert( !static_cast<bool>(wParam
) );
564 nRet
= static_cast<LRESULT
>(ImplSalYield(
565 false, static_cast<bool>( lParam
) ));
568 case SAL_MSG_STARTTIMER
:
570 sal_uInt64 nTime
= tools::Time::GetSystemTicks();
571 if ( nTime
< static_cast<sal_uInt64
>( lParam
) )
572 nTime
= static_cast<sal_uInt64
>( lParam
) - nTime
;
575 assert( pTimer
!= nullptr );
576 pTimer
->ImplStart( nTime
);
580 case SAL_MSG_STOPTIMER
:
581 assert( pTimer
!= nullptr );
585 CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEFRAME
, ImplSalCreateFrame( GetSalData()->mpInstance
,
586 reinterpret_cast<HWND
>(lParam
), static_cast<SalFrameStyleFlags
>(wParam
)) )
587 CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATEHWND
, ImplSalReCreateHWND(
588 reinterpret_cast<HWND
>(wParam
), reinterpret_cast<HWND
>(lParam
), false) )
589 CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATECHILDHWND
, ImplSalReCreateHWND(
590 reinterpret_cast<HWND
>(wParam
), reinterpret_cast<HWND
>(lParam
), true) )
591 CASE_NOYIELDLOCK( SAL_MSG_DESTROYFRAME
, delete reinterpret_cast<SalFrame
*>(lParam
) )
593 case SAL_MSG_DESTROYHWND
:
594 // We only destroy the native window here. We do NOT destroy the SalFrame contained
595 // in the structure (GetWindowPtr()).
596 if (DestroyWindow(reinterpret_cast<HWND
>(lParam
)) == 0)
598 OSL_FAIL("DestroyWindow failed!");
599 // Failure: We remove the SalFrame from the window structure. So we avoid that
600 // the window structure may contain an invalid pointer, once the SalFrame is deleted.
601 SetWindowPtr(reinterpret_cast<HWND
>(lParam
), nullptr);
605 CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEOBJECT
, ImplSalCreateObject(
606 GetSalData()->mpInstance
, reinterpret_cast<WinSalFrame
*>(lParam
)) )
607 CASE_NOYIELDLOCK( SAL_MSG_DESTROYOBJECT
, delete reinterpret_cast<SalObject
*>(lParam
) )
608 CASE_NOYIELDLOCK_RESULT( SAL_MSG_GETCACHEDDC
, GetDCEx(
609 reinterpret_cast<HWND
>(wParam
), nullptr, DCX_CACHE
) )
610 CASE_NOYIELDLOCK( SAL_MSG_RELEASEDC
, ReleaseDC(
611 reinterpret_cast<HWND
>(wParam
), reinterpret_cast<HDC
>(lParam
)) )
613 case SAL_MSG_TIMER_CALLBACK
:
614 assert( pTimer
!= nullptr );
615 pTimer
->ImplHandleTimerEvent( wParam
);
619 assert( pTimer
!= nullptr );
620 pTimer
->ImplHandle_WM_TIMER( wParam
);
623 case SAL_MSG_FORCE_REAL_TIMER
:
624 assert(pTimer
!= nullptr);
625 pTimer
->SetForceRealTimer(true);
639 #undef CASE_NOYIELDLOCK
640 #undef CASE_NOYIELDLOCK_RESULT
642 LRESULT CALLBACK
SalComWndProcW( HWND hWnd
, UINT nMsg
, WPARAM wParam
, LPARAM lParam
)
648 nRet
= SalComWndProc( hWnd
, nMsg
, wParam
, lParam
, bDef
);
650 __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
655 if ( !ImplHandleGlobalMsg( hWnd
, nMsg
, wParam
, lParam
, nRet
) )
656 nRet
= DefWindowProcW( hWnd
, nMsg
, wParam
, lParam
);
667 static std::vector
<MsgRange
> GetOtherRanges( VclInputFlags nType
)
669 assert( nType
!= VCL_INPUT_ANY
);
671 // this array must be kept sorted!
672 const UINT nExcludeMsgIds
[] =
682 WM_MOUSEFIRST
, // 512
693 SAL_MSG_POSTMOVE
, // WM_USER+136
694 SAL_MSG_POSTCALLSIZE
, // WM_USER+137
696 SAL_MSG_TIMER_CALLBACK
, // WM_USER+162
700 const unsigned MAX_EXCL
= SAL_N_ELEMENTS( nExcludeMsgIds
);
702 bool aExcludeMsgList
[ MAX_EXCL
] = { false, };
703 std::vector
<MsgRange
> aResult
;
705 // set the excluded values
706 if ( !(nType
& VclInputFlags::MOUSE
) )
708 for ( unsigned i
= 0; nExcludeMsgIds
[ 6 + i
] <= WM_MOUSELAST
; ++i
)
709 aExcludeMsgList
[ 6 + i
] = true;
712 if ( !(nType
& VclInputFlags::KEYBOARD
) )
713 aExcludeMsgList
[ 4 ] = true;
715 if ( !(nType
& VclInputFlags::PAINT
) )
717 aExcludeMsgList
[ 1 ] = true;
718 aExcludeMsgList
[ 2 ] = true;
719 aExcludeMsgList
[ 3 ] = true;
720 aExcludeMsgList
[ 16 ] = true;
721 aExcludeMsgList
[ 17 ] = true;
724 if ( !(nType
& VclInputFlags::TIMER
) )
726 aExcludeMsgList
[ 5 ] = true;
727 aExcludeMsgList
[ 18 ] = true;
730 // build the message ranges to check
731 MsgRange aRange
= { 0, 0 };
733 for ( unsigned i
= 1; i
< MAX_EXCL
; ++i
)
735 if ( aExcludeMsgList
[ i
] )
739 if ( nExcludeMsgIds
[ i
] == aRange
.nStart
)
746 aRange
.nEnd
= nExcludeMsgIds
[ i
] - 1;
747 aResult
.push_back( aRange
);
749 aRange
.nStart
= aRange
.nEnd
+ 2;
754 if ( aRange
.nStart
!= UINT_MAX
)
756 aRange
.nEnd
= UINT_MAX
;
757 aResult
.push_back( aRange
);
763 bool WinSalInstance::AnyInput( VclInputFlags nType
)
767 if ( nType
& VclInputFlags::TIMER
)
769 const WinSalTimer
* pTimer
= static_cast<WinSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
770 if ( pTimer
&& pTimer
->HasTimerElapsed() )
774 if ( (nType
& VCL_INPUT_ANY
) == VCL_INPUT_ANY
)
776 // revert bugfix for #108919# which never reported timeouts when called from the timer handler
777 // which made the application completely unresponsive during background formatting
778 if ( PeekMessageW( &aMsg
, nullptr, 0, 0, PM_NOREMOVE
| PM_NOYIELD
) )
783 const bool bCheck_KEYBOARD (nType
& VclInputFlags::KEYBOARD
);
784 const bool bCheck_OTHER (nType
& VclInputFlags::OTHER
);
786 // If there is a modifier key event, it counts as OTHER
787 // Previously we were simply ignoring these events...
788 if ( bCheck_KEYBOARD
|| bCheck_OTHER
)
790 if ( PeekMessageW( &aMsg
, nullptr, WM_KEYDOWN
, WM_KEYDOWN
,
791 PM_NOREMOVE
| PM_NOYIELD
) )
793 const bool bIsModifier
= ( (aMsg
.wParam
== VK_SHIFT
) ||
794 (aMsg
.wParam
== VK_CONTROL
) || (aMsg
.wParam
== VK_MENU
) );
795 if ( bCheck_KEYBOARD
&& !bIsModifier
)
797 if ( bCheck_OTHER
&& bIsModifier
)
802 // Other checks for all messages not excluded.
803 // The less we exclude, the less ranges have to be checked!
806 // TIMER and KEYBOARD are already handled, so always exclude them!
807 VclInputFlags nOtherType
= nType
&
808 ~VclInputFlags(VclInputFlags::KEYBOARD
| VclInputFlags::TIMER
);
810 std::vector
<MsgRange
> aMsgRangeList( GetOtherRanges( nOtherType
) );
811 for ( MsgRange
const & aRange
: aMsgRangeList
)
812 if ( PeekMessageW( &aMsg
, nullptr, aRange
.nStart
,
813 aRange
.nEnd
, PM_NOREMOVE
| PM_NOYIELD
) )
816 // MOUSE and PAINT already handled, so skip further checks
820 if ( nType
& VclInputFlags::MOUSE
)
822 if ( PeekMessageW( &aMsg
, nullptr, WM_MOUSEFIRST
, WM_MOUSELAST
,
823 PM_NOREMOVE
| PM_NOYIELD
) )
827 if ( nType
& VclInputFlags::PAINT
)
829 if ( PeekMessageW( &aMsg
, nullptr, WM_PAINT
, WM_PAINT
,
830 PM_NOREMOVE
| PM_NOYIELD
) )
833 if ( PeekMessageW( &aMsg
, nullptr, WM_SIZE
, WM_SIZE
,
834 PM_NOREMOVE
| PM_NOYIELD
) )
837 if ( PeekMessageW( &aMsg
, nullptr, SAL_MSG_POSTCALLSIZE
, SAL_MSG_POSTCALLSIZE
,
838 PM_NOREMOVE
| PM_NOYIELD
) )
841 if ( PeekMessageW( &aMsg
, nullptr, WM_MOVE
, WM_MOVE
,
842 PM_NOREMOVE
| PM_NOYIELD
) )
845 if ( PeekMessageW( &aMsg
, nullptr, SAL_MSG_POSTMOVE
, SAL_MSG_POSTMOVE
,
846 PM_NOREMOVE
| PM_NOYIELD
) )
854 SalFrame
* WinSalInstance::CreateChildFrame( SystemParentData
* pSystemParentData
, SalFrameStyleFlags nSalFrameStyle
)
856 // to switch to Main-Thread
857 return reinterpret_cast<SalFrame
*>(static_cast<sal_IntPtr
>(SendMessageW( mhComWnd
, SAL_MSG_CREATEFRAME
, static_cast<WPARAM
>(nSalFrameStyle
), reinterpret_cast<LPARAM
>(pSystemParentData
->hWnd
) )));
860 SalFrame
* WinSalInstance::CreateFrame( SalFrame
* pParent
, SalFrameStyleFlags nSalFrameStyle
)
862 // to switch to Main-Thread
865 hWndParent
= static_cast<WinSalFrame
*>(pParent
)->mhWnd
;
867 hWndParent
= nullptr;
868 return reinterpret_cast<SalFrame
*>(static_cast<sal_IntPtr
>(SendMessageW( mhComWnd
, SAL_MSG_CREATEFRAME
, static_cast<WPARAM
>(nSalFrameStyle
), reinterpret_cast<LPARAM
>(hWndParent
) )));
871 void WinSalInstance::DestroyFrame( SalFrame
* pFrame
)
873 OpenGLContext::prepareForYield();
874 SendMessageW( mhComWnd
, SAL_MSG_DESTROYFRAME
, 0, reinterpret_cast<LPARAM
>(pFrame
) );
877 SalObject
* WinSalInstance::CreateObject( SalFrame
* pParent
,
878 SystemWindowData
* /*pWindowData*/, // SystemWindowData meaningless on Windows
881 // to switch to Main-Thread
882 return reinterpret_cast<SalObject
*>(static_cast<sal_IntPtr
>(SendMessageW( mhComWnd
, SAL_MSG_CREATEOBJECT
, 0, reinterpret_cast<LPARAM
>(static_cast<WinSalFrame
*>(pParent
)) )));
885 void WinSalInstance::DestroyObject( SalObject
* pObject
)
887 SendMessageW( mhComWnd
, SAL_MSG_DESTROYOBJECT
, 0, reinterpret_cast<LPARAM
>(pObject
) );
890 OUString
WinSalInstance::GetConnectionIdentifier()
895 /** Add a file to the system shells recent document list if there is any.
896 This function may have no effect under Unix because there is no
897 standard API among the different desktop managers.
900 The file url of the document.
902 void WinSalInstance::AddToRecentDocumentList(const OUString
& rFileUrl
, const OUString
& /*rMimeType*/, const OUString
& rDocumentService
)
904 if (Application::IsHeadlessModeEnabled())
907 OUString system_path
;
908 osl::FileBase::RC rc
= osl::FileBase::getSystemPathFromFileURL(rFileUrl
, system_path
);
910 OSL_ENSURE(osl::FileBase::E_None
== rc
, "Invalid file url");
912 if (osl::FileBase::E_None
== rc
)
914 IShellItem
* pShellItem
= nullptr;
916 HRESULT hr
= SHCreateItemFromParsingName(o3tl::toW(system_path
.getStr()), nullptr, IID_PPV_ARGS(&pShellItem
));
918 if ( SUCCEEDED(hr
) && pShellItem
)
920 OUString sApplicationName
;
922 if ( rDocumentService
== "com.sun.star.text.TextDocument" ||
923 rDocumentService
== "com.sun.star.text.GlobalDocument" ||
924 rDocumentService
== "com.sun.star.text.WebDocument" ||
925 rDocumentService
== "com.sun.star.xforms.XMLFormDocument" )
926 sApplicationName
= "Writer";
927 else if ( rDocumentService
== "com.sun.star.sheet.SpreadsheetDocument" ||
928 rDocumentService
== "com.sun.star.chart2.ChartDocument" )
929 sApplicationName
= "Calc";
930 else if ( rDocumentService
== "com.sun.star.presentation.PresentationDocument" )
931 sApplicationName
= "Impress";
932 else if ( rDocumentService
== "com.sun.star.drawing.DrawingDocument" )
933 sApplicationName
= "Draw";
934 else if ( rDocumentService
== "com.sun.star.formula.FormulaProperties" )
935 sApplicationName
= "Math";
936 else if ( rDocumentService
== "com.sun.star.sdb.DatabaseDocument" ||
937 rDocumentService
== "com.sun.star.sdb.OfficeDatabaseDocument" ||
938 rDocumentService
== "com.sun.star.sdb.RelationDesign" ||
939 rDocumentService
== "com.sun.star.sdb.QueryDesign" ||
940 rDocumentService
== "com.sun.star.sdb.TableDesign" ||
941 rDocumentService
== "com.sun.star.sdb.DataSourceBrowser" )
942 sApplicationName
= "Base";
944 if ( !sApplicationName
.isEmpty() )
946 OUString
sApplicationID("TheDocumentFoundation.LibreOffice." + sApplicationName
);
949 info
.psi
= pShellItem
;
950 info
.pszAppID
= o3tl::toW(sApplicationID
.getStr());
952 SHAddToRecentDocs ( SHARD_APPIDINFO
, &info
);
956 // For whatever reason, we could not use the SHARD_APPIDINFO semantics
957 SHAddToRecentDocs(SHARD_PATHW
, system_path
.getStr());
961 SalTimer
* WinSalInstance::CreateSalTimer()
963 return new WinSalTimer();
966 std::shared_ptr
<SalBitmap
> WinSalInstance::CreateSalBitmap()
968 if (OpenGLHelper::isVCLOpenGLEnabled())
969 return std::make_shared
<OpenGLSalBitmap
>();
971 return std::make_shared
<WinSalBitmap
>();
974 int WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(int, LPEXCEPTION_POINTERS pExceptionInfo
)
976 // Decide if an exception is a c++ (mostly UNO) exception or a process violation.
977 // Depending on this information we pass process violations directly to our signal handler ...
978 // and c++ (UNO) exceptions are sended to the following code on the current stack.
979 // Problem behind: user32.dll sometime consumes exceptions/process violations .-)
982 static const DWORD EXCEPTION_MSC_CPP_EXCEPTION
= 0xE06D7363;
984 if (pExceptionInfo
->ExceptionRecord
->ExceptionCode
== EXCEPTION_MSC_CPP_EXCEPTION
)
985 return EXCEPTION_CONTINUE_SEARCH
;
987 return UnhandledExceptionFilter( pExceptionInfo
);
990 typedef LONG NTSTATUS
;
991 typedef NTSTATUS(WINAPI
* RtlGetVersion_t
)(PRTL_OSVERSIONINFOW
);
992 constexpr NTSTATUS STATUS_SUCCESS
= 0x00000000;
994 OUString
WinSalInstance::getOSVersion()
996 OUStringBuffer
aVer(50); // capacity for string like "Windows 6.1 Service Pack 1 build 7601"
997 aVer
.append("Windows ");
998 // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
999 // subject to manifest-based behavior since Windows 8.1, so give wrong results.
1000 // Another approach would be to use NetWkstaGetInfo, but that has some small
1001 // reported delays (some milliseconds), and might get slower in domains with
1002 // poor network connections.
1003 // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
1004 bool bHaveVerFromKernel32
= false;
1005 if (HMODULE h_kernel32
= GetModuleHandleW(L
"kernel32.dll"))
1007 wchar_t szPath
[MAX_PATH
];
1008 DWORD dwCount
= GetModuleFileNameW(h_kernel32
, szPath
, SAL_N_ELEMENTS(szPath
));
1009 if (dwCount
!= 0 && dwCount
< SAL_N_ELEMENTS(szPath
))
1011 dwCount
= GetFileVersionInfoSizeW(szPath
, nullptr);
1014 std::unique_ptr
<char[]> ver(new char[dwCount
]);
1015 if (GetFileVersionInfoW(szPath
, 0, dwCount
, ver
.get()) != FALSE
)
1017 void* pBlock
= nullptr;
1019 if (VerQueryValueW(ver
.get(), L
"\\", &pBlock
, &dwBlockSz
) != FALSE
&& dwBlockSz
>= sizeof(VS_FIXEDFILEINFO
))
1021 VS_FIXEDFILEINFO
* vi1
= static_cast<VS_FIXEDFILEINFO
*>(pBlock
);
1022 aVer
.append(OUString::number(HIWORD(vi1
->dwProductVersionMS
)) + "."
1023 + OUString::number(LOWORD(vi1
->dwProductVersionMS
)));
1024 bHaveVerFromKernel32
= true;
1030 // Now use RtlGetVersion (which is not subject to deprecation for GetVersion(Ex) API)
1031 // to get build number and SP info
1032 bool bHaveVerFromRtlGetVersion
= false;
1033 if (HMODULE h_ntdll
= GetModuleHandleW(L
"ntdll.dll"))
1035 if (auto RtlGetVersion
1036 = reinterpret_cast<RtlGetVersion_t
>(GetProcAddress(h_ntdll
, "RtlGetVersion")))
1038 RTL_OSVERSIONINFOW vi2
{}; // initialize with zeroes - a better alternative to memset
1039 vi2
.dwOSVersionInfoSize
= sizeof(vi2
);
1040 if (STATUS_SUCCESS
== RtlGetVersion(&vi2
))
1042 if (!bHaveVerFromKernel32
) // we failed above; let's hope this would be useful
1043 aVer
.append(OUString::number(vi2
.dwMajorVersion
) + "."
1044 + OUString::number(vi2
.dwMinorVersion
));
1046 if (vi2
.szCSDVersion
[0])
1047 aVer
.append(o3tl::toU(vi2
.szCSDVersion
)).append(" ");
1048 aVer
.append("Build " + OUString::number(vi2
.dwBuildNumber
));
1049 bHaveVerFromRtlGetVersion
= true;
1053 if (!bHaveVerFromKernel32
&& !bHaveVerFromRtlGetVersion
)
1054 aVer
.append("unknown");
1055 return aVer
.makeStringAndClear();
1058 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */