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 "WinSystemIOS.h"
11 #include "ServiceBroker.h"
12 #include "VideoSyncIos.h"
13 #include "WinEventsIOS.h"
14 #include "cores/AudioEngine/Sinks/AESinkDARWINIOS.h"
15 #include "cores/RetroPlayer/process/ios/RPProcessInfoIOS.h"
16 #include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.h"
17 #include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
18 #include "cores/VideoPlayer/DVDCodecs/Video/VTB.h"
19 #include "cores/VideoPlayer/Process/ios/ProcessInfoIOS.h"
20 #include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGLES.h"
21 #include "cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.h"
22 #include "cores/VideoPlayer/VideoRenderers/RenderFactory.h"
23 #include "filesystem/SpecialProtocol.h"
24 #include "guilib/DispResource.h"
25 #include "guilib/Texture.h"
26 #include "messaging/ApplicationMessenger.h"
27 #include "rendering/gles/ScreenshotSurfaceGLES.h"
28 #include "settings/DisplaySettings.h"
29 #include "settings/Settings.h"
30 #include "settings/SettingsComponent.h"
31 #include "utils/StringUtils.h"
32 #include "utils/log.h"
33 #include "windowing/GraphicContext.h"
34 #include "windowing/WindowSystemFactory.h"
36 #import "platform/darwin/ios/IOSScreenManager.h"
37 #import "platform/darwin/ios/XBMCController.h"
43 #import <Foundation/Foundation.h>
44 #import <OpenGLES/ES2/gl.h>
45 #import <OpenGLES/ES2/glext.h>
46 #import <QuartzCore/CADisplayLink.h>
49 #define CONST_TOUCHSCREEN "Touchscreen"
50 #define CONST_EXTERNAL "External"
52 // IOSDisplayLinkCallback is declared in the lower part of the file
53 @interface IOSDisplayLinkCallback : NSObject
55 @private CVideoSyncIos *_videoSyncImpl;
57 @property (nonatomic, setter=SetVideoSyncImpl:) CVideoSyncIos *_videoSyncImpl;
58 - (void) runDisplayLink;
62 using namespace MESSAGING;
64 struct CADisplayLinkWrapper
67 IOSDisplayLinkCallback *callbackClass;
70 void CWinSystemIOS::Register()
72 KODI::WINDOWING::CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem);
75 std::unique_ptr<CWinSystemBase> CWinSystemIOS::CreateWinSystem()
77 return std::make_unique<CWinSystemIOS>();
80 int CWinSystemIOS::GetDisplayIndexFromSettings()
82 std::string currentScreen = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
85 if (currentScreen == CONST_EXTERNAL)
87 if ([[UIScreen screens] count] > 1)
91 else// screen 1 is setup but not connected
93 // force internal screen
101 CWinSystemIOS::CWinSystemIOS() : CWinSystemBase()
103 m_bIsBackgrounded = false;
104 m_pDisplayLink = new CADisplayLinkWrapper;
105 m_pDisplayLink->callbackClass = [[IOSDisplayLinkCallback alloc] init];
106 m_winEvents = std::make_unique<CWinEventsIOS>();
108 CAESinkDARWINIOS::Register();
111 CWinSystemIOS::~CWinSystemIOS()
113 delete m_pDisplayLink;
116 bool CWinSystemIOS::InitWindowSystem()
118 return CWinSystemBase::InitWindowSystem();
121 bool CWinSystemIOS::DestroyWindowSystem()
126 bool CWinSystemIOS::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
128 //NSLog(@"%s", __PRETTY_FUNCTION__);
130 if(!SetFullScreen(fullScreen, res, false))
133 [g_xbmcController setFramebuffer];
135 m_bWindowCreated = true;
139 const char *tmpExtensions = (const char*) glGetString(GL_EXTENSIONS);
140 if (tmpExtensions != NULL)
142 m_eglext += tmpExtensions;
147 CLog::Log(LOGDEBUG, "EGL_EXTENSIONS:{}", m_eglext);
149 // register platform dependent objects
150 CDVDFactoryCodec::ClearHWAccels();
151 VTB::CDecoder::Register();
152 VIDEOPLAYER::CRendererFactory::ClearRenderer();
153 CLinuxRendererGLES::Register();
154 CRendererVTB::Register();
155 VIDEOPLAYER::CProcessInfoIOS::Register();
156 RETRO::CRPProcessInfoIOS::Register();
157 RETRO::CRPProcessInfoIOS::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGLES);
158 CScreenshotSurfaceGLES::Register();
163 bool CWinSystemIOS::DestroyWindow()
168 bool CWinSystemIOS::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
170 //NSLog(@"%s", __PRETTY_FUNCTION__);
172 if (m_nWidth != newWidth || m_nHeight != newHeight)
175 m_nHeight = newHeight;
178 CRenderSystemGLES::ResetRenderSystem(newWidth, newHeight);
183 bool CWinSystemIOS::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
185 //NSLog(@"%s", __PRETTY_FUNCTION__);
187 m_nWidth = res.iWidth;
188 m_nHeight = res.iHeight;
189 m_bFullScreen = fullScreen;
191 CLog::Log(LOGDEBUG, "About to switch to {} x {}", m_nWidth, m_nHeight);
192 SwitchToVideoMode(res.iWidth, res.iHeight, static_cast<double>(res.fRefreshRate));
193 CRenderSystemGLES::ResetRenderSystem(res.iWidth, res.iHeight);
198 UIScreenMode *getModeForResolution(int width, int height, unsigned int screenIdx)
200 auto screen = UIScreen.screens[screenIdx];
201 for (UIScreenMode* mode in screen.availableModes)
203 //for main screen also find modes where width and height are
204 //exchanged (because of the 90°degree rotated buildinscreens)
205 auto modeSize = mode.size;
206 if ((modeSize.width == width && modeSize.height == height) ||
207 (screenIdx == 0 && modeSize.width == height && modeSize.height == width))
209 CLog::Log(LOGDEBUG, "Found matching mode: {} x {}", modeSize.width, modeSize.height);
213 CLog::Log(LOGERROR,"No matching mode found!");
217 bool CWinSystemIOS::SwitchToVideoMode(int width, int height, double refreshrate)
220 int screenIdx = GetDisplayIndexFromSettings();
222 //get the mode to pass to the controller
223 UIScreenMode *newMode = getModeForResolution(width, height, screenIdx);
227 ret = [g_xbmcController changeScreen:screenIdx withMode:newMode];
232 bool CWinSystemIOS::GetScreenResolution(int* w, int* h, double* fps, int screenIdx)
234 UIScreen *screen = [[UIScreen screens] objectAtIndex:screenIdx];
235 CGSize screenSize = [screen currentMode].size;
236 *w = screenSize.width;
237 *h = screenSize.height;
240 //if current mode is 0x0 (happens with external screens which aren't active)
241 //then use the preferred mode
242 if(*h == 0 || *w ==0)
244 UIScreenMode *firstMode = [screen preferredMode];
245 *w = firstMode.size.width;
246 *h = firstMode.size.height;
249 // for mainscreen use the eagl bounds from xbmcController
250 // because mainscreen is might be 90° rotate dependend on
251 // the device and eagl gives the correct values in all cases.
254 // at very first start up we cache the internal screen resolution
255 // because when using external screens and need to go back
256 // to internal we are not able to determine the eagl bounds
257 // before we really switched back to internal
258 // but display settings ask for the internal resolution before
259 // switching. So we give the cached values back in that case.
260 if (m_internalTouchscreenResolutionWidth == -1 &&
261 m_internalTouchscreenResolutionHeight == -1)
263 m_internalTouchscreenResolutionWidth = [g_xbmcController getScreenSize].width;
264 m_internalTouchscreenResolutionHeight = [g_xbmcController getScreenSize].height;
267 *w = m_internalTouchscreenResolutionWidth;
268 *h = m_internalTouchscreenResolutionHeight;
270 CLog::Log(LOGDEBUG, "Current resolution Screen: {} with {} x {}", screenIdx, *w, *h);
274 void CWinSystemIOS::UpdateResolutions()
276 // Add display resolution
279 CWinSystemBase::UpdateResolutions();
281 int screenIdx = GetDisplayIndexFromSettings();
283 //first screen goes into the current desktop mode
284 if(GetScreenResolution(&w, &h, &fps, screenIdx))
285 UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), screenIdx == 0 ? CONST_TOUCHSCREEN : CONST_EXTERNAL, w, h, fps, 0);
287 CDisplaySettings::GetInstance().ClearCustomResolutions();
289 //now just fill in the possible resolutions for the attached screens
290 //and push to the resolution info vector
291 FillInVideoModes(screenIdx);
294 void CWinSystemIOS::FillInVideoModes(int screenIdx)
296 // Add full screen settings for additional monitors
299 // atm we don't get refreshrate info from iOS
300 // but this may change in the future. In that case
301 // we will adapt this code for filling some
302 // useful info into this local var :)
303 double refreshrate = 0.0;
304 //screen 0 is mainscreen - 1 has to be the external one...
305 UIScreen *aScreen = [[UIScreen screens]objectAtIndex:screenIdx];
306 //found external screen
307 for ( UIScreenMode *mode in [aScreen availableModes] )
310 h = mode.size.height;
312 UpdateDesktopResolution(res, screenIdx == 0 ? CONST_TOUCHSCREEN : CONST_EXTERNAL, w, h, refreshrate, 0);
313 CLog::Log(LOGINFO, "Found possible resolution for display {} with {} x {}", screenIdx, w, h);
315 CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res);
316 CDisplaySettings::GetInstance().AddResolutionInfo(res);
320 bool CWinSystemIOS::IsExtSupported(const char* extension) const
322 if(strncmp(extension, "EGL_", 4) != 0)
323 return CRenderSystemGLES::IsExtSupported(extension);
331 return m_eglext.find(name) != std::string::npos;
334 bool CWinSystemIOS::BeginRender()
338 [g_xbmcController setFramebuffer];
340 rtn = CRenderSystemGLES::BeginRender();
344 bool CWinSystemIOS::EndRender()
348 rtn = CRenderSystemGLES::EndRender();
352 void CWinSystemIOS::Register(IDispResource *resource)
354 std::unique_lock<CCriticalSection> lock(m_resourceSection);
355 m_resources.push_back(resource);
358 void CWinSystemIOS::Unregister(IDispResource* resource)
360 std::unique_lock<CCriticalSection> lock(m_resourceSection);
361 std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
362 if (i != m_resources.end())
363 m_resources.erase(i);
366 void CWinSystemIOS::OnAppFocusChange(bool focus)
368 std::unique_lock<CCriticalSection> lock(m_resourceSection);
369 m_bIsBackgrounded = !focus;
370 CLog::Log(LOGDEBUG, "CWinSystemIOS::OnAppFocusChange: {}", focus ? 1 : 0);
371 for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
372 (*i)->OnAppFocusChange(focus);
375 //--------------------------------------------------------------
376 //-------------------DisplayLink stuff
377 @implementation IOSDisplayLinkCallback
378 @synthesize _videoSyncImpl;
379 //--------------------------------------------------------------
380 - (void) runDisplayLink
384 if (_videoSyncImpl != nil)
386 _videoSyncImpl->IosVblankHandler();
392 bool CWinSystemIOS::InitDisplayLink(CVideoSyncIos *syncImpl)
394 //init with the appropriate display link for the
396 if([[IOSScreenManager sharedInstance] isExternalScreen])
398 fprintf(stderr,"InitDisplayLink on external");
402 fprintf(stderr,"InitDisplayLink on internal");
405 unsigned int currentScreenIdx = [[IOSScreenManager sharedInstance] GetScreenIdx];
406 UIScreen * currentScreen = [[UIScreen screens] objectAtIndex:currentScreenIdx];
407 [m_pDisplayLink->callbackClass SetVideoSyncImpl:syncImpl];
408 m_pDisplayLink->impl = [currentScreen displayLinkWithTarget:m_pDisplayLink->callbackClass selector:@selector(runDisplayLink)];
410 [m_pDisplayLink->impl setPreferredFramesPerSecond:0];
411 [m_pDisplayLink->impl addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
412 return m_pDisplayLink->impl != nil;
415 void CWinSystemIOS::DeinitDisplayLink(void)
417 if (m_pDisplayLink->impl)
419 [m_pDisplayLink->impl invalidate];
420 m_pDisplayLink->impl = nil;
421 [m_pDisplayLink->callbackClass SetVideoSyncImpl:nil];
424 //------------DisplayLink stuff end
425 //--------------------------------------------------------------
427 void CWinSystemIOS::PresentRenderImpl(bool rendered)
431 [g_xbmcController presentFramebuffer];
434 bool CWinSystemIOS::HasCursor()
436 // apple touch devices
440 void CWinSystemIOS::NotifyAppActiveChange(bool bActivated)
442 if (bActivated && m_bWasFullScreenBeforeMinimize && !CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot())
443 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
446 bool CWinSystemIOS::Minimize()
448 m_bWasFullScreenBeforeMinimize = CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot();
449 if (m_bWasFullScreenBeforeMinimize)
450 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
455 bool CWinSystemIOS::Restore()
460 bool CWinSystemIOS::Hide()
465 bool CWinSystemIOS::Show(bool raise)
470 CVEAGLContext CWinSystemIOS::GetEAGLContextObj()
472 return [g_xbmcController getEAGLContextObj];
475 std::vector<std::string> CWinSystemIOS::GetConnectedOutputs()
477 std::vector<std::string> outputs;
478 outputs.emplace_back("Default");
479 outputs.emplace_back(CONST_TOUCHSCREEN);
480 if ([[UIScreen screens] count] > 1)
482 outputs.emplace_back(CONST_EXTERNAL);
488 void CWinSystemIOS::MoveToTouchscreen()
490 CDisplaySettings::GetInstance().SetMonitor(CONST_TOUCHSCREEN);
493 std::unique_ptr<CVideoSync> CWinSystemIOS::GetVideoSync(CVideoReferenceClock* clock)
495 std::unique_ptr<CVideoSync> pVSync(new CVideoSyncIos(clock, *this));
499 bool CWinSystemIOS::MessagePump()
501 return m_winEvents->MessagePump();