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