nss: upgrade to release 3.73
[LibreOffice.git] / vcl / headless / svpinst.cxx
blob9654243f5ac3bc39ebfc4b6d4829801c10678f42
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 <config_features.h>
21 #include <sal/config.h>
23 #include <mutex>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <pthread.h>
29 #include <sys/time.h>
30 #include <sys/poll.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>
38 #if HAVE_FEATURE_UI
39 # include <vcl/opengl/OpenGLContext.hxx>
40 #endif
42 #include <headless/svpinst.hxx>
43 #include <headless/svpframe.hxx>
44 #include <headless/svpdummies.hxx>
45 #include <headless/svpvd.hxx>
46 #ifdef IOS
47 # include <quartz/salbmp.h>
48 # include <quartz/salgdi.h>
49 # include <quartz/salvd.h>
50 #else
51 # include <cairo.h>
52 # include <headless/svpgdi.hxx>
53 #endif
54 #include <headless/svpbmp.hxx>
56 #include <salframe.hxx>
57 #include <svdata.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);
76 #endif
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;
83 m_nTimeoutMS = 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);
91 #endif
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()));
104 if (!pMutex)
105 return;
106 if (pMutex->m_FeedbackFDs[0] != -1)
108 if (log)
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()));
121 if (!pMutex)
122 return;
123 if (pipe (pMutex->m_FeedbackFDs) == -1)
125 if (log)
127 SAL_WARN("vcl.headless", "Could not create feedback pipe: " << strerror(errno));
128 std::abort();
131 else
133 if (log)
135 SAL_INFO("vcl.headless", "CreateWakeupPipe: Created feedback pipe: [" << pMutex->m_FeedbackFDs[0] << "," << pMutex->m_FeedbackFDs[1] << "]");
138 int flags;
140 // set close-on-exec descriptor flag.
141 if ((flags = fcntl (pMutex->m_FeedbackFDs[0], F_GETFD)) != -1)
143 flags |= FD_CLOEXEC;
144 (void) fcntl(pMutex->m_FeedbackFDs[0], F_SETFD, flags);
146 if ((flags = fcntl (pMutex->m_FeedbackFDs[1], F_GETFD)) != -1)
148 flags |= FD_CLOEXEC;
149 (void) fcntl(pMutex->m_FeedbackFDs[1], F_SETFD, flags);
152 // retain the default blocking I/O for feedback pipe
156 void SvpSalInstance::TriggerUserEventProcessing()
158 Wakeup();
161 #ifndef NDEBUG
162 static bool g_CheckedMutex = false;
163 #endif
165 void SvpSalInstance::Wakeup(SvpRequest const request)
167 #ifndef NDEBUG
168 if (!g_CheckedMutex)
170 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr
171 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
172 g_CheckedMutex = true;
174 #endif
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 )
191 bool bRet = false;
192 if( m_aTimeout.tv_sec ) // timer is started
194 timeval aTimeOfDay;
195 gettimeofday( &aTimeOfDay, nullptr );
196 if( aTimeOfDay >= m_aTimeout )
198 bRet = true;
199 if( bExecuteTimers )
201 // timed out, update timeout
202 m_aTimeout = aTimeOfDay;
203 m_aTimeout += m_nTimeoutMS;
205 osl::Guard< comphelper::SolarMutex > aGuard( GetYieldMutex() );
207 // notify
208 ImplSVData* pSVData = ImplGetSVData();
209 if( pSVData->maSchedCtx.mpSalTimer )
210 pSVData->maSchedCtx.mpSalTimer->CallCallback();
214 return bRet;
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 )
229 delete pFrame;
232 SalObject* SvpSalInstance::CreateObject( SalFrame*, SystemWindowData*, bool )
234 return new SvpSalObject;
237 void SvpSalInstance::DestroyObject( SalObject* pObject )
239 delete pObject;
242 #ifndef IOS
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);
251 #ifndef ANDROID
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;
256 #else
257 //ANDROID case
258 (void)pGd;
259 cairo_surface_t* pPreExistingTarget = nullptr;
260 #endif
261 std::unique_ptr<SalVirtualDevice> pNew(new SvpSalVirtualDevice(eFormat, pSvpSalGraphics->getSurface(), pPreExistingTarget));
262 pNew->SetSize( nDX, nDY );
263 return pNew;
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;
274 if (!gOptions)
276 gOptions = cairo_font_options_create();
277 cairo_font_options_set_antialias(gOptions, CAIRO_ANTIALIAS_GRAY);
279 return gOptions;
282 #else // IOS
284 const cairo_font_options_t* SvpSalInstance::GetCairoFontOptions()
286 return nullptr;
289 #endif
291 SalTimer* SvpSalInstance::CreateSalTimer()
293 return new SvpSalTimer( this );
296 SalSystem* SvpSalInstance::CreateSalSystem()
298 return new SvpSalSystem();
301 std::shared_ptr<SalBitmap> SvpSalInstance::CreateSalBitmap()
303 #ifdef IOS
304 return std::make_shared<QuartzSalBitmap>();
305 #else
306 return std::make_shared<SvpSalBitmap>();
307 #endif
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();
319 #ifndef NDEBUG
320 if (!g_CheckedMutex)
322 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr
323 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
324 g_CheckedMutex = true;
326 #endif
327 SvpSalYieldMutex *const pMutex(static_cast<SvpSalYieldMutex*>(GetYieldMutex()));
328 pMutex->m_NonMainWaitingYieldCond.set();
331 SvpSalYieldMutex::SvpSalYieldMutex()
333 #ifndef IOS
334 m_FeedbackFDs[0] = m_FeedbackFDs[1] = -1;
335 #endif
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())
347 if (m_bNoYieldLock)
348 return;
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;
359 break;
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));
375 while (true);
377 else
379 m_aMutex.acquire();
381 ++m_nCount;
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())
390 if (m_bNoYieldLock)
391 return 1;
392 else
393 return SalYieldMutex::doRelease(bUnlockAll);
395 sal_uInt32 nCount;
397 // read m_nCount before doRelease
398 bool const isReleased(bUnlockAll || m_nCount == 1);
399 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
401 if (isReleased)
403 if (vcl::lok::isUnipoll())
405 if (pInst)
406 pInst->Wakeup(SvpRequest::NONE);
408 else
410 std::scoped_lock<std::mutex> g(m_WakeUpMainMutex);
411 m_wakeUpMain = true;
412 m_WakeUpMainCond.notify_one();
416 return nCount;
419 bool SvpSalYieldMutex::IsCurrentThread() const
421 if (GetSalData()->m_pInstance->IsMainThread() && m_bNoYieldLock)
423 return true;
425 else
427 return SalYieldMutex::IsCurrentThread();
431 bool SvpSalInstance::IsMainThread() const
433 return osl::Thread::getCurrentIdentifier() == m_MainThread;
436 void SvpSalInstance::updateMainThread()
438 if (!IsMainThread())
440 m_MainThread = osl::Thread::getCurrentIdentifier();
441 ImplGetSVData()->mnMainThreadId = osl::Thread::getCurrentIdentifier();
445 bool SvpSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
447 #ifndef NDEBUG
448 if (!g_CheckedMutex)
450 assert(dynamic_cast<SvpSalYieldMutex*>(GetYieldMutex()) != nullptr
451 && "This SvpSalInstance function requires use of SvpSalYieldMutex");
452 g_CheckedMutex = true;
454 #endif
456 // first, process current user events
457 bool bEvent = DispatchUserEvents(bHandleAllCurrentEvents);
458 if (!bHandleAllCurrentEvents && bEvent)
459 return true;
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()));
471 if (IsMainThread())
473 // in kit case
474 if (bWait && !bSkipPoll)
476 sal_Int64 nTimeoutMicroS = 0;
477 if (m_aTimeout.tv_sec) // Timer is started.
479 timeval Timeout;
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));
486 else
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;
497 // External poll.
498 if (pSVData->mpPollClosure != nullptr &&
499 pSVData->mpPollCallback(pSVData->mpPollClosure, nTimeoutMicroS) < 0)
500 pSVData->maAppData.mbAppQuit = true;
502 else
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; });
511 else
513 int nTimeoutMS = nTimeoutMicroS / 1000;
514 if ( nTimeoutMicroS % 1000 )
515 nTimeoutMS += 1;
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 );
524 else if (bSkipPoll)
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 );
549 return bSkipPoll;
552 bool SvpSalInstance::AnyInput( VclInputFlags nType )
554 if( nType & VclInputFlags::TIMER )
555 return CheckTimeout( false );
556 return false;
559 OUString SvpSalInstance::GetConnectionIdentifier()
561 return OUString();
564 void SvpSalInstance::StopTimer()
566 m_aTimeout.tv_sec = 0;
567 m_aTimeout.tv_usec = 0;
568 m_nTimeoutMS = 0;
571 void SvpSalInstance::StartTimer( sal_uInt64 nMS )
573 timeval aPrevTimeout (m_aTimeout);
574 gettimeofday (&m_aTimeout, nullptr);
576 m_nTimeoutMS = nMS;
577 m_aTimeout += m_nTimeoutMS;
579 if ((aPrevTimeout > m_aTimeout) || (aPrevTimeout.tv_sec == 0))
581 // Wakeup from previous timeout (or stopped timer).
582 Wakeup();
586 void SvpSalInstance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&)
590 std::shared_ptr<vcl::BackendCapabilities> SvpSalInstance::GetBackendCapabilities()
592 auto pBackendCapabilities = SalInstance::GetBackendCapabilities();
593 #ifndef IOS
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;
596 #endif
597 return pBackendCapabilities;
600 //obviously doesn't actually do anything, it's just a nonfunctional stub
602 #if HAVE_FEATURE_UI
604 namespace {
606 class SvpOpenGLContext : public OpenGLContext
608 GLWindow m_aGLWin;
609 private:
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;
621 #else
623 class SvpOpenGLContext
627 OpenGLContext* SvpSalInstance::CreateOpenGLContext()
629 return nullptr;
632 #endif
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: */