#include "config.h"
#include "DisplayLink.h"
#if ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)
#include "EventDispatcherMessages.h"
#include "WebProcessMessages.h"
#include <wtf/ProcessPrivilege.h>
namespace WebKit {
bool DisplayLink::shouldSendIPCOnBackgroundQueue { false };
constexpr unsigned maxFireCountWithoutObservers { 20 };
DisplayLink::DisplayLink(WebCore::PlatformDisplayID displayID)
: m_displayID(displayID)
{
ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &m_displayLink);
if (error) {
WTFLogAlways("Could not create a display link for display %u: error %d", displayID, error);
return;
}
error = CVDisplayLinkSetOutputCallback(m_displayLink, displayLinkCallback, this);
if (error) {
WTFLogAlways("Could not set the display link output callback for display %u: error %d", displayID, error);
return;
}
}
DisplayLink::~DisplayLink()
{
ASSERT(hasProcessPrivilege(ProcessPrivilege::CanCommunicateWithWindowServer));
ASSERT(m_displayLink);
if (!m_displayLink)
return;
CVDisplayLinkStop(m_displayLink);
CVDisplayLinkRelease(m_displayLink);
}
Optional<unsigned> DisplayLink::nominalFramesPerSecond() const
{
CVTime refreshPeriod = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(m_displayLink);
return round((double)refreshPeriod.timeScale / (double)refreshPeriod.timeValue);
}
void DisplayLink::addObserver(IPC::Connection& connection, DisplayLinkObserverID observerID)
{
ASSERT(RunLoop::isMain());
{
LockHolder locker(m_observersLock);
m_observers.ensure(&connection, [] {
return Vector<DisplayLinkObserverID> { };
}).iterator->value.append(observerID);
}
if (!CVDisplayLinkIsRunning(m_displayLink)) {
CVReturn error = CVDisplayLinkStart(m_displayLink);
if (error)
WTFLogAlways("Could not start the display link: %d", error);
}
}
void DisplayLink::removeObserver(IPC::Connection& connection, DisplayLinkObserverID observerID)
{
ASSERT(RunLoop::isMain());
LockHolder locker(m_observersLock);
auto it = m_observers.find(&connection);
if (it == m_observers.end())
return;
bool removed = it->value.removeFirst(observerID);
ASSERT_UNUSED(removed, removed);
if (it->value.isEmpty())
m_observers.remove(it);
}
void DisplayLink::removeObservers(IPC::Connection& connection)
{
ASSERT(RunLoop::isMain());
LockHolder locker(m_observersLock);
m_observers.remove(&connection);
}
CVReturn DisplayLink::displayLinkCallback(CVDisplayLinkRef displayLinkRef, const CVTimeStamp*, const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* data)
{
static_cast<DisplayLink*>(data)->notifyObserversDisplayWasRefreshed();
return kCVReturnSuccess;
}
void DisplayLink::notifyObserversDisplayWasRefreshed()
{
ASSERT(!RunLoop::isMain());
LockHolder locker(m_observersLock);
if (m_observers.isEmpty()) {
if (++m_fireCountWithoutObservers >= maxFireCountWithoutObservers)
CVDisplayLinkStop(m_displayLink);
return;
}
m_fireCountWithoutObservers = 0;
for (auto& connection : m_observers.keys()) {
if (shouldSendIPCOnBackgroundQueue)
connection->send(Messages::EventDispatcher::DisplayWasRefreshed(m_displayID), 0);
else
connection->send(Messages::WebProcess::DisplayWasRefreshed(m_displayID), 0);
}
}
}
#endif // ENABLE(WEBPROCESS_WINDOWSERVER_BLOCKING)