bump product version to 6.4.0.3
[LibreOffice.git] / vcl / headless / svpinst.cxx
blob4687bf1dce4c8ba9329e87bb5bd3ccf49ae05ce0
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 <config_features.h>
21 #include <sal/config.h>
23 #include <mutex>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <pthread.h>
29 #include <sys/time.h>
30 #include <sys/poll.h>
32 #include <sal/types.h>
33 #include <sal/log.hxx>
35 #include <vcl/virdev.hxx>
36 #include <vcl/inputtypes.hxx>
37 #include <vcl/lok.hxx>
38 #if HAVE_FEATURE_UI
39 # include <vcl/opengl/OpenGLContext.hxx>
40 #endif
42 #include <headless/svpinst.hxx>
43 #include <headless/svpframe.hxx>
44 #include <headless/svpdummies.hxx>
45 #include <headless/svpvd.hxx>
46 #ifdef IOS
47 #include <quartz/salbmp.h>
48 #include <quartz/salgdi.h>
49 #include <quartz/salvd.h>
50 #else
51 #include <headless/svpgdi.hxx>
52 #endif
53 #include <headless/svpbmp.hxx>
55 #include <salframe.hxx>
56 #include <svdata.hxx>
57 #include <unx/gendata.hxx>
58 // FIXME: remove when we re-work the svp mainloop
59 #include <unx/salunxtime.h>
60 #include <comphelper/lok.hxx>
62 SvpSalInstance* SvpSalInstance::s_pDefaultInstance = nullptr;
64 #if !defined(ANDROID) && !defined(IOS)
66 static void atfork_child()
68 if (SvpSalInstance::s_pDefaultInstance != nullptr)
70 SvpSalInstance::s_pDefaultInstance->CloseWakeupPipe(false);
71 SvpSalInstance::s_pDefaultInstance->CreateWakeupPipe(false);
75 #endif
77 SvpSalInstance::SvpSalInstance( std::unique_ptr<SalYieldMutex> pMutex )
78 : SalGenericInstance( std::move(pMutex) )
80 m_aTimeout.tv_sec = 0;
81 m_aTimeout.tv_usec = 0;
82 m_nTimeoutMS = 0;
84 m_MainThread = osl::Thread::getCurrentIdentifier();
85 CreateWakeupPipe(true);
86 if( s_pDefaultInstance == nullptr )
87 s_pDefaultInstance = this;
88 #if !defined(ANDROID) && !defined(IOS)
89 pthread_atfork(nullptr, nullptr, atfork_child);
90 #endif
93 SvpSalInstance::~SvpSalInstance()
95 if( s_pDefaultInstance == this )
96 s_pDefaultInstance = nullptr;
97 CloseWakeupPipe(true);
100 void SvpSalInstance::CloseWakeupPipe(bool log)
102 SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()));
103 if (!pMutex)
104 return;
105 if (pMutex->m_FeedbackFDs[0] != -1)
107 if (log)
109 SAL_INFO("vcl.headless", "CloseWakeupPipe: Closing inherited feedback pipe: [" << pMutex->m_FeedbackFDs[0] << "," << pMutex->m_FeedbackFDs[1] << "]");
111 close (pMutex->m_FeedbackFDs[0]);
112 close (pMutex->m_FeedbackFDs[1]);
113 pMutex->m_FeedbackFDs[0] = pMutex->m_FeedbackFDs[1] = -1;
117 void SvpSalInstance::CreateWakeupPipe(bool log)
119 SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()));
120 if (!pMutex)
121 return;
122 if (pipe (pMutex->m_FeedbackFDs) == -1)
124 if (log)
126 SAL_WARN("vcl.headless", "Could not create feedback pipe: " << strerror(errno));
127 std::abort();
130 else
132 if (log)
134 SAL_INFO("vcl.headless", "CreateWakeupPipe: Created feedback pipe: [" << pMutex->m_FeedbackFDs[0] << "," << pMutex->m_FeedbackFDs[1] << "]");
137 int flags;
139 // set close-on-exec descriptor flag.
140 if ((flags = fcntl (pMutex->m_FeedbackFDs[0], F_GETFD)) != -1)
142 flags |= FD_CLOEXEC;
143 (void) fcntl(pMutex->m_FeedbackFDs[0], F_SETFD, flags);
145 if ((flags = fcntl (pMutex->m_FeedbackFDs[1], F_GETFD)) != -1)
147 flags |= FD_CLOEXEC;
148 (void) fcntl(pMutex->m_FeedbackFDs[1], F_SETFD, flags);
151 // retain the default blocking I/O for feedback pipe
155 void SvpSalInstance::TriggerUserEventProcessing()
157 Wakeup();
160 #ifndef NDEBUG
161 static bool g_CheckedMutex = false;
162 #endif
164 void SvpSalInstance::Wakeup(SvpRequest const request)
166 #ifndef NDEBUG
167 if (!g_CheckedMutex)
169 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr
170 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
171 g_CheckedMutex = true;
173 #endif
175 ImplSVData* pSVData = ImplGetSVData();
177 if (pSVData->mpWakeCallback && pSVData->mpPollClosure)
178 pSVData->mpWakeCallback(pSVData->mpPollClosure);
180 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
181 std::scoped_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
182 if (request != SvpRequest::NONE)
183 pMutex->m_Request = request;
184 pMutex->m_wakeUpMain = true;
185 pMutex->m_WakeUpMainCond.notify_one();
188 bool SvpSalInstance::CheckTimeout( bool bExecuteTimers )
190 bool bRet = false;
191 if( m_aTimeout.tv_sec ) // timer is started
193 timeval aTimeOfDay;
194 gettimeofday( &aTimeOfDay, nullptr );
195 if( aTimeOfDay >= m_aTimeout )
197 bRet = true;
198 if( bExecuteTimers )
200 // timed out, update timeout
201 m_aTimeout = aTimeOfDay;
202 m_aTimeout += m_nTimeoutMS;
204 osl::Guard< comphelper::SolarMutex > aGuard( GetYieldMutex() );
206 // notify
207 ImplSVData* pSVData = ImplGetSVData();
208 if( pSVData->maSchedCtx.mpSalTimer )
209 pSVData->maSchedCtx.mpSalTimer->CallCallback();
213 return bRet;
216 SalFrame* SvpSalInstance::CreateChildFrame( SystemParentData* /*pParent*/, SalFrameStyleFlags nStyle )
218 return new SvpSalFrame( this, nullptr, nStyle );
221 SalFrame* SvpSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle )
223 return new SvpSalFrame( this, pParent, nStyle );
226 void SvpSalInstance::DestroyFrame( SalFrame* pFrame )
228 delete pFrame;
231 SalObject* SvpSalInstance::CreateObject( SalFrame*, SystemWindowData*, bool )
233 return new SvpSalObject;
236 void SvpSalInstance::DestroyObject( SalObject* pObject )
238 delete pObject;
241 #ifndef IOS
243 std::unique_ptr<SalVirtualDevice> SvpSalInstance::CreateVirtualDevice(SalGraphics* pGraphics,
244 long &nDX, long &nDY,
245 DeviceFormat eFormat,
246 const SystemGraphicsData* pGd)
248 SvpSalGraphics *pSvpSalGraphics = dynamic_cast<SvpSalGraphics*>(pGraphics);
249 assert(pSvpSalGraphics);
250 #ifndef ANDROID
251 // tdf#127529 normally pPreExistingTarget is null and we are a true virtualdevice drawing to a backing buffer.
252 // Occasionally, for canvas/slideshow, pPreExistingTarget is pre-provided as a hack to use the vcl drawing
253 // apis to render onto a preexisting cairo surface. The necessity for that precedes the use of cairo in vcl proper
254 cairo_surface_t* pPreExistingTarget = pGd ? static_cast<cairo_surface_t*>(pGd->pSurface) : nullptr;
255 #else
256 //ANDROID case
257 (void)pGd;
258 cairo_surface_t* pPreExistingTarget = nullptr;
259 #endif
260 std::unique_ptr<SalVirtualDevice> pNew(new SvpSalVirtualDevice(eFormat, pSvpSalGraphics->getSurface(), pPreExistingTarget));
261 pNew->SetSize( nDX, nDY );
262 return pNew;
265 cairo_surface_t* get_underlying_cairo_surface(const VirtualDevice& rDevice)
267 return static_cast<SvpSalVirtualDevice*>(rDevice.mpVirDev.get())->GetSurface();
270 #endif
272 SalTimer* SvpSalInstance::CreateSalTimer()
274 return new SvpSalTimer( this );
277 SalSystem* SvpSalInstance::CreateSalSystem()
279 return new SvpSalSystem();
282 std::shared_ptr<SalBitmap> SvpSalInstance::CreateSalBitmap()
284 #ifdef IOS
285 return std::make_shared<QuartzSalBitmap>();
286 #else
287 return std::make_shared<SvpSalBitmap>();
288 #endif
291 void SvpSalInstance::ProcessEvent( SalUserEvent aEvent )
293 aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
294 if( aEvent.m_nEvent == SalEvent::Resize )
296 // this would be a good time to post a paint
297 const SvpSalFrame* pSvpFrame = static_cast<const SvpSalFrame*>( aEvent.m_pFrame);
298 pSvpFrame->PostPaint();
300 #ifndef NDEBUG
301 if (!g_CheckedMutex)
303 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr
304 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
305 g_CheckedMutex = true;
307 #endif
308 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
309 pMutex->m_NonMainWaitingYieldCond.set();
312 SvpSalYieldMutex::SvpSalYieldMutex()
314 #ifndef IOS
315 m_FeedbackFDs[0] = m_FeedbackFDs[1] = -1;
316 #endif
319 SvpSalYieldMutex::~SvpSalYieldMutex()
323 void SvpSalYieldMutex::doAcquire(sal_uInt32 const nLockCount)
325 SvpSalInstance *const pInst = static_cast<SvpSalInstance *>(GetSalData()->m_pInstance);
326 if (pInst && pInst->IsMainThread())
328 if (m_bNoYieldLock)
329 return;
333 SvpRequest request = SvpRequest::NONE;
335 std::unique_lock<std::mutex> g(m_WakeUpMainMutex);
336 if (m_aMutex.tryToAcquire()) {
337 // if there's a request, the other thread holds m_aMutex
338 assert(m_Request == SvpRequest::NONE);
339 m_wakeUpMain = false;
340 break;
342 m_WakeUpMainCond.wait(g, [this]() { return m_wakeUpMain; });
343 m_wakeUpMain = false;
344 std::swap(m_Request, request);
346 if (request != SvpRequest::NONE)
348 // nested Yield on behalf of another thread
349 assert(!m_bNoYieldLock);
350 m_bNoYieldLock = true;
351 bool const bEvents = pInst->DoYield(false, request == SvpRequest::MainThreadDispatchAllEvents);
352 m_bNoYieldLock = false;
353 write(m_FeedbackFDs[1], &bEvents, sizeof(bool));
356 while (true);
358 else
360 m_aMutex.acquire();
362 ++m_nCount;
363 SalYieldMutex::doAcquire(nLockCount - 1);
366 sal_uInt32 SvpSalYieldMutex::doRelease(bool const bUnlockAll)
368 SvpSalInstance *const pInst = static_cast<SvpSalInstance *>(GetSalData()->m_pInstance);
369 if (pInst && pInst->IsMainThread())
371 if (m_bNoYieldLock)
372 return 1;
373 else
374 return SalYieldMutex::doRelease(bUnlockAll);
376 sal_uInt32 nCount;
378 // read m_nCount before doRelease
379 bool const isReleased(bUnlockAll || m_nCount == 1);
380 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
382 if (isReleased)
384 if (vcl::lok::isUnipoll())
386 if (pInst)
387 pInst->Wakeup(SvpRequest::NONE);
389 else
391 std::scoped_lock<std::mutex> g(m_WakeUpMainMutex);
392 m_wakeUpMain = true;
393 m_WakeUpMainCond.notify_one();
397 return nCount;
400 bool SvpSalYieldMutex::IsCurrentThread() const
402 if (GetSalData()->m_pInstance->IsMainThread() && m_bNoYieldLock)
404 return true;
406 else
408 return SalYieldMutex::IsCurrentThread();
412 bool SvpSalInstance::IsMainThread() const
414 return osl::Thread::getCurrentIdentifier() == m_MainThread;
417 void SvpSalInstance::updateMainThread()
419 if (!IsMainThread())
421 m_MainThread = osl::Thread::getCurrentIdentifier();
422 ImplGetSVData()->mnMainThreadId = osl::Thread::getCurrentIdentifier();
426 bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
428 #ifndef NDEBUG
429 if (!g_CheckedMutex)
431 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr
432 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
433 g_CheckedMutex = true;
435 #endif
437 // first, process current user events
438 bool bEvent = DispatchUserEvents(bHandleAllCurrentEvents);
439 if (!bHandleAllCurrentEvents && bEvent)
440 return true;
442 bEvent = CheckTimeout() || bEvent;
444 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
446 if (IsMainThread())
448 if (bWait && ! bEvent)
450 int nTimeoutMS = 0;
451 if (m_aTimeout.tv_sec) // Timer is started.
453 timeval Timeout;
454 // determine remaining timeout.
455 gettimeofday (&Timeout, nullptr);
456 if (m_aTimeout > Timeout)
458 int nTimeoutMicroS = m_aTimeout.tv_usec - Timeout.tv_usec;
459 nTimeoutMS = (m_aTimeout.tv_sec - Timeout.tv_sec) * 1000
460 + nTimeoutMicroS / 1000;
461 if ( nTimeoutMicroS % 1000 )
462 nTimeoutMS += 1;
465 else
466 nTimeoutMS = -1; // wait until something happens
468 ImplSVData* pSVData = ImplGetSVData();
469 sal_uInt32 nAcquireCount = ReleaseYieldMutexAll();
471 if (pSVData->mpPollCallback)
473 // Poll for events from the LOK client.
474 if (nTimeoutMS < 0)
475 nTimeoutMS = 5000;
477 // External poll.
478 if (pSVData->mpPollClosure != nullptr &&
479 pSVData->mpPollCallback(pSVData->mpPollClosure, nTimeoutMS * 1000 /* us */) < 0)
480 pSVData->maAppData.mbAppQuit = true;
482 else
484 std::unique_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
485 // wait for doRelease() or Wakeup() to set the condition
486 if (nTimeoutMS == -1)
488 pMutex->m_WakeUpMainCond.wait(g,
489 [pMutex]() { return pMutex->m_wakeUpMain; });
491 else
493 pMutex->m_WakeUpMainCond.wait_for(g,
494 std::chrono::milliseconds(nTimeoutMS),
495 [pMutex]() { return pMutex->m_wakeUpMain; });
497 // here no need to check m_Request because Acquire will do it
499 AcquireYieldMutex( nAcquireCount );
501 else if (bEvent)
503 pMutex->m_NonMainWaitingYieldCond.set(); // wake up other threads
506 else // !IsMainThread()
508 Wakeup(bHandleAllCurrentEvents
509 ? SvpRequest::MainThreadDispatchAllEvents
510 : SvpRequest::MainThreadDispatchOneEvent);
512 bool bDidWork(false);
513 // blocking read (for synchronisation)
514 auto const nRet = read(pMutex->m_FeedbackFDs[0], &bDidWork, sizeof(bool));
515 assert(nRet == 1); (void) nRet;
516 if (!bDidWork && bWait)
518 // block & release YieldMutex until the main thread does something
519 pMutex->m_NonMainWaitingYieldCond.reset();
520 sal_uInt32 nAcquireCount = ReleaseYieldMutexAll();
521 pMutex->m_NonMainWaitingYieldCond.wait();
522 AcquireYieldMutex( nAcquireCount );
526 return bEvent;
529 bool SvpSalInstance::AnyInput( VclInputFlags nType )
531 if( nType & VclInputFlags::TIMER )
532 return CheckTimeout( false );
533 return false;
536 OUString SvpSalInstance::GetConnectionIdentifier()
538 return OUString();
541 void SvpSalInstance::StopTimer()
543 m_aTimeout.tv_sec = 0;
544 m_aTimeout.tv_usec = 0;
545 m_nTimeoutMS = 0;
548 void SvpSalInstance::StartTimer( sal_uInt64 nMS )
550 timeval aPrevTimeout (m_aTimeout);
551 gettimeofday (&m_aTimeout, nullptr);
553 m_nTimeoutMS = nMS;
554 m_aTimeout += m_nTimeoutMS;
556 if ((aPrevTimeout > m_aTimeout) || (aPrevTimeout.tv_sec == 0))
558 // Wakeup from previous timeout (or stopped timer).
559 Wakeup();
563 void SvpSalInstance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&)
567 std::shared_ptr<vcl::BackendCapabilities> SvpSalInstance::GetBackendCapabilities()
569 auto pBackendCapabilities = SalInstance::GetBackendCapabilities();
570 pBackendCapabilities->mbSupportsBitmap32 = true;
571 return pBackendCapabilities;
574 //obviously doesn't actually do anything, it's just a nonfunctional stub
576 #if HAVE_FEATURE_UI
578 class SvpOpenGLContext : public OpenGLContext
580 GLWindow m_aGLWin;
581 private:
582 virtual const GLWindow& getOpenGLWindow() const override { return m_aGLWin; }
583 virtual GLWindow& getModifiableOpenGLWindow() override { return m_aGLWin; }
586 OpenGLContext* SvpSalInstance::CreateOpenGLContext()
588 return new SvpOpenGLContext;
591 #else
593 class SvpOpenGLContext
597 OpenGLContext* SvpSalInstance::CreateOpenGLContext()
599 return nullptr;
603 #endif
605 SvpSalTimer::~SvpSalTimer()
609 void SvpSalTimer::Stop()
611 m_pInstance->StopTimer();
614 void SvpSalTimer::Start( sal_uInt64 nMS )
616 m_pInstance->StartTimer( nMS );
619 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */