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 .
20 #include <config_features.h>
21 #include <sal/config.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>
39 # include <vcl/opengl/OpenGLContext.hxx>
42 #include <headless/svpinst.hxx>
43 #include <headless/svpframe.hxx>
44 #include <headless/svpdummies.hxx>
45 #include <headless/svpvd.hxx>
47 #include <quartz/salbmp.h>
48 #include <quartz/salgdi.h>
49 #include <quartz/salvd.h>
51 #include <headless/svpgdi.hxx>
53 #include <headless/svpbmp.hxx>
55 #include <salframe.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);
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;
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
);
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()));
105 if (pMutex
->m_FeedbackFDs
[0] != -1)
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()));
122 if (pipe (pMutex
->m_FeedbackFDs
) == -1)
126 SAL_WARN("vcl.headless", "Could not create feedback pipe: " << strerror(errno
));
134 SAL_INFO("vcl.headless", "CreateWakeupPipe: Created feedback pipe: [" << pMutex
->m_FeedbackFDs
[0] << "," << pMutex
->m_FeedbackFDs
[1] << "]");
139 // set close-on-exec descriptor flag.
140 if ((flags
= fcntl (pMutex
->m_FeedbackFDs
[0], F_GETFD
)) != -1)
143 (void) fcntl(pMutex
->m_FeedbackFDs
[0], F_SETFD
, flags
);
145 if ((flags
= fcntl (pMutex
->m_FeedbackFDs
[1], F_GETFD
)) != -1)
148 (void) fcntl(pMutex
->m_FeedbackFDs
[1], F_SETFD
, flags
);
151 // retain the default blocking I/O for feedback pipe
155 void SvpSalInstance::TriggerUserEventProcessing()
161 static bool g_CheckedMutex
= false;
164 void SvpSalInstance::Wakeup(SvpRequest
const request
)
169 assert(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()) != nullptr
170 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
171 g_CheckedMutex
= true;
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
)
191 if( m_aTimeout
.tv_sec
) // timer is started
194 gettimeofday( &aTimeOfDay
, nullptr );
195 if( aTimeOfDay
>= m_aTimeout
)
200 // timed out, update timeout
201 m_aTimeout
= aTimeOfDay
;
202 m_aTimeout
+= m_nTimeoutMS
;
204 osl::Guard
< comphelper::SolarMutex
> aGuard( GetYieldMutex() );
207 ImplSVData
* pSVData
= ImplGetSVData();
208 if( pSVData
->maSchedCtx
.mpSalTimer
)
209 pSVData
->maSchedCtx
.mpSalTimer
->CallCallback();
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
)
231 SalObject
* SvpSalInstance::CreateObject( SalFrame
*, SystemWindowData
*, bool )
233 return new SvpSalObject
;
236 void SvpSalInstance::DestroyObject( SalObject
* pObject
)
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
);
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;
258 cairo_surface_t
* pPreExistingTarget
= nullptr;
260 std::unique_ptr
<SalVirtualDevice
> pNew(new SvpSalVirtualDevice(eFormat
, pSvpSalGraphics
->getSurface(), pPreExistingTarget
));
261 pNew
->SetSize( nDX
, nDY
);
265 cairo_surface_t
* get_underlying_cairo_surface(const VirtualDevice
& rDevice
)
267 return static_cast<SvpSalVirtualDevice
*>(rDevice
.mpVirDev
.get())->GetSurface();
272 SalTimer
* SvpSalInstance::CreateSalTimer()
274 return new SvpSalTimer( this );
277 SalSystem
* SvpSalInstance::CreateSalSystem()
279 return new SvpSalSystem();
282 std::shared_ptr
<SalBitmap
> SvpSalInstance::CreateSalBitmap()
285 return std::make_shared
<QuartzSalBitmap
>();
287 return std::make_shared
<SvpSalBitmap
>();
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();
303 assert(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()) != nullptr
304 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
305 g_CheckedMutex
= true;
308 SvpSalYieldMutex
*const pMutex(static_cast<SvpSalYieldMutex
*>(GetYieldMutex()));
309 pMutex
->m_NonMainWaitingYieldCond
.set();
312 SvpSalYieldMutex::SvpSalYieldMutex()
315 m_FeedbackFDs
[0] = m_FeedbackFDs
[1] = -1;
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())
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;
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));
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())
374 return SalYieldMutex::doRelease(bUnlockAll
);
378 // read m_nCount before doRelease
379 bool const isReleased(bUnlockAll
|| m_nCount
== 1);
380 nCount
= comphelper::SolarMutex::doRelease( bUnlockAll
);
384 if (vcl::lok::isUnipoll())
387 pInst
->Wakeup(SvpRequest::NONE
);
391 std::scoped_lock
<std::mutex
> g(m_WakeUpMainMutex
);
393 m_WakeUpMainCond
.notify_one();
400 bool SvpSalYieldMutex::IsCurrentThread() const
402 if (GetSalData()->m_pInstance
->IsMainThread() && m_bNoYieldLock
)
408 return SalYieldMutex::IsCurrentThread();
412 bool SvpSalInstance::IsMainThread() const
414 return osl::Thread::getCurrentIdentifier() == m_MainThread
;
417 void SvpSalInstance::updateMainThread()
421 m_MainThread
= osl::Thread::getCurrentIdentifier();
422 ImplGetSVData()->mnMainThreadId
= osl::Thread::getCurrentIdentifier();
426 bool SvpSalInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
431 assert(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()) != nullptr
432 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
433 g_CheckedMutex
= true;
437 // first, process current user events
438 bool bEvent
= DispatchUserEvents(bHandleAllCurrentEvents
);
439 if (!bHandleAllCurrentEvents
&& bEvent
)
442 bEvent
= CheckTimeout() || bEvent
;
444 SvpSalYieldMutex
*const pMutex(static_cast<SvpSalYieldMutex
*>(GetYieldMutex()));
448 if (bWait
&& ! bEvent
)
451 if (m_aTimeout
.tv_sec
) // Timer is started.
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 )
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.
478 if (pSVData
->mpPollClosure
!= nullptr &&
479 pSVData
->mpPollCallback(pSVData
->mpPollClosure
, nTimeoutMS
* 1000 /* us */) < 0)
480 pSVData
->maAppData
.mbAppQuit
= true;
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
; });
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
);
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
);
529 bool SvpSalInstance::AnyInput( VclInputFlags nType
)
531 if( nType
& VclInputFlags::TIMER
)
532 return CheckTimeout( false );
536 OUString
SvpSalInstance::GetConnectionIdentifier()
541 void SvpSalInstance::StopTimer()
543 m_aTimeout
.tv_sec
= 0;
544 m_aTimeout
.tv_usec
= 0;
548 void SvpSalInstance::StartTimer( sal_uInt64 nMS
)
550 timeval
aPrevTimeout (m_aTimeout
);
551 gettimeofday (&m_aTimeout
, nullptr);
554 m_aTimeout
+= m_nTimeoutMS
;
556 if ((aPrevTimeout
> m_aTimeout
) || (aPrevTimeout
.tv_sec
== 0))
558 // Wakeup from previous timeout (or stopped timer).
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
578 class SvpOpenGLContext
: public OpenGLContext
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
;
593 class SvpOpenGLContext
597 OpenGLContext
* SvpSalInstance::CreateOpenGLContext()
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: */