2 * Copyright (C) 2010-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "WinSystemTVOS.h"
11 #include "ServiceBroker.h"
12 #import "cores/AudioEngine/Sinks/AESinkDARWINTVOS.h"
13 #include "cores/RetroPlayer/process/ios/RPProcessInfoIOS.h"
14 #include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.h"
15 #include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
16 #include "cores/VideoPlayer/DVDCodecs/Video/VTB.h"
17 #include "cores/VideoPlayer/Process/ios/ProcessInfoIOS.h"
18 #include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGLES.h"
19 #include "cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.h"
20 #include "cores/VideoPlayer/VideoRenderers/RenderFactory.h"
21 #include "filesystem/SpecialProtocol.h"
22 #include "guilib/DispResource.h"
23 #include "guilib/Texture.h"
24 #include "messaging/ApplicationMessenger.h"
25 #include "settings/DisplaySettings.h"
26 #include "settings/Settings.h"
27 #include "settings/SettingsComponent.h"
28 #include "utils/StringUtils.h"
29 #include "utils/log.h"
30 #include "windowing/GraphicContext.h"
31 #include "windowing/OSScreenSaver.h"
32 #include "windowing/WindowSystemFactory.h"
33 #import "windowing/tvos/OSScreenSaverTVOS.h"
34 #import "windowing/tvos/VideoSyncTVos.h"
35 #import "windowing/tvos/WinEventsTVOS.h"
37 #import "platform/darwin/DarwinUtils.h"
38 #import "platform/darwin/tvos/TVOSDisplayManager.h"
39 #import "platform/darwin/tvos/XBMCController.h"
45 #import <Foundation/Foundation.h>
46 #import <OpenGLES/ES2/gl.h>
47 #import <OpenGLES/ES2/glext.h>
48 #import <QuartzCore/CADisplayLink.h>
50 using namespace std::chrono_literals;
52 #define CONST_HDMI "HDMI"
54 // if there was a devicelost callback
55 // but no device reset for 3 secs
56 // a timeout fires the reset callback
57 // (for ensuring that e.x. AE isn't stuck)
58 constexpr auto LOST_DEVICE_TIMEOUT_MS{3000ms};
60 // TVOSDisplayLinkCallback is defined in the lower part of the file
61 @interface TVOSDisplayLinkCallback : NSObject
64 CVideoSyncTVos* videoSyncImpl;
66 @property(nonatomic, setter=SetVideoSyncImpl:) CVideoSyncTVos* videoSyncImpl;
67 - (void)runDisplayLink;
71 using namespace MESSAGING;
73 struct CADisplayLinkWrapper
76 TVOSDisplayLinkCallback* callbackClass;
79 void CWinSystemTVOS::Register()
81 KODI::WINDOWING::CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem);
84 std::unique_ptr<CWinSystemBase> CWinSystemTVOS::CreateWinSystem()
86 return std::make_unique<CWinSystemTVOS>();
89 void CWinSystemTVOS::MessagePush(XBMC_Event* newEvent)
91 dynamic_cast<CWinEventsTVOS&>(*m_winEvents).MessagePush(newEvent);
94 size_t CWinSystemTVOS::GetQueueSize()
96 return dynamic_cast<CWinEventsTVOS&>(*m_winEvents).GetQueueSize();
99 void CWinSystemTVOS::AnnounceOnLostDevice()
101 std::unique_lock<CCriticalSection> lock(m_resourceSection);
102 // tell any shared resources
103 CLog::Log(LOGDEBUG, "CWinSystemTVOS::AnnounceOnLostDevice");
104 for (auto dispResource : m_resources)
105 dispResource->OnLostDisplay();
108 void CWinSystemTVOS::AnnounceOnResetDevice()
110 std::unique_lock<CCriticalSection> lock(m_resourceSection);
111 // tell any shared resources
112 CLog::Log(LOGDEBUG, "CWinSystemTVOS::AnnounceOnResetDevice");
113 for (auto dispResource : m_resources)
114 dispResource->OnResetDisplay();
117 void CWinSystemTVOS::StartLostDeviceTimer()
119 if (m_lostDeviceTimer.IsRunning())
120 m_lostDeviceTimer.Restart();
122 m_lostDeviceTimer.Start(LOST_DEVICE_TIMEOUT_MS, false);
125 void CWinSystemTVOS::StopLostDeviceTimer()
127 m_lostDeviceTimer.Stop();
131 int CWinSystemTVOS::GetDisplayIndexFromSettings()
133 // ATV only supports 1 screen with id = 0
137 CWinSystemTVOS::CWinSystemTVOS() : CWinSystemBase(), m_lostDeviceTimer(this)
139 m_bIsBackgrounded = false;
140 m_pDisplayLink = new CADisplayLinkWrapper;
141 m_pDisplayLink->callbackClass = [[TVOSDisplayLinkCallback alloc] init];
143 m_winEvents = std::make_unique<CWinEventsTVOS>();
145 CAESinkDARWINTVOS::Register();
148 CWinSystemTVOS::~CWinSystemTVOS()
150 m_pDisplayLink->callbackClass = nil;
151 delete m_pDisplayLink;
154 bool CWinSystemTVOS::InitWindowSystem()
156 return CWinSystemBase::InitWindowSystem();
159 bool CWinSystemTVOS::DestroyWindowSystem()
164 std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> CWinSystemTVOS::GetOSScreenSaverImpl()
166 std::unique_ptr<KODI::WINDOWING::IOSScreenSaver> screensaver =
167 std::make_unique<COSScreenSaverTVOS>();
171 bool CWinSystemTVOS::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
173 if (!SetFullScreen(fullScreen, res, false))
176 [g_xbmcController setFramebuffer];
178 m_bWindowCreated = true;
182 const char* tmpExtensions = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
183 if (tmpExtensions != nullptr)
185 m_eglext += tmpExtensions;
189 CLog::Log(LOGDEBUG, "EGL_EXTENSIONS: {}", m_eglext);
191 // register platform dependent objects
192 CDVDFactoryCodec::ClearHWAccels();
193 VTB::CDecoder::Register();
194 VIDEOPLAYER::CRendererFactory::ClearRenderer();
195 CLinuxRendererGLES::Register();
196 CRendererVTB::Register();
197 VIDEOPLAYER::CProcessInfoIOS::Register();
198 RETRO::CRPProcessInfoIOS::Register();
199 RETRO::CRPProcessInfoIOS::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGLES);
204 bool CWinSystemTVOS::DestroyWindow()
209 bool CWinSystemTVOS::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
211 if (m_nWidth != newWidth || m_nHeight != newHeight)
214 m_nHeight = newHeight;
217 CRenderSystemGLES::ResetRenderSystem(newWidth, newHeight);
222 bool CWinSystemTVOS::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
224 m_nWidth = res.iWidth;
225 m_nHeight = res.iHeight;
226 m_bFullScreen = fullScreen;
228 CLog::Log(LOGDEBUG, "About to switch to {} x {} @ {}", m_nWidth, m_nHeight, res.fRefreshRate);
229 SwitchToVideoMode(res.iWidth, res.iHeight, static_cast<double>(res.fRefreshRate));
230 CRenderSystemGLES::ResetRenderSystem(res.iWidth, res.iHeight);
235 bool CWinSystemTVOS::SwitchToVideoMode(int width, int height, double refreshrate)
237 /*! @todo Currently support SDR dynamic range only. HDR shouldn't be done during
238 * a modeswitch. Look to create supplemental method to handle sdr/hdr enable
240 [g_xbmcController.displayManager displayRateSwitch:refreshrate
241 withDynamicRange:0 /*dynamicRange*/];
245 bool CWinSystemTVOS::GetScreenResolution(int* w, int* h, double* fps, int screenIdx)
247 *w = [g_xbmcController.displayManager getScreenSize].width;
248 *h = [g_xbmcController.displayManager getScreenSize].height;
249 *fps = static_cast<double>([g_xbmcController.displayManager getDisplayRate]);
251 CLog::Log(LOGDEBUG, "Current resolution Screen: {} with {} x {} @ {}", screenIdx, *w, *h, *fps);
255 void CWinSystemTVOS::UpdateResolutions()
257 // Add display resolution
260 CWinSystemBase::UpdateResolutions();
262 int screenIdx = GetDisplayIndexFromSettings();
264 //first screen goes into the current desktop mode
265 if (GetScreenResolution(&w, &h, &fps, screenIdx))
266 UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP),
267 CONST_HDMI, w, h, fps, 0);
269 CDisplaySettings::GetInstance().ClearCustomResolutions();
271 //now just fill in the possible resolutions for the attached screens
272 //and push to the resolution info vector
273 FillInVideoModes(screenIdx);
276 void CWinSystemTVOS::FillInVideoModes(int screenIdx)
278 // Potential refresh rates
279 std::vector<float> supportedDispRefreshRates = {23.976f, 24.000f, 25.000f, 29.970f,
280 30.000f, 50.000f, 59.940f, 60.000f};
282 UIScreen* aScreen = UIScreen.screens[screenIdx];
283 UIScreenMode* mode = aScreen.currentMode;
284 int w = mode.size.width;
285 int h = mode.size.height;
287 //! @Todo handle different resolutions than native (ie 720p/1080p on a 4k display)
289 for (float refreshrate : supportedDispRefreshRates)
292 UpdateDesktopResolution(res, CONST_HDMI, w, h, refreshrate, 0);
293 CLog::Log(LOGINFO, "Found possible resolution for display {} with {} x {} RefreshRate:{} ",
294 screenIdx, w, h, refreshrate);
296 CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res);
297 CDisplaySettings::GetInstance().AddResolutionInfo(res);
301 bool CWinSystemTVOS::IsExtSupported(const char* extension) const
303 if (strncmp(extension, "EGL_", 4) != 0)
304 return CRenderSystemGLES::IsExtSupported(extension);
306 std::string name = ' ' + std::string(extension) + ' ';
308 return m_eglext.find(name) != std::string::npos;
312 bool CWinSystemTVOS::BeginRender()
316 [g_xbmcController setFramebuffer];
318 rtn = CRenderSystemGLES::BeginRender();
322 bool CWinSystemTVOS::EndRender()
326 rtn = CRenderSystemGLES::EndRender();
330 void CWinSystemTVOS::Register(IDispResource* resource)
332 std::unique_lock<CCriticalSection> lock(m_resourceSection);
333 m_resources.push_back(resource);
336 void CWinSystemTVOS::Unregister(IDispResource* resource)
338 std::unique_lock<CCriticalSection> lock(m_resourceSection);
339 std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
340 if (i != m_resources.end())
341 m_resources.erase(i);
344 void CWinSystemTVOS::OnAppFocusChange(bool focus)
346 std::unique_lock<CCriticalSection> lock(m_resourceSection);
347 m_bIsBackgrounded = !focus;
348 CLog::Log(LOGDEBUG, "CWinSystemTVOS::OnAppFocusChange: {}", focus ? 1 : 0);
349 for (auto dispResource : m_resources)
350 dispResource->OnAppFocusChange(focus);
353 //--------------------------------------------------------------
354 //-------------------DisplayLink stuff
355 @implementation TVOSDisplayLinkCallback
356 @synthesize videoSyncImpl;
357 //--------------------------------------------------------------
358 - (void)runDisplayLink
362 if (videoSyncImpl != nullptr)
363 videoSyncImpl->TVosVblankHandler();
368 bool CWinSystemTVOS::InitDisplayLink(CVideoSyncTVos* syncImpl)
370 unsigned int currentScreenIdx = GetDisplayIndexFromSettings();
371 UIScreen* currentScreen = UIScreen.screens[currentScreenIdx];
372 m_pDisplayLink->callbackClass.videoSyncImpl = syncImpl;
373 m_pDisplayLink->impl = [currentScreen displayLinkWithTarget:m_pDisplayLink->callbackClass
374 selector:@selector(runDisplayLink)];
376 [m_pDisplayLink->impl addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
377 return m_pDisplayLink->impl != nil;
380 void CWinSystemTVOS::DeinitDisplayLink(void)
382 if (m_pDisplayLink->impl)
384 [m_pDisplayLink->impl invalidate];
385 m_pDisplayLink->impl = nil;
386 [m_pDisplayLink->callbackClass SetVideoSyncImpl:nil];
389 //------------DisplayLink stuff end
390 //--------------------------------------------------------------
392 void CWinSystemTVOS::PresentRenderImpl(bool rendered)
396 [g_xbmcController presentFramebuffer];
399 bool CWinSystemTVOS::HasCursor()
404 void CWinSystemTVOS::NotifyAppActiveChange(bool bActivated)
406 if (bActivated && m_bWasFullScreenBeforeMinimize &&
407 !CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot())
408 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
411 bool CWinSystemTVOS::Minimize()
413 m_bWasFullScreenBeforeMinimize =
414 CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot();
415 if (m_bWasFullScreenBeforeMinimize)
416 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
421 bool CWinSystemTVOS::Restore()
426 bool CWinSystemTVOS::Hide()
431 bool CWinSystemTVOS::Show(bool raise)
436 CVEAGLContext CWinSystemTVOS::GetEAGLContextObj()
438 return [g_xbmcController getEAGLContextObj];
441 std::vector<std::string> CWinSystemTVOS::GetConnectedOutputs()
443 std::vector<std::string> outputs;
444 outputs.emplace_back("Default");
445 outputs.emplace_back(CONST_HDMI);
450 bool CWinSystemTVOS::MessagePump()
452 return m_winEvents->MessagePump();