summaryrefslogtreecommitdiffstats
path: root/xbmc/windowing/ios/WinSystemIOS.mm
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/windowing/ios/WinSystemIOS.mm')
-rw-r--r--xbmc/windowing/ios/WinSystemIOS.mm501
1 files changed, 501 insertions, 0 deletions
diff --git a/xbmc/windowing/ios/WinSystemIOS.mm b/xbmc/windowing/ios/WinSystemIOS.mm
new file mode 100644
index 0000000..805c573
--- /dev/null
+++ b/xbmc/windowing/ios/WinSystemIOS.mm
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2010-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "WinSystemIOS.h"
+
+#include "ServiceBroker.h"
+#include "VideoSyncIos.h"
+#include "WinEventsIOS.h"
+#include "cores/AudioEngine/Sinks/AESinkDARWINIOS.h"
+#include "cores/RetroPlayer/process/ios/RPProcessInfoIOS.h"
+#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGLES.h"
+#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h"
+#include "cores/VideoPlayer/DVDCodecs/Video/VTB.h"
+#include "cores/VideoPlayer/Process/ios/ProcessInfoIOS.h"
+#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGLES.h"
+#include "cores/VideoPlayer/VideoRenderers/LinuxRendererGLES.h"
+#include "cores/VideoPlayer/VideoRenderers/RenderFactory.h"
+#include "filesystem/SpecialProtocol.h"
+#include "guilib/DispResource.h"
+#include "guilib/Texture.h"
+#include "messaging/ApplicationMessenger.h"
+#include "rendering/gles/ScreenshotSurfaceGLES.h"
+#include "settings/DisplaySettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/WindowSystemFactory.h"
+
+#import "platform/darwin/ios/IOSScreenManager.h"
+#import "platform/darwin/ios/XBMCController.h"
+
+#include <mutex>
+#include <vector>
+
+#import <Foundation/Foundation.h>
+#import <OpenGLES/ES2/gl.h>
+#import <OpenGLES/ES2/glext.h>
+#import <QuartzCore/CADisplayLink.h>
+#import <dlfcn.h>
+
+#define CONST_TOUCHSCREEN "Touchscreen"
+#define CONST_EXTERNAL "External"
+
+// IOSDisplayLinkCallback is declared in the lower part of the file
+@interface IOSDisplayLinkCallback : NSObject
+{
+@private CVideoSyncIos *_videoSyncImpl;
+}
+@property (nonatomic, setter=SetVideoSyncImpl:) CVideoSyncIos *_videoSyncImpl;
+- (void) runDisplayLink;
+@end
+
+using namespace KODI;
+using namespace MESSAGING;
+
+struct CADisplayLinkWrapper
+{
+ CADisplayLink* impl;
+ IOSDisplayLinkCallback *callbackClass;
+};
+
+void CWinSystemIOS::Register()
+{
+ KODI::WINDOWING::CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem);
+}
+
+std::unique_ptr<CWinSystemBase> CWinSystemIOS::CreateWinSystem()
+{
+ return std::make_unique<CWinSystemIOS>();
+}
+
+int CWinSystemIOS::GetDisplayIndexFromSettings()
+{
+ std::string currentScreen = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
+
+ int screenIdx = 0;
+ if (currentScreen == CONST_EXTERNAL)
+ {
+ if ([[UIScreen screens] count] > 1)
+ {
+ screenIdx = 1;
+ }
+ else// screen 1 is setup but not connected
+ {
+ // force internal screen
+ MoveToTouchscreen();
+ }
+ }
+
+ return screenIdx;
+}
+
+CWinSystemIOS::CWinSystemIOS() : CWinSystemBase()
+{
+ m_bIsBackgrounded = false;
+ m_pDisplayLink = new CADisplayLinkWrapper;
+ m_pDisplayLink->callbackClass = [[IOSDisplayLinkCallback alloc] init];
+ m_winEvents.reset(new CWinEventsIOS());
+
+ CAESinkDARWINIOS::Register();
+}
+
+CWinSystemIOS::~CWinSystemIOS()
+{
+ delete m_pDisplayLink;
+}
+
+bool CWinSystemIOS::InitWindowSystem()
+{
+ return CWinSystemBase::InitWindowSystem();
+}
+
+bool CWinSystemIOS::DestroyWindowSystem()
+{
+ return true;
+}
+
+bool CWinSystemIOS::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res)
+{
+ //NSLog(@"%s", __PRETTY_FUNCTION__);
+
+ if(!SetFullScreen(fullScreen, res, false))
+ return false;
+
+ [g_xbmcController setFramebuffer];
+
+ m_bWindowCreated = true;
+
+ m_eglext = " ";
+
+ const char *tmpExtensions = (const char*) glGetString(GL_EXTENSIONS);
+ if (tmpExtensions != NULL)
+ {
+ m_eglext += tmpExtensions;
+ }
+
+ m_eglext += " ";
+
+ CLog::Log(LOGDEBUG, "EGL_EXTENSIONS:{}", m_eglext);
+
+ // register platform dependent objects
+ CDVDFactoryCodec::ClearHWAccels();
+ VTB::CDecoder::Register();
+ VIDEOPLAYER::CRendererFactory::ClearRenderer();
+ CLinuxRendererGLES::Register();
+ CRendererVTB::Register();
+ VIDEOPLAYER::CProcessInfoIOS::Register();
+ RETRO::CRPProcessInfoIOS::Register();
+ RETRO::CRPProcessInfoIOS::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGLES);
+ CScreenshotSurfaceGLES::Register();
+
+ return true;
+}
+
+bool CWinSystemIOS::DestroyWindow()
+{
+ return true;
+}
+
+bool CWinSystemIOS::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
+{
+ //NSLog(@"%s", __PRETTY_FUNCTION__);
+
+ if (m_nWidth != newWidth || m_nHeight != newHeight)
+ {
+ m_nWidth = newWidth;
+ m_nHeight = newHeight;
+ }
+
+ CRenderSystemGLES::ResetRenderSystem(newWidth, newHeight);
+
+ return true;
+}
+
+bool CWinSystemIOS::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
+{
+ //NSLog(@"%s", __PRETTY_FUNCTION__);
+
+ m_nWidth = res.iWidth;
+ m_nHeight = res.iHeight;
+ m_bFullScreen = fullScreen;
+
+ CLog::Log(LOGDEBUG, "About to switch to {} x {}", m_nWidth, m_nHeight);
+ SwitchToVideoMode(res.iWidth, res.iHeight, static_cast<double>(res.fRefreshRate));
+ CRenderSystemGLES::ResetRenderSystem(res.iWidth, res.iHeight);
+
+ return true;
+}
+
+UIScreenMode *getModeForResolution(int width, int height, unsigned int screenIdx)
+{
+ auto screen = UIScreen.screens[screenIdx];
+ for (UIScreenMode* mode in screen.availableModes)
+ {
+ //for main screen also find modes where width and height are
+ //exchanged (because of the 90°degree rotated buildinscreens)
+ auto modeSize = mode.size;
+ if ((modeSize.width == width && modeSize.height == height) ||
+ (screenIdx == 0 && modeSize.width == height && modeSize.height == width))
+ {
+ CLog::Log(LOGDEBUG, "Found matching mode: {} x {}", modeSize.width, modeSize.height);
+ return mode;
+ }
+ }
+ CLog::Log(LOGERROR,"No matching mode found!");
+ return nil;
+}
+
+bool CWinSystemIOS::SwitchToVideoMode(int width, int height, double refreshrate)
+{
+ bool ret = false;
+ int screenIdx = GetDisplayIndexFromSettings();
+
+ //get the mode to pass to the controller
+ UIScreenMode *newMode = getModeForResolution(width, height, screenIdx);
+
+ if(newMode)
+ {
+ ret = [g_xbmcController changeScreen:screenIdx withMode:newMode];
+ }
+ return ret;
+}
+
+bool CWinSystemIOS::GetScreenResolution(int* w, int* h, double* fps, int screenIdx)
+{
+ UIScreen *screen = [[UIScreen screens] objectAtIndex:screenIdx];
+ CGSize screenSize = [screen currentMode].size;
+ *w = screenSize.width;
+ *h = screenSize.height;
+ *fps = 0.0;
+
+ //if current mode is 0x0 (happens with external screens which aren't active)
+ //then use the preferred mode
+ if(*h == 0 || *w ==0)
+ {
+ UIScreenMode *firstMode = [screen preferredMode];
+ *w = firstMode.size.width;
+ *h = firstMode.size.height;
+ }
+
+ // for mainscreen use the eagl bounds from xbmcController
+ // because mainscreen is might be 90° rotate dependend on
+ // the device and eagl gives the correct values in all cases.
+ if(screenIdx == 0)
+ {
+ // at very first start up we cache the internal screen resolution
+ // because when using external screens and need to go back
+ // to internal we are not able to determine the eagl bounds
+ // before we really switched back to internal
+ // but display settings ask for the internal resolution before
+ // switching. So we give the cached values back in that case.
+ if (m_internalTouchscreenResolutionWidth == -1 &&
+ m_internalTouchscreenResolutionHeight == -1)
+ {
+ m_internalTouchscreenResolutionWidth = [g_xbmcController getScreenSize].width;
+ m_internalTouchscreenResolutionHeight = [g_xbmcController getScreenSize].height;
+ }
+
+ *w = m_internalTouchscreenResolutionWidth;
+ *h = m_internalTouchscreenResolutionHeight;
+ }
+ CLog::Log(LOGDEBUG, "Current resolution Screen: {} with {} x {}", screenIdx, *w, *h);
+ return true;
+}
+
+void CWinSystemIOS::UpdateResolutions()
+{
+ // Add display resolution
+ int w, h;
+ double fps;
+ CWinSystemBase::UpdateResolutions();
+
+ int screenIdx = GetDisplayIndexFromSettings();
+
+ //first screen goes into the current desktop mode
+ if(GetScreenResolution(&w, &h, &fps, screenIdx))
+ UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), screenIdx == 0 ? CONST_TOUCHSCREEN : CONST_EXTERNAL, w, h, fps, 0);
+
+ CDisplaySettings::GetInstance().ClearCustomResolutions();
+
+ //now just fill in the possible resolutions for the attached screens
+ //and push to the resolution info vector
+ FillInVideoModes(screenIdx);
+}
+
+void CWinSystemIOS::FillInVideoModes(int screenIdx)
+{
+ // Add full screen settings for additional monitors
+ RESOLUTION_INFO res;
+ int w, h;
+ // atm we don't get refreshrate info from iOS
+ // but this may change in the future. In that case
+ // we will adapt this code for filling some
+ // useful info into this local var :)
+ double refreshrate = 0.0;
+ //screen 0 is mainscreen - 1 has to be the external one...
+ UIScreen *aScreen = [[UIScreen screens]objectAtIndex:screenIdx];
+ //found external screen
+ for ( UIScreenMode *mode in [aScreen availableModes] )
+ {
+ w = mode.size.width;
+ h = mode.size.height;
+
+ UpdateDesktopResolution(res, screenIdx == 0 ? CONST_TOUCHSCREEN : CONST_EXTERNAL, w, h, refreshrate, 0);
+ CLog::Log(LOGINFO, "Found possible resolution for display {} with {} x {}", screenIdx, w, h);
+
+ CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res);
+ CDisplaySettings::GetInstance().AddResolutionInfo(res);
+ }
+}
+
+bool CWinSystemIOS::IsExtSupported(const char* extension) const
+{
+ if(strncmp(extension, "EGL_", 4) != 0)
+ return CRenderSystemGLES::IsExtSupported(extension);
+
+ std::string name;
+
+ name = " ";
+ name += extension;
+ name += " ";
+
+ return m_eglext.find(name) != std::string::npos;
+}
+
+bool CWinSystemIOS::BeginRender()
+{
+ bool rtn;
+
+ [g_xbmcController setFramebuffer];
+
+ rtn = CRenderSystemGLES::BeginRender();
+ return rtn;
+}
+
+bool CWinSystemIOS::EndRender()
+{
+ bool rtn;
+
+ rtn = CRenderSystemGLES::EndRender();
+ return rtn;
+}
+
+void CWinSystemIOS::Register(IDispResource *resource)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ m_resources.push_back(resource);
+}
+
+void CWinSystemIOS::Unregister(IDispResource* resource)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
+ if (i != m_resources.end())
+ m_resources.erase(i);
+}
+
+void CWinSystemIOS::OnAppFocusChange(bool focus)
+{
+ std::unique_lock<CCriticalSection> lock(m_resourceSection);
+ m_bIsBackgrounded = !focus;
+ CLog::Log(LOGDEBUG, "CWinSystemIOS::OnAppFocusChange: {}", focus ? 1 : 0);
+ for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
+ (*i)->OnAppFocusChange(focus);
+}
+
+//--------------------------------------------------------------
+//-------------------DisplayLink stuff
+@implementation IOSDisplayLinkCallback
+@synthesize _videoSyncImpl;
+//--------------------------------------------------------------
+- (void) runDisplayLink
+{
+ @autoreleasepool
+ {
+ if (_videoSyncImpl != nil)
+ {
+ _videoSyncImpl->IosVblankHandler();
+ }
+ }
+}
+@end
+
+bool CWinSystemIOS::InitDisplayLink(CVideoSyncIos *syncImpl)
+{
+ //init with the appropriate display link for the
+ //used screen
+ if([[IOSScreenManager sharedInstance] isExternalScreen])
+ {
+ fprintf(stderr,"InitDisplayLink on external");
+ }
+ else
+ {
+ fprintf(stderr,"InitDisplayLink on internal");
+ }
+
+ unsigned int currentScreenIdx = [[IOSScreenManager sharedInstance] GetScreenIdx];
+ UIScreen * currentScreen = [[UIScreen screens] objectAtIndex:currentScreenIdx];
+ [m_pDisplayLink->callbackClass SetVideoSyncImpl:syncImpl];
+ m_pDisplayLink->impl = [currentScreen displayLinkWithTarget:m_pDisplayLink->callbackClass selector:@selector(runDisplayLink)];
+
+ [m_pDisplayLink->impl setPreferredFramesPerSecond:0];
+ [m_pDisplayLink->impl addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
+ return m_pDisplayLink->impl != nil;
+}
+
+void CWinSystemIOS::DeinitDisplayLink(void)
+{
+ if (m_pDisplayLink->impl)
+ {
+ [m_pDisplayLink->impl invalidate];
+ m_pDisplayLink->impl = nil;
+ [m_pDisplayLink->callbackClass SetVideoSyncImpl:nil];
+ }
+}
+//------------DisplayLink stuff end
+//--------------------------------------------------------------
+
+void CWinSystemIOS::PresentRenderImpl(bool rendered)
+{
+ //glFlush;
+ if (rendered)
+ [g_xbmcController presentFramebuffer];
+}
+
+bool CWinSystemIOS::HasCursor()
+{
+ // apple touch devices
+ return false;
+}
+
+void CWinSystemIOS::NotifyAppActiveChange(bool bActivated)
+{
+ if (bActivated && m_bWasFullScreenBeforeMinimize && !CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot())
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
+}
+
+bool CWinSystemIOS::Minimize()
+{
+ m_bWasFullScreenBeforeMinimize = CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenRoot();
+ if (m_bWasFullScreenBeforeMinimize)
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_TOGGLEFULLSCREEN);
+
+ return true;
+}
+
+bool CWinSystemIOS::Restore()
+{
+ return false;
+}
+
+bool CWinSystemIOS::Hide()
+{
+ return true;
+}
+
+bool CWinSystemIOS::Show(bool raise)
+{
+ return true;
+}
+
+CVEAGLContext CWinSystemIOS::GetEAGLContextObj()
+{
+ return [g_xbmcController getEAGLContextObj];
+}
+
+std::vector<std::string> CWinSystemIOS::GetConnectedOutputs()
+{
+ std::vector<std::string> outputs;
+ outputs.emplace_back("Default");
+ outputs.emplace_back(CONST_TOUCHSCREEN);
+ if ([[UIScreen screens] count] > 1)
+ {
+ outputs.emplace_back(CONST_EXTERNAL);
+ }
+
+ return outputs;
+}
+
+void CWinSystemIOS::MoveToTouchscreen()
+{
+ CDisplaySettings::GetInstance().SetMonitor(CONST_TOUCHSCREEN);
+}
+
+std::unique_ptr<CVideoSync> CWinSystemIOS::GetVideoSync(void *clock)
+{
+ std::unique_ptr<CVideoSync> pVSync(new CVideoSyncIos(clock, *this));
+ return pVSync;
+}
+
+bool CWinSystemIOS::MessagePump()
+{
+ return m_winEvents->MessagePump();
+}