[Test] Added tests for CUtil::SplitParams
[xbmc.git] / xbmc / windowing / ios / WinSystemIOS.mm
blob8935a342a671ff0e0c1294b8edf592b0759ac3e5
1 /*
2  *  Copyright (C) 2010-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
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"
39 #include <memory>
40 #include <mutex>
41 #include <vector>
43 #import <Foundation/Foundation.h>
44 #import <OpenGLES/ES2/gl.h>
45 #import <OpenGLES/ES2/glext.h>
46 #import <QuartzCore/CADisplayLink.h>
47 #import <dlfcn.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;
59 @end
61 using namespace KODI;
62 using namespace MESSAGING;
64 struct CADisplayLinkWrapper
66   CADisplayLink* impl;
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);
84   int screenIdx = 0;
85   if (currentScreen == CONST_EXTERNAL)
86   {
87     if ([[UIScreen screens] count] > 1)
88     {
89       screenIdx = 1;
90     }
91     else// screen 1 is setup but not connected
92     {
93       // force internal screen
94       MoveToTouchscreen();
95     }
96   }
98   return screenIdx;
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()
123   return true;
126 bool CWinSystemIOS::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
128   //NSLog(@"%s", __PRETTY_FUNCTION__);
130   if(!SetFullScreen(fullScreen, res, false))
131     return false;
133   [g_xbmcController setFramebuffer];
135   m_bWindowCreated = true;
137   m_eglext  = " ";
139   const char *tmpExtensions = (const char*) glGetString(GL_EXTENSIONS);
140   if (tmpExtensions != NULL)
141   {
142     m_eglext += tmpExtensions;
143   }
145   m_eglext += " ";
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();
160   return true;
163 bool CWinSystemIOS::DestroyWindow()
165   return true;
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)
173   {
174     m_nWidth  = newWidth;
175     m_nHeight = newHeight;
176   }
178   CRenderSystemGLES::ResetRenderSystem(newWidth, newHeight);
180   return true;
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);
195   return true;
198 UIScreenMode *getModeForResolution(int width, int height, unsigned int screenIdx)
200   auto screen = UIScreen.screens[screenIdx];
201   for (UIScreenMode* mode in screen.availableModes)
202   {
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))
208     {
209       CLog::Log(LOGDEBUG, "Found matching mode: {} x {}", modeSize.width, modeSize.height);
210       return mode;
211     }
212   }
213   CLog::Log(LOGERROR,"No matching mode found!");
214   return nil;
217 bool CWinSystemIOS::SwitchToVideoMode(int width, int height, double refreshrate)
219   bool ret = false;
220   int screenIdx = GetDisplayIndexFromSettings();
222   //get the mode to pass to the controller
223   UIScreenMode *newMode = getModeForResolution(width, height, screenIdx);
225   if(newMode)
226   {
227     ret = [g_xbmcController changeScreen:screenIdx withMode:newMode];
228   }
229   return ret;
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;
238   *fps = 0.0;
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)
243   {
244     UIScreenMode *firstMode = [screen preferredMode];
245     *w = firstMode.size.width;
246     *h = firstMode.size.height;
247   }
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.
252   if(screenIdx == 0)
253   {
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)
262     {
263       m_internalTouchscreenResolutionWidth = [g_xbmcController getScreenSize].width;
264       m_internalTouchscreenResolutionHeight = [g_xbmcController getScreenSize].height;
265     }
267     *w = m_internalTouchscreenResolutionWidth;
268     *h = m_internalTouchscreenResolutionHeight;
269   }
270   CLog::Log(LOGDEBUG, "Current resolution Screen: {} with {} x {}", screenIdx, *w, *h);
271   return true;
274 void CWinSystemIOS::UpdateResolutions()
276   // Add display resolution
277   int w, h;
278   double fps;
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
297   RESOLUTION_INFO res;
298   int w, h;
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] )
308   {
309     w = mode.size.width;
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);
317   }
320 bool CWinSystemIOS::IsExtSupported(const char* extension) const
322   if(strncmp(extension, "EGL_", 4) != 0)
323     return CRenderSystemGLES::IsExtSupported(extension);
325   std::string name;
327   name  = " ";
328   name += extension;
329   name += " ";
331   return m_eglext.find(name) != std::string::npos;
334 bool CWinSystemIOS::BeginRender()
336   bool rtn;
338   [g_xbmcController setFramebuffer];
340   rtn = CRenderSystemGLES::BeginRender();
341   return rtn;
344 bool CWinSystemIOS::EndRender()
346   bool rtn;
348   rtn = CRenderSystemGLES::EndRender();
349   return rtn;
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
382   @autoreleasepool
383   {
384     if (_videoSyncImpl != nil)
385     {
386       _videoSyncImpl->IosVblankHandler();
387     }
388   }
390 @end
392 bool CWinSystemIOS::InitDisplayLink(CVideoSyncIos *syncImpl)
394   //init with the appropriate display link for the
395   //used screen
396   if([[IOSScreenManager sharedInstance] isExternalScreen])
397   {
398     fprintf(stderr,"InitDisplayLink on external");
399   }
400   else
401   {
402     fprintf(stderr,"InitDisplayLink on internal");
403   }
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)
418   {
419     [m_pDisplayLink->impl invalidate];
420     m_pDisplayLink->impl = nil;
421     [m_pDisplayLink->callbackClass SetVideoSyncImpl:nil];
422   }
424 //------------DisplayLink stuff end
425 //--------------------------------------------------------------
427 void CWinSystemIOS::PresentRenderImpl(bool rendered)
429   //glFlush;
430   if (rendered)
431     [g_xbmcController presentFramebuffer];
434 bool CWinSystemIOS::HasCursor()
436   // apple touch devices
437   return false;
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);
452   return true;
455 bool CWinSystemIOS::Restore()
457   return false;
460 bool CWinSystemIOS::Hide()
462   return true;
465 bool CWinSystemIOS::Show(bool raise)
467   return true;
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)
481   {
482     outputs.emplace_back(CONST_EXTERNAL);
483   }
485   return outputs;
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));
496   return pVSync;
499 bool CWinSystemIOS::MessagePump()
501   return m_winEvents->MessagePump();