bump product version to 6.4.0.3
[LibreOffice.git] / vcl / win / app / salinst.cxx
blob1994a274f8b497e3aad6e7456bed7880c0690fa8
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 <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>
50 #include <salsys.hxx>
52 #include <desktop/crashreport.hxx>
54 #if defined _MSC_VER
55 #ifndef min
56 #define min(a,b) (((a) < (b)) ? (a) : (b))
57 #endif
58 #ifndef max
59 #define max(a,b) (((a) > (b)) ? (a) : (b))
60 #endif
61 #endif
63 #include <prewin.h>
65 #include <gdiplus.h>
66 #include <shlobj.h>
68 #include <postwin.h>
70 void SalAbort( const OUString& rErrorText, bool )
72 ImplFreeSalGDI();
74 if ( rErrorText.isEmpty() )
76 // make sure crash reporter is triggered
77 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
78 FatalAppExitW( 0, L"Application Error" );
80 else
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()
96 protected:
97 virtual void doAcquire( sal_uInt32 nLockCount ) override;
98 virtual sal_uInt32 doRelease( bool bUnlockAll ) override;
100 static void BeforeReleaseHandler();
102 public:
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
122 GdiFlush();
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 )
135 return;
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.
143 do {
144 // reset condition *before* acquiring!
145 m_condition.reset();
146 if (m_aMutex.tryToAcquire())
147 break;
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);
152 while ( true );
154 else
155 m_aMutex.acquire();
156 ++m_nCount;
157 --nLockCount;
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() )
166 return 1;
168 sal_uInt32 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
169 // wake up ImplSalYieldMutexAcquireWithWait() after release
170 if ( 0 == m_nCount )
171 m_condition.set();
172 return nCount;
175 bool SalYieldMutex::tryToAcquire()
177 WinSalInstance* pInst = GetSalData()->mpInstance;
178 if ( pInst )
180 if ( pInst->m_nNoYieldLock && pInst->IsMainThread() )
181 return true;
182 else
183 return comphelper::SolarMutex::tryToAcquire();
185 else
186 return false;
189 void ImplSalYieldMutexAcquireWithWait( sal_uInt32 nCount )
191 WinSalInstance* pInst = GetSalData()->mpInstance;
192 if ( pInst )
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;
205 if ( pInst )
207 GdiFlush();
208 pInst->GetYieldMutex()->release();
212 bool SalYieldMutex::IsCurrentThread() const
214 if ( !GetSalData()->mpInstance->m_nNoYieldLock )
215 return SolarMutex::IsCurrentThread();
216 else
217 return GetSalData()->mpInstance->IsMainThread();
220 void SalData::initKeyCodeMap()
222 UINT nKey;
223 #define initKey( a, b )\
224 nKey = LOWORD( VkKeyScanW( a ) );\
225 if( nKey < 0xffff )\
226 maVKMap[ nKey ] = b;
228 maVKMap.clear();
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 );
247 // SalData
249 SalData::SalData()
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
271 int i;
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;
296 // init with NULL
297 gdiplusToken = 0;
299 initKeyCodeMap();
301 SetSalData( this );
302 initNWF();
304 CoInitialize(nullptr); // put main thread in Single Threaded Apartment (STA)
305 static Gdiplus::GdiplusStartupInput gdiplusStartupInput;
306 Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
309 SalData::~SalData()
311 deInitNWF();
312 SetSalData( nullptr );
314 CoUninitialize();
316 if (gdiplusToken)
317 Gdiplus::GdiplusShutdown(gdiplusToken);
320 extern "C" {
321 VCLPLUG_WIN_PUBLIC SalInstance* create_SalInstance()
323 SalData* pSalData = new SalData();
325 STARTUPINFOW aSI;
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 ) )
347 return nullptr;
349 aWndClassEx.hIcon = nullptr;
350 aWndClassEx.hIconSm = nullptr;
351 aWndClassEx.style |= CS_SAVEBITS;
352 aWndClassEx.lpszClassName = SAL_SUBFRAME_CLASSNAMEW;
353 if ( !RegisterClassExW( &aWndClassEx ) )
354 return nullptr;
356 // shadow effect for popups on XP
357 aWndClassEx.style |= CS_DROPSHADOW;
358 aWndClassEx.lpszClassName = SAL_TMPSUBFRAME_CLASSNAMEW;
359 if ( !RegisterClassExW( &aWndClassEx ) )
360 return nullptr;
362 aWndClassEx.style = 0;
363 aWndClassEx.lpfnWndProc = SalComWndProcW;
364 aWndClassEx.cbWndExtra = 0;
365 aWndClassEx.lpszClassName = SAL_COM_CLASSNAMEW;
366 if ( !RegisterClassExW( &aWndClassEx ) )
367 return nullptr;
369 HWND hComWnd = CreateWindowExW( WS_EX_TOOLWINDOW, SAL_COM_CLASSNAMEW,
370 L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
371 pSalData->mhInst, nullptr );
372 if ( !hComWnd )
373 return 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
383 ImplInitSalGDI();
385 return pInst;
389 WinSalInstance::WinSalInstance()
390 : SalInstance(std::make_unique<SalYieldMutex>())
391 , mhInst( nullptr )
392 , mhComWnd( nullptr )
393 , m_nNoYieldLock( 0 )
395 ImplSVData* pSVData = ImplGetSVData();
396 pSVData->maAppData.mxToolkitName = OUString("win");
399 WinSalInstance::~WinSalInstance()
401 ImplFreeSalGDI();
402 DestroyWindow( mhComWnd );
405 static LRESULT ImplSalDispatchMessage( const MSG* pMsg )
407 SalData* pSalData = GetSalData();
408 if ( pSalData->mpFirstObject && ImplSalPreDispatchMsg( pMsg ) )
409 return 0;
410 LRESULT lResult = DispatchMessageW( pMsg );
411 if ( pSalData->mpFirstObject )
412 ImplSalPostDispatchMsg( pMsg );
413 return lResult;
416 bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
418 static sal_uInt32 nLastTicks = 0;
419 MSG aMsg;
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 );
426 if ( bNoYieldLock )
427 return false;
429 sal_uInt32 nCurTicks = 0;
430 if ( bHandleAllCurrentEvents )
431 nCurTicks = GetTickCount();
433 bool bHadNewerEvent = false;
436 bOneEvent = PeekMessageW( &aMsg, nullptr, 0, 0, PM_REMOVE );
437 if ( bOneEvent )
439 bWasMsg = true;
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) )
455 break;
457 while( true );
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();
466 bWasMsg = true;
469 if ( bHandleAllCurrentEvents )
470 nLastTicks = nCurTicks;
472 if ( bWait && !bWasMsg )
474 if ( GetMessageW( &aMsg, nullptr, 0, 0 ) )
476 bWasMsg = true;
477 TranslateMessage( &aMsg );
478 ImplSalDispatchMessage( &aMsg );
482 // we're back in the main loop after resize or move
483 if ( pTimer )
484 pTimer->SetForceRealTimer( false );
486 return bWasMsg;
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();
507 bDidWork = true;
510 else
512 bDidWork = ImplSalYield( bWait, bHandleAllCurrentEvents );
513 if ( bDidWork )
514 maWaitingYieldCond.set();
517 return bDidWork;
520 #define CASE_NOYIELDLOCK( salmsg, function ) \
521 case salmsg: \
522 if (bIsOtherThreadMessage) \
524 ++pInst->m_nNoYieldLock; \
525 function; \
526 --pInst->m_nNoYieldLock; \
528 else \
530 DBG_TESTSOLARMUTEX(); \
531 function; \
533 break;
535 #define CASE_NOYIELDLOCK_RESULT( salmsg, function ) \
536 case salmsg: \
537 if (bIsOtherThreadMessage) \
539 ++pInst->m_nNoYieldLock; \
540 nRet = reinterpret_cast<LRESULT>( function ); \
541 --pInst->m_nNoYieldLock; \
543 else \
545 DBG_TESTSOLARMUTEX(); \
546 nRet = reinterpret_cast<LRESULT>( function ); \
548 break;
550 LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, bool& rDef )
552 const BOOL bIsOtherThreadMessage = InSendMessage();
553 LRESULT nRet = 0;
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);
560 switch ( nMsg )
562 case SAL_MSG_THREADYIELD:
563 assert( !static_cast<bool>(wParam) );
564 nRet = static_cast<LRESULT>(ImplSalYield(
565 false, static_cast<bool>( lParam ) ));
566 break;
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;
573 else
574 nTime = 0;
575 assert( pTimer != nullptr );
576 pTimer->ImplStart( nTime );
577 break;
580 case SAL_MSG_STOPTIMER:
581 assert( pTimer != nullptr );
582 pTimer->ImplStop();
583 break;
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);
603 break;
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 );
616 break;
618 case WM_TIMER:
619 assert( pTimer != nullptr );
620 pTimer->ImplHandle_WM_TIMER( wParam );
621 break;
623 case SAL_MSG_FORCE_REAL_TIMER:
624 assert(pTimer != nullptr);
625 pTimer->SetForceRealTimer(true);
626 break;
628 case SAL_MSG_DUMMY:
629 break;
631 default:
632 rDef = true;
633 break;
636 return nRet;
639 #undef CASE_NOYIELDLOCK
640 #undef CASE_NOYIELDLOCK_RESULT
642 LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
644 bool bDef = false;
645 LRESULT nRet = 0;
646 __try
648 nRet = SalComWndProc( hWnd, nMsg, wParam, lParam, bDef );
650 __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
653 if ( bDef )
655 if ( !ImplHandleGlobalMsg( hWnd, nMsg, wParam, lParam, nRet ) )
656 nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam );
658 return nRet;
661 struct MsgRange
663 UINT nStart;
664 UINT nEnd;
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[] =
676 WM_MOVE, // 3
677 WM_SIZE, // 5
678 WM_PAINT, // 15
679 WM_KEYDOWN, // 256
680 WM_TIMER, // 275
682 WM_MOUSEFIRST, // 512
683 513,
684 514,
685 515,
686 516,
687 517,
688 518,
689 519,
690 520,
691 WM_MOUSELAST, // 521
693 SAL_MSG_POSTMOVE, // WM_USER+136
694 SAL_MSG_POSTCALLSIZE, // WM_USER+137
696 SAL_MSG_TIMER_CALLBACK, // WM_USER+162
698 UINT_MAX
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 };
732 bool doEnd = true;
733 for ( unsigned i = 1; i < MAX_EXCL; ++i )
735 if ( aExcludeMsgList[ i ] )
737 if ( !doEnd )
739 if ( nExcludeMsgIds[ i ] == aRange.nStart )
740 ++aRange.nStart;
741 else
742 doEnd = true;
744 if ( doEnd )
746 aRange.nEnd = nExcludeMsgIds[ i ] - 1;
747 aResult.push_back( aRange );
748 doEnd = false;
749 aRange.nStart = aRange.nEnd + 2;
754 if ( aRange.nStart != UINT_MAX )
756 aRange.nEnd = UINT_MAX;
757 aResult.push_back( aRange );
760 return aResult;
763 bool WinSalInstance::AnyInput( VclInputFlags nType )
765 MSG aMsg;
767 if ( nType & VclInputFlags::TIMER )
769 const WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
770 if ( pTimer && pTimer->HasTimerElapsed() )
771 return true;
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 ) )
779 return true;
781 else
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 )
796 return true;
797 if ( bCheck_OTHER && bIsModifier )
798 return true;
802 // Other checks for all messages not excluded.
803 // The less we exclude, the less ranges have to be checked!
804 if ( bCheck_OTHER )
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 ) )
814 return true;
816 // MOUSE and PAINT already handled, so skip further checks
817 return false;
820 if ( nType & VclInputFlags::MOUSE )
822 if ( PeekMessageW( &aMsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST,
823 PM_NOREMOVE | PM_NOYIELD ) )
824 return true;
827 if ( nType & VclInputFlags::PAINT )
829 if ( PeekMessageW( &aMsg, nullptr, WM_PAINT, WM_PAINT,
830 PM_NOREMOVE | PM_NOYIELD ) )
831 return true;
833 if ( PeekMessageW( &aMsg, nullptr, WM_SIZE, WM_SIZE,
834 PM_NOREMOVE | PM_NOYIELD ) )
835 return true;
837 if ( PeekMessageW( &aMsg, nullptr, SAL_MSG_POSTCALLSIZE, SAL_MSG_POSTCALLSIZE,
838 PM_NOREMOVE | PM_NOYIELD ) )
839 return true;
841 if ( PeekMessageW( &aMsg, nullptr, WM_MOVE, WM_MOVE,
842 PM_NOREMOVE | PM_NOYIELD ) )
843 return true;
845 if ( PeekMessageW( &aMsg, nullptr, SAL_MSG_POSTMOVE, SAL_MSG_POSTMOVE,
846 PM_NOREMOVE | PM_NOYIELD ) )
847 return true;
851 return false;
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
863 HWND hWndParent;
864 if ( pParent )
865 hWndParent = static_cast<WinSalFrame*>(pParent)->mhWnd;
866 else
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
879 bool /*bShow*/ )
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()
892 return OUString();
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.
899 @param aFileUrl
900 The file url of the document.
902 void WinSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& rDocumentService)
904 if (Application::IsHeadlessModeEnabled())
905 return;
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);
948 SHARDAPPIDINFO info;
949 info.psi = pShellItem;
950 info.pszAppID = o3tl::toW(sApplicationID.getStr());
952 SHAddToRecentDocs ( SHARD_APPIDINFO, &info );
953 return;
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>();
970 else
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 .-)
980 // see also #112221#
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);
1012 if (dwCount != 0)
1014 std::unique_ptr<char[]> ver(new char[dwCount]);
1015 if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
1017 void* pBlock = nullptr;
1018 UINT dwBlockSz = 0;
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));
1045 aVer.append(" ");
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: */