1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
37 #include <quartz/salbmp.h>
38 #include <quartz/salgdi.h>
39 #include <quartz/salvd.h>
41 #include <headless/svpbmp.hxx>
42 #include <headless/svpgdi.hxx>
44 #include <salframe.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);
65 SvpSalInstance::SvpSalInstance( SalYieldMutex
*pMutex
)
66 : SalGenericInstance( pMutex
)
68 m_aTimeout
.tv_sec
= 0;
69 m_aTimeout
.tv_usec
= 0;
73 m_MainThread
= osl::Thread::getCurrentIdentifier();
74 CreateWakeupPipe(true);
76 if( s_pDefaultInstance
== nullptr )
77 s_pDefaultInstance
= this;
78 #if !defined(ANDROID) && !defined(IOS)
79 pthread_atfork(nullptr, nullptr, atfork_child
);
83 SvpSalInstance::~SvpSalInstance()
85 if( s_pDefaultInstance
== this )
86 s_pDefaultInstance
= nullptr;
88 CloseWakeupPipe(true);
94 void SvpSalInstance::CloseWakeupPipe(bool log
)
96 SvpSalYieldMutex
*const pMutex(dynamic_cast<SvpSalYieldMutex
*>(mpSalYieldMutex
.get()));
99 if (pMutex
->m_FeedbackFDs
[0] != -1)
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()));
116 if (pipe (pMutex
->m_FeedbackFDs
) == -1)
120 SAL_WARN("vcl.headless", "Could not create feedback pipe: " << strerror(errno
));
128 SAL_INFO("vcl.headless", "CreateWakeupPipe: Created feedback pipe: [" << pMutex
->m_FeedbackFDs
[0] << "," << pMutex
->m_FeedbackFDs
[1] << "]");
133 // set close-on-exec descriptor flag.
134 if ((flags
= fcntl (pMutex
->m_FeedbackFDs
[0], F_GETFD
)) != -1)
137 (void) fcntl(pMutex
->m_FeedbackFDs
[0], F_SETFD
, flags
);
139 if ((flags
= fcntl (pMutex
->m_FeedbackFDs
[1], F_GETFD
)) != -1)
142 (void) fcntl(pMutex
->m_FeedbackFDs
[1], F_SETFD
, flags
);
145 // retain the default blocking I/O for feedback pipe
151 void SvpSalInstance::TriggerUserEventProcessing()
157 static bool g_CheckedMutex
= false;
160 void SvpSalInstance::Wakeup(SvpRequest
const request
)
165 assert(dynamic_cast<SvpSalYieldMutex
*>(mpSalYieldMutex
.get()) != nullptr
166 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
167 g_CheckedMutex
= true;
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();
184 bool SvpSalInstance::CheckTimeout( bool bExecuteTimers
)
187 if( m_aTimeout
.tv_sec
) // timer is started
190 gettimeofday( &aTimeOfDay
, nullptr );
191 if( aTimeOfDay
>= m_aTimeout
)
196 // timed out, update timeout
197 m_aTimeout
= aTimeOfDay
;
198 m_aTimeout
+= m_nTimeoutMS
;
200 osl::Guard
< comphelper::SolarMutex
> aGuard( mpSalYieldMutex
.get() );
203 ImplSVData
* pSVData
= ImplGetSVData();
204 if( pSVData
->maSchedCtx
.mpSalTimer
)
205 pSVData
->maSchedCtx
.mpSalTimer
->CallCallback();
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
)
227 SalObject
* SvpSalInstance::CreateObject( SalFrame
*, SystemWindowData
*, bool )
229 return new SvpSalObject();
232 void SvpSalInstance::DestroyObject( SalObject
* pObject
)
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
);
251 SalTimer
* SvpSalInstance::CreateSalTimer()
253 return new SvpSalTimer( this );
256 SalSystem
* SvpSalInstance::CreateSalSystem()
258 return new SvpSalSystem();
261 SalBitmap
* SvpSalInstance::CreateSalBitmap()
264 return new QuartzSalBitmap();
266 return new SvpSalBitmap();
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();
282 assert(dynamic_cast<SvpSalYieldMutex
*>(mpSalYieldMutex
.get()) != nullptr
283 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
284 g_CheckedMutex
= true;
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())
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;
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;
333 write(m_FeedbackFDs
[1], &bEvents
, sizeof(bool));
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())
355 return SalYieldMutex::doRelease(bUnlockAll
);
359 // read m_nCount before doRelease
360 bool const isReleased(bUnlockAll
|| m_nCount
== 1);
361 nCount
= comphelper::GenericSolarMutex::doRelease( bUnlockAll
);
363 std::unique_lock
<std::mutex
> g(m_WakeUpMainMutex
);
365 m_WakeUpMainCond
.notify_one();
371 bool SvpSalYieldMutex::IsCurrentThread() const
373 if (GetSalData()->m_pInstance
->IsMainThread() && m_bNoYieldLock
)
379 return SalYieldMutex::IsCurrentThread();
383 bool SvpSalInstance::IsMainThread() const
385 return osl::Thread::getCurrentIdentifier() == m_MainThread
;
388 void SvpSalInstance::updateMainThread()
391 m_MainThread
= osl::Thread::getCurrentIdentifier();
394 bool SvpSalInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
399 assert(dynamic_cast<SvpSalYieldMutex
*>(mpSalYieldMutex
.get()) != nullptr
400 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
401 g_CheckedMutex
= true;
405 // first, process current user events
406 bool bEvent
= DispatchUserEvents( bHandleAllCurrentEvents
);
407 if ( !bHandleAllCurrentEvents
&& bEvent
)
410 bEvent
= CheckTimeout() || bEvent
;
412 SvpSalYieldMutex
*const pMutex(static_cast<SvpSalYieldMutex
*>(mpSalYieldMutex
.get()));
416 if (bWait
&& ! bEvent
)
419 if (m_aTimeout
.tv_sec
) // Timer is started.
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 )
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
; });
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
);
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);
468 // blocking read (for synchronisation)
469 auto const nRet
= read(pMutex
->m_FeedbackFDs
[0], &bDidWork
, sizeof(bool));
470 assert(nRet
== 1); (void) nRet
;
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
);
485 bool SvpSalInstance::AnyInput( VclInputFlags nType
)
487 if( nType
& VclInputFlags::TIMER
)
488 return CheckTimeout( false );
492 SalSession
* SvpSalInstance::CreateSalSession()
497 OUString
SvpSalInstance::GetConnectionIdentifier()
502 void SvpSalInstance::StopTimer()
504 m_aTimeout
.tv_sec
= 0;
505 m_aTimeout
.tv_usec
= 0;
509 void SvpSalInstance::StartTimer( sal_uLong nMS
)
511 timeval
aPrevTimeout (m_aTimeout
);
512 gettimeofday (&m_aTimeout
, nullptr);
515 m_aTimeout
+= m_nTimeoutMS
;
517 if ((aPrevTimeout
> m_aTimeout
) || (aPrevTimeout
.tv_sec
== 0))
519 // Wakeup from previous timeout (or stopped timer).
524 void SvpSalInstance::AddToRecentDocumentList(const OUString
&, const OUString
&, const OUString
&)
528 //obviously doesn't actually do anything, it's just a nonfunctional stub
532 class SvpOpenGLContext
536 OpenGLContext
* SvpSalInstance::CreateOpenGLContext()
543 class SvpOpenGLContext
: public OpenGLContext
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
;
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: */