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>
28 #include <sal/log.hxx>
30 #include <vcl/virdev.hxx>
31 #include <vcl/inputtypes.hxx>
32 #include <vcl/lok.hxx>
34 # include <vcl/opengl/OpenGLContext.hxx>
37 #include <headless/svpinst.hxx>
38 #include <headless/svpframe.hxx>
39 #include <headless/svpdummies.hxx>
40 #include <headless/svpvd.hxx>
42 #include <quartz/salbmp.h>
43 #include <quartz/salgdi.h>
44 #include <quartz/salvd.h>
46 #include <headless/svpgdi.hxx>
48 #include <headless/svpbmp.hxx>
50 #include <salframe.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);
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;
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
);
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()));
100 if (pMutex
->m_FeedbackFDs
[0] != -1)
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()));
117 if (pipe (pMutex
->m_FeedbackFDs
) == -1)
121 SAL_WARN("vcl.headless", "Could not create feedback pipe: " << strerror(errno
));
129 SAL_INFO("vcl.headless", "CreateWakeupPipe: Created feedback pipe: [" << pMutex
->m_FeedbackFDs
[0] << "," << pMutex
->m_FeedbackFDs
[1] << "]");
134 // set close-on-exec descriptor flag.
135 if ((flags
= fcntl (pMutex
->m_FeedbackFDs
[0], F_GETFD
)) != -1)
138 (void) fcntl(pMutex
->m_FeedbackFDs
[0], F_SETFD
, flags
);
140 if ((flags
= fcntl (pMutex
->m_FeedbackFDs
[1], F_GETFD
)) != -1)
143 (void) fcntl(pMutex
->m_FeedbackFDs
[1], F_SETFD
, flags
);
146 // retain the default blocking I/O for feedback pipe
150 void SvpSalInstance::TriggerUserEventProcessing()
156 static bool g_CheckedMutex
= false;
159 void SvpSalInstance::Wakeup(SvpRequest
const request
)
164 assert(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()) != nullptr
165 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
166 g_CheckedMutex
= true;
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
)
186 if( m_aTimeout
.tv_sec
) // timer is started
189 gettimeofday( &aTimeOfDay
, nullptr );
190 if( aTimeOfDay
>= m_aTimeout
)
195 // timed out, update timeout
196 m_aTimeout
= aTimeOfDay
;
197 m_aTimeout
+= m_nTimeoutMS
;
199 osl::Guard
< comphelper::SolarMutex
> aGuard( GetYieldMutex() );
202 ImplSVData
* pSVData
= ImplGetSVData();
203 if( pSVData
->maSchedCtx
.mpSalTimer
)
204 pSVData
->maSchedCtx
.mpSalTimer
->CallCallback();
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
)
226 SalObject
* SvpSalInstance::CreateObject( SalFrame
*, SystemWindowData
*, bool )
228 return new SvpSalObject();
231 void SvpSalInstance::DestroyObject( SalObject
* pObject
)
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
);
250 cairo_surface_t
* get_underlying_cairo_surface(const VirtualDevice
& rDevice
)
252 return static_cast<SvpSalVirtualDevice
*>(rDevice
.mpVirDev
.get())->GetSurface();
257 SalTimer
* SvpSalInstance::CreateSalTimer()
259 return new SvpSalTimer( this );
262 SalSystem
* SvpSalInstance::CreateSalSystem()
264 return new SvpSalSystem();
267 std::shared_ptr
<SalBitmap
> SvpSalInstance::CreateSalBitmap()
270 return std::make_shared
<QuartzSalBitmap
>();
272 return std::make_shared
<SvpSalBitmap
>();
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();
288 assert(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()) != nullptr
289 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
290 g_CheckedMutex
= true;
293 SvpSalYieldMutex
*const pMutex(static_cast<SvpSalYieldMutex
*>(GetYieldMutex()));
294 pMutex
->m_NonMainWaitingYieldCond
.set();
297 SvpSalYieldMutex::SvpSalYieldMutex()
300 m_FeedbackFDs
[0] = m_FeedbackFDs
[1] = -1;
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())
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;
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));
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())
359 return SalYieldMutex::doRelease(bUnlockAll
);
363 // read m_nCount before doRelease
364 bool const isReleased(bUnlockAll
|| m_nCount
== 1);
365 nCount
= comphelper::SolarMutex::doRelease( bUnlockAll
);
369 if (vcl::lok::isUnipoll())
372 pInst
->Wakeup(SvpRequest::NONE
);
376 std::unique_lock
<std::mutex
> g(m_WakeUpMainMutex
);
378 m_WakeUpMainCond
.notify_one();
385 bool SvpSalYieldMutex::IsCurrentThread() const
387 if (GetSalData()->m_pInstance
->IsMainThread() && m_bNoYieldLock
)
393 return SalYieldMutex::IsCurrentThread();
397 bool SvpSalInstance::IsMainThread() const
399 return osl::Thread::getCurrentIdentifier() == m_MainThread
;
402 void SvpSalInstance::updateMainThread()
406 m_MainThread
= osl::Thread::getCurrentIdentifier();
407 ImplGetSVData()->mnMainThreadId
= osl::Thread::getCurrentIdentifier();
411 bool SvpSalInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
416 assert(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()) != nullptr
417 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
418 g_CheckedMutex
= true;
422 // first, process current user events
423 bool bEvent
= DispatchUserEvents(bHandleAllCurrentEvents
);
424 if (!bHandleAllCurrentEvents
&& bEvent
)
427 bEvent
= CheckTimeout() || bEvent
;
429 SvpSalYieldMutex
*const pMutex(static_cast<SvpSalYieldMutex
*>(GetYieldMutex()));
433 if (bWait
&& ! bEvent
)
436 if (m_aTimeout
.tv_sec
) // Timer is started.
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 )
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.
463 if (pSVData
->mpPollClosure
!= nullptr &&
464 pSVData
->mpPollCallback(pSVData
->mpPollClosure
, nTimeoutMS
* 1000 /* us */) < 0)
465 pSVData
->maAppData
.mbAppQuit
= true;
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
; });
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
);
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
);
514 bool SvpSalInstance::AnyInput( VclInputFlags nType
)
516 if( nType
& VclInputFlags::TIMER
)
517 return CheckTimeout( false );
521 OUString
SvpSalInstance::GetConnectionIdentifier()
526 void SvpSalInstance::StopTimer()
528 m_aTimeout
.tv_sec
= 0;
529 m_aTimeout
.tv_usec
= 0;
533 void SvpSalInstance::StartTimer( sal_uInt64 nMS
)
535 timeval
aPrevTimeout (m_aTimeout
);
536 gettimeofday (&m_aTimeout
, nullptr);
539 m_aTimeout
+= m_nTimeoutMS
;
541 if ((aPrevTimeout
> m_aTimeout
) || (aPrevTimeout
.tv_sec
== 0))
543 // Wakeup from previous timeout (or stopped timer).
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
563 class SvpOpenGLContext
567 OpenGLContext
* SvpSalInstance::CreateOpenGLContext()
574 class SvpOpenGLContext
: public OpenGLContext
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
;
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: */