tdf#116301: write correct content type for diagramDrawing
[LibreOffice.git] / vcl / headless / svpinst.cxx
blob412e86c55351579596b017c5a6e933da6b9dad4c
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>
29 #include <vcl/inputtypes.hxx>
30 #include <vcl/opengl/OpenGLContext.hxx>
32 #include <headless/svpinst.hxx>
33 #include <headless/svpframe.hxx>
34 #include <headless/svpdummies.hxx>
35 #include <headless/svpvd.hxx>
36 #ifdef IOS
37 #include <quartz/salbmp.h>
38 #include <quartz/salgdi.h>
39 #include <quartz/salvd.h>
40 #endif
41 #include <headless/svpbmp.hxx>
42 #include <headless/svpgdi.hxx>
44 #include <salframe.hxx>
45 #include <svdata.hxx>
46 #include <unx/gendata.hxx>
47 // FIXME: remove when we re-work the svp mainloop
48 #include <unx/salunxtime.h>
50 SvpSalInstance* SvpSalInstance::s_pDefaultInstance = nullptr;
52 #if !defined(ANDROID) && !defined(IOS)
54 static void atfork_child()
56 if (SvpSalInstance::s_pDefaultInstance != nullptr)
58 SvpSalInstance::s_pDefaultInstance->CloseWakeupPipe(false);
59 SvpSalInstance::s_pDefaultInstance->CreateWakeupPipe(false);
63 #endif
65 SvpSalInstance::SvpSalInstance( SalYieldMutex *pMutex )
66 : SalGenericInstance( pMutex )
68 m_aTimeout.tv_sec = 0;
69 m_aTimeout.tv_usec = 0;
70 m_nTimeoutMS = 0;
72 #ifndef IOS
73 m_MainThread = osl::Thread::getCurrentIdentifier();
74 CreateWakeupPipe(true);
75 #endif
76 if( s_pDefaultInstance == nullptr )
77 s_pDefaultInstance = this;
78 #if !defined(ANDROID) && !defined(IOS)
79 pthread_atfork(nullptr, nullptr, atfork_child);
80 #endif
83 SvpSalInstance::~SvpSalInstance()
85 if( s_pDefaultInstance == this )
86 s_pDefaultInstance = nullptr;
87 #ifndef IOS
88 CloseWakeupPipe(true);
89 #endif
92 #ifndef IOS
94 void SvpSalInstance::CloseWakeupPipe(bool log)
96 SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()));
97 if (!pMutex)
98 return;
99 if (pMutex->m_FeedbackFDs[0] != -1)
101 if (log)
103 SAL_INFO("vcl.headless", "CloseWakeupPipe: Closing inherited feedback pipe: [" << pMutex->m_FeedbackFDs[0] << "," << pMutex->m_FeedbackFDs[1] << "]");
105 close (pMutex->m_FeedbackFDs[0]);
106 close (pMutex->m_FeedbackFDs[1]);
107 pMutex->m_FeedbackFDs[0] = pMutex->m_FeedbackFDs[1] = -1;
111 void SvpSalInstance::CreateWakeupPipe(bool log)
113 SvpSalYieldMutex *const pMutex(dynamic_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()));
114 if (!pMutex)
115 return;
116 if (pipe (pMutex->m_FeedbackFDs) == -1)
118 if (log)
120 SAL_WARN("vcl.headless", "Could not create feedback pipe: " << strerror(errno));
121 std::abort();
124 else
126 if (log)
128 SAL_INFO("vcl.headless", "CreateWakeupPipe: Created feedback pipe: [" << pMutex->m_FeedbackFDs[0] << "," << pMutex->m_FeedbackFDs[1] << "]");
131 int flags;
133 // set close-on-exec descriptor flag.
134 if ((flags = fcntl (pMutex->m_FeedbackFDs[0], F_GETFD)) != -1)
136 flags |= FD_CLOEXEC;
137 (void) fcntl(pMutex->m_FeedbackFDs[0], F_SETFD, flags);
139 if ((flags = fcntl (pMutex->m_FeedbackFDs[1], F_GETFD)) != -1)
141 flags |= FD_CLOEXEC;
142 (void) fcntl(pMutex->m_FeedbackFDs[1], F_SETFD, flags);
145 // retain the default blocking I/O for feedback pipe
149 #endif
151 void SvpSalInstance::TriggerUserEventProcessing()
153 Wakeup();
156 #ifndef NDEBUG
157 static bool g_CheckedMutex = false;
158 #endif
160 void SvpSalInstance::Wakeup(SvpRequest const request)
162 #ifndef NDEBUG
163 if (!g_CheckedMutex)
165 assert(dynamic_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()) != nullptr
166 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
167 g_CheckedMutex = true;
169 #endif
170 #ifdef IOS
171 (void)request;
172 #else
173 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()));
174 std::unique_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
175 if (request != SvpRequest::NONE)
177 pMutex->m_Request = request;
179 pMutex->m_wakeUpMain = true;
180 pMutex->m_WakeUpMainCond.notify_one();
181 #endif
184 bool SvpSalInstance::CheckTimeout( bool bExecuteTimers )
186 bool bRet = false;
187 if( m_aTimeout.tv_sec ) // timer is started
189 timeval aTimeOfDay;
190 gettimeofday( &aTimeOfDay, nullptr );
191 if( aTimeOfDay >= m_aTimeout )
193 bRet = true;
194 if( bExecuteTimers )
196 // timed out, update timeout
197 m_aTimeout = aTimeOfDay;
198 m_aTimeout += m_nTimeoutMS;
200 osl::Guard< comphelper::SolarMutex > aGuard( mpSalYieldMutex.get() );
202 // notify
203 ImplSVData* pSVData = ImplGetSVData();
204 if( pSVData->maSchedCtx.mpSalTimer )
205 pSVData->maSchedCtx.mpSalTimer->CallCallback();
209 return bRet;
212 SalFrame* SvpSalInstance::CreateChildFrame( SystemParentData* /*pParent*/, SalFrameStyleFlags nStyle )
214 return new SvpSalFrame( this, nullptr, nStyle );
217 SalFrame* SvpSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nStyle )
219 return new SvpSalFrame( this, pParent, nStyle );
222 void SvpSalInstance::DestroyFrame( SalFrame* pFrame )
224 delete pFrame;
227 SalObject* SvpSalInstance::CreateObject( SalFrame*, SystemWindowData*, bool )
229 return new SvpSalObject();
232 void SvpSalInstance::DestroyObject( SalObject* pObject )
234 delete pObject;
237 #ifndef IOS
239 SalVirtualDevice* SvpSalInstance::CreateVirtualDevice( SalGraphics* /* pGraphics */,
240 long &nDX, long &nDY,
241 DeviceFormat eFormat,
242 const SystemGraphicsData* /* pData */ )
244 SvpSalVirtualDevice* pNew = new SvpSalVirtualDevice(eFormat, 1);
245 pNew->SetSize( nDX, nDY );
246 return pNew;
249 #endif
251 SalTimer* SvpSalInstance::CreateSalTimer()
253 return new SvpSalTimer( this );
256 SalSystem* SvpSalInstance::CreateSalSystem()
258 return new SvpSalSystem();
261 SalBitmap* SvpSalInstance::CreateSalBitmap()
263 #ifdef IOS
264 return new QuartzSalBitmap();
265 #else
266 return new SvpSalBitmap();
267 #endif
270 void SvpSalInstance::ProcessEvent( SalUserEvent aEvent )
272 aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
273 if( aEvent.m_nEvent == SalEvent::Resize )
275 // this would be a good time to post a paint
276 const SvpSalFrame* pSvpFrame = static_cast<const SvpSalFrame*>( aEvent.m_pFrame);
277 pSvpFrame->PostPaint();
279 #ifndef NDEBUG
280 if (!g_CheckedMutex)
282 assert(dynamic_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()) != nullptr
283 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
284 g_CheckedMutex = true;
286 #endif
287 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()));
288 pMutex->m_NonMainWaitingYieldCond.set();
291 SvpSalYieldMutex::SvpSalYieldMutex()
293 m_FeedbackFDs[0] = m_FeedbackFDs[1] = -1;
296 SvpSalYieldMutex::~SvpSalYieldMutex()
300 void SvpSalYieldMutex::doAcquire(sal_uInt32 const nLockCount)
302 SvpSalInstance *const pInst = static_cast<SvpSalInstance *>(GetSalData()->m_pInstance);
303 if (pInst && pInst->IsMainThread())
305 if (m_bNoYieldLock)
306 return;
310 SvpRequest request = SvpRequest::NONE;
312 std::unique_lock<std::mutex> g(m_WakeUpMainMutex);
313 if (m_aMutex.tryToAcquire()) {
314 // if there's a request, the other thread holds m_aMutex
315 assert(m_Request == SvpRequest::NONE);
316 m_wakeUpMain = false;
317 break;
319 m_WakeUpMainCond.wait(g, [this]() { return m_wakeUpMain; });
320 m_wakeUpMain = false;
321 std::swap(m_Request, request);
323 if (request != SvpRequest::NONE)
325 // nested Yield on behalf of another thread
326 assert(!m_bNoYieldLock);
327 m_bNoYieldLock = true;
328 bool const bEvents = pInst->DoYield(false, request == SvpRequest::MainThreadDispatchAllEvents);
329 m_bNoYieldLock = false;
330 #ifdef IOS
331 (void)bEvents;
332 #else
333 write(m_FeedbackFDs[1], &bEvents, sizeof(bool));
334 #endif
337 while (true);
339 else
341 m_aMutex.acquire();
343 ++m_nCount;
344 SalYieldMutex::doAcquire(nLockCount - 1);
347 sal_uInt32 SvpSalYieldMutex::doRelease(bool const bUnlockAll)
349 SvpSalInstance *const pInst = static_cast<SvpSalInstance *>(GetSalData()->m_pInstance);
350 if (pInst && pInst->IsMainThread())
352 if (m_bNoYieldLock)
353 return 1;
354 else
355 return SalYieldMutex::doRelease(bUnlockAll);
357 sal_uInt32 nCount;
359 // read m_nCount before doRelease
360 bool const isReleased(bUnlockAll || m_nCount == 1);
361 nCount = comphelper::GenericSolarMutex::doRelease( bUnlockAll );
362 if (isReleased) {
363 std::unique_lock<std::mutex> g(m_WakeUpMainMutex);
364 m_wakeUpMain = true;
365 m_WakeUpMainCond.notify_one();
368 return nCount;
371 bool SvpSalYieldMutex::IsCurrentThread() const
373 if (GetSalData()->m_pInstance->IsMainThread() && m_bNoYieldLock)
375 return true;
377 else
379 return SalYieldMutex::IsCurrentThread();
383 bool SvpSalInstance::IsMainThread() const
385 return osl::Thread::getCurrentIdentifier() == m_MainThread;
388 void SvpSalInstance::updateMainThread()
390 if (!IsMainThread())
391 m_MainThread = osl::Thread::getCurrentIdentifier();
394 bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
396 #ifndef NDEBUG
397 if (!g_CheckedMutex)
399 assert(dynamic_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()) != nullptr
400 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
401 g_CheckedMutex = true;
403 #endif
405 // first, process current user events
406 bool bEvent = DispatchUserEvents( bHandleAllCurrentEvents );
407 if ( !bHandleAllCurrentEvents && bEvent )
408 return true;
410 bEvent = CheckTimeout() || bEvent;
412 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(mpSalYieldMutex.get()));
414 if (IsMainThread())
416 if (bWait && ! bEvent)
418 int nTimeoutMS = 0;
419 if (m_aTimeout.tv_sec) // Timer is started.
421 timeval Timeout;
422 // determine remaining timeout.
423 gettimeofday (&Timeout, nullptr);
424 if (m_aTimeout > Timeout)
426 int nTimeoutMicroS = m_aTimeout.tv_usec - Timeout.tv_usec;
427 nTimeoutMS = (m_aTimeout.tv_sec - Timeout.tv_sec) * 1000
428 + nTimeoutMicroS / 1000;
429 if ( nTimeoutMicroS % 1000 )
430 nTimeoutMS += 1;
433 else
434 nTimeoutMS = -1; // wait until something happens
436 sal_uInt32 nAcquireCount = ReleaseYieldMutexAll();
438 std::unique_lock<std::mutex> g(pMutex->m_WakeUpMainMutex);
439 // wait for doRelease() or Wakeup() to set the condition
440 if (nTimeoutMS == -1)
442 pMutex->m_WakeUpMainCond.wait(g,
443 [pMutex]() { return pMutex->m_wakeUpMain; });
445 else
447 pMutex->m_WakeUpMainCond.wait_for(g,
448 std::chrono::milliseconds(nTimeoutMS),
449 [pMutex]() { return pMutex->m_wakeUpMain; });
451 // here no need to check m_Request because Acquire will do it
453 AcquireYieldMutex( nAcquireCount );
455 else if (bEvent)
457 pMutex->m_NonMainWaitingYieldCond.set(); // wake up other threads
460 else // !IsMainThread()
462 Wakeup(bHandleAllCurrentEvents
463 ? SvpRequest::MainThreadDispatchAllEvents
464 : SvpRequest::MainThreadDispatchOneEvent);
466 bool bDidWork(false);
467 #ifndef IOS
468 // blocking read (for synchronisation)
469 auto const nRet = read(pMutex->m_FeedbackFDs[0], &bDidWork, sizeof(bool));
470 assert(nRet == 1); (void) nRet;
471 #endif
472 if (!bDidWork && bWait)
474 // block & release YieldMutex until the main thread does something
475 pMutex->m_NonMainWaitingYieldCond.reset();
476 sal_uInt32 nAcquireCount = ReleaseYieldMutexAll();
477 pMutex->m_NonMainWaitingYieldCond.wait();
478 AcquireYieldMutex( nAcquireCount );
482 return bEvent;
485 bool SvpSalInstance::AnyInput( VclInputFlags nType )
487 if( nType & VclInputFlags::TIMER )
488 return CheckTimeout( false );
489 return false;
492 SalSession* SvpSalInstance::CreateSalSession()
494 return nullptr;
497 OUString SvpSalInstance::GetConnectionIdentifier()
499 return OUString();
502 void SvpSalInstance::StopTimer()
504 m_aTimeout.tv_sec = 0;
505 m_aTimeout.tv_usec = 0;
506 m_nTimeoutMS = 0;
509 void SvpSalInstance::StartTimer( sal_uLong nMS )
511 timeval aPrevTimeout (m_aTimeout);
512 gettimeofday (&m_aTimeout, nullptr);
514 m_nTimeoutMS = nMS;
515 m_aTimeout += m_nTimeoutMS;
517 if ((aPrevTimeout > m_aTimeout) || (aPrevTimeout.tv_sec == 0))
519 // Wakeup from previous timeout (or stopped timer).
520 Wakeup();
524 void SvpSalInstance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&)
528 //obviously doesn't actually do anything, it's just a nonfunctional stub
530 #ifdef LIBO_HEADLESS
532 class SvpOpenGLContext
536 OpenGLContext* SvpSalInstance::CreateOpenGLContext()
538 return nullptr;
541 #else
543 class SvpOpenGLContext : public OpenGLContext
545 GLWindow m_aGLWin;
546 private:
547 virtual const GLWindow& getOpenGLWindow() const override { return m_aGLWin; }
548 virtual GLWindow& getModifiableOpenGLWindow() override { return m_aGLWin; }
551 OpenGLContext* SvpSalInstance::CreateOpenGLContext()
553 return new SvpOpenGLContext;
556 #endif
558 SvpSalTimer::~SvpSalTimer()
562 void SvpSalTimer::Stop()
564 m_pInstance->StopTimer();
567 void SvpSalTimer::Start( sal_uLong nMS )
569 m_pInstance->StartTimer( nMS );
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */