Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / vcl / win / app / salinst.cxx
blob1d9afa7b7265e9dfbd123020f59ec6862ad3257d
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 <tools/time.hxx>
27 #include <comphelper/solarmutex.hxx>
28 #include <o3tl/char16_t2wchar_t.hxx>
30 #include <vcl/inputtypes.hxx>
31 #include <vcl/opengl/OpenGLHelper.hxx>
32 #include <vcl/opengl/OpenGLContext.hxx>
33 #include <vcl/timer.hxx>
35 #include <opengl/salbmp.hxx>
36 #include <opengl/win/gdiimpl.hxx>
37 #include <win/wincomp.hxx>
38 #include <win/salids.hrc>
39 #include <win/saldata.hxx>
40 #include <win/salinst.h>
41 #include <win/salframe.h>
42 #include <win/salobj.h>
43 #include <win/saltimer.h>
44 #include <win/salbmp.h>
45 #include <win/winlayout.hxx>
47 #include <salimestatus.hxx>
48 #include <salsys.hxx>
50 #include <desktop/crashreport.hxx>
52 #if defined _MSC_VER
53 #ifndef min
54 #define min(a,b) (((a) < (b)) ? (a) : (b))
55 #endif
56 #ifndef max
57 #define max(a,b) (((a) > (b)) ? (a) : (b))
58 #endif
59 #endif
61 #include <prewin.h>
63 #include <gdiplus.h>
64 #include <shlobj.h>
66 #include <postwin.h>
68 void SalAbort( const OUString& rErrorText, bool )
70 ImplFreeSalGDI();
72 if ( rErrorText.isEmpty() )
74 // make sure crash reporter is triggered
75 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
76 FatalAppExitW( 0, L"Application Error" );
78 else
80 CrashReporter::AddKeyValue("AbortMessage", rErrorText);
81 // make sure crash reporter is triggered
82 RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
83 FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) );
87 LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam );
89 class SalYieldMutex : public comphelper::GenericSolarMutex
91 public: // for ImplSalYield() and ImplSalYieldMutexAcquireWithWait()
92 osl::Condition m_condition; /// for MsgWaitForMultipleObjects()
94 protected:
95 virtual void doAcquire( sal_uInt32 nLockCount ) override;
96 virtual sal_uInt32 doRelease( bool bUnlockAll ) override;
98 static void BeforeReleaseHandler();
100 public:
101 explicit SalYieldMutex();
103 virtual bool IsCurrentThread() const override;
104 virtual bool tryToAcquire() override;
107 SalYieldMutex::SalYieldMutex()
109 SetBeforeReleaseHandler( &SalYieldMutex::BeforeReleaseHandler );
112 void SalYieldMutex::BeforeReleaseHandler()
114 OpenGLContext::prepareForYield();
116 if ( GetSalData()->mnAppThreadId != GetCurrentThreadId() )
118 // If we don't call these message, the Output from the
119 // Java clients doesn't come in the right order
120 GdiFlush();
124 /// note: while VCL is fully up and running (other threads started and
125 /// before shutdown), the main thread must acquire SolarMutex only via
126 /// this function to avoid deadlock
127 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
129 WinSalInstance* pInst = GetSalData()->mpInstance;
130 if ( pInst && pInst->IsMainThread() )
132 if ( pInst->m_nNoYieldLock )
133 return;
134 // tdf#96887 If this is the main thread, then we must wait for two things:
135 // - the mpSalYieldMutex being freed
136 // - SendMessage() being triggered
137 // This can nicely be done using MsgWaitForMultipleObjects. The 2nd one is
138 // needed because if we don't reschedule, then we create deadlocks if a
139 // Window's create/destroy is called via SendMessage() from another thread.
140 // Have a look at the osl_waitCondition implementation for more info.
141 do {
142 // reset condition *before* acquiring!
143 m_condition.reset();
144 if (m_aMutex.tryToAcquire())
145 break;
146 // wait for SalYieldMutex::release() to set the condition
147 osl::Condition::Result res = m_condition.wait();
148 assert(osl::Condition::Result::result_ok == res);
150 while ( true );
152 else
153 m_aMutex.acquire();
154 ++m_nCount;
155 --nLockCount;
157 comphelper::GenericSolarMutex::doAcquire( nLockCount );
160 sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll )
162 WinSalInstance* pInst = GetSalData()->mpInstance;
163 if ( pInst && pInst->m_nNoYieldLock && pInst->IsMainThread() )
164 return 1;
166 sal_uInt32 nCount = comphelper::GenericSolarMutex::doRelease( bUnlockAll );
167 // wake up ImplSalYieldMutexAcquireWithWait() after release
168 if ( 0 == m_nCount )
169 m_condition.set();
170 return nCount;
173 bool SalYieldMutex::tryToAcquire()
175 WinSalInstance* pInst = GetSalData()->mpInstance;
176 if ( pInst )
178 if ( pInst->m_nNoYieldLock && pInst->IsMainThread() )
179 return true;
180 else
181 return comphelper::GenericSolarMutex::tryToAcquire();
183 else
184 return false;
187 void ImplSalYieldMutexAcquireWithWait( sal_uInt32 nCount )
189 WinSalInstance* pInst = GetSalData()->mpInstance;
190 if ( pInst )
191 pInst->mpSalYieldMutex->acquire( nCount );
194 bool ImplSalYieldMutexTryToAcquire()
196 WinSalInstance* pInst = GetSalData()->mpInstance;
197 return pInst && pInst->mpSalYieldMutex->tryToAcquire();
200 void ImplSalYieldMutexRelease()
202 WinSalInstance* pInst = GetSalData()->mpInstance;
203 if ( pInst )
205 GdiFlush();
206 pInst->mpSalYieldMutex->release();
210 bool SalYieldMutex::IsCurrentThread() const
212 if ( !GetSalData()->mpInstance->m_nNoYieldLock )
213 // For the Windows backend, the LO identifier is the system thread ID
214 return m_nThreadId == GetCurrentThreadId();
215 else
216 return GetSalData()->mpInstance->IsMainThread();
219 void SalData::initKeyCodeMap()
221 UINT nKey;
222 #define initKey( a, b )\
223 nKey = LOWORD( VkKeyScanW( a ) );\
224 if( nKey < 0xffff )\
225 maVKMap[ nKey ] = b;
227 maVKMap.clear();
229 initKey( L'+', KEY_ADD );
230 initKey( L'-', KEY_SUBTRACT );
231 initKey( L'*', KEY_MULTIPLY );
232 initKey( L'/', KEY_DIVIDE );
233 initKey( L'.', KEY_POINT );
234 initKey( L',', KEY_COMMA );
235 initKey( L'<', KEY_LESS );
236 initKey( L'>', KEY_GREATER );
237 initKey( L'=', KEY_EQUAL );
238 initKey( L'~', KEY_TILDE );
239 initKey( L'`', KEY_QUOTELEFT );
240 initKey( L'[', KEY_BRACKETLEFT );
241 initKey( L']', KEY_BRACKETRIGHT );
242 initKey( L';', KEY_SEMICOLON );
243 initKey( L'\'', KEY_QUOTERIGHT );
246 // SalData
248 SalData::SalData()
250 mhInst = nullptr; // default instance handle
251 mnCmdShow = 0; // default frame show style
252 mhDitherPal = nullptr; // dither palette
253 mhDitherDIB = nullptr; // dither memory handle
254 mpDitherDIB = nullptr; // dither memory
255 mpDitherDIBData = nullptr; // beginning of DIB data
256 mpDitherDiff = nullptr; // Dither mapping table
257 mpDitherLow = nullptr; // Dither mapping table
258 mpDitherHigh = nullptr; // Dither mapping table
259 mhSalObjMsgHook = nullptr; // hook to get interesting msg for SalObject
260 mhWantLeaveMsg = nullptr; // window handle, that want a MOUSELEAVE message
261 mpMouseLeaveTimer = nullptr; // Timer for MouseLeave Test
262 mpInstance = nullptr; // pointer of first instance
263 mpFirstFrame = nullptr; // pointer of first frame
264 mpFirstObject = nullptr; // pointer of first object window
265 mpFirstVD = nullptr; // first VirDev
266 mpFirstPrinter = nullptr; // first printing printer
267 mpHDCCache = nullptr; // Cache for three DC's
268 mh50Bmp = nullptr; // 50% Bitmap
269 mh50Brush = nullptr; // 50% Brush
270 int i;
271 for(i=0; i<MAX_STOCKPEN; i++)
273 maStockPenColorAry[i] = 0;
274 mhStockPenAry[i] = nullptr;
276 for(i=0; i<MAX_STOCKBRUSH; i++)
278 maStockBrushColorAry[i] = 0;
279 mhStockBrushAry[i] = nullptr;
281 mnStockPenCount = 0; // count of static pens
282 mnStockBrushCount = 0; // count of static brushes
283 mnSalObjWantKeyEvt = 0; // KeyEvent for the SalObj hook
284 mnCacheDCInUse = 0; // count of CacheDC in use
285 mbObjClassInit = false; // is SALOBJECTCLASS initialised
286 mbInPalChange = false; // is in WM_QUERYNEWPALETTE
287 mnAppThreadId = 0; // Id from Application-Thread
288 mbScrSvrEnabled = FALSE; // ScreenSaver enabled
289 mpFirstIcon = nullptr; // icon cache, points to first icon, NULL if none
290 mpTempFontItem = nullptr;
291 mbThemeChanged = false; // true if visual theme was changed: throw away theme handles
292 mbThemeMenuSupport = false;
294 // init with NULL
295 gdiplusToken = 0;
297 initKeyCodeMap();
299 SetSalData( this );
300 initNWF();
303 SalData::~SalData()
305 deInitNWF();
306 SetSalData( nullptr );
309 void InitSalData()
311 SalData* pSalData = new SalData;
312 CoInitialize(nullptr); // put main thread in Single Threaded Apartment (STA)
314 // init GDIPlus
315 static Gdiplus::GdiplusStartupInput gdiplusStartupInput;
316 Gdiplus::GdiplusStartup(&pSalData->gdiplusToken, &gdiplusStartupInput, nullptr);
319 void DeInitSalData()
321 CoUninitialize();
322 SalData* pSalData = GetSalData();
324 // deinit GDIPlus
325 if(pSalData)
327 Gdiplus::GdiplusShutdown(pSalData->gdiplusToken);
330 delete pSalData;
333 void InitSalMain()
335 // remember data, copied from WinMain
336 SalData* pData = GetSalData();
337 if ( pData ) // Im AppServer NULL
339 STARTUPINFOW aSI;
340 aSI.cb = sizeof( aSI );
341 GetStartupInfoW( &aSI );
342 pData->mhInst = GetModuleHandleW( nullptr );
343 pData->mnCmdShow = aSI.wShowWindow;
347 SalInstance* CreateSalInstance()
349 SalData* pSalData = GetSalData();
351 pSalData->mnAppThreadId = GetCurrentThreadId();
353 // register frame class
354 WNDCLASSEXW aWndClassEx;
355 aWndClassEx.cbSize = sizeof( aWndClassEx );
356 aWndClassEx.style = CS_OWNDC;
357 aWndClassEx.lpfnWndProc = SalFrameWndProcW;
358 aWndClassEx.cbClsExtra = 0;
359 aWndClassEx.cbWndExtra = SAL_FRAME_WNDEXTRA;
360 aWndClassEx.hInstance = pSalData->mhInst;
361 aWndClassEx.hCursor = nullptr;
362 aWndClassEx.hbrBackground = nullptr;
363 aWndClassEx.lpszMenuName = nullptr;
364 aWndClassEx.lpszClassName = SAL_FRAME_CLASSNAMEW;
365 ImplLoadSalIcon( SAL_RESID_ICON_DEFAULT, aWndClassEx.hIcon, aWndClassEx.hIconSm );
366 if ( !RegisterClassExW( &aWndClassEx ) )
367 return nullptr;
369 aWndClassEx.hIcon = nullptr;
370 aWndClassEx.hIconSm = nullptr;
371 aWndClassEx.style |= CS_SAVEBITS;
372 aWndClassEx.lpszClassName = SAL_SUBFRAME_CLASSNAMEW;
373 if ( !RegisterClassExW( &aWndClassEx ) )
374 return nullptr;
376 // shadow effect for popups on XP
377 aWndClassEx.style |= CS_DROPSHADOW;
378 aWndClassEx.lpszClassName = SAL_TMPSUBFRAME_CLASSNAMEW;
379 if ( !RegisterClassExW( &aWndClassEx ) )
380 return nullptr;
382 aWndClassEx.style = 0;
383 aWndClassEx.lpfnWndProc = SalComWndProcW;
384 aWndClassEx.cbWndExtra = 0;
385 aWndClassEx.lpszClassName = SAL_COM_CLASSNAMEW;
386 if ( !RegisterClassExW( &aWndClassEx ) )
387 return nullptr;
389 HWND hComWnd = CreateWindowExW( WS_EX_TOOLWINDOW, SAL_COM_CLASSNAMEW,
390 L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
391 pSalData->mhInst, nullptr );
392 if ( !hComWnd )
393 return nullptr;
395 WinSalInstance* pInst = new WinSalInstance;
397 // init instance (only one instance in this version !!!)
398 pSalData->mpInstance = pInst;
399 pInst->mhInst = pSalData->mhInst;
400 pInst->mhComWnd = hComWnd;
402 // init static GDI Data
403 ImplInitSalGDI();
405 return pInst;
408 void DestroySalInstance( SalInstance* pInst )
410 SalData* pSalData = GetSalData();
412 // (only one instance in this version !!!)
414 ImplFreeSalGDI();
416 // reset instance
417 if ( pSalData->mpInstance == pInst )
418 pSalData->mpInstance = nullptr;
420 delete pInst;
423 WinSalInstance::WinSalInstance()
424 : mhComWnd( nullptr )
425 , m_nNoYieldLock( 0 )
427 mpSalYieldMutex = new SalYieldMutex();
428 mpSalYieldMutex->acquire();
431 WinSalInstance::~WinSalInstance()
433 mpSalYieldMutex->release();
434 delete mpSalYieldMutex;
435 DestroyWindow( mhComWnd );
438 comphelper::SolarMutex* WinSalInstance::GetYieldMutex()
440 return mpSalYieldMutex;
443 sal_uInt32 WinSalInstance::ReleaseYieldMutexAll()
445 return mpSalYieldMutex->release( true/*bUnlockAll*/ );
448 void WinSalInstance::AcquireYieldMutex( sal_uInt32 nCount )
450 mpSalYieldMutex->acquire( nCount );
453 static LRESULT ImplSalDispatchMessage( const MSG* pMsg )
455 SalData* pSalData = GetSalData();
456 if ( pSalData->mpFirstObject && ImplSalPreDispatchMsg( pMsg ) )
457 return 0;
458 LRESULT lResult = DispatchMessageW( pMsg );
459 if ( pSalData->mpFirstObject )
460 ImplSalPostDispatchMsg( pMsg );
461 return lResult;
464 bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
466 static sal_uInt32 nLastTicks = 0;
467 MSG aMsg;
468 bool bWasMsg = false, bOneEvent = false, bWasTimeoutMsg = false;
469 ImplSVData *const pSVData = ImplGetSVData();
470 WinSalTimer* pTimer = static_cast<WinSalTimer*>( pSVData->maSchedCtx.mpSalTimer );
471 const bool bNoYieldLock = (GetSalData()->mpInstance->m_nNoYieldLock > 0);
473 assert( !bNoYieldLock );
474 if ( bNoYieldLock )
475 return false;
477 sal_uInt32 nCurTicks = 0;
478 if ( bHandleAllCurrentEvents )
479 nCurTicks = GetTickCount();
481 bool bHadNewerEvent = false;
484 bOneEvent = PeekMessageW( &aMsg, nullptr, 0, 0, PM_REMOVE );
485 if ( bOneEvent )
487 bWasMsg = true;
488 TranslateMessage( &aMsg );
489 LRESULT nRet = ImplSalDispatchMessage( &aMsg );
491 if ( !bWasTimeoutMsg )
492 bWasTimeoutMsg = (SAL_MSG_TIMER_CALLBACK == aMsg.message)
493 && static_cast<bool>( nRet );
495 if ( bHandleAllCurrentEvents
496 && !bHadNewerEvent && aMsg.time > nCurTicks
497 && (nLastTicks <= nCurTicks || aMsg.time < nLastTicks) )
498 bHadNewerEvent = true;
499 bOneEvent = !bHadNewerEvent;
502 if ( !(bHandleAllCurrentEvents && bOneEvent) )
503 break;
505 while( true );
507 // 0ms timeouts are handled out-of-bounds to prevent busy-locking the
508 // event loop with timeout messages.
509 // We ensure we never handle more then one timeout per call.
510 // This way we'll always process a normal system message.
511 if ( !bWasTimeoutMsg && pTimer && pTimer->IsDirectTimeout() )
513 pTimer->ImplHandleElapsedTimer();
514 bWasMsg = true;
517 if ( bHandleAllCurrentEvents )
518 nLastTicks = nCurTicks;
520 if ( bWait && !bWasMsg )
522 if ( GetMessageW( &aMsg, nullptr, 0, 0 ) )
524 bWasMsg = true;
525 TranslateMessage( &aMsg );
526 ImplSalDispatchMessage( &aMsg );
530 // we're back in the main loop after resize or move
531 if ( pTimer )
532 pTimer->SetForceRealTimer( false );
534 return bWasMsg;
537 bool WinSalInstance::IsMainThread() const
539 const SalData* pSalData = GetSalData();
540 return pSalData->mnAppThreadId == GetCurrentThreadId();
543 bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
545 bool bDidWork = false;
546 SolarMutexReleaser aReleaser;
547 if ( !IsMainThread() )
549 bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD,
550 WPARAM(false), static_cast<LPARAM>(bHandleAllCurrentEvents) );
551 if ( !bDidWork && bWait )
553 maWaitingYieldCond.reset();
554 maWaitingYieldCond.wait();
555 bDidWork = true;
558 else
560 bDidWork = ImplSalYield( bWait, bHandleAllCurrentEvents );
561 if ( bDidWork )
562 maWaitingYieldCond.set();
565 return bDidWork;
568 #define CASE_NOYIELDLOCK( salmsg, function ) \
569 case salmsg: \
570 if (bIsOtherThreadMessage) \
572 ++pInst->m_nNoYieldLock; \
573 function; \
574 --pInst->m_nNoYieldLock; \
576 else \
578 DBG_TESTSOLARMUTEX(); \
579 function; \
581 break;
583 #define CASE_NOYIELDLOCK_RESULT( salmsg, function ) \
584 case salmsg: \
585 if (bIsOtherThreadMessage) \
587 ++pInst->m_nNoYieldLock; \
588 nRet = reinterpret_cast<LRESULT>( function ); \
589 --pInst->m_nNoYieldLock; \
591 else \
593 DBG_TESTSOLARMUTEX(); \
594 nRet = reinterpret_cast<LRESULT>( function ); \
596 break;
598 LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, bool& rDef )
600 const BOOL bIsOtherThreadMessage = InSendMessage();
601 LRESULT nRet = 0;
602 WinSalInstance *pInst = GetSalData()->mpInstance;
603 WinSalTimer *const pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
605 SAL_INFO("vcl.gdi.wndproc", "SalComWndProc(nMsg=" << nMsg << ", wParam=" << wParam
606 << ", lParam=" << lParam << "); inSendMsg: " << bIsOtherThreadMessage);
608 switch ( nMsg )
610 case SAL_MSG_THREADYIELD:
611 assert( !static_cast<bool>(wParam) );
612 nRet = static_cast<LRESULT>(ImplSalYield(
613 false, static_cast<bool>( lParam ) ));
614 break;
616 case SAL_MSG_STARTTIMER:
618 sal_uInt64 nTime = tools::Time::GetSystemTicks();
619 if ( nTime < static_cast<sal_uInt64>( lParam ) )
620 nTime = static_cast<sal_uInt64>( lParam ) - nTime;
621 else
622 nTime = 0;
623 assert( pTimer != nullptr );
624 pTimer->ImplStart( nTime );
625 break;
628 case SAL_MSG_STOPTIMER:
629 assert( pTimer != nullptr );
630 pTimer->ImplStop();
631 break;
633 CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEFRAME, ImplSalCreateFrame( GetSalData()->mpInstance,
634 reinterpret_cast<HWND>(lParam), static_cast<SalFrameStyleFlags>(wParam)) )
635 CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATEHWND, ImplSalReCreateHWND(
636 reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), false) )
637 CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATECHILDHWND, ImplSalReCreateHWND(
638 reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), true) )
639 CASE_NOYIELDLOCK( SAL_MSG_DESTROYFRAME, delete reinterpret_cast<SalFrame*>(lParam) )
641 case SAL_MSG_DESTROYHWND:
642 // We only destroy the native window here. We do NOT destroy the SalFrame contained
643 // in the structure (GetWindowPtr()).
644 if (DestroyWindow(reinterpret_cast<HWND>(lParam)) == 0)
646 OSL_FAIL("DestroyWindow failed!");
647 // Failure: We remove the SalFrame from the window structure. So we avoid that
648 // the window structure may contain an invalid pointer, once the SalFrame is deleted.
649 SetWindowPtr(reinterpret_cast<HWND>(lParam), nullptr);
651 break;
653 CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEOBJECT, ImplSalCreateObject(
654 GetSalData()->mpInstance, reinterpret_cast<WinSalFrame*>(lParam)) )
655 CASE_NOYIELDLOCK( SAL_MSG_DESTROYOBJECT, delete reinterpret_cast<SalObject*>(lParam) )
656 CASE_NOYIELDLOCK_RESULT( SAL_MSG_GETCACHEDDC, GetDCEx(
657 reinterpret_cast<HWND>(wParam), nullptr, DCX_CACHE) )
658 CASE_NOYIELDLOCK( SAL_MSG_RELEASEDC, ReleaseDC(
659 reinterpret_cast<HWND>(wParam), reinterpret_cast<HDC>(lParam)) )
661 case SAL_MSG_TIMER_CALLBACK:
662 assert( pTimer != nullptr );
663 nRet = static_cast<LRESULT>( pTimer->ImplHandleTimerEvent( wParam ) );
664 break;
666 case WM_TIMER:
667 assert( pTimer != nullptr );
668 nRet = static_cast<LRESULT>( pTimer->ImplHandle_WM_TIMER( wParam ) );
669 break;
671 case SAL_MSG_DUMMY:
672 break;
674 default:
675 rDef = true;
676 break;
679 return nRet;
682 #undef CASE_NOYIELDLOCK
683 #undef CASE_NOYIELDLOCK_RESULT
685 LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
687 bool bDef = false;
688 LRESULT nRet = 0;
689 __try
691 nRet = SalComWndProc( hWnd, nMsg, wParam, lParam, bDef );
693 __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
696 if ( bDef )
698 if ( !ImplHandleGlobalMsg( hWnd, nMsg, wParam, lParam, nRet ) )
699 nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam );
701 return nRet;
704 struct MsgRange
706 UINT nStart;
707 UINT nEnd;
710 static std::vector<MsgRange> GetOtherRanges( VclInputFlags nType )
712 assert( nType != VCL_INPUT_ANY );
714 // this array must be kept sorted!
715 const UINT nExcludeMsgIds[] =
719 WM_MOVE, // 3
720 WM_SIZE, // 5
721 WM_PAINT, // 15
722 WM_KEYDOWN, // 256
723 WM_TIMER, // 275
725 WM_MOUSEFIRST, // 512
726 513,
727 514,
728 515,
729 516,
730 517,
731 518,
732 519,
733 520,
734 WM_MOUSELAST, // 521
736 SAL_MSG_POSTMOVE, // WM_USER+136
737 SAL_MSG_POSTCALLSIZE, // WM_USER+137
739 SAL_MSG_TIMER_CALLBACK, // WM_USER+162
741 UINT_MAX
743 const unsigned MAX_EXCL = SAL_N_ELEMENTS( nExcludeMsgIds );
745 bool aExcludeMsgList[ MAX_EXCL ] = { false, };
746 std::vector<MsgRange> aResult;
748 // set the excluded values
749 if ( !(nType & VclInputFlags::MOUSE) )
751 for ( unsigned i = 0; nExcludeMsgIds[ 6 + i ] <= WM_MOUSELAST; ++i )
752 aExcludeMsgList[ 6 + i ] = true;
755 if ( !(nType & VclInputFlags::KEYBOARD) )
756 aExcludeMsgList[ 4 ] = true;
758 if ( !(nType & VclInputFlags::PAINT) )
760 aExcludeMsgList[ 1 ] = true;
761 aExcludeMsgList[ 2 ] = true;
762 aExcludeMsgList[ 3 ] = true;
763 aExcludeMsgList[ 16 ] = true;
764 aExcludeMsgList[ 17 ] = true;
767 if ( !(nType & VclInputFlags::TIMER) )
769 aExcludeMsgList[ 5 ] = true;
770 aExcludeMsgList[ 18 ] = true;
773 // build the message ranges to check
774 MsgRange aRange = { 0, 0 };
775 bool doEnd = true;
776 for ( unsigned i = 1; i < MAX_EXCL; ++i )
778 if ( aExcludeMsgList[ i ] )
780 if ( !doEnd )
782 if ( nExcludeMsgIds[ i ] == aRange.nStart )
783 ++aRange.nStart;
784 else
785 doEnd = true;
787 if ( doEnd )
789 aRange.nEnd = nExcludeMsgIds[ i ] - 1;
790 aResult.push_back( aRange );
791 doEnd = false;
792 aRange.nStart = aRange.nEnd + 2;
797 if ( aRange.nStart != UINT_MAX )
799 aRange.nEnd = UINT_MAX;
800 aResult.push_back( aRange );
803 return aResult;
806 bool WinSalInstance::AnyInput( VclInputFlags nType )
808 MSG aMsg;
810 if ( nType & VclInputFlags::TIMER )
812 const WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
813 if ( pTimer && pTimer->HasTimerElapsed() )
814 return true;
817 if ( (nType & VCL_INPUT_ANY) == VCL_INPUT_ANY )
819 // revert bugfix for #108919# which never reported timeouts when called from the timer handler
820 // which made the application completely unresponsive during background formatting
821 if ( PeekMessageW( &aMsg, nullptr, 0, 0, PM_NOREMOVE | PM_NOYIELD ) )
822 return true;
824 else
826 const bool bCheck_KEYBOARD (nType & VclInputFlags::KEYBOARD);
827 const bool bCheck_OTHER (nType & VclInputFlags::OTHER);
829 // If there is a modifier key event, it counts as OTHER
830 // Previously we were simply ignoring these events...
831 if ( bCheck_KEYBOARD || bCheck_OTHER )
833 if ( PeekMessageW( &aMsg, nullptr, WM_KEYDOWN, WM_KEYDOWN,
834 PM_NOREMOVE | PM_NOYIELD ) )
836 const bool bIsModifier = ( (aMsg.wParam == VK_SHIFT) ||
837 (aMsg.wParam == VK_CONTROL) || (aMsg.wParam == VK_MENU) );
838 if ( bCheck_KEYBOARD && !bIsModifier )
839 return true;
840 if ( bCheck_OTHER && bIsModifier )
841 return true;
845 // Other checks for all messages not excluded.
846 // The less we exclude, the less ranges have to be checked!
847 if ( bCheck_OTHER )
849 // TIMER and KEYBOARD are already handled, so always exclude them!
850 VclInputFlags nOtherType = nType &
851 ~VclInputFlags(VclInputFlags::KEYBOARD | VclInputFlags::TIMER);
853 std::vector<MsgRange> aMsgRangeList( GetOtherRanges( nOtherType ) );
854 for ( MsgRange const & aRange : aMsgRangeList )
855 if ( PeekMessageW( &aMsg, nullptr, aRange.nStart,
856 aRange.nEnd, PM_NOREMOVE | PM_NOYIELD ) )
857 return true;
859 // MOUSE and PAINT already handled, so skip further checks
860 return false;
863 if ( nType & VclInputFlags::MOUSE )
865 if ( PeekMessageW( &aMsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST,
866 PM_NOREMOVE | PM_NOYIELD ) )
867 return true;
870 if ( nType & VclInputFlags::PAINT )
872 if ( PeekMessageW( &aMsg, nullptr, WM_PAINT, WM_PAINT,
873 PM_NOREMOVE | PM_NOYIELD ) )
874 return true;
876 if ( PeekMessageW( &aMsg, nullptr, WM_SIZE, WM_SIZE,
877 PM_NOREMOVE | PM_NOYIELD ) )
878 return true;
880 if ( PeekMessageW( &aMsg, nullptr, SAL_MSG_POSTCALLSIZE, SAL_MSG_POSTCALLSIZE,
881 PM_NOREMOVE | PM_NOYIELD ) )
882 return true;
884 if ( PeekMessageW( &aMsg, nullptr, WM_MOVE, WM_MOVE,
885 PM_NOREMOVE | PM_NOYIELD ) )
886 return true;
888 if ( PeekMessageW( &aMsg, nullptr, SAL_MSG_POSTMOVE, SAL_MSG_POSTMOVE,
889 PM_NOREMOVE | PM_NOYIELD ) )
890 return true;
894 return false;
897 SalFrame* WinSalInstance::CreateChildFrame( SystemParentData* pSystemParentData, SalFrameStyleFlags nSalFrameStyle )
899 // to switch to Main-Thread
900 return reinterpret_cast<SalFrame*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEFRAME, static_cast<WPARAM>(nSalFrameStyle), reinterpret_cast<LPARAM>(pSystemParentData->hWnd) )));
903 SalFrame* WinSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle )
905 // to switch to Main-Thread
906 HWND hWndParent;
907 if ( pParent )
908 hWndParent = static_cast<WinSalFrame*>(pParent)->mhWnd;
909 else
910 hWndParent = nullptr;
911 return reinterpret_cast<SalFrame*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEFRAME, static_cast<WPARAM>(nSalFrameStyle), reinterpret_cast<LPARAM>(hWndParent) )));
914 void WinSalInstance::DestroyFrame( SalFrame* pFrame )
916 OpenGLContext::prepareForYield();
917 SendMessageW( mhComWnd, SAL_MSG_DESTROYFRAME, 0, reinterpret_cast<LPARAM>(pFrame) );
920 SalObject* WinSalInstance::CreateObject( SalFrame* pParent,
921 SystemWindowData* /*pWindowData*/, // SystemWindowData meaningless on Windows
922 bool /*bShow*/ )
924 // to switch to Main-Thread
925 return reinterpret_cast<SalObject*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEOBJECT, 0, reinterpret_cast<LPARAM>(static_cast<WinSalFrame*>(pParent)) )));
928 void WinSalInstance::DestroyObject( SalObject* pObject )
930 SendMessageW( mhComWnd, SAL_MSG_DESTROYOBJECT, 0, reinterpret_cast<LPARAM>(pObject) );
933 OUString WinSalInstance::GetConnectionIdentifier()
935 return OUString();
938 /** Add a file to the system shells recent document list if there is any.
939 This function may have no effect under Unix because there is no
940 standard API among the different desktop managers.
942 @param aFileUrl
943 The file url of the document.
945 void WinSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& rDocumentService)
947 if (Application::IsHeadlessModeEnabled())
948 return;
950 OUString system_path;
951 osl::FileBase::RC rc = osl::FileBase::getSystemPathFromFileURL(rFileUrl, system_path);
953 OSL_ENSURE(osl::FileBase::E_None == rc, "Invalid file url");
955 if (osl::FileBase::E_None == rc)
957 IShellItem* pShellItem = nullptr;
959 HRESULT hr = SHCreateItemFromParsingName(o3tl::toW(system_path.getStr()), nullptr, IID_PPV_ARGS(&pShellItem));
961 if ( SUCCEEDED(hr) && pShellItem )
963 OUString sApplicationName;
965 if ( rDocumentService == "com.sun.star.text.TextDocument" ||
966 rDocumentService == "com.sun.star.text.GlobalDocument" ||
967 rDocumentService == "com.sun.star.text.WebDocument" ||
968 rDocumentService == "com.sun.star.xforms.XMLFormDocument" )
969 sApplicationName = "Writer";
970 else if ( rDocumentService == "com.sun.star.sheet.SpreadsheetDocument" ||
971 rDocumentService == "com.sun.star.chart2.ChartDocument" )
972 sApplicationName = "Calc";
973 else if ( rDocumentService == "com.sun.star.presentation.PresentationDocument" )
974 sApplicationName = "Impress";
975 else if ( rDocumentService == "com.sun.star.drawing.DrawingDocument" )
976 sApplicationName = "Draw";
977 else if ( rDocumentService == "com.sun.star.formula.FormulaProperties" )
978 sApplicationName = "Math";
979 else if ( rDocumentService == "com.sun.star.sdb.DatabaseDocument" ||
980 rDocumentService == "com.sun.star.sdb.OfficeDatabaseDocument" ||
981 rDocumentService == "com.sun.star.sdb.RelationDesign" ||
982 rDocumentService == "com.sun.star.sdb.QueryDesign" ||
983 rDocumentService == "com.sun.star.sdb.TableDesign" ||
984 rDocumentService == "com.sun.star.sdb.DataSourceBrowser" )
985 sApplicationName = "Base";
987 if ( !sApplicationName.isEmpty() )
989 OUString sApplicationID("TheDocumentFoundation.LibreOffice.");
990 sApplicationID += sApplicationName;
992 SHARDAPPIDINFO info;
993 info.psi = pShellItem;
994 info.pszAppID = o3tl::toW(sApplicationID.getStr());
996 SHAddToRecentDocs ( SHARD_APPIDINFO, &info );
997 return;
1000 // For whatever reason, we could not use the SHARD_APPIDINFO semantics
1001 SHAddToRecentDocs(SHARD_PATHW, system_path.getStr());
1005 SalTimer* WinSalInstance::CreateSalTimer()
1007 return new WinSalTimer();
1010 SalBitmap* WinSalInstance::CreateSalBitmap()
1012 if (OpenGLHelper::isVCLOpenGLEnabled())
1013 return new OpenGLSalBitmap();
1014 else
1015 return new WinSalBitmap();
1018 const OUString& SalGetDesktopEnvironment()
1020 static OUString aDesktopEnvironment( "Windows" );
1021 return aDesktopEnvironment;
1024 SalSession* WinSalInstance::CreateSalSession()
1026 return nullptr;
1029 int WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(int, LPEXCEPTION_POINTERS pExceptionInfo)
1031 // Decide if an exception is a c++ (mostly UNO) exception or a process violation.
1032 // Depending on this information we pass process violations directly to our signal handler ...
1033 // and c++ (UNO) exceptions are sended to the following code on the current stack.
1034 // Problem behind: user32.dll sometime consumes exceptions/process violations .-)
1035 // see also #112221#
1037 static const DWORD EXCEPTION_MSC_CPP_EXCEPTION = 0xE06D7363;
1039 if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_MSC_CPP_EXCEPTION)
1040 return EXCEPTION_CONTINUE_SEARCH;
1042 return UnhandledExceptionFilter( pExceptionInfo );
1045 OUString WinSalInstance::getOSVersion()
1047 // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
1048 // subject to manifest-based behavior since Windows 8.1, so give wrong results.
1049 // Another approach would be to use NetWkstaGetInfo, but that has some small
1050 // reported delays (some milliseconds), and might get slower in domains with
1051 // poor network connections.
1052 // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
1053 HINSTANCE hLibrary = LoadLibraryW(L"kernel32.dll");
1054 if (hLibrary != nullptr)
1056 wchar_t szPath[MAX_PATH];
1057 DWORD dwCount = GetModuleFileNameW(hLibrary, szPath, SAL_N_ELEMENTS(szPath));
1058 FreeLibrary(hLibrary);
1059 if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath))
1061 dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
1062 if (dwCount != 0)
1064 std::unique_ptr<char> ver(new char[dwCount]);
1065 if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
1067 void* pBlock = nullptr;
1068 UINT dwBlockSz = 0;
1069 if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
1071 VS_FIXEDFILEINFO *vinfo = static_cast<VS_FIXEDFILEINFO *>(pBlock);
1072 OUStringBuffer aVer;
1073 aVer.append("Windows ");
1074 aVer.append(static_cast<sal_Int32>(HIWORD(vinfo->dwProductVersionMS)));
1075 aVer.append(".");
1076 aVer.append(static_cast<sal_Int32>(LOWORD(vinfo->dwProductVersionMS)));
1077 return aVer.makeStringAndClear();
1083 return "unknown";
1086 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */