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>
52 # include <headless/svpgdi.hxx>
54 #include <headless/svpbmp.hxx>
56 #include <salframe.hxx>
58 #include <unx/gendata.hxx>
59 // FIXME: remove when we re-work the svp mainloop
60 #include <unx/salunxtime.h>
61 #include <comphelper/lok.hxx>
63 SvpSalInstance
* SvpSalInstance::s_pDefaultInstance
= nullptr;
65 #if !defined(ANDROID) && !defined(IOS)
67 static void atfork_child()
69 if (SvpSalInstance::s_pDefaultInstance
!= nullptr)
71 SvpSalInstance::s_pDefaultInstance
->CloseWakeupPipe(false);
72 SvpSalInstance::s_pDefaultInstance
->CreateWakeupPipe(false);
78 SvpSalInstance::SvpSalInstance( std::unique_ptr
<SalYieldMutex
> pMutex
)
79 : SalGenericInstance( std::move(pMutex
) )
81 m_aTimeout
.tv_sec
= 0;
82 m_aTimeout
.tv_usec
= 0;
85 m_MainThread
= osl::Thread::getCurrentIdentifier();
86 CreateWakeupPipe(true);
87 if( s_pDefaultInstance
== nullptr )
88 s_pDefaultInstance
= this;
89 #if !defined(ANDROID) && !defined(IOS)
90 pthread_atfork(nullptr, nullptr, atfork_child
);
94 SvpSalInstance::~SvpSalInstance()
96 if( s_pDefaultInstance
== this )
97 s_pDefaultInstance
= nullptr;
98 CloseWakeupPipe(true);
101 void SvpSalInstance::CloseWakeupPipe(bool log
)
103 SvpSalYieldMutex
*const pMutex(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()));
106 if (pMutex
->m_FeedbackFDs
[0] != -1)
110 SAL_INFO("vcl.headless", "CloseWakeupPipe: Closing inherited feedback pipe: [" << pMutex
->m_FeedbackFDs
[0] << "," << pMutex
->m_FeedbackFDs
[1] << "]");
112 close (pMutex
->m_FeedbackFDs
[0]);
113 close (pMutex
->m_FeedbackFDs
[1]);
114 pMutex
->m_FeedbackFDs
[0] = pMutex
->m_FeedbackFDs
[1] = -1;
118 void SvpSalInstance::CreateWakeupPipe(bool log
)
120 SvpSalYieldMutex
*const pMutex(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()));
123 if (pipe (pMutex
->m_FeedbackFDs
) == -1)
127 SAL_WARN("vcl.headless", "Could not create feedback pipe: " << strerror(errno
));
135 SAL_INFO("vcl.headless", "CreateWakeupPipe: Created feedback pipe: [" << pMutex
->m_FeedbackFDs
[0] << "," << pMutex
->m_FeedbackFDs
[1] << "]");
140 // set close-on-exec descriptor flag.
141 if ((flags
= fcntl (pMutex
->m_FeedbackFDs
[0], F_GETFD
)) != -1)
144 (void) fcntl(pMutex
->m_FeedbackFDs
[0], F_SETFD
, flags
);
146 if ((flags
= fcntl (pMutex
->m_FeedbackFDs
[1], F_GETFD
)) != -1)
149 (void) fcntl(pMutex
->m_FeedbackFDs
[1], F_SETFD
, flags
);
152 // retain the default blocking I/O for feedback pipe
156 void SvpSalInstance::TriggerUserEventProcessing()
162 static bool g_CheckedMutex
= false;
165 void SvpSalInstance::Wakeup(SvpRequest
const request
)
170 assert(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()) != nullptr
171 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
172 g_CheckedMutex
= true;
176 ImplSVData
* pSVData
= ImplGetSVData();
178 if (pSVData
->mpWakeCallback
&& pSVData
->mpPollClosure
)
179 pSVData
->mpWakeCallback(pSVData
->mpPollClosure
);
181 SvpSalYieldMutex
*const pMutex(static_cast<SvpSalYieldMutex
*>(GetYieldMutex()));
182 std::scoped_lock
<std::mutex
> g(pMutex
->m_WakeUpMainMutex
);
183 if (request
!= SvpRequest::NONE
)
184 pMutex
->m_Request
= request
;
185 pMutex
->m_wakeUpMain
= true;
186 pMutex
->m_WakeUpMainCond
.notify_one();
189 bool SvpSalInstance::CheckTimeout( bool bExecuteTimers
)
192 if( m_aTimeout
.tv_sec
) // timer is started
195 gettimeofday( &aTimeOfDay
, nullptr );
196 if( aTimeOfDay
>= m_aTimeout
)
201 // timed out, update timeout
202 m_aTimeout
= aTimeOfDay
;
203 m_aTimeout
+= m_nTimeoutMS
;
205 osl::Guard
< comphelper::SolarMutex
> aGuard( GetYieldMutex() );
208 ImplSVData
* pSVData
= ImplGetSVData();
209 if( pSVData
->maSchedCtx
.mpSalTimer
)
210 pSVData
->maSchedCtx
.mpSalTimer
->CallCallback();
217 SalFrame
* SvpSalInstance::CreateChildFrame( SystemParentData
* /*pParent*/, SalFrameStyleFlags nStyle
)
219 return new SvpSalFrame( this, nullptr, nStyle
);
222 SalFrame
* SvpSalInstance::CreateFrame( SalFrame
* pParent
, SalFrameStyleFlags nStyle
)
224 return new SvpSalFrame( this, pParent
, nStyle
);
227 void SvpSalInstance::DestroyFrame( SalFrame
* pFrame
)
232 SalObject
* SvpSalInstance::CreateObject( SalFrame
*, SystemWindowData
*, bool )
234 return new SvpSalObject
;
237 void SvpSalInstance::DestroyObject( SalObject
* pObject
)
244 std::unique_ptr
<SalVirtualDevice
> SvpSalInstance::CreateVirtualDevice(SalGraphics
* pGraphics
,
245 tools::Long
&nDX
, tools::Long
&nDY
,
246 DeviceFormat eFormat
,
247 const SystemGraphicsData
* pGd
)
249 SvpSalGraphics
*pSvpSalGraphics
= dynamic_cast<SvpSalGraphics
*>(pGraphics
);
250 assert(pSvpSalGraphics
);
252 // tdf#127529 normally pPreExistingTarget is null and we are a true virtualdevice drawing to a backing buffer.
253 // Occasionally, for canvas/slideshow, pPreExistingTarget is pre-provided as a hack to use the vcl drawing
254 // apis to render onto a preexisting cairo surface. The necessity for that precedes the use of cairo in vcl proper
255 cairo_surface_t
* pPreExistingTarget
= pGd
? static_cast<cairo_surface_t
*>(pGd
->pSurface
) : nullptr;
259 cairo_surface_t
* pPreExistingTarget
= nullptr;
261 std::unique_ptr
<SalVirtualDevice
> pNew(new SvpSalVirtualDevice(eFormat
, pSvpSalGraphics
->getSurface(), pPreExistingTarget
));
262 pNew
->SetSize( nDX
, nDY
);
266 cairo_surface_t
* get_underlying_cairo_surface(const VirtualDevice
& rDevice
)
268 return static_cast<SvpSalVirtualDevice
*>(rDevice
.mpVirDev
.get())->GetSurface();
271 const cairo_font_options_t
* SvpSalInstance::GetCairoFontOptions()
273 static cairo_font_options_t
*gOptions
= nullptr;
276 gOptions
= cairo_font_options_create();
277 cairo_font_options_set_antialias(gOptions
, CAIRO_ANTIALIAS_GRAY
);
284 const cairo_font_options_t
* SvpSalInstance::GetCairoFontOptions()
291 SalTimer
* SvpSalInstance::CreateSalTimer()
293 return new SvpSalTimer( this );
296 SalSystem
* SvpSalInstance::CreateSalSystem()
298 return new SvpSalSystem();
301 std::shared_ptr
<SalBitmap
> SvpSalInstance::CreateSalBitmap()
304 return std::make_shared
<QuartzSalBitmap
>();
306 return std::make_shared
<SvpSalBitmap
>();
310 void SvpSalInstance::ProcessEvent( SalUserEvent aEvent
)
312 aEvent
.m_pFrame
->CallCallback( aEvent
.m_nEvent
, aEvent
.m_pData
);
313 if( aEvent
.m_nEvent
== SalEvent::Resize
)
315 // this would be a good time to post a paint
316 const SvpSalFrame
* pSvpFrame
= static_cast<const SvpSalFrame
*>( aEvent
.m_pFrame
);
317 pSvpFrame
->PostPaint();
322 assert(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()) != nullptr
323 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
324 g_CheckedMutex
= true;
327 SvpSalYieldMutex
*const pMutex(static_cast<SvpSalYieldMutex
*>(GetYieldMutex()));
328 pMutex
->m_NonMainWaitingYieldCond
.set();
331 SvpSalYieldMutex::SvpSalYieldMutex()
334 m_FeedbackFDs
[0] = m_FeedbackFDs
[1] = -1;
338 SvpSalYieldMutex::~SvpSalYieldMutex()
342 void SvpSalYieldMutex::doAcquire(sal_uInt32
const nLockCount
)
344 SvpSalInstance
*const pInst
= static_cast<SvpSalInstance
*>(GetSalData()->m_pInstance
);
345 if (pInst
&& pInst
->IsMainThread())
352 SvpRequest request
= SvpRequest::NONE
;
354 std::unique_lock
<std::mutex
> g(m_WakeUpMainMutex
);
355 if (m_aMutex
.tryToAcquire()) {
356 // if there's a request, the other thread holds m_aMutex
357 assert(m_Request
== SvpRequest::NONE
);
358 m_wakeUpMain
= false;
361 m_WakeUpMainCond
.wait(g
, [this]() { return m_wakeUpMain
; });
362 m_wakeUpMain
= false;
363 std::swap(m_Request
, request
);
365 if (request
!= SvpRequest::NONE
)
367 // nested Yield on behalf of another thread
368 assert(!m_bNoYieldLock
);
369 m_bNoYieldLock
= true;
370 bool const bEvents
= pInst
->DoYield(false, request
== SvpRequest::MainThreadDispatchAllEvents
);
371 m_bNoYieldLock
= false;
372 write(m_FeedbackFDs
[1], &bEvents
, sizeof(bool));
382 SalYieldMutex::doAcquire(nLockCount
- 1);
385 sal_uInt32
SvpSalYieldMutex::doRelease(bool const bUnlockAll
)
387 SvpSalInstance
*const pInst
= static_cast<SvpSalInstance
*>(GetSalData()->m_pInstance
);
388 if (pInst
&& pInst
->IsMainThread())
393 return SalYieldMutex::doRelease(bUnlockAll
);
397 // read m_nCount before doRelease
398 bool const isReleased(bUnlockAll
|| m_nCount
== 1);
399 nCount
= comphelper::SolarMutex::doRelease( bUnlockAll
);
403 if (vcl::lok::isUnipoll())
406 pInst
->Wakeup(SvpRequest::NONE
);
410 std::scoped_lock
<std::mutex
> g(m_WakeUpMainMutex
);
412 m_WakeUpMainCond
.notify_one();
419 bool SvpSalYieldMutex::IsCurrentThread() const
421 if (GetSalData()->m_pInstance
->IsMainThread() && m_bNoYieldLock
)
427 return SalYieldMutex::IsCurrentThread();
431 bool SvpSalInstance::IsMainThread() const
433 return osl::Thread::getCurrentIdentifier() == m_MainThread
;
436 void SvpSalInstance::updateMainThread()
440 m_MainThread
= osl::Thread::getCurrentIdentifier();
441 ImplGetSVData()->mnMainThreadId
= osl::Thread::getCurrentIdentifier();
445 bool SvpSalInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
450 assert(dynamic_cast<SvpSalYieldMutex
*>(GetYieldMutex()) != nullptr
451 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
452 g_CheckedMutex
= true;
456 // first, process current user events
457 bool bEvent
= DispatchUserEvents(bHandleAllCurrentEvents
);
458 if (!bHandleAllCurrentEvents
&& bEvent
)
461 ImplSVData
* pSVData
= ImplGetSVData();
463 bool bTimeout
= CheckTimeout();
464 bool bSkipPoll
= bEvent
;
465 if (pSVData
->mpPollCallback
== nullptr)
466 bSkipPoll
= bEvent
|| bTimeout
;
467 // else - give the poll-callback visibility into waiting timeouts too.
469 SvpSalYieldMutex
*const pMutex(static_cast<SvpSalYieldMutex
*>(GetYieldMutex()));
474 if (bWait
&& !bSkipPoll
)
476 sal_Int64 nTimeoutMicroS
= 0;
477 if (m_aTimeout
.tv_sec
) // Timer is started.
480 // determine remaining timeout.
481 gettimeofday (&Timeout
, nullptr);
482 if (m_aTimeout
> Timeout
)
483 nTimeoutMicroS
= ((m_aTimeout
.tv_sec
- Timeout
.tv_sec
) * 1000 * 1000 +
484 (m_aTimeout
.tv_usec
- Timeout
.tv_usec
));
487 nTimeoutMicroS
= -1; // wait until something happens
489 sal_uInt32 nAcquireCount
= ReleaseYieldMutexAll();
491 if (pSVData
->mpPollCallback
)
493 // Poll for events from the LOK client.
494 if (nTimeoutMicroS
< 0)
495 nTimeoutMicroS
= 5000 * 1000;
498 if (pSVData
->mpPollClosure
!= nullptr &&
499 pSVData
->mpPollCallback(pSVData
->mpPollClosure
, nTimeoutMicroS
) < 0)
500 pSVData
->maAppData
.mbAppQuit
= true;
504 std::unique_lock
<std::mutex
> g(pMutex
->m_WakeUpMainMutex
);
505 // wait for doRelease() or Wakeup() to set the condition
506 if (nTimeoutMicroS
== -1)
508 pMutex
->m_WakeUpMainCond
.wait(g
,
509 [pMutex
]() { return pMutex
->m_wakeUpMain
; });
513 int nTimeoutMS
= nTimeoutMicroS
/ 1000;
514 if ( nTimeoutMicroS
% 1000 )
516 pMutex
->m_WakeUpMainCond
.wait_for(g
,
517 std::chrono::milliseconds(nTimeoutMS
),
518 [pMutex
]() { return pMutex
->m_wakeUpMain
; });
520 // here no need to check m_Request because Acquire will do it
522 AcquireYieldMutex( nAcquireCount
);
526 pMutex
->m_NonMainWaitingYieldCond
.set(); // wake up other threads
529 else // !IsMainThread()
531 Wakeup(bHandleAllCurrentEvents
532 ? SvpRequest::MainThreadDispatchAllEvents
533 : SvpRequest::MainThreadDispatchOneEvent
);
535 bool bDidWork(false);
536 // blocking read (for synchronisation)
537 auto const nRet
= read(pMutex
->m_FeedbackFDs
[0], &bDidWork
, sizeof(bool));
538 assert(nRet
== 1); (void) nRet
;
539 if (!bDidWork
&& bWait
)
541 // block & release YieldMutex until the main thread does something
542 pMutex
->m_NonMainWaitingYieldCond
.reset();
543 sal_uInt32 nAcquireCount
= ReleaseYieldMutexAll();
544 pMutex
->m_NonMainWaitingYieldCond
.wait();
545 AcquireYieldMutex( nAcquireCount
);
552 bool SvpSalInstance::AnyInput( VclInputFlags nType
)
554 if( nType
& VclInputFlags::TIMER
)
555 return CheckTimeout( false );
559 OUString
SvpSalInstance::GetConnectionIdentifier()
564 void SvpSalInstance::StopTimer()
566 m_aTimeout
.tv_sec
= 0;
567 m_aTimeout
.tv_usec
= 0;
571 void SvpSalInstance::StartTimer( sal_uInt64 nMS
)
573 timeval
aPrevTimeout (m_aTimeout
);
574 gettimeofday (&m_aTimeout
, nullptr);
577 m_aTimeout
+= m_nTimeoutMS
;
579 if ((aPrevTimeout
> m_aTimeout
) || (aPrevTimeout
.tv_sec
== 0))
581 // Wakeup from previous timeout (or stopped timer).
586 void SvpSalInstance::AddToRecentDocumentList(const OUString
&, const OUString
&, const OUString
&)
590 std::shared_ptr
<vcl::BackendCapabilities
> SvpSalInstance::GetBackendCapabilities()
592 auto pBackendCapabilities
= SalInstance::GetBackendCapabilities();
594 // Note: This code is used for iOS, too. Let's not use 32-bit bitmaps with included alpha on iOS for now.
595 pBackendCapabilities
->mbSupportsBitmap32
= true;
597 return pBackendCapabilities
;
600 //obviously doesn't actually do anything, it's just a nonfunctional stub
606 class SvpOpenGLContext
: public OpenGLContext
610 virtual const GLWindow
& getOpenGLWindow() const override
{ return m_aGLWin
; }
611 virtual GLWindow
& getModifiableOpenGLWindow() override
{ return m_aGLWin
; }
616 OpenGLContext
* SvpSalInstance::CreateOpenGLContext()
618 return new SvpOpenGLContext
;
623 class SvpOpenGLContext
627 OpenGLContext
* SvpSalInstance::CreateOpenGLContext()
634 SvpSalTimer::~SvpSalTimer()
638 void SvpSalTimer::Stop()
640 m_pInstance
->StopTimer();
643 void SvpSalTimer::Start( sal_uInt64 nMS
)
645 m_pInstance
->StartTimer( nMS
);
648 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */