tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / vcl / headless / svpinst.cxx
blob54ac078f83f0446cbdbf0994ab9b07bbc361ac20
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 <sal/config.h>
22 #include <mutex>
24 #include <pthread.h>
25 #include <sys/time.h>
27 #include <sal/types.h>
29 #include <vcl/virdev.hxx>
30 #include <vcl/inputtypes.hxx>
31 #include <vcl/lok.hxx>
33 #include <headless/svpinst.hxx>
34 #include <headless/svpframe.hxx>
35 #include <headless/svpdummies.hxx>
36 #include <headless/svpvd.hxx>
37 #ifdef IOS
38 # include <quartz/salbmp.h>
39 # include <quartz/salgdi.h>
40 # include <quartz/salvd.h>
41 #else
42 # include <cairo.h>
43 # include <headless/svpgdi.hxx>
44 #endif
45 #include <headless/svpbmp.hxx>
47 #include <salframe.hxx>
48 #include <svdata.hxx>
49 // FIXME: remove when we re-work the svp mainloop
50 #include <unx/salunxtime.h>
51 #include <tools/debug.hxx>
52 #include <comphelper/lok.hxx>
53 #include <o3tl/unreachable.hxx>
55 #if defined EMSCRIPTEN
56 #include <emscripten.h>
57 #endif
59 SvpSalInstance* SvpSalInstance::s_pDefaultInstance = nullptr;
61 #ifndef NDEBUG
62 static bool g_CheckedMutex = false;
64 #define DBG_TESTSVPYIELDMUTEX() \
65 do { \
66 if (!g_CheckedMutex) \
67 { \
68 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr \
69 && "This SvpSalInstance function requires use of SvpSalYieldMutex"); \
70 g_CheckedMutex = true; \
71 } \
72 } while(false)
74 #else // NDEBUG
75 #define DBG_TESTSVPYIELDMUTEX() ((void)0)
76 #endif
78 #if !defined(ANDROID) && !defined(IOS) && !defined(EMSCRIPTEN)
80 static void atfork_child()
82 if (SvpSalInstance::s_pDefaultInstance != nullptr)
84 SvpSalInstance::s_pDefaultInstance->CloseWakeupPipe();
88 #endif
90 SvpSalInstance::SvpSalInstance( std::unique_ptr<SalYieldMutex> pMutex )
91 : SalGenericInstance( std::move(pMutex) )
93 m_aTimeout.tv_sec = 0;
94 m_aTimeout.tv_usec = 0;
95 m_nTimeoutMS = 0;
97 m_MainThread = osl::Thread::getCurrentIdentifier();
98 if( s_pDefaultInstance == nullptr )
99 s_pDefaultInstance = this;
100 #if !defined(ANDROID) && !defined(IOS) && !defined(EMSCRIPTEN)
101 pthread_atfork(nullptr, nullptr, atfork_child);
102 #endif
103 #if defined EMSCRIPTEN
104 ImplGetSVData()->maAppData.m_bUseSystemLoop = true;
105 #endif
108 SvpSalInstance::~SvpSalInstance()
110 if( s_pDefaultInstance == this )
111 s_pDefaultInstance = nullptr;
112 CloseWakeupPipe();
115 void SvpSalInstance::CloseWakeupPipe()
117 SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()));
118 if (!pMutex)
119 return;
120 while (!pMutex->m_FeedbackPipe.empty())
121 pMutex->m_FeedbackPipe.pop();
124 void SvpSalInstance::TriggerUserEventProcessing()
126 Wakeup();
129 void SvpSalInstance::Wakeup(SvpRequest const request)
131 DBG_TESTSVPYIELDMUTEX();
133 ImplSVData* pSVData = ImplGetSVData();
134 if (pSVData->mpWakeCallback && pSVData->mpPollClosure)
135 pSVData->mpWakeCallback(pSVData->mpPollClosure);
137 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
138 std::scoped_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
139 if (request != SvpRequest::NONE)
140 pMutex->m_Request = request;
141 pMutex->m_wakeUpMain = true;
142 pMutex->m_WakeUpMainCond.notify_one();
145 bool SvpSalInstance::CheckTimeout( bool bExecuteTimers )
147 bool bRet = false;
148 if( m_aTimeout.tv_sec ) // timer is started
150 timeval aTimeOfDay;
151 gettimeofday( &aTimeOfDay, nullptr );
152 if( aTimeOfDay >= m_aTimeout )
154 bRet = true;
155 if( bExecuteTimers )
157 // timed out, update timeout
158 m_aTimeout = aTimeOfDay;
159 m_aTimeout += m_nTimeoutMS;
161 osl::Guard< comphelper::SolarMutex > aGuard( GetYieldMutex() );
163 // notify
164 ImplSVData* pSVData = ImplGetSVData();
165 if( pSVData->maSchedCtx.mpSalTimer )
166 pSVData->maSchedCtx.mpSalTimer->CallCallback();
170 return bRet;
173 SalFrame* SvpSalInstance::CreateChildFrame( SystemParentData* /*pParent*/, SalFrameStyleFlags nStyle )
175 return new SvpSalFrame( this, nullptr, nStyle );
178 SalFrame* SvpSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle )
180 return new SvpSalFrame( this, pParent, nStyle );
183 void SvpSalInstance::DestroyFrame( SalFrame* pFrame )
185 delete pFrame;
188 SalObject* SvpSalInstance::CreateObject( SalFrame*, SystemWindowData*, bool )
190 return new SvpSalObject;
193 void SvpSalInstance::DestroyObject( SalObject* pObject )
195 delete pObject;
198 #ifndef IOS
200 std::unique_ptr<SalVirtualDevice> SvpSalInstance::CreateVirtualDevice(SalGraphics& rGraphics,
201 tools::Long &nDX, tools::Long &nDY,
202 DeviceFormat /*eFormat*/,
203 const SystemGraphicsData* pGd)
205 SvpSalGraphics *pSvpSalGraphics = dynamic_cast<SvpSalGraphics*>(&rGraphics);
206 assert(pSvpSalGraphics);
207 #ifndef ANDROID
208 // tdf#127529 normally pPreExistingTarget is null and we are a true virtualdevice drawing to a backing buffer.
209 // Occasionally, for canvas/slideshow, pPreExistingTarget is pre-provided as a hack to use the vcl drawing
210 // apis to render onto a preexisting cairo surface. The necessity for that precedes the use of cairo in vcl proper
211 cairo_surface_t* pPreExistingTarget = pGd ? static_cast<cairo_surface_t*>(pGd->pSurface) : nullptr;
212 #else
213 //ANDROID case
214 (void)pGd;
215 cairo_surface_t* pPreExistingTarget = nullptr;
216 #endif
217 std::unique_ptr<SalVirtualDevice> xNew(new SvpSalVirtualDevice(pSvpSalGraphics->getSurface(), pPreExistingTarget));
218 if (!xNew->SetSize(nDX, nDY))
219 xNew.reset();
220 return xNew;
223 cairo_surface_t* get_underlying_cairo_surface(const VirtualDevice& rDevice)
225 return static_cast<SvpSalVirtualDevice*>(rDevice.mpVirDev.get())->GetSurface();
228 const cairo_font_options_t* SvpSalInstance::GetCairoFontOptions()
230 static cairo_font_options_t *gOptions = nullptr;
231 if (!gOptions)
233 gOptions = cairo_font_options_create();
234 cairo_font_options_set_antialias(gOptions, CAIRO_ANTIALIAS_GRAY);
236 return gOptions;
239 #else // IOS
241 const cairo_font_options_t* SvpSalInstance::GetCairoFontOptions()
243 return nullptr;
246 #endif
248 SalTimer* SvpSalInstance::CreateSalTimer()
250 return new SvpSalTimer( this );
253 SalSystem* SvpSalInstance::CreateSalSystem()
255 return new SvpSalSystem();
258 std::shared_ptr<SalBitmap> SvpSalInstance::CreateSalBitmap()
260 #ifdef IOS
261 return std::make_shared<QuartzSalBitmap>();
262 #else
263 return std::make_shared<SvpSalBitmap>();
264 #endif
267 void SvpSalInstance::ProcessEvent( SalUserEvent aEvent )
269 DBG_TESTSVPYIELDMUTEX();
271 aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
272 if( aEvent.m_nEvent == SalEvent::Resize )
274 // this would be a good time to post a paint
275 const SvpSalFrame* pSvpFrame = static_cast<const SvpSalFrame*>( aEvent.m_pFrame);
276 pSvpFrame->PostPaint();
279 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
280 pMutex->m_NonMainWaitingYieldCond.set();
283 #if defined EMSCRIPTEN
285 static void loop(void * arg) {
286 SolarMutexGuard g;
287 static_cast<SvpSalInstance *>(arg)->ImplYield(false, false);
290 bool SvpSalInstance::DoExecute(int &) {
291 assert(Application::IsUseSystemEventLoop());
292 // emscripten_set_main_loop will unwind the stack by throwing a JavaScript exception, so we need
293 // to manually undo the call of AcquireYieldMutex() done in InitVCL:
294 ReleaseYieldMutex(false);
295 // Somewhat randomly use an fps=100 argument so the loop callback is called 100 times per
296 // second:
297 emscripten_set_main_loop_arg(loop, this, 100, 1);
298 O3TL_UNREACHABLE;
301 void SvpSalInstance::DoQuit() {
302 assert(Application::IsUseSystemEventLoop());
305 #endif
307 SvpSalYieldMutex::SvpSalYieldMutex()
311 SvpSalYieldMutex::~SvpSalYieldMutex()
315 void SvpSalYieldMutex::doAcquire(sal_uInt32 const nLockCount)
317 auto *const pInst = static_cast<SvpSalInstance*>(GetSalInstance());
318 if (pInst && pInst->IsMainThread())
320 if (m_bNoYieldLock)
321 return;
325 SvpRequest request = SvpRequest::NONE;
327 std::unique_lock<std::mutex> g(m_WakeUpMainMutex);
328 if (m_aMutex.tryToAcquire()) {
329 // if there's a request, the other thread holds m_aMutex
330 assert(m_Request == SvpRequest::NONE);
331 m_wakeUpMain = false;
332 break;
334 m_WakeUpMainCond.wait(g, [this]() { return m_wakeUpMain; });
335 m_wakeUpMain = false;
336 std::swap(m_Request, request);
338 if (request != SvpRequest::NONE)
340 // nested Yield on behalf of another thread
341 assert(!m_bNoYieldLock);
342 m_bNoYieldLock = true;
343 bool const bEvents = pInst->DoYield(false, request == SvpRequest::MainThreadDispatchAllEvents);
344 m_bNoYieldLock = false;
346 std::lock_guard lock(m_FeedbackMutex);
347 m_FeedbackPipe.push(bEvents);
349 m_FeedbackCV.notify_all();
352 while (true);
354 else
356 m_aMutex.acquire();
358 ++m_nCount;
359 SalYieldMutex::doAcquire(nLockCount - 1);
362 sal_uInt32 SvpSalYieldMutex::doRelease(bool const bUnlockAll)
364 auto *const pInst = static_cast<SvpSalInstance*>(GetSalInstance());
365 if (pInst && pInst->IsMainThread())
367 if (m_bNoYieldLock)
368 return 1;
369 else
370 return SalYieldMutex::doRelease(bUnlockAll);
372 sal_uInt32 nCount;
374 // read m_nCount before doRelease
375 bool const isReleased(bUnlockAll || m_nCount == 1);
376 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
378 if (isReleased)
380 if (vcl::lok::isUnipoll())
382 if (pInst)
383 pInst->Wakeup();
385 else
387 std::scoped_lock<std::mutex> g(m_WakeUpMainMutex);
388 m_wakeUpMain = true;
389 m_WakeUpMainCond.notify_one();
393 return nCount;
396 bool SvpSalYieldMutex::IsCurrentThread() const
398 if (GetSalInstance()->IsMainThread() && m_bNoYieldLock)
399 return true;
400 else
401 return SalYieldMutex::IsCurrentThread();
404 bool SvpSalInstance::IsMainThread() const
406 return osl::Thread::getCurrentIdentifier() == m_MainThread;
409 void SvpSalInstance::updateMainThread()
411 if (!IsMainThread())
413 m_MainThread = osl::Thread::getCurrentIdentifier();
414 ImplGetSVData()->mnMainThreadId = osl::Thread::getCurrentIdentifier();
418 bool SvpSalInstance::ImplYield(bool bWait, bool bHandleAllCurrentEvents)
420 DBG_TESTSVPYIELDMUTEX();
421 DBG_TESTSOLARMUTEX();
422 assert(IsMainThread());
424 bool bWasEvent = DispatchUserEvents(bHandleAllCurrentEvents);
425 if (!bHandleAllCurrentEvents && bWasEvent)
426 return true;
428 // CheckTimeout() invokes the sal timer, which invokes the scheduler.
429 bWasEvent = CheckTimeout() || bWasEvent;
430 const bool bMustSleep = bWait && !bWasEvent;
432 // This is wrong and must be removed!
433 // We always want to drop the SolarMutex on yield; that is the whole point of yield.
434 // If we know the LOK client has pending input events, then don't yet return, so those events
435 // can be processed as well.
436 if (!bMustSleep && !comphelper::LibreOfficeKit::anyInput())
437 return bWasEvent;
439 sal_Int64 nTimeoutMicroS = 0;
440 if (bMustSleep)
442 if (m_aTimeout.tv_sec) // Timer is started.
444 timeval Timeout;
445 // determine remaining timeout.
446 gettimeofday (&Timeout, nullptr);
447 if (m_aTimeout > Timeout)
448 nTimeoutMicroS = ((m_aTimeout.tv_sec - Timeout.tv_sec) * 1000 * 1000 +
449 (m_aTimeout.tv_usec - Timeout.tv_usec));
451 else
452 nTimeoutMicroS = -1; // wait until something happens
455 SolarMutexReleaser aReleaser;
457 if (vcl::lok::isUnipoll())
459 ImplSVData* pSVData = ImplGetSVData();
460 if (pSVData->mpPollClosure)
462 int nPollResult = pSVData->mpPollCallback(pSVData->mpPollClosure, nTimeoutMicroS);
463 if (nPollResult < 0)
464 pSVData->maAppData.mbAppQuit = true;
465 bWasEvent = bWasEvent || (nPollResult != 0);
468 else if (bMustSleep)
470 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
471 std::unique_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
472 // wait for doRelease() or Wakeup() to set the condition
473 if (nTimeoutMicroS == -1)
475 pMutex->m_WakeUpMainCond.wait(g,
476 [pMutex]() { return pMutex->m_wakeUpMain; });
478 else
480 int nTimeoutMS = nTimeoutMicroS / 1000;
481 if (nTimeoutMicroS % 1000)
482 nTimeoutMS += 1;
483 pMutex->m_WakeUpMainCond.wait_for(g,
484 std::chrono::milliseconds(nTimeoutMS),
485 [pMutex]() { return pMutex->m_wakeUpMain; });
487 // here no need to check m_Request because Acquire will do it
490 return bWasEvent;
493 bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
495 DBG_TESTSVPYIELDMUTEX();
496 DBG_TESTSOLARMUTEX();
498 bool bWasEvent(false);
499 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
501 if (IsMainThread())
503 bWasEvent = ImplYield(bWait, bHandleAllCurrentEvents);
504 if (bWasEvent)
505 pMutex->m_NonMainWaitingYieldCond.set(); // wake up other threads
507 else
509 // TODO: use a SolarMutexReleaser here and drop the m_bNoYieldLock usage
510 Wakeup(bHandleAllCurrentEvents
511 ? SvpRequest::MainThreadDispatchAllEvents
512 : SvpRequest::MainThreadDispatchOneEvent);
514 // blocking read (for synchronisation)
516 std::unique_lock lock(pMutex->m_FeedbackMutex);
517 pMutex->m_FeedbackCV.wait(lock, [pMutex] { return !pMutex->m_FeedbackPipe.empty(); });
518 bWasEvent = pMutex->m_FeedbackPipe.front();
519 pMutex->m_FeedbackPipe.pop();
521 if (!bWasEvent && bWait)
523 // block & release YieldMutex until the main thread does something
524 pMutex->m_NonMainWaitingYieldCond.reset();
525 SolarMutexReleaser aReleaser;
526 pMutex->m_NonMainWaitingYieldCond.wait();
530 return bWasEvent;
533 bool SvpSalInstance::AnyInput( VclInputFlags nType )
535 if( nType & VclInputFlags::TIMER )
536 return CheckTimeout( false );
537 return false;
540 OUString SvpSalInstance::GetConnectionIdentifier()
542 return OUString();
545 void SvpSalInstance::StopTimer()
547 m_aTimeout.tv_sec = 0;
548 m_aTimeout.tv_usec = 0;
549 m_nTimeoutMS = 0;
552 void SvpSalInstance::StartTimer( sal_uInt64 nMS )
554 timeval aPrevTimeout (m_aTimeout);
555 gettimeofday (&m_aTimeout, nullptr);
557 m_nTimeoutMS = nMS;
558 m_aTimeout += m_nTimeoutMS;
560 if ((aPrevTimeout > m_aTimeout) || (aPrevTimeout.tv_sec == 0))
562 // Wakeup from previous timeout (or stopped timer).
563 Wakeup();
567 void SvpSalInstance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&)
571 SvpSalTimer::~SvpSalTimer()
575 void SvpSalTimer::Stop()
577 m_pInstance->StopTimer();
580 void SvpSalTimer::Start( sal_uInt64 nMS )
582 m_pInstance->StartTimer( nMS );
585 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */