bump product version to 6.3.0.0.beta1
[LibreOffice.git] / vcl / headless / svpinst.cxx
blob02e332b92fbf3b5f43730960d9e12856ec9cc722
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 <unistd.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <pthread.h>
24 #include <sys/time.h>
25 #include <sys/poll.h>
27 #include <sal/types.h>
28 #include <sal/log.hxx>
30 #include <vcl/virdev.hxx>
31 #include <vcl/inputtypes.hxx>
32 #include <vcl/lok.hxx>
33 #ifndef LIBO_HEADLESS
34 # include <vcl/opengl/OpenGLContext.hxx>
35 #endif
37 #include <headless/svpinst.hxx>
38 #include <headless/svpframe.hxx>
39 #include <headless/svpdummies.hxx>
40 #include <headless/svpvd.hxx>
41 #ifdef IOS
42 #include <quartz/salbmp.h>
43 #include <quartz/salgdi.h>
44 #include <quartz/salvd.h>
45 #else
46 #include <headless/svpgdi.hxx>
47 #endif
48 #include <headless/svpbmp.hxx>
50 #include <salframe.hxx>
51 #include <svdata.hxx>
52 #include <unx/gendata.hxx>
53 // FIXME: remove when we re-work the svp mainloop
54 #include <unx/salunxtime.h>
55 #include <comphelper/lok.hxx>
57 SvpSalInstance* SvpSalInstance::s_pDefaultInstance = nullptr;
59 #if !defined(ANDROID) && !defined(IOS)
61 static void atfork_child()
63 if (SvpSalInstance::s_pDefaultInstance != nullptr)
65 SvpSalInstance::s_pDefaultInstance->CloseWakeupPipe(false);
66 SvpSalInstance::s_pDefaultInstance->CreateWakeupPipe(false);
70 #endif
72 SvpSalInstance::SvpSalInstance( std::unique_ptr<SalYieldMutex> pMutex )
73 : SalGenericInstance( std::move(pMutex) )
75 m_aTimeout.tv_sec = 0;
76 m_aTimeout.tv_usec = 0;
77 m_nTimeoutMS = 0;
79 m_MainThread = osl::Thread::getCurrentIdentifier();
80 CreateWakeupPipe(true);
81 if( s_pDefaultInstance == nullptr )
82 s_pDefaultInstance = this;
83 #if !defined(ANDROID) && !defined(IOS)
84 pthread_atfork(nullptr, nullptr, atfork_child);
85 #endif
88 SvpSalInstance::~SvpSalInstance()
90 if( s_pDefaultInstance == this )
91 s_pDefaultInstance = nullptr;
92 CloseWakeupPipe(true);
95 void SvpSalInstance::CloseWakeupPipe(bool log)
97 SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()));
98 if (!pMutex)
99 return;
100 if (pMutex->m_FeedbackFDs[0] != -1)
102 if (log)
104 SAL_INFO("vcl.headless", "CloseWakeupPipe: Closing inherited feedback pipe: [" << pMutex->m_FeedbackFDs[0] << "," << pMutex->m_FeedbackFDs[1] << "]");
106 close (pMutex->m_FeedbackFDs[0]);
107 close (pMutex->m_FeedbackFDs[1]);
108 pMutex->m_FeedbackFDs[0] = pMutex->m_FeedbackFDs[1] = -1;
112 void SvpSalInstance::CreateWakeupPipe(bool log)
114 SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()));
115 if (!pMutex)
116 return;
117 if (pipe (pMutex->m_FeedbackFDs) == -1)
119 if (log)
121 SAL_WARN("vcl.headless", "Could not create feedback pipe: " << strerror(errno));
122 std::abort();
125 else
127 if (log)
129 SAL_INFO("vcl.headless", "CreateWakeupPipe: Created feedback pipe: [" << pMutex->m_FeedbackFDs[0] << "," << pMutex->m_FeedbackFDs[1] << "]");
132 int flags;
134 // set close-on-exec descriptor flag.
135 if ((flags = fcntl (pMutex->m_FeedbackFDs[0], F_GETFD)) != -1)
137 flags |= FD_CLOEXEC;
138 (void) fcntl(pMutex->m_FeedbackFDs[0], F_SETFD, flags);
140 if ((flags = fcntl (pMutex->m_FeedbackFDs[1], F_GETFD)) != -1)
142 flags |= FD_CLOEXEC;
143 (void) fcntl(pMutex->m_FeedbackFDs[1], F_SETFD, flags);
146 // retain the default blocking I/O for feedback pipe
150 void SvpSalInstance::TriggerUserEventProcessing()
152 Wakeup();
155 #ifndef NDEBUG
156 static bool g_CheckedMutex = false;
157 #endif
159 void SvpSalInstance::Wakeup(SvpRequest const request)
161 #ifndef NDEBUG
162 if (!g_CheckedMutex)
164 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr
165 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
166 g_CheckedMutex = true;
168 #endif
170 ImplSVData* pSVData = ImplGetSVData();
172 if (pSVData->mpWakeCallback && pSVData->mpPollClosure)
173 pSVData->mpWakeCallback(pSVData->mpPollClosure);
175 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
176 std::unique_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
177 if (request != SvpRequest::NONE)
178 pMutex->m_Request = request;
179 pMutex->m_wakeUpMain = true;
180 pMutex->m_WakeUpMainCond.notify_one();
183 bool SvpSalInstance::CheckTimeout( bool bExecuteTimers )
185 bool bRet = false;
186 if( m_aTimeout.tv_sec ) // timer is started
188 timeval aTimeOfDay;
189 gettimeofday( &aTimeOfDay, nullptr );
190 if( aTimeOfDay >= m_aTimeout )
192 bRet = true;
193 if( bExecuteTimers )
195 // timed out, update timeout
196 m_aTimeout = aTimeOfDay;
197 m_aTimeout += m_nTimeoutMS;
199 osl::Guard< comphelper::SolarMutex > aGuard( GetYieldMutex() );
201 // notify
202 ImplSVData* pSVData = ImplGetSVData();
203 if( pSVData->maSchedCtx.mpSalTimer )
204 pSVData->maSchedCtx.mpSalTimer->CallCallback();
208 return bRet;
211 SalFrame* SvpSalInstance::CreateChildFrame( SystemParentData* /*pParent*/, SalFrameStyleFlags nStyle )
213 return new SvpSalFrame( this, nullptr, nStyle );
216 SalFrame* SvpSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle )
218 return new SvpSalFrame( this, pParent, nStyle );
221 void SvpSalInstance::DestroyFrame( SalFrame* pFrame )
223 delete pFrame;
226 SalObject* SvpSalInstance::CreateObject( SalFrame*, SystemWindowData*, bool )
228 return new SvpSalObject();
231 void SvpSalInstance::DestroyObject( SalObject* pObject )
233 delete pObject;
236 #ifndef IOS
238 std::unique_ptr<SalVirtualDevice> SvpSalInstance::CreateVirtualDevice( SalGraphics* pGraphics,
239 long &nDX, long &nDY,
240 DeviceFormat eFormat,
241 const SystemGraphicsData* /* pData */ )
243 SvpSalGraphics *pSvpSalGraphics = dynamic_cast<SvpSalGraphics*>(pGraphics);
244 assert(pSvpSalGraphics);
245 std::unique_ptr<SalVirtualDevice> pNew(new SvpSalVirtualDevice(eFormat, pSvpSalGraphics->getSurface()));
246 pNew->SetSize( nDX, nDY );
247 return pNew;
250 cairo_surface_t* get_underlying_cairo_surface(const VirtualDevice& rDevice)
252 return static_cast<SvpSalVirtualDevice*>(rDevice.mpVirDev.get())->GetSurface();
255 #endif
257 SalTimer* SvpSalInstance::CreateSalTimer()
259 return new SvpSalTimer( this );
262 SalSystem* SvpSalInstance::CreateSalSystem()
264 return new SvpSalSystem();
267 std::shared_ptr<SalBitmap> SvpSalInstance::CreateSalBitmap()
269 #ifdef IOS
270 return std::make_shared<QuartzSalBitmap>();
271 #else
272 return std::make_shared<SvpSalBitmap>();
273 #endif
276 void SvpSalInstance::ProcessEvent( SalUserEvent aEvent )
278 aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
279 if( aEvent.m_nEvent == SalEvent::Resize )
281 // this would be a good time to post a paint
282 const SvpSalFrame* pSvpFrame = static_cast<const SvpSalFrame*>( aEvent.m_pFrame);
283 pSvpFrame->PostPaint();
285 #ifndef NDEBUG
286 if (!g_CheckedMutex)
288 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr
289 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
290 g_CheckedMutex = true;
292 #endif
293 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
294 pMutex->m_NonMainWaitingYieldCond.set();
297 SvpSalYieldMutex::SvpSalYieldMutex()
299 #ifndef IOS
300 m_FeedbackFDs[0] = m_FeedbackFDs[1] = -1;
301 #endif
304 SvpSalYieldMutex::~SvpSalYieldMutex()
308 void SvpSalYieldMutex::doAcquire(sal_uInt32 const nLockCount)
310 SvpSalInstance *const pInst = static_cast<SvpSalInstance *>(GetSalData()->m_pInstance);
311 if (pInst && pInst->IsMainThread())
313 if (m_bNoYieldLock)
314 return;
318 SvpRequest request = SvpRequest::NONE;
320 std::unique_lock<std::mutex> g(m_WakeUpMainMutex);
321 if (m_aMutex.tryToAcquire()) {
322 // if there's a request, the other thread holds m_aMutex
323 assert(m_Request == SvpRequest::NONE);
324 m_wakeUpMain = false;
325 break;
327 m_WakeUpMainCond.wait(g, [this]() { return m_wakeUpMain; });
328 m_wakeUpMain = false;
329 std::swap(m_Request, request);
331 if (request != SvpRequest::NONE)
333 // nested Yield on behalf of another thread
334 assert(!m_bNoYieldLock);
335 m_bNoYieldLock = true;
336 bool const bEvents = pInst->DoYield(false, request == SvpRequest::MainThreadDispatchAllEvents);
337 m_bNoYieldLock = false;
338 write(m_FeedbackFDs[1], &bEvents, sizeof(bool));
341 while (true);
343 else
345 m_aMutex.acquire();
347 ++m_nCount;
348 SalYieldMutex::doAcquire(nLockCount - 1);
351 sal_uInt32 SvpSalYieldMutex::doRelease(bool const bUnlockAll)
353 SvpSalInstance *const pInst = static_cast<SvpSalInstance *>(GetSalData()->m_pInstance);
354 if (pInst && pInst->IsMainThread())
356 if (m_bNoYieldLock)
357 return 1;
358 else
359 return SalYieldMutex::doRelease(bUnlockAll);
361 sal_uInt32 nCount;
363 // read m_nCount before doRelease
364 bool const isReleased(bUnlockAll || m_nCount == 1);
365 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
367 if (isReleased)
369 if (vcl::lok::isUnipoll())
371 if (pInst)
372 pInst->Wakeup(SvpRequest::NONE);
374 else
376 std::unique_lock<std::mutex> g(m_WakeUpMainMutex);
377 m_wakeUpMain = true;
378 m_WakeUpMainCond.notify_one();
382 return nCount;
385 bool SvpSalYieldMutex::IsCurrentThread() const
387 if (GetSalData()->m_pInstance->IsMainThread() && m_bNoYieldLock)
389 return true;
391 else
393 return SalYieldMutex::IsCurrentThread();
397 bool SvpSalInstance::IsMainThread() const
399 return osl::Thread::getCurrentIdentifier() == m_MainThread;
402 void SvpSalInstance::updateMainThread()
404 if (!IsMainThread())
406 m_MainThread = osl::Thread::getCurrentIdentifier();
407 ImplGetSVData()->mnMainThreadId = osl::Thread::getCurrentIdentifier();
411 bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
413 #ifndef NDEBUG
414 if (!g_CheckedMutex)
416 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr
417 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
418 g_CheckedMutex = true;
420 #endif
422 // first, process current user events
423 bool bEvent = DispatchUserEvents(bHandleAllCurrentEvents);
424 if (!bHandleAllCurrentEvents && bEvent)
425 return true;
427 bEvent = CheckTimeout() || bEvent;
429 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
431 if (IsMainThread())
433 if (bWait && ! bEvent)
435 int nTimeoutMS = 0;
436 if (m_aTimeout.tv_sec) // Timer is started.
438 timeval Timeout;
439 // determine remaining timeout.
440 gettimeofday (&Timeout, nullptr);
441 if (m_aTimeout > Timeout)
443 int nTimeoutMicroS = m_aTimeout.tv_usec - Timeout.tv_usec;
444 nTimeoutMS = (m_aTimeout.tv_sec - Timeout.tv_sec) * 1000
445 + nTimeoutMicroS / 1000;
446 if ( nTimeoutMicroS % 1000 )
447 nTimeoutMS += 1;
450 else
451 nTimeoutMS = -1; // wait until something happens
453 ImplSVData* pSVData = ImplGetSVData();
454 sal_uInt32 nAcquireCount = ReleaseYieldMutexAll();
456 if (pSVData->mpPollCallback)
458 // Poll for events from the LOK client.
459 if (nTimeoutMS < 0)
460 nTimeoutMS = 5000;
462 // External poll.
463 if (pSVData->mpPollClosure != nullptr &&
464 pSVData->mpPollCallback(pSVData->mpPollClosure, nTimeoutMS * 1000 /* us */) < 0)
465 pSVData->maAppData.mbAppQuit = true;
467 else
469 std::unique_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
470 // wait for doRelease() or Wakeup() to set the condition
471 if (nTimeoutMS == -1)
473 pMutex->m_WakeUpMainCond.wait(g,
474 [pMutex]() { return pMutex->m_wakeUpMain; });
476 else
478 pMutex->m_WakeUpMainCond.wait_for(g,
479 std::chrono::milliseconds(nTimeoutMS),
480 [pMutex]() { return pMutex->m_wakeUpMain; });
482 // here no need to check m_Request because Acquire will do it
484 AcquireYieldMutex( nAcquireCount );
486 else if (bEvent)
488 pMutex->m_NonMainWaitingYieldCond.set(); // wake up other threads
491 else // !IsMainThread()
493 Wakeup(bHandleAllCurrentEvents
494 ? SvpRequest::MainThreadDispatchAllEvents
495 : SvpRequest::MainThreadDispatchOneEvent);
497 bool bDidWork(false);
498 // blocking read (for synchronisation)
499 auto const nRet = read(pMutex->m_FeedbackFDs[0], &bDidWork, sizeof(bool));
500 assert(nRet == 1); (void) nRet;
501 if (!bDidWork && bWait)
503 // block & release YieldMutex until the main thread does something
504 pMutex->m_NonMainWaitingYieldCond.reset();
505 sal_uInt32 nAcquireCount = ReleaseYieldMutexAll();
506 pMutex->m_NonMainWaitingYieldCond.wait();
507 AcquireYieldMutex( nAcquireCount );
511 return bEvent;
514 bool SvpSalInstance::AnyInput( VclInputFlags nType )
516 if( nType & VclInputFlags::TIMER )
517 return CheckTimeout( false );
518 return false;
521 OUString SvpSalInstance::GetConnectionIdentifier()
523 return OUString();
526 void SvpSalInstance::StopTimer()
528 m_aTimeout.tv_sec = 0;
529 m_aTimeout.tv_usec = 0;
530 m_nTimeoutMS = 0;
533 void SvpSalInstance::StartTimer( sal_uInt64 nMS )
535 timeval aPrevTimeout (m_aTimeout);
536 gettimeofday (&m_aTimeout, nullptr);
538 m_nTimeoutMS = nMS;
539 m_aTimeout += m_nTimeoutMS;
541 if ((aPrevTimeout > m_aTimeout) || (aPrevTimeout.tv_sec == 0))
543 // Wakeup from previous timeout (or stopped timer).
544 Wakeup();
548 void SvpSalInstance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&)
552 std::shared_ptr<vcl::BackendCapabilities> SvpSalInstance::GetBackendCapabilities()
554 auto pBackendCapabilities = SalInstance::GetBackendCapabilities();
555 pBackendCapabilities->mbSupportsBitmap32 = true;
556 return pBackendCapabilities;
559 //obviously doesn't actually do anything, it's just a nonfunctional stub
561 #ifdef LIBO_HEADLESS
563 class SvpOpenGLContext
567 OpenGLContext* SvpSalInstance::CreateOpenGLContext()
569 return nullptr;
572 #else
574 class SvpOpenGLContext : public OpenGLContext
576 GLWindow m_aGLWin;
577 private:
578 virtual const GLWindow& getOpenGLWindow() const override { return m_aGLWin; }
579 virtual GLWindow& getModifiableOpenGLWindow() override { return m_aGLWin; }
582 OpenGLContext* SvpSalInstance::CreateOpenGLContext()
584 return new SvpOpenGLContext;
587 #endif
589 SvpSalTimer::~SvpSalTimer()
593 void SvpSalTimer::Stop()
595 m_pInstance->StopTimer();
598 void SvpSalTimer::Start( sal_uInt64 nMS )
600 m_pInstance->StartTimer( nMS );
603 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */