calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / vcl / win / app / salinst.cxx
blob789186fee8b505f1013493078a097e7b43902879
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 <rtl/ustrbuf.hxx>
27 #include <sal/log.hxx>
28 #include <tools/debug.hxx>
29 #include <tools/time.hxx>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/solarmutex.hxx>
32 #include <comphelper/windowserrorstring.hxx>
33 #include <com/sun/star/uno/Reference.h>
34 #include <o3tl/char16_t2wchar_t.hxx>
36 #include <dndhelper.hxx>
37 #include <vcl/inputtypes.hxx>
38 #include <vcl/opengl/OpenGLContext.hxx>
39 #include <vcl/sysdata.hxx>
40 #include <vcl/timer.hxx>
41 #include <vclpluginapi.h>
43 #include <win/dnd_source.hxx>
44 #include <win/dnd_target.hxx>
45 #include <win/wincomp.hxx>
46 #include <win/salids.hrc>
47 #include <win/saldata.hxx>
48 #include <win/salinst.h>
49 #include <win/salframe.h>
50 #include <win/salobj.h>
51 #include <win/saltimer.h>
52 #include <win/salbmp.h>
53 #include <win/winlayout.hxx>
55 #include <config_features.h>
56 #include <vcl/skia/SkiaHelper.hxx>
57 #if HAVE_FEATURE_SKIA
58 #include <config_skia.h>
59 #include <skia/salbmp.hxx>
60 #include <skia/win/gdiimpl.hxx>
61 #endif
63 #include <salsys.hxx>
65 #include <desktop/crashreport.hxx>
67 #include <prewin.h>
69 #include <gdiplus.h>
70 #include <shlobj.h>
72 #include <postwin.h>
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()
81 protected:
82 virtual void doAcquire( sal_uInt32 nLockCount ) override;
83 virtual sal_uInt32 doRelease( bool bUnlockAll ) override;
85 static void BeforeReleaseHandler();
87 public:
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
107 GdiFlush();
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 )
120 return;
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.
129 do {
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())
134 break;
135 // reset condition *before* acquiring!
136 m_condition.reset();
137 if (m_aMutex.tryToAcquire())
138 break;
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);
142 (void) res;
144 while ( true );
146 else
147 m_aMutex.acquire();
148 ++m_nCount;
149 --nLockCount;
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() )
158 return 1;
160 sal_uInt32 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
161 // wake up ImplSalYieldMutexAcquireWithWait() after release
162 if ( 0 == m_nCount )
163 m_condition.set();
164 return nCount;
167 bool SalYieldMutex::tryToAcquire()
169 WinSalInstance* pInst = GetSalData()->mpInstance;
170 if ( pInst )
172 if ( pInst->m_nNoYieldLock && pInst->IsMainThread() )
173 return true;
174 else
175 return comphelper::SolarMutex::tryToAcquire();
177 else
178 return false;
181 void ImplSalYieldMutexAcquireWithWait( sal_uInt32 nCount )
183 WinSalInstance* pInst = GetSalData()->mpInstance;
184 if ( pInst )
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;
197 if ( pInst )
199 GdiFlush();
200 pInst->GetYieldMutex()->release();
204 bool SalYieldMutex::IsCurrentThread() const
206 if ( !GetSalData()->mpInstance->m_nNoYieldLock )
207 return SolarMutex::IsCurrentThread();
208 else
209 return GetSalData()->mpInstance->IsMainThread();
212 void SalData::initKeyCodeMap()
214 UINT nKey;
215 #define initKey( a, b )\
216 nKey = LOWORD( VkKeyScanW( a ) );\
217 if( nKey < 0xffff )\
218 maVKMap[ nKey ] = b;
220 maVKMap.clear();
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 );
242 // SalData
244 SalData::SalData()
245 : sal::systools::CoInitializeGuard(COINIT_APARTMENTTHREADED, false,
246 sal::systools::CoInitializeGuard::WhenFailed::NoThrow)
247 // put main thread in Single Threaded Apartment (STA)
249 mhInst = nullptr; // default instance handle
250 mnCmdShow = 0; // default frame show style
251 mhDitherPal = nullptr; // dither palette
252 mhDitherDIB = nullptr; // dither memory handle
253 mpDitherDIB = nullptr; // dither memory
254 mpDitherDIBData = nullptr; // beginning of DIB data
255 mpDitherDiff = nullptr; // Dither mapping table
256 mpDitherLow = nullptr; // Dither mapping table
257 mpDitherHigh = nullptr; // Dither mapping table
258 mhSalObjMsgHook = nullptr; // hook to get interesting msg for SalObject
259 mhWantLeaveMsg = nullptr; // window handle, that want a MOUSELEAVE message
260 mpMouseLeaveTimer = nullptr; // Timer for MouseLeave Test
261 mpInstance = nullptr; // pointer of first instance
262 mpFirstFrame = nullptr; // pointer of first frame
263 mpFirstObject = nullptr; // pointer of first object window
264 mpFirstVD = nullptr; // first VirDev
265 mpFirstPrinter = nullptr; // first printing printer
266 mpHDCCache = nullptr; // Cache for three DC's
267 mh50Bmp = nullptr; // 50% Bitmap
268 mh50Brush = nullptr; // 50% Brush
269 int i;
270 for(i=0; i<MAX_STOCKPEN; i++)
272 maStockPenColorAry[i] = 0;
273 mhStockPenAry[i] = nullptr;
275 for(i=0; i<MAX_STOCKBRUSH; i++)
277 maStockBrushColorAry[i] = 0;
278 mhStockBrushAry[i] = nullptr;
280 mnStockPenCount = 0; // count of static pens
281 mnStockBrushCount = 0; // count of static brushes
282 mnSalObjWantKeyEvt = 0; // KeyEvent for the SalObj hook
283 mnCacheDCInUse = 0; // count of CacheDC in use
284 mbObjClassInit = false; // is SALOBJECTCLASS initialised
285 mbInPalChange = false; // is in WM_QUERYNEWPALETTE
286 mnAppThreadId = 0; // Id from Application-Thread
287 mpFirstIcon = nullptr; // icon cache, points to first icon, NULL if none
288 mpSharedTempFontItem = nullptr;
289 mpOtherTempFontItem = nullptr;
290 mbThemeChanged = false; // true if visual theme was changed: throw away theme handles
291 mbThemeMenuSupport = false;
293 // init with NULL
294 gdiplusToken = 0;
296 initKeyCodeMap();
298 SetSalData( this );
299 initNWF();
301 static Gdiplus::GdiplusStartupInput gdiplusStartupInput;
302 Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
305 SalData::~SalData()
307 deInitNWF();
308 SetSalData( nullptr );
310 if (gdiplusToken)
311 Gdiplus::GdiplusShutdown(gdiplusToken);
314 bool OSSupportsDarkMode()
316 bool bRet = false;
317 if (HMODULE h_ntdll = GetModuleHandleW(L"ntdll.dll"))
319 typedef LONG(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
320 if (auto RtlGetVersion
321 = reinterpret_cast<RtlGetVersion_t>(GetProcAddress(h_ntdll, "RtlGetVersion")))
323 RTL_OSVERSIONINFOW vi2{};
324 vi2.dwOSVersionInfoSize = sizeof(vi2);
325 if (RtlGetVersion(&vi2) == 0)
327 if (vi2.dwMajorVersion > 10)
328 bRet = true;
329 else if (vi2.dwMajorVersion == 10)
331 if (vi2.dwMinorVersion > 0)
332 bRet = true;
333 else if (vi2.dwBuildNumber >= 18362)
334 bRet = true;
339 return bRet;
342 namespace {
344 enum PreferredAppMode
346 Default,
347 AllowDark,
348 ForceDark,
349 ForceLight,
355 extern "C" {
356 VCLPLUG_WIN_PUBLIC SalInstance* create_SalInstance()
358 SalData* pSalData = new SalData();
360 STARTUPINFOW aSI;
361 aSI.cb = sizeof( aSI );
362 GetStartupInfoW( &aSI );
363 pSalData->mhInst = GetModuleHandleW( nullptr );
364 pSalData->mnCmdShow = aSI.wShowWindow;
366 pSalData->mnAppThreadId = GetCurrentThreadId();
368 static bool bSetAllowDarkMode = OSSupportsDarkMode(); // too early to additionally check LibreOffice's config
369 if (bSetAllowDarkMode)
371 typedef PreferredAppMode(WINAPI* SetPreferredAppMode_t)(PreferredAppMode);
372 if (HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
374 if (auto SetPreferredAppMode = reinterpret_cast<SetPreferredAppMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(135))))
375 SetPreferredAppMode(AllowDark);
376 FreeLibrary(hUxthemeLib);
380 // register frame class
381 WNDCLASSEXW aWndClassEx;
382 aWndClassEx.cbSize = sizeof( aWndClassEx );
383 aWndClassEx.style = CS_OWNDC;
384 aWndClassEx.lpfnWndProc = SalFrameWndProcW;
385 aWndClassEx.cbClsExtra = 0;
386 aWndClassEx.cbWndExtra = SAL_FRAME_WNDEXTRA;
387 aWndClassEx.hInstance = pSalData->mhInst;
388 aWndClassEx.hCursor = nullptr;
389 aWndClassEx.hbrBackground = nullptr;
390 aWndClassEx.lpszMenuName = nullptr;
391 aWndClassEx.lpszClassName = SAL_FRAME_CLASSNAMEW;
392 ImplLoadSalIcon( SAL_RESID_ICON_DEFAULT, aWndClassEx.hIcon, aWndClassEx.hIconSm );
393 if ( !RegisterClassExW( &aWndClassEx ) )
394 return nullptr;
396 aWndClassEx.hIcon = nullptr;
397 aWndClassEx.hIconSm = nullptr;
398 aWndClassEx.style |= CS_SAVEBITS;
399 aWndClassEx.lpszClassName = SAL_SUBFRAME_CLASSNAMEW;
400 if ( !RegisterClassExW( &aWndClassEx ) )
401 return nullptr;
403 // shadow effect for popups on XP
404 aWndClassEx.style |= CS_DROPSHADOW;
405 aWndClassEx.lpszClassName = SAL_TMPSUBFRAME_CLASSNAMEW;
406 if ( !RegisterClassExW( &aWndClassEx ) )
407 return nullptr;
409 aWndClassEx.style = 0;
410 aWndClassEx.lpfnWndProc = SalComWndProcW;
411 aWndClassEx.cbWndExtra = 0;
412 aWndClassEx.lpszClassName = SAL_COM_CLASSNAMEW;
413 if ( !RegisterClassExW( &aWndClassEx ) )
414 return nullptr;
416 HWND hComWnd = CreateWindowExW( WS_EX_TOOLWINDOW, SAL_COM_CLASSNAMEW,
417 L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
418 pSalData->mhInst, nullptr );
419 if ( !hComWnd )
420 return nullptr;
422 WinSalInstance* pInst = new WinSalInstance;
424 // init instance (only one instance in this version !!!)
425 pSalData->mpInstance = pInst;
426 pInst->mhInst = pSalData->mhInst;
427 pInst->mhComWnd = hComWnd;
429 // init static GDI Data
430 ImplInitSalGDI();
432 return pInst;
436 WinSalInstance::WinSalInstance()
437 : SalInstance(std::make_unique<SalYieldMutex>())
438 , mhInst( nullptr )
439 , mhComWnd( nullptr )
440 , m_nNoYieldLock( 0 )
442 ImplSVData* pSVData = ImplGetSVData();
443 pSVData->maAppData.mxToolkitName = OUString("win");
444 m_bSupportsOpenGL = true;
445 #if HAVE_FEATURE_SKIA
446 WinSkiaSalGraphicsImpl::prepareSkia();
447 #if SKIA_USE_BITMAP32
448 if (SkiaHelper::isVCLSkiaEnabled())
449 m_bSupportsBitmap32 = true;
450 #endif
451 #endif
454 WinSalInstance::~WinSalInstance()
456 ImplFreeSalGDI();
457 DestroyWindow( mhComWnd );
458 #if HAVE_FEATURE_SKIA
459 SkiaHelper::cleanup();
460 #endif
463 void WinSalInstance::AfterAppInit()
465 // (1) Ideally this would be done at the place that creates the thread, but since this thread is normally
466 // just the default/main thread, that is not possible.
467 // (2) Don't do this on unix, where it causes tools like pstree on Linux to
468 // confusingly report soffice.bin as VCL Main instead.
469 osl_setThreadName("VCL Main");
472 static LRESULT ImplSalDispatchMessage( const MSG* pMsg )
474 SalData* pSalData = GetSalData();
475 if ( pSalData->mpFirstObject && ImplSalPreDispatchMsg( pMsg ) )
476 return 0;
477 LRESULT lResult = DispatchMessageW( pMsg );
478 if ( pSalData->mpFirstObject )
479 ImplSalPostDispatchMsg( pMsg );
480 return lResult;
483 // probably can't be static, because of SalTimer friend? (static gives C4211)
484 bool ImplSalYield(const bool bWait, const bool bHandleAllCurrentEvents)
486 // used to abort further message processing on tick count wraps
487 static sal_uInt32 nLastTicks = 0;
489 // we should never yield in m_nNoYieldLock mode!
490 const bool bNoYieldLock = (GetSalData()->mpInstance->m_nNoYieldLock > 0);
491 assert(!bNoYieldLock);
492 if (bNoYieldLock)
493 return false;
495 MSG aMsg;
496 bool bWasMsg = false, bWasTimeoutMsg = false;
497 WinSalTimer* pTimer = static_cast<WinSalTimer*>(ImplGetSVData()->maSchedCtx.mpSalTimer);
499 sal_uInt32 nCurTicks = GetTickCount();
503 if (!PeekMessageW(&aMsg, nullptr, 0, 0, PM_REMOVE))
504 break;
506 bWasMsg = true;
507 TranslateMessage(&aMsg);
508 LRESULT nRet = ImplSalDispatchMessage(&aMsg);
510 bWasTimeoutMsg |= (SAL_MSG_TIMER_CALLBACK == aMsg.message) && static_cast<bool>(nRet);
512 if (!bHandleAllCurrentEvents)
513 break;
515 if ((aMsg.time > nCurTicks) && (nLastTicks <= nCurTicks || aMsg.time < nLastTicks))
516 break;
518 while( true );
520 // 0ms timeouts are handled out-of-bounds to prevent busy-locking the
521 // event loop with timeout messages.
522 // We ensure we never handle more than one timeout per call.
523 // This way we'll always process a normal system message.
524 if ( !bWasTimeoutMsg && pTimer && pTimer->IsDirectTimeout() )
526 pTimer->ImplHandleElapsedTimer();
527 bWasMsg = true;
530 nLastTicks = nCurTicks;
532 if ( bWait && !bWasMsg )
534 switch (GetMessageW(&aMsg, nullptr, 0, 0))
536 case -1:
537 SAL_WARN("vcl.schedule", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
538 // should we std::abort() / SalAbort here?
539 break;
540 case 0:
541 SAL_INFO("vcl.schedule", "GetMessageW received WM_QUIT while waiting");
542 break;
543 default:
544 bWasMsg = true;
545 TranslateMessage(&aMsg);
546 ImplSalDispatchMessage(&aMsg);
547 break;
551 // If we enabled ForceRealTimer mode skipping our direct timeout processing,
552 // mainly because some Windows API call spawns its own nested message loop,
553 // switch back to our own processing (like after window resize or move)
554 if ( pTimer )
555 pTimer->SetForceRealTimer( false );
557 return bWasMsg;
560 bool WinSalInstance::IsMainThread() const
562 const SalData* pSalData = GetSalData();
563 return pSalData->mnAppThreadId == GetCurrentThreadId();
566 bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
568 bool bDidWork = false;
569 SolarMutexReleaser aReleaser;
570 if ( !IsMainThread() )
572 bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD,
573 WPARAM(false), static_cast<LPARAM>(bHandleAllCurrentEvents) );
574 if ( !bDidWork && bWait )
576 maWaitingYieldCond.reset();
577 maWaitingYieldCond.wait();
578 bDidWork = true;
581 else
583 bDidWork = ImplSalYield( bWait, bHandleAllCurrentEvents );
584 if ( bDidWork )
585 maWaitingYieldCond.set();
588 return bDidWork;
591 #define CASE_NOYIELDLOCK( salmsg, function ) \
592 case salmsg: \
593 if (bIsOtherThreadMessage) \
595 ++pInst->m_nNoYieldLock; \
596 function; \
597 --pInst->m_nNoYieldLock; \
599 else \
601 DBG_TESTSOLARMUTEX(); \
602 function; \
604 break;
606 #define CASE_NOYIELDLOCK_RESULT( salmsg, function ) \
607 case salmsg: \
608 if (bIsOtherThreadMessage) \
610 ++pInst->m_nNoYieldLock; \
611 nRet = reinterpret_cast<LRESULT>( function ); \
612 --pInst->m_nNoYieldLock; \
614 else \
616 DBG_TESTSOLARMUTEX(); \
617 nRet = reinterpret_cast<LRESULT>( function ); \
619 break;
621 LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, bool& rDef )
623 const bool bIsOtherThreadMessage = InSendMessage();
624 LRESULT nRet = 0;
625 WinSalInstance *pInst = GetSalData()->mpInstance;
626 WinSalTimer *const pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
628 SAL_INFO("vcl.gdi.wndproc", "SalComWndProc(nMsg=" << nMsg << ", wParam=" << wParam
629 << ", lParam=" << lParam << "); inSendMsg: " << bIsOtherThreadMessage);
631 if (ImplGetSVData()->mbDeInit)
633 SAL_WARN("vcl.gdi.wndproc", "ignoring timer event because we are shutting down");
634 return 0;
637 switch ( nMsg )
639 case SAL_MSG_THREADYIELD:
640 assert( !static_cast<bool>(wParam) );
641 nRet = static_cast<LRESULT>(ImplSalYield(
642 false, static_cast<bool>( lParam ) ));
643 break;
645 case SAL_MSG_STARTTIMER:
647 auto const nParam = static_cast<sal_uInt64>( lParam );
648 sal_uInt64 nTime = tools::Time::GetSystemTicks();
649 if ( nTime < nParam )
650 nTime = nParam - nTime;
651 else
652 nTime = 0;
653 assert( pTimer != nullptr );
654 pTimer->ImplStart( nTime );
655 break;
658 case SAL_MSG_STOPTIMER:
659 assert( pTimer != nullptr );
660 pTimer->ImplStop();
661 break;
663 CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEFRAME, ImplSalCreateFrame( GetSalData()->mpInstance,
664 reinterpret_cast<HWND>(lParam), static_cast<SalFrameStyleFlags>(wParam)) )
665 CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATEHWND, ImplSalReCreateHWND(
666 reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), false) )
667 CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATECHILDHWND, ImplSalReCreateHWND(
668 reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), true) )
669 CASE_NOYIELDLOCK( SAL_MSG_DESTROYFRAME, delete reinterpret_cast<SalFrame*>(lParam) )
671 case SAL_MSG_DESTROYHWND:
672 // We only destroy the native window here. We do NOT destroy the SalFrame contained
673 // in the structure (GetWindowPtr()).
674 if (DestroyWindow(reinterpret_cast<HWND>(lParam)) == 0)
676 OSL_FAIL("DestroyWindow failed!");
677 // Failure: We remove the SalFrame from the window structure. So we avoid that
678 // the window structure may contain an invalid pointer, once the SalFrame is deleted.
679 SetWindowPtr(reinterpret_cast<HWND>(lParam), nullptr);
681 break;
683 CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEOBJECT, ImplSalCreateObject(
684 GetSalData()->mpInstance, reinterpret_cast<WinSalFrame*>(lParam)) )
685 CASE_NOYIELDLOCK( SAL_MSG_DESTROYOBJECT, delete reinterpret_cast<SalObject*>(lParam) )
686 CASE_NOYIELDLOCK_RESULT( SAL_MSG_GETCACHEDDC, GetDCEx(
687 reinterpret_cast<HWND>(wParam), nullptr, DCX_CACHE) )
688 CASE_NOYIELDLOCK( SAL_MSG_RELEASEDC, ReleaseDC(
689 reinterpret_cast<HWND>(wParam), reinterpret_cast<HDC>(lParam)) )
691 case SAL_MSG_TIMER_CALLBACK:
692 assert( pTimer != nullptr );
693 pTimer->ImplHandleTimerEvent( wParam );
694 break;
696 case WM_TIMER:
697 assert( pTimer != nullptr );
698 pTimer->ImplHandle_WM_TIMER( wParam );
699 break;
701 case SAL_MSG_FORCE_REAL_TIMER:
702 assert(pTimer != nullptr);
703 pTimer->SetForceRealTimer(true);
704 break;
706 case SAL_MSG_DUMMY:
707 break;
709 default:
710 rDef = true;
711 break;
714 return nRet;
717 #undef CASE_NOYIELDLOCK
718 #undef CASE_NOYIELDLOCK_RESULT
720 LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
722 bool bDef = false;
723 LRESULT nRet = 0;
724 __try
726 nRet = SalComWndProc( hWnd, nMsg, wParam, lParam, bDef );
728 __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
731 if ( bDef )
733 if ( !ImplHandleGlobalMsg( hWnd, nMsg, wParam, lParam, nRet ) )
734 nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam );
736 return nRet;
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() )
745 return true;
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 ))
756 return true;
758 else
760 UINT flags = 0;
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 )
766 flags |= QS_KEY;
768 if ( nType & VclInputFlags::MOUSE )
769 flags |= QS_MOUSE;
771 if ( nType & VclInputFlags::PAINT )
772 flags |= QS_PAINT;
774 if ( nType & VclInputFlags::TIMER )
775 flags |= QS_TIMER;
777 if( nType & VclInputFlags::OTHER )
778 flags |= QS_ALLEVENTS & ~QS_KEY & ~QS_MOUSE & ~QS_PAINT & ~QS_TIMER;
780 if( GetQueueStatus( flags ))
781 return true;
784 return false;
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
796 HWND hWndParent;
797 if ( pParent )
798 hWndParent = static_cast<WinSalFrame*>(pParent)->mhWnd;
799 else
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
812 bool /*bShow*/ )
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()
825 return OUString();
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.
832 @param aFileUrl
833 The file url of the document.
835 void WinSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& rDocumentService)
837 if (Application::IsHeadlessModeEnabled())
838 return;
840 OUString system_path;
841 osl::FileBase::RC rc = osl::FileBase::getSystemPathFromFileURL(rFileUrl, system_path);
843 OSL_ENSURE(osl::FileBase::E_None == rc, "Invalid file url");
845 if (osl::FileBase::E_None == rc)
847 IShellItem* pShellItem = nullptr;
849 HRESULT hr = SHCreateItemFromParsingName(o3tl::toW(system_path.getStr()), nullptr, IID_PPV_ARGS(&pShellItem));
851 if ( SUCCEEDED(hr) && pShellItem )
853 OUString sApplicationName;
855 if ( rDocumentService == "com.sun.star.text.TextDocument" ||
856 rDocumentService == "com.sun.star.text.GlobalDocument" ||
857 rDocumentService == "com.sun.star.text.WebDocument" ||
858 rDocumentService == "com.sun.star.xforms.XMLFormDocument" )
859 sApplicationName = "Writer";
860 else if ( rDocumentService == "com.sun.star.sheet.SpreadsheetDocument" ||
861 rDocumentService == "com.sun.star.chart2.ChartDocument" )
862 sApplicationName = "Calc";
863 else if ( rDocumentService == "com.sun.star.presentation.PresentationDocument" )
864 sApplicationName = "Impress";
865 else if ( rDocumentService == "com.sun.star.drawing.DrawingDocument" )
866 sApplicationName = "Draw";
867 else if ( rDocumentService == "com.sun.star.formula.FormulaProperties" )
868 sApplicationName = "Math";
869 else if ( rDocumentService == "com.sun.star.sdb.DatabaseDocument" ||
870 rDocumentService == "com.sun.star.sdb.OfficeDatabaseDocument" ||
871 rDocumentService == "com.sun.star.sdb.RelationDesign" ||
872 rDocumentService == "com.sun.star.sdb.QueryDesign" ||
873 rDocumentService == "com.sun.star.sdb.TableDesign" ||
874 rDocumentService == "com.sun.star.sdb.DataSourceBrowser" )
875 sApplicationName = "Base";
877 if ( !sApplicationName.isEmpty() )
879 OUString sApplicationID("Collabora.CollaboraOffice." + sApplicationName);
881 SHARDAPPIDINFO info;
882 info.psi = pShellItem;
883 info.pszAppID = o3tl::toW(sApplicationID.getStr());
885 SHAddToRecentDocs ( SHARD_APPIDINFO, &info );
886 return;
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>();
904 else
905 #endif
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 .-)
915 // see also #112221#
917 static const DWORD EXCEPTION_MSC_CPP_EXCEPTION = 0xE06D7363;
919 if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_MSC_CPP_EXCEPTION)
920 return EXCEPTION_CONTINUE_SEARCH;
922 return UnhandledExceptionFilter( pExceptionInfo );
925 typedef LONG NTSTATUS;
926 typedef NTSTATUS(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
927 constexpr NTSTATUS STATUS_SUCCESS = 0x00000000;
929 OUString WinSalInstance::getOSVersion()
931 OUStringBuffer aVer(50); // capacity for string like "Windows 6.1 Service Pack 1 build 7601"
932 aVer.append("Windows ");
933 // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
934 // subject to manifest-based behavior since Windows 8.1, so give wrong results.
935 // Another approach would be to use NetWkstaGetInfo, but that has some small
936 // reported delays (some milliseconds), and might get slower in domains with
937 // poor network connections.
938 // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
939 bool bHaveVerFromKernel32 = false;
940 if (HMODULE h_kernel32 = GetModuleHandleW(L"kernel32.dll"))
942 wchar_t szPath[MAX_PATH];
943 DWORD dwCount = GetModuleFileNameW(h_kernel32, szPath, SAL_N_ELEMENTS(szPath));
944 if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath))
946 dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
947 if (dwCount != 0)
949 std::unique_ptr<char[]> ver(new char[dwCount]);
950 if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
952 void* pBlock = nullptr;
953 UINT dwBlockSz = 0;
954 if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
956 VS_FIXEDFILEINFO* vi1 = static_cast<VS_FIXEDFILEINFO*>(pBlock);
957 aVer.append(OUString::number(HIWORD(vi1->dwProductVersionMS)) + "."
958 + OUString::number(LOWORD(vi1->dwProductVersionMS)));
959 bHaveVerFromKernel32 = true;
965 // Now use RtlGetVersion (which is not subject to deprecation for GetVersion(Ex) API)
966 // to get build number and SP info
967 bool bHaveVerFromRtlGetVersion = false;
968 if (HMODULE h_ntdll = GetModuleHandleW(L"ntdll.dll"))
970 if (auto RtlGetVersion
971 = reinterpret_cast<RtlGetVersion_t>(GetProcAddress(h_ntdll, "RtlGetVersion")))
973 RTL_OSVERSIONINFOW vi2{}; // initialize with zeroes - a better alternative to memset
974 vi2.dwOSVersionInfoSize = sizeof(vi2);
975 if (STATUS_SUCCESS == RtlGetVersion(&vi2))
977 if (!bHaveVerFromKernel32) // we failed above; let's hope this would be useful
978 aVer.append(OUString::number(vi2.dwMajorVersion) + "."
979 + OUString::number(vi2.dwMinorVersion));
980 aVer.append(" ");
981 if (vi2.szCSDVersion[0])
982 aVer.append(OUString::Concat(o3tl::toU(vi2.szCSDVersion)) + " ");
983 aVer.append("Build " + OUString::number(vi2.dwBuildNumber));
984 bHaveVerFromRtlGetVersion = true;
988 if (!bHaveVerFromKernel32 && !bHaveVerFromRtlGetVersion)
989 aVer.append("unknown");
990 return aVer.makeStringAndClear();
993 void WinSalInstance::BeforeAbort(const OUString&, bool)
995 ImplFreeSalGDI();
998 css::uno::Reference<css::uno::XInterface> WinSalInstance::ImplCreateDragSource(const SystemEnvData* pSysEnv)
1000 return vcl::OleDnDHelper(new DragSource(comphelper::getProcessComponentContext()),
1001 reinterpret_cast<sal_IntPtr>(pSysEnv->hWnd), vcl::DragOrDrop::Drag);
1004 css::uno::Reference<css::uno::XInterface> WinSalInstance::ImplCreateDropTarget(const SystemEnvData* pSysEnv)
1006 return vcl::OleDnDHelper(new DropTarget(comphelper::getProcessComponentContext()),
1007 reinterpret_cast<sal_IntPtr>(pSysEnv->hWnd), vcl::DragOrDrop::Drop);
1010 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */