From 70f9573b3ed0e3edd3fe1a16e95c71c62dd4520e Mon Sep 17 00:00:00 2001 From: fuzzard Date: Mon, 20 Sep 2021 18:19:26 +1000 Subject: [osx][windowing] relocate SDL specific code to an SDL folder relocates SDL implementations to a specific SDL folder --- cmake/treedata/osx/subdirs.txt | 1 + .../VideoRenderers/HwDecRender/RendererVTBGL.cpp | 4 +- xbmc/platform/darwin/osx/CocoaInterface.mm | 4 +- xbmc/settings/DisplaySettings.cpp | 4 +- xbmc/windowing/osx/CMakeLists.txt | 9 - xbmc/windowing/osx/SDL/CMakeLists.txt | 10 + xbmc/windowing/osx/SDL/WinEventsSDL.cpp | 244 +++ xbmc/windowing/osx/SDL/WinEventsSDL.h | 24 + xbmc/windowing/osx/SDL/WinSystemOSXSDL.h | 110 ++ xbmc/windowing/osx/SDL/WinSystemOSXSDL.mm | 1845 ++++++++++++++++++++ xbmc/windowing/osx/WinEventsOSX.h | 4 +- xbmc/windowing/osx/WinEventsSDL.cpp | 244 --- xbmc/windowing/osx/WinEventsSDL.h | 22 - xbmc/windowing/osx/WinSystemOSX.h | 110 -- xbmc/windowing/osx/WinSystemOSX.mm | 1845 -------------------- xbmc/windowing/osx/WinSystemOSXGL.h | 4 +- 16 files changed, 2248 insertions(+), 2236 deletions(-) create mode 100644 xbmc/windowing/osx/SDL/CMakeLists.txt create mode 100644 xbmc/windowing/osx/SDL/WinEventsSDL.cpp create mode 100644 xbmc/windowing/osx/SDL/WinEventsSDL.h create mode 100644 xbmc/windowing/osx/SDL/WinSystemOSXSDL.h create mode 100644 xbmc/windowing/osx/SDL/WinSystemOSXSDL.mm delete mode 100644 xbmc/windowing/osx/WinEventsSDL.cpp delete mode 100644 xbmc/windowing/osx/WinEventsSDL.h delete mode 100644 xbmc/windowing/osx/WinSystemOSX.h delete mode 100644 xbmc/windowing/osx/WinSystemOSX.mm diff --git a/cmake/treedata/osx/subdirs.txt b/cmake/treedata/osx/subdirs.txt index fea7db61a4..a13635d390 100644 --- a/cmake/treedata/osx/subdirs.txt +++ b/cmake/treedata/osx/subdirs.txt @@ -14,3 +14,4 @@ xbmc/platform/posix/filesystem platform/posix/filesystem xbmc/platform/posix/network platform/posix/network xbmc/platform/posix/utils platform/posix/utils xbmc/windowing/osx windowing/osx +xbmc/windowing/osx/SDL windowing/osx/SDL diff --git a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.cpp b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.cpp index d567c4e7f0..5802e61838 100644 --- a/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.cpp +++ b/xbmc/cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.cpp @@ -14,7 +14,9 @@ #include "cores/VideoPlayer/DVDCodecs/Video/VTB.h" #include "utils/log.h" #include "windowing/WinSystem.h" -#include "windowing/osx/WinSystemOSX.h" +#if defined(HAS_SDL) +#include "windowing/osx/SDL/WinSystemOSXSDL.h" +#endif #include "platform/darwin/osx/CocoaInterface.h" diff --git a/xbmc/platform/darwin/osx/CocoaInterface.mm b/xbmc/platform/darwin/osx/CocoaInterface.mm index bae2dfdeda..0da58c48b5 100644 --- a/xbmc/platform/darwin/osx/CocoaInterface.mm +++ b/xbmc/platform/darwin/osx/CocoaInterface.mm @@ -11,7 +11,9 @@ #import "DllPaths_generated.h" #include "ServiceBroker.h" #include "utils/log.h" -#include "windowing/osx/WinSystemOSX.h" +#if defined(HAS_SDL) +#include "windowing/osx/SDL/WinSystemOSXSDL.h" +#endif #import #import diff --git a/xbmc/settings/DisplaySettings.cpp b/xbmc/settings/DisplaySettings.cpp index 4c490caccf..47c80dae32 100644 --- a/xbmc/settings/DisplaySettings.cpp +++ b/xbmc/settings/DisplaySettings.cpp @@ -42,7 +42,9 @@ #include "windowing/X11/WinSystemX11.h" #elif defined(TARGET_DARWIN_OSX) #define WIN_SYSTEM_CLASS CWinSystemOSX -#include "windowing/osx/WinSystemOSX.h" +#if defined(HAS_SDL) +#include "windowing/osx/SDL/WinSystemOSXSDL.h" +#endif #elif defined(TARGET_ANDROID) #elif defined(TARGET_DARWIN_IOS) #define WIN_SYSTEM_CLASS CWinSystemIOS diff --git a/xbmc/windowing/osx/CMakeLists.txt b/xbmc/windowing/osx/CMakeLists.txt index 1cbf3dd95d..d90a5063aa 100644 --- a/xbmc/windowing/osx/CMakeLists.txt +++ b/xbmc/windowing/osx/CMakeLists.txt @@ -1,19 +1,10 @@ set(SOURCES CocoaDPMSSupport.cpp OSScreenSaverOSX.cpp - WinEventsOSX.mm - WinSystemOSX.mm VideoSyncOsx.mm) set(HEADERS CocoaDPMSSupport.h OSScreenSaverOSX.h - WinEventsOSX.h - WinSystemOSX.h VideoSyncOsx.h) -if(SDL_FOUND) - list(APPEND SOURCES WinEventsSDL.cpp) - list(APPEND HEADERS WinEventsSDL.h) -endif() - if(OPENGL_FOUND) list(APPEND SOURCES WinSystemOSXGL.mm) list(APPEND HEADERS WinSystemOSXGL.h) diff --git a/xbmc/windowing/osx/SDL/CMakeLists.txt b/xbmc/windowing/osx/SDL/CMakeLists.txt new file mode 100644 index 0000000000..33e25d8432 --- /dev/null +++ b/xbmc/windowing/osx/SDL/CMakeLists.txt @@ -0,0 +1,10 @@ +if(SDL_FOUND) + set(SOURCES WinEventsSDL.cpp + WinSystemOSXSDL.mm) + set(HEADERS WinEventsSDL.h + WinSystemOSXSDL.h) +endif() + +if(SOURCES) + core_add_library(windowing_osx_SDL) +endif() diff --git a/xbmc/windowing/osx/SDL/WinEventsSDL.cpp b/xbmc/windowing/osx/SDL/WinEventsSDL.cpp new file mode 100644 index 0000000000..1141e23e54 --- /dev/null +++ b/xbmc/windowing/osx/SDL/WinEventsSDL.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2005-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 "WinEventsSDL.h" + +#include "AppInboundProtocol.h" +#include "Application.h" +#include "GUIUserMessages.h" +#include "ServiceBroker.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "input/InputManager.h" +#include "input/Key.h" +#include "input/mouse/MouseStat.h" +#include "messaging/ApplicationMessenger.h" +#include "settings/DisplaySettings.h" +#include "windowing/WinSystem.h" + +#include "platform/darwin/osx/CocoaInterface.h" + +using namespace KODI::MESSAGING; + +bool CWinEventsOSX::MessagePump() +{ + SDL_Event event; + bool ret = false; + + while (SDL_PollEvent(&event)) + { + switch(event.type) + { + case SDL_QUIT: + if (!g_application.m_bStop) + CApplicationMessenger::GetInstance().PostMsg(TMSG_QUIT); + break; + + case SDL_ACTIVEEVENT: + //If the window was inconified or restored + if( event.active.state & SDL_APPACTIVE ) + { + std::shared_ptr appPort = CServiceBroker::GetAppPort(); + if (appPort) + appPort->SetRenderGUI(event.active.gain != 0); + CServiceBroker::GetWinSystem()->NotifyAppActiveChange(g_application.GetRenderGUI()); + } + else if (event.active.state & SDL_APPINPUTFOCUS) + { + g_application.m_AppFocused = event.active.gain != 0; + std::shared_ptr appPort = CServiceBroker::GetAppPort(); + if (appPort && g_application.m_AppFocused) + appPort->SetRenderGUI(g_application.m_AppFocused); + CServiceBroker::GetWinSystem()->NotifyAppFocusChange(g_application.m_AppFocused); + } + break; + + case SDL_KEYDOWN: + { + // process any platform specific shortcuts before handing off to XBMC + if (ProcessOSXShortcuts(event)) + { + ret = true; + break; + } + + XBMC_Event newEvent = {}; + newEvent.type = XBMC_KEYDOWN; + newEvent.key.keysym.scancode = event.key.keysym.scancode; + newEvent.key.keysym.sym = (XBMCKey) event.key.keysym.sym; + newEvent.key.keysym.unicode = event.key.keysym.unicode; + + // Check if the Windows keys are down because SDL doesn't flag this. + uint16_t mod = event.key.keysym.mod; + uint8_t* keystate = SDL_GetKeyState(NULL); + if (keystate[SDLK_LSUPER] || keystate[SDLK_RSUPER]) + mod |= XBMCKMOD_LSUPER; + newEvent.key.keysym.mod = (XBMCMod) mod; + + // don't handle any more messages in the queue until we've handled keydown, + // if a keyup is in the queue it will reset the keypress before it is handled. + std::shared_ptr appPort = CServiceBroker::GetAppPort(); + if (appPort) + ret |= appPort->OnEvent(newEvent); + break; + } + + case SDL_KEYUP: + { + XBMC_Event newEvent = {}; + newEvent.type = XBMC_KEYUP; + newEvent.key.keysym.scancode = event.key.keysym.scancode; + newEvent.key.keysym.sym = (XBMCKey) event.key.keysym.sym; + newEvent.key.keysym.mod =(XBMCMod) event.key.keysym.mod; + newEvent.key.keysym.unicode = event.key.keysym.unicode; + + std::shared_ptr appPort = CServiceBroker::GetAppPort(); + if (appPort) + ret |= appPort->OnEvent(newEvent); + break; + } + + case SDL_MOUSEBUTTONDOWN: + { + XBMC_Event newEvent = {}; + newEvent.type = XBMC_MOUSEBUTTONDOWN; + newEvent.button.button = event.button.button; + newEvent.button.x = event.button.x; + newEvent.button.y = event.button.y; + + std::shared_ptr appPort = CServiceBroker::GetAppPort(); + if (appPort) + ret |= appPort->OnEvent(newEvent); + break; + } + + case SDL_MOUSEBUTTONUP: + { + XBMC_Event newEvent = {}; + newEvent.type = XBMC_MOUSEBUTTONUP; + newEvent.button.button = event.button.button; + newEvent.button.x = event.button.x; + newEvent.button.y = event.button.y; + + std::shared_ptr appPort = CServiceBroker::GetAppPort(); + if (appPort) + ret |= appPort->OnEvent(newEvent); + break; + } + + case SDL_MOUSEMOTION: + { + if (0 == (SDL_GetAppState() & SDL_APPMOUSEFOCUS)) + { + CServiceBroker::GetInputManager().SetMouseActive(false); + // See CApplication::ProcessSlow() for a description as to why we call Cocoa_HideMouse. + // this is here to restore the pointer when toggling back to window mode from fullscreen. + Cocoa_ShowMouse(); + break; + } + XBMC_Event newEvent = {}; + newEvent.type = XBMC_MOUSEMOTION; + newEvent.motion.x = event.motion.x; + newEvent.motion.y = event.motion.y; + + std::shared_ptr appPort = CServiceBroker::GetAppPort(); + if (appPort) + ret |= appPort->OnEvent(newEvent); + break; + } + case SDL_VIDEORESIZE: + { + // Under newer osx versions sdl is so fucked up that it even fires resize events + // that exceed the screen size (maybe some HiDP incompatibility in old SDL?) + // ensure to ignore those events because it will mess with windowed size + if((event.resize.w > CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iWidth) || + (event.resize.h > CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iHeight)) + { + break; + } + XBMC_Event newEvent = {}; + newEvent.type = XBMC_VIDEORESIZE; + newEvent.resize.w = event.resize.w; + newEvent.resize.h = event.resize.h; + std::shared_ptr appPort = CServiceBroker::GetAppPort(); + if (appPort) + ret |= appPort->OnEvent(newEvent); + CServiceBroker::GetGUI()->GetWindowManager().MarkDirty(); + break; + } + case SDL_USEREVENT: + { + XBMC_Event newEvent = {}; + newEvent.type = XBMC_USEREVENT; + newEvent.user.code = event.user.code; + std::shared_ptr appPort = CServiceBroker::GetAppPort(); + if (appPort) + ret |= appPort->OnEvent(newEvent); + break; + } + case SDL_VIDEOEXPOSE: + CServiceBroker::GetGUI()->GetWindowManager().MarkDirty(); + break; + } + memset(&event, 0, sizeof(SDL_Event)); + } + + return ret; +} + +bool CWinEventsOSX::ProcessOSXShortcuts(SDL_Event& event) +{ + static bool shift = false, cmd = false; + + cmd = !!(SDL_GetModState() & (KMOD_LMETA | KMOD_RMETA )); + shift = !!(SDL_GetModState() & (KMOD_LSHIFT | KMOD_RSHIFT)); + + if (cmd && event.key.type == SDL_KEYDOWN) + { + char keysymbol = event.key.keysym.sym; + + // if the unicode is in the ascii range + // use this instead for getting the real + // character based on the used keyboard layout + // see http://lists.libsdl.org/pipermail/sdl-libsdl.org/2004-May/043716.html + bool isControl = (event.key.keysym.mod & KMOD_CTRL) != 0; + if (!isControl && !(event.key.keysym.unicode & 0xff80)) + keysymbol = event.key.keysym.unicode; + + switch(keysymbol) + { + case SDLK_q: // CMD-q to quit + if (!g_application.m_bStop) + CApplicationMessenger::GetInstance().PostMsg(TMSG_QUIT); + return true; + + case SDLK_f: // CMD-Ctrl-f to toggle fullscreen + if (!isControl) + return false; + g_application.OnAction(CAction(ACTION_TOGGLE_FULLSCREEN)); + return true; + + case SDLK_s: // CMD-3 to take a screenshot + g_application.OnAction(CAction(ACTION_TAKE_SCREENSHOT)); + return true; + + case SDLK_h: // CMD-h to hide + CServiceBroker::GetWinSystem()->Hide(); + return true; + + case SDLK_m: // CMD-m to minimize + CApplicationMessenger::GetInstance().PostMsg(TMSG_MINIMIZE); + return true; + + default: + return false; + } + } + + return false; +} diff --git a/xbmc/windowing/osx/SDL/WinEventsSDL.h b/xbmc/windowing/osx/SDL/WinEventsSDL.h new file mode 100644 index 0000000000..06e6325b50 --- /dev/null +++ b/xbmc/windowing/osx/SDL/WinEventsSDL.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2005-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. + */ + +#pragma once + +#include "windowing/WinEvents.h" + +#include + +class CWinEventsOSX : public IWinEvents +{ +public: + CWinEventsOSX() = default; + ~CWinEventsOSX() override = default; + bool MessagePump() override; + +private: + static bool ProcessOSXShortcuts(SDL_Event& event); +}; diff --git a/xbmc/windowing/osx/SDL/WinSystemOSXSDL.h b/xbmc/windowing/osx/SDL/WinSystemOSXSDL.h new file mode 100644 index 0000000000..4622c7d9f5 --- /dev/null +++ b/xbmc/windowing/osx/SDL/WinSystemOSXSDL.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2005-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. + */ + +#pragma once + +#include "threads/CriticalSection.h" +#include "threads/SystemClock.h" +#include "threads/Timer.h" +#include "windowing/WinSystem.h" + +#include +#include +#include + +typedef struct SDL_Surface SDL_Surface; + +class IDispResource; +class CWinEventsOSX; +class CWinSystemOSXImpl; +#ifdef __OBJC__ +@class NSOpenGLContext; +#endif + +class CWinSystemOSX : public CWinSystemBase, public ITimerCallback +{ +public: + + CWinSystemOSX(); + ~CWinSystemOSX() override; + + // ITimerCallback interface + void OnTimeout() override; + + // CWinSystemBase + bool InitWindowSystem() override; + bool DestroyWindowSystem() override; + bool CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) override; + bool DestroyWindow() override; + bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) override; + bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override; + void UpdateResolutions() override; + void NotifyAppFocusChange(bool bGaining) override; + void ShowOSMouse(bool show) override; + bool Minimize() override; + bool Restore() override; + bool Hide() override; + bool Show(bool raise = true) override; + void OnMove(int x, int y) override; + + std::string GetClipboardText() override; + + void Register(IDispResource *resource) override; + void Unregister(IDispResource *resource) override; + + std::unique_ptr GetVideoSync(void* clock) override; + + void WindowChangedScreen(); + + void AnnounceOnLostDevice(); + void AnnounceOnResetDevice(); + void HandleOnResetDevice(); + void StartLostDeviceTimer(); + void StopLostDeviceTimer(); + + void* GetCGLContextObj(); +#ifdef __OBJC__ + NSOpenGLContext* GetNSOpenGLContext(); +#else + void* GetNSOpenGLContext(); +#endif + void GetConnectedOutputs(std::vector *outputs); + + // winevents override + bool MessagePump() override; + +protected: + std::unique_ptr GetOSScreenSaverImpl() override; + + void HandlePossibleRefreshrateChange(); + void GetScreenResolution(int* w, int* h, double* fps, int screenIdx); + void EnableVSync(bool enable); + bool SwitchToVideoMode(int width, int height, double refreshrate); + void FillInVideoModes(); + bool FlushBuffer(void); + bool IsObscured(void); + void StartTextInput(); + void StopTextInput(); + + std::unique_ptr m_impl; + SDL_Surface* m_SDLSurface; + CWinEventsOSX *m_osx_events; + bool m_obscured; + std::chrono::time_point m_obscured_timecheck; + + bool m_movedToOtherScreen; + int m_lastDisplayNr; + double m_refreshRate; + + CCriticalSection m_resourceSection; + std::vector m_resources; + CTimer m_lostDeviceTimer; + bool m_delayDispReset; + XbmcThreads::EndTime m_dispResetTimer; + int m_updateGLContext = 0; +}; diff --git a/xbmc/windowing/osx/SDL/WinSystemOSXSDL.mm b/xbmc/windowing/osx/SDL/WinSystemOSXSDL.mm new file mode 100644 index 0000000000..c304ac9a9b --- /dev/null +++ b/xbmc/windowing/osx/SDL/WinSystemOSXSDL.mm @@ -0,0 +1,1845 @@ +/* + * Copyright (C) 2005-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 "WinSystemOSXSDL.h" + +#include "AppInboundProtocol.h" +#include "CompileInfo.h" +#include "ServiceBroker.h" +#include "cores/AudioEngine/AESinkFactory.h" +#include "cores/AudioEngine/Sinks/AESinkDARWINOSX.h" +#include "cores/RetroPlayer/process/osx/RPProcessInfoOSX.h" +#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGL.h" +#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h" +#include "cores/VideoPlayer/DVDCodecs/Video/VTB.h" +#include "cores/VideoPlayer/Process/osx/ProcessInfoOSX.h" +#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.h" +#include "cores/VideoPlayer/VideoRenderers/LinuxRendererGL.h" +#include "cores/VideoPlayer/VideoRenderers/RenderFactory.h" +#include "guilib/DispResource.h" +#include "guilib/GUIWindowManager.h" +#include "input/KeyboardStat.h" +#include "messaging/ApplicationMessenger.h" +#include "rendering/gl/ScreenshotSurfaceGL.h" +#include "settings/DisplaySettings.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "threads/SingleLock.h" +#include "utils/StringUtils.h" +#include "utils/SystemInfo.h" +#include "utils/log.h" +#include "windowing/osx/CocoaDPMSSupport.h" +#include "windowing/osx/OSScreenSaverOSX.h" +#include "windowing/osx/SDL/WinEventsSDL.h" +#include "windowing/osx/VideoSyncOsx.h" + +#include "platform/darwin/DarwinUtils.h" +#include "platform/darwin/DictionaryUtils.h" +#include "platform/darwin/osx/CocoaInterface.h" +#import "platform/darwin/osx/OSXTextInputResponder.h" +#include "platform/darwin/osx/XBMCHelper.h" + +#include +#include + +#import +#import +#import +#import +#import + +// turn off deprecated warning spew. +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +using namespace KODI; +using namespace MESSAGING; +using namespace WINDOWING; +using namespace std::chrono_literals; + +//------------------------------------------------------------------------------------------ +// special object-c class for handling the NSWindowDidMoveNotification callback. +@interface windowDidMoveNoteClass : NSObject +{ + void *m_userdata; +} ++ (windowDidMoveNoteClass*) initWith: (void*) userdata; +- (void) windowDidMoveNotification:(NSNotification*) note; +@end + +@implementation windowDidMoveNoteClass ++ (windowDidMoveNoteClass*) initWith: (void*) userdata +{ + windowDidMoveNoteClass *windowDidMove = [windowDidMoveNoteClass new]; + windowDidMove->m_userdata = userdata; + return windowDidMove; +} +- (void) windowDidMoveNotification:(NSNotification*) note +{ + CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata; + if (!winsys) + return; + + NSOpenGLContext* context = [NSOpenGLContext currentContext]; + if (context) + { + if ([context view]) + { + NSPoint window_origin = [[[context view] window] frame].origin; + XBMC_Event newEvent = {}; + newEvent.type = XBMC_VIDEOMOVE; + newEvent.move.x = window_origin.x; + newEvent.move.y = window_origin.y; + std::shared_ptr appPort = CServiceBroker::GetAppPort(); + if (appPort) + appPort->OnEvent(newEvent); + } + } +} +@end +//------------------------------------------------------------------------------------------ +// special object-c class for handling the NSWindowDidReSizeNotification callback. +@interface windowDidReSizeNoteClass : NSObject +{ + void *m_userdata; +} ++ (windowDidReSizeNoteClass*) initWith: (void*) userdata; +- (void) windowDidReSizeNotification:(NSNotification*) note; +@end +@implementation windowDidReSizeNoteClass ++ (windowDidReSizeNoteClass*) initWith: (void*) userdata +{ + windowDidReSizeNoteClass *windowDidReSize = [windowDidReSizeNoteClass new]; + windowDidReSize->m_userdata = userdata; + return windowDidReSize; +} +- (void) windowDidReSizeNotification:(NSNotification*) note +{ + CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata; + if (!winsys) + return; + +} +@end + +//------------------------------------------------------------------------------------------ +// special object-c class for handling the NSWindowDidChangeScreenNotification callback. +@interface windowDidChangeScreenNoteClass : NSObject +{ + void *m_userdata; +} ++ (windowDidChangeScreenNoteClass*) initWith: (void*) userdata; +- (void) windowDidChangeScreenNotification:(NSNotification*) note; +@end +@implementation windowDidChangeScreenNoteClass ++ (windowDidChangeScreenNoteClass*) initWith: (void*) userdata +{ + windowDidChangeScreenNoteClass *windowDidChangeScreen = [windowDidChangeScreenNoteClass new]; + windowDidChangeScreen->m_userdata = userdata; + return windowDidChangeScreen; +} +- (void) windowDidChangeScreenNotification:(NSNotification*) note +{ + CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata; + if (!winsys) + return; + winsys->WindowChangedScreen(); +} +@end +//------------------------------------------------------------------------------------------ + +class CWinSystemOSXImpl +{ +public: + NSOpenGLContext* m_glContext; + static NSOpenGLContext* m_lastOwnedContext; + + windowDidMoveNoteClass* m_windowDidMove; + windowDidReSizeNoteClass* m_windowDidReSize; + windowDidChangeScreenNoteClass* m_windowChangedScreen; +}; + +NSOpenGLContext* CWinSystemOSXImpl::m_lastOwnedContext = nil; + + +#define MAX_DISPLAYS 32 +// if there was a devicelost callback +// but no device reset for 3 secs +// a timeout fires the reset callback +// (for ensuring that e.x. AE isn't stuck) +constexpr auto LOST_DEVICE_TIMEOUT_MS = 3000ms; +static NSWindow* blankingWindows[MAX_DISPLAYS]; + +//------------------------------------------------------------------------------------------ +CRect CGRectToCRect(CGRect cgrect) +{ + CRect crect = CRect( + cgrect.origin.x, + cgrect.origin.y, + cgrect.origin.x + cgrect.size.width, + cgrect.origin.y + cgrect.size.height); + return crect; +} +//--------------------------------------------------------------------------------- +void SetMenuBarVisible(bool visible) +{ + if(visible) + { + [[NSApplication sharedApplication] + setPresentationOptions: NSApplicationPresentationDefault]; + } + else + { + [[NSApplication sharedApplication] + setPresentationOptions: NSApplicationPresentationHideMenuBar | + NSApplicationPresentationHideDock]; + } +} +//--------------------------------------------------------------------------------- +CGDirectDisplayID GetDisplayID(int screen_index) +{ + CGDirectDisplayID displayArray[MAX_DISPLAYS]; + CGDisplayCount numDisplays; + + // Get the list of displays. + CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays); + if (screen_index >= 0 && screen_index < numDisplays) + return(displayArray[screen_index]); + else + return(displayArray[0]); +} + +size_t DisplayBitsPerPixelForMode(CGDisplayModeRef mode) +{ + size_t bitsPerPixel = 0; + + CFStringRef pixEnc = CGDisplayModeCopyPixelEncoding(mode); + if(CFStringCompare(pixEnc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + { + bitsPerPixel = 32; + } + else if(CFStringCompare(pixEnc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + { + bitsPerPixel = 16; + } + else if(CFStringCompare(pixEnc, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) + { + bitsPerPixel = 8; + } + + CFRelease(pixEnc); + + return bitsPerPixel; +} + +CFArrayRef GetAllDisplayModes(CGDirectDisplayID display) +{ + int value = 1; + + CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value); + if (!number) + { + CLog::Log(LOGERROR, "GetAllDisplayModes - could not create Number!"); + return NULL; + } + + CFStringRef key = kCGDisplayShowDuplicateLowResolutionModes; + CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&key, (const void **)&number, 1, NULL, NULL); + CFRelease(number); + + if (!options) + { + CLog::Log(LOGERROR, "GetAllDisplayModes - could not create Dictionary!"); + return NULL; + } + + CFArrayRef displayModes = CGDisplayCopyAllDisplayModes(display, options); + CFRelease(options); + + if (!displayModes) + { + CLog::Log(LOGERROR, "GetAllDisplayModes - no displaymodes found!"); + return NULL; + } + + return displayModes; +} + +// mimic former behavior of deprecated CGDisplayBestModeForParameters +CGDisplayModeRef BestMatchForMode(CGDirectDisplayID display, size_t bitsPerPixel, size_t width, size_t height, boolean_t &match) +{ + + // Get a copy of the current display mode + CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(display); + + // Loop through all display modes to determine the closest match. + // CGDisplayBestModeForParameters is deprecated on 10.6 so we will emulate it's behavior + // Try to find a mode with the requested depth and equal or greater dimensions first. + // If no match is found, try to find a mode with greater depth and same or greater dimensions. + // If still no match is found, just use the current mode. + CFArrayRef allModes = GetAllDisplayModes(display); + + for(int i = 0; i < CFArrayGetCount(allModes); i++) { + CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i); + + if(DisplayBitsPerPixelForMode(mode) != bitsPerPixel) + continue; + + if((CGDisplayModeGetWidth(mode) == width) && (CGDisplayModeGetHeight(mode) == height)) + { + CGDisplayModeRelease(displayMode); // release the copy we got before ... + displayMode = mode; + match = true; + break; + } + } + + // No depth match was found + if(!match) + { + for(int i = 0; i < CFArrayGetCount(allModes); i++) + { + CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i); + if(DisplayBitsPerPixelForMode(mode) >= bitsPerPixel) + continue; + + if((CGDisplayModeGetWidth(mode) == width) && (CGDisplayModeGetHeight(mode) == height)) + { + displayMode = mode; + match = true; + break; + } + } + } + + CFRelease(allModes); + + return displayMode; +} + +CGDirectDisplayID GetDisplayIDFromScreen(NSScreen *screen) +{ + NSDictionary* screenInfo = [screen deviceDescription]; + NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"]; + + return (CGDirectDisplayID)[screenID longValue]; +} + +int GetDisplayIndex(CGDirectDisplayID display) +{ + CGDirectDisplayID displayArray[MAX_DISPLAYS]; + CGDisplayCount numDisplays; + + // Get the list of displays. + CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays); + while (numDisplays > 0) + { + if (display == displayArray[--numDisplays]) + return numDisplays; + } + return -1; +} + +void BlankOtherDisplays(int screen_index) +{ + int i; + int numDisplays = [[NSScreen screens] count]; + + // zero out blankingWindows for debugging + for (i=0; i 0) + { + screenName = [localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]]; + } + } + + if (screenName == nil) + { + screenName = [[NSString alloc] initWithFormat:@"%i", displayID]; + } + else + { + // ensure screen name is unique by appending displayid + screenName = [screenName stringByAppendingFormat:@" (%@)", [@(displayID) stringValue]]; + } + + return screenName; +} + +int GetDisplayIndex(const std::string& dispName) +{ + int ret = 0; + + // Add full screen settings for additional monitors + int numDisplays = [[NSScreen screens] count]; + + for (int disp = 0; disp < numDisplays; disp++) + { + NSString *name = screenNameForDisplay(GetDisplayID(disp)); + if ([name UTF8String] == dispName) + { + ret = disp; + break; + } + } + + return ret; +} + +void ShowHideNSWindow(NSWindow *wind, bool show) +{ + if (show) + [wind orderFront:nil]; + else + [wind orderOut:nil]; +} + +static NSWindow *curtainWindow; +void fadeInDisplay(NSScreen *theScreen, double fadeTime) +{ + int fadeSteps = 100; + double fadeInterval = (fadeTime / (double) fadeSteps); + + if (curtainWindow != nil) + { + for (int step = 0; step < fadeSteps; step++) + { + double fade = 1.0 - (step * fadeInterval); + [curtainWindow setAlphaValue:fade]; + + NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval]; + [[NSRunLoop currentRunLoop] runUntilDate:nextDate]; + } + } + [curtainWindow close]; + curtainWindow = nil; + + [NSCursor unhide]; +} + +void fadeOutDisplay(NSScreen *theScreen, double fadeTime) +{ + int fadeSteps = 100; + double fadeInterval = (fadeTime / (double) fadeSteps); + + [NSCursor hide]; + + curtainWindow = [[NSWindow alloc] + initWithContentRect:[theScreen frame] + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:YES + screen:theScreen]; + + [curtainWindow setAlphaValue:0.0]; + [curtainWindow setBackgroundColor:[NSColor blackColor]]; + [curtainWindow setLevel:NSScreenSaverWindowLevel]; + + [curtainWindow makeKeyAndOrderFront:nil]; + [curtainWindow setFrame:[curtainWindow + frameRectForContentRect:[theScreen frame]] + display:YES + animate:NO]; + + for (int step = 0; step < fadeSteps; step++) + { + double fade = step * fadeInterval; + [curtainWindow setAlphaValue:fade]; + + NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval]; + [[NSRunLoop currentRunLoop] runUntilDate:nextDate]; + } +} + +// try to find mode that matches the desired size, refreshrate +// non interlaced, nonstretched, safe for hardware +CGDisplayModeRef GetMode(int width, int height, double refreshrate, int screenIdx) +{ + if ( screenIdx >= (signed)[[NSScreen screens] count]) + return NULL; + + Boolean stretched; + Boolean interlaced; + Boolean safeForHardware; + Boolean televisionoutput; + int w, h, bitsperpixel; + double rate; + RESOLUTION_INFO res; + + CLog::Log(LOGDEBUG, "GetMode looking for suitable mode with {} x {} @ {:f} Hz on display {}", + width, height, refreshrate, screenIdx); + + CFArrayRef displayModes = GetAllDisplayModes(GetDisplayID(screenIdx)); + + if (!displayModes) + return NULL; + + for (int i=0; i < CFArrayGetCount(displayModes); ++i) + { + CGDisplayModeRef displayMode = (CGDisplayModeRef)CFArrayGetValueAtIndex(displayModes, i); + uint32_t flags = CGDisplayModeGetIOFlags(displayMode); + stretched = flags & kDisplayModeStretchedFlag ? true : false; + interlaced = flags & kDisplayModeInterlacedFlag ? true : false; + bitsperpixel = DisplayBitsPerPixelForMode(displayMode); + safeForHardware = flags & kDisplayModeSafetyFlags ? true : false; + televisionoutput = flags & kDisplayModeTelevisionFlag ? true : false; + w = CGDisplayModeGetWidth(displayMode); + h = CGDisplayModeGetHeight(displayMode); + rate = CGDisplayModeGetRefreshRate(displayMode); + + + if ((bitsperpixel == 32) && + (safeForHardware == YES) && + (stretched == NO) && + (interlaced == NO) && + (w == width) && + (h == height) && + (rate == refreshrate || rate == 0)) + { + CLog::Log(LOGDEBUG, "GetMode found a match!"); + return displayMode; + } + } + + CFRelease(displayModes); + CLog::Log(LOGERROR, "GetMode - no match found!"); + return NULL; +} + +//--------------------------------------------------------------------------------- +static void DisplayReconfigured(CGDirectDisplayID display, + CGDisplayChangeSummaryFlags flags, void* userData) +{ + CWinSystemOSX *winsys = (CWinSystemOSX*)userData; + if (!winsys) + return; + + CLog::Log(LOGDEBUG, "CWinSystemOSX::DisplayReconfigured with flags {}", flags); + + // we fire the callbacks on start of configuration + // or when the mode set was finished + // or when we are called with flags == 0 (which is undocumented but seems to happen + // on some macs - we treat it as device reset) + + // first check if we need to call OnLostDevice + if (flags & kCGDisplayBeginConfigurationFlag) + { + // pre/post-reconfiguration changes + RESOLUTION res = CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution(); + if (res == RES_INVALID) + return; + + NSScreen* pScreen = nil; + unsigned int screenIdx = 0; + + if ( screenIdx < [[NSScreen screens] count] ) + { + pScreen = [[NSScreen screens] objectAtIndex:screenIdx]; + } + + // kCGDisplayBeginConfigurationFlag is only fired while the screen is still + // valid + if (pScreen) + { + CGDirectDisplayID xbmc_display = GetDisplayIDFromScreen(pScreen); + if (xbmc_display == display) + { + // we only respond to changes on the display we are running on. + winsys->AnnounceOnLostDevice(); + winsys->StartLostDeviceTimer(); + } + } + } + else // the else case checks if we need to call OnResetDevice + { + // we fire if kCGDisplaySetModeFlag is set or if flags == 0 + // (which is undocumented but seems to happen + // on some macs - we treat it as device reset) + // we also don't check the screen here as we might not even have + // one anymore (e.x. when tv is turned off) + if (flags & kCGDisplaySetModeFlag || flags == 0) + { + winsys->StopLostDeviceTimer(); // no need to timeout - we've got the callback + winsys->HandleOnResetDevice(); + } + } + + if ((flags & kCGDisplayAddFlag) || (flags & kCGDisplayRemoveFlag)) + winsys->UpdateResolutions(); +} + +//------------------------------------------------------------------------------ +NSOpenGLContext* CreateWindowedContext(NSOpenGLContext* shareCtx); +void ResizeWindowInternal(int newWidth, int newHeight, int newLeft, int newTop, NSView* last_view); + +//------------------------------------------------------------------------------ +CWinSystemOSX::CWinSystemOSX() + : CWinSystemBase() + , m_impl{new CWinSystemOSXImpl} + , m_lostDeviceTimer(this) +{ + m_SDLSurface = NULL; + m_osx_events = NULL; + m_obscured = false; + m_obscured_timecheck = std::chrono::steady_clock::now() + std::chrono::milliseconds(1000); + m_lastDisplayNr = -1; + m_movedToOtherScreen = false; + m_refreshRate = 0.0; + m_delayDispReset = false; + + m_winEvents.reset(new CWinEventsOSX()); + + AE::CAESinkFactory::ClearSinks(); + CAESinkDARWINOSX::Register(); + m_dpms = std::make_shared(); +} + +CWinSystemOSX::~CWinSystemOSX() = default; + +void CWinSystemOSX::StartLostDeviceTimer() +{ + if (m_lostDeviceTimer.IsRunning()) + m_lostDeviceTimer.Restart(); + else + m_lostDeviceTimer.Start(LOST_DEVICE_TIMEOUT_MS, false); +} + +void CWinSystemOSX::StopLostDeviceTimer() +{ + m_lostDeviceTimer.Stop(); +} + +void CWinSystemOSX::OnTimeout() +{ + HandleOnResetDevice(); +} + +bool CWinSystemOSX::InitWindowSystem() +{ + CLog::LogF(LOGINFO, "Setup SDL"); + + /* Clean up on exit, exit on window close and interrupt */ + std::atexit(SDL_Quit); + + if (SDL_Init(SDL_INIT_VIDEO) != 0) + { + CLog::LogF(LOGFATAL, "Unable to initialize SDL: {}", SDL_GetError()); + return false; + } + // SDL_Init will install a handler for segfaults, restore the default handler. + signal(SIGSEGV, SIG_DFL); + + SDL_EnableUNICODE(1); + + // set repeat to 10ms to ensure repeat time < frame time + // so that hold times can be reliably detected + SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 10); + + if (!CWinSystemBase::InitWindowSystem()) + return false; + + m_osx_events = new CWinEventsOSX(); + + CGDisplayRegisterReconfigurationCallback(DisplayReconfigured, (void*)this); + + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + auto windowDidMove = [windowDidMoveNoteClass initWith:this]; + [center addObserver:windowDidMove + selector:@selector(windowDidMoveNotification:) + name:NSWindowDidMoveNotification object:nil]; + m_impl->m_windowDidMove = windowDidMove; + + auto windowDidReSize = [windowDidReSizeNoteClass initWith:this]; + [center addObserver:windowDidReSize + selector:@selector(windowDidReSizeNotification:) + name:NSWindowDidResizeNotification object:nil]; + m_impl->m_windowDidReSize = windowDidReSize; + + auto windowDidChangeScreen = [windowDidChangeScreenNoteClass initWith:this]; + [center addObserver:windowDidChangeScreen + selector:@selector(windowDidChangeScreenNotification:) + name:NSWindowDidChangeScreenNotification object:nil]; + m_impl->m_windowChangedScreen = windowDidChangeScreen; + + return true; +} + +bool CWinSystemOSX::DestroyWindowSystem() +{ + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center removeObserver:m_impl->m_windowDidMove name:NSWindowDidMoveNotification object:nil]; + [center removeObserver:m_impl->m_windowDidReSize name:NSWindowDidResizeNotification object:nil]; + [center removeObserver:m_impl->m_windowChangedScreen + name:NSWindowDidChangeScreenNotification + object:nil]; + + CGDisplayRemoveReconfigurationCallback(DisplayReconfigured, (void*)this); + + delete m_osx_events; + m_osx_events = NULL; + + UnblankDisplays(); + m_impl->m_glContext = nil; + return true; +} + +bool CWinSystemOSX::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) +{ + // force initial window creation to be windowed, if fullscreen, it will switch to it below + // fixes the white screen of death if starting fullscreen and switching to windowed. + RESOLUTION_INFO resInfo = CDisplaySettings::GetInstance().GetResolutionInfo(RES_WINDOW); + m_nWidth = resInfo.iWidth; + m_nHeight = resInfo.iHeight; + m_bFullScreen = false; + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + // Enable vertical sync to avoid any tearing. + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); + + m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, SDL_OPENGL | SDL_RESIZABLE); + if (!m_SDLSurface) + return false; + + // the context SDL creates isn't full screen compatible, so we create new one + // first, find the current contect and make sure a view is attached + NSOpenGLContext* cur_context = [NSOpenGLContext currentContext]; + NSView* view = [cur_context view]; + if (!view) + return false; + + if (CDisplaySettings::GetInstance().GetCurrentResolution() != RES_WINDOW) + { + // If we are not starting up windowed, then hide the initial SDL window + // so we do not see it flash before the fade-out and switch to fullscreen. + ShowHideNSWindow([view window], false); + } + + // disassociate view from context + [cur_context clearDrawable]; + + // release the context + if (CWinSystemOSXImpl::m_lastOwnedContext == cur_context) + { + [ NSOpenGLContext clearCurrentContext ]; + [ cur_context clearDrawable ]; + cur_context = nil; + } + + // create a new context + auto new_context = CreateWindowedContext(nil); + if (!new_context) + return false; + + // associate with current view + [new_context setView:view]; + [new_context makeCurrentContext]; + + // set the window title + [[[new_context view] window] + setTitle:[NSString stringWithFormat:@"%s Media Center", CCompileInfo::GetAppName()]]; + + m_impl->m_glContext = new_context; + CWinSystemOSXImpl::m_lastOwnedContext = new_context; + m_bWindowCreated = true; + + // get screen refreshrate - this is needed + // when we startup in windowed mode and don't run through SetFullScreen + int dummy; + GetScreenResolution(&dummy, &dummy, &m_refreshRate, m_lastDisplayNr); + + // register platform dependent objects + CDVDFactoryCodec::ClearHWAccels(); + VTB::CDecoder::Register(); + VIDEOPLAYER::CRendererFactory::ClearRenderer(); + CLinuxRendererGL::Register(); + CRendererVTB::Register(); + VIDEOPLAYER::CProcessInfoOSX::Register(); + RETRO::CRPProcessInfoOSX::Register(); + RETRO::CRPProcessInfoOSX::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGL); + CScreenshotSurfaceGL::Register(); + + return true; +} + +bool CWinSystemOSX::DestroyWindow() +{ + return true; +} + +extern "C" void SDL_SetWidthHeight(int w, int h); +void ResizeWindowInternal(int newWidth, int newHeight, int newLeft, int newTop, NSView* last_view) +{ + if (last_view && [last_view window]) + { + auto size = NSMakeSize(newWidth, newHeight); + NSWindow* lastWindow = [last_view window]; + [lastWindow setContentSize:size]; + [lastWindow update]; + [last_view setFrameSize:size]; + } +} +bool CWinSystemOSX::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) +{ + if (!m_impl->m_glContext) + return false; + + NSOpenGLContext* context = [NSOpenGLContext currentContext]; + NSView* view; + NSWindow* window; + + view = [context view]; + + if (view) + { + // It seems, that in macOS 10.15 this defaults to YES, but we currently do not support + // Retina resolutions properly. Ensure that the view uses a 1 pixel per point framebuffer. + view.wantsBestResolutionOpenGLSurface = NO; + } + + if (view && (newWidth > 0) && (newHeight > 0)) + { + window = [view window]; + if (window) + { + [window setContentSize:NSMakeSize(newWidth, newHeight)]; + [window update]; + [view setFrameSize:NSMakeSize(newWidth, newHeight)]; + [context update]; + // this is needed in case we traverse from fullscreen screen 2 + // to windowed on screen 1 directly where in ScreenChangedNotification + // we don't have a window to get the current screen on + // in that case ResizeWindow is called at a later stage from SetFullScreen(false) + // and we can grab the correct display number here then + m_lastDisplayNr = GetDisplayIndex(GetDisplayIDFromScreen( [window screen] )); + } + } + + // HACK: resize SDL's view manually so that mouse bounds are correctly updated. + // there are two parts to this, the internal SDL (current_video->screen) and + // the cocoa view ( handled in SetFullScreen). + SDL_SetWidthHeight(newWidth, newHeight); + + [context makeCurrentContext]; + + m_nWidth = newWidth; + m_nHeight = newHeight; + m_impl->m_glContext = context; + CServiceBroker::GetWinSystem()->GetGfxContext().SetFPS(m_refreshRate); + + return true; +} + +static bool needtoshowme = true; + +bool CWinSystemOSX::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) +{ + static NSWindow* windowedFullScreenwindow = NULL; + static NSScreen* last_window_screen = NULL; + static NSPoint last_window_origin; + static NSView* last_view = NULL; + static NSSize last_view_size; + static NSPoint last_view_origin; + static NSInteger last_window_level = NSNormalWindowLevel; + bool was_fullscreen = m_bFullScreen; + NSOpenGLContext* cur_context; + + // Fade to black to hide resolution-switching flicker and garbage. + CGDisplayFadeReservationToken fade_token = DisplayFadeToBlack(needtoshowme); + + // If we're already fullscreen then we must be moving to a different display. + // or if we are still on the same display - it might be only a refreshrate/resolution + // change request. + // Recurse to reset fullscreen mode and then continue. + if (was_fullscreen && fullScreen) + { + needtoshowme = false; + ShowHideNSWindow([last_view window], needtoshowme); + RESOLUTION_INFO& window = CDisplaySettings::GetInstance().GetResolutionInfo(RES_WINDOW); + CWinSystemOSX::SetFullScreen(false, window, blankOtherDisplays); + needtoshowme = true; + } + + const std::shared_ptr settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + m_lastDisplayNr = GetDisplayIndex(settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR)); + m_nWidth = res.iWidth; + m_nHeight = res.iHeight; + m_bFullScreen = fullScreen; + + cur_context = [NSOpenGLContext currentContext]; + + //handle resolution/refreshrate switching early here + if (m_bFullScreen) + { + // switch videomode + SwitchToVideoMode(res.iWidth, res.iHeight, static_cast(res.fRefreshRate)); + } + + //no context? done. + if (!cur_context) + { + DisplayFadeFromBlack(fade_token, needtoshowme); + return false; + } + + if (windowedFullScreenwindow != NULL) + { + [windowedFullScreenwindow close]; + windowedFullScreenwindow = nil; + } + + if (m_bFullScreen) + { + // FullScreen Mode + NSOpenGLContext* newContext = NULL; + + // Save info about the windowed context so we can restore it when returning to windowed. + last_view = [cur_context view]; + last_view_size = [last_view frame].size; + last_view_origin = [last_view frame].origin; + last_window_screen = [[last_view window] screen]; + last_window_origin = [[last_view window] frame].origin; + last_window_level = [[last_view window] level]; + + // This is Cocoa Windowed FullScreen Mode + // Get the screen rect of our current display + NSScreen* pScreen = [[NSScreen screens] objectAtIndex:m_lastDisplayNr]; + NSRect screenRect = [pScreen frame]; + + // remove frame origin offset of original display + screenRect.origin = NSZeroPoint; + + // make a new window to act as the windowedFullScreen + windowedFullScreenwindow = [[NSWindow alloc] initWithContentRect:screenRect + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO + screen:pScreen]; + + [windowedFullScreenwindow setBackgroundColor:[NSColor blackColor]]; + [windowedFullScreenwindow makeKeyAndOrderFront:nil]; + + // make our window the same level as the rest to enable cmd+tab switching + [windowedFullScreenwindow setLevel:NSNormalWindowLevel]; + // this will make our window topmost and hide all system messages + //[windowedFullScreenwindow setLevel:CGShieldingWindowLevel()]; + + // ...and the original one beneath it and on the same screen. + [[last_view window] setLevel:NSNormalWindowLevel-1]; + [[last_view window] setFrameOrigin:[pScreen frame].origin]; + // expand the mouse bounds in SDL view to fullscreen + [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)]; + [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ]; + + NSView* blankView = [[NSView alloc] init]; + [windowedFullScreenwindow setContentView:blankView]; + [windowedFullScreenwindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)]; + [windowedFullScreenwindow update]; + [blankView setFrameSize:NSMakeSize(m_nWidth, m_nHeight)]; + + // Obtain windowed pixel format and create a new context. + newContext = CreateWindowedContext(cur_context); + [newContext setView:blankView]; + + // Hide the menu bar. + SetMenuBarVisible(false); + + // Blank other displays if requested. + if (blankOtherDisplays) + BlankOtherDisplays(m_lastDisplayNr); + + // Hide the mouse. + [NSCursor hide]; + + // Release old context if we created it. + if (CWinSystemOSXImpl::m_lastOwnedContext == cur_context) + { + [ NSOpenGLContext clearCurrentContext ]; + [ cur_context clearDrawable ]; + } + + // activate context + [newContext makeCurrentContext]; + CWinSystemOSXImpl::m_lastOwnedContext = newContext; + } + else + { + // Windowed Mode + // exit fullscreen + [cur_context clearDrawable]; + + [NSCursor unhide]; + + // Show menubar. + SetMenuBarVisible(true); + + // restore the windowed window level + [[last_view window] setLevel:last_window_level]; + + // Get rid of the new window we created. + if (windowedFullScreenwindow != nil) + { + [windowedFullScreenwindow close]; + windowedFullScreenwindow = nil; + } + + // Unblank. + // Force the unblank when returning from fullscreen, we get called with blankOtherDisplays set false. + //if (blankOtherDisplays) + UnblankDisplays(); + + // create our new context (sharing with the current one) + auto newContext = CreateWindowedContext(cur_context); + if (!newContext) + return false; + + // Assign view from old context, move back to original screen. + [newContext setView:last_view]; + [[last_view window] setFrameOrigin:last_window_origin]; + // return the mouse bounds in SDL view to previous size + [ last_view setFrameSize:last_view_size ]; + [ last_view setFrameOrigin:last_view_origin ]; + // done with restoring windowed window, don't set last_view to NULL as we can lose it under dual displays. + //last_window_screen = NULL; + + // Release the fullscreen context. + if (CWinSystemOSXImpl::m_lastOwnedContext == cur_context) + { + [ NSOpenGLContext clearCurrentContext ]; + [ cur_context clearDrawable ]; + } + + // Activate context. + [newContext makeCurrentContext]; + CWinSystemOSXImpl::m_lastOwnedContext = newContext; + } + + DisplayFadeFromBlack(fade_token, needtoshowme); + + ShowHideNSWindow([last_view window], needtoshowme); + // need to make sure SDL tracks any window size changes + ResizeWindow(m_nWidth, m_nHeight, -1, -1); + ResizeWindowInternal(m_nWidth, m_nHeight, -1, -1, last_view); + // restore origin once again when going to windowed mode + if (!fullScreen) + { + [[last_view window] setFrameOrigin:last_window_origin]; + } + HandlePossibleRefreshrateChange(); + + m_updateGLContext = 0; + return true; +} + +void CWinSystemOSX::UpdateResolutions() +{ + CWinSystemBase::UpdateResolutions(); + + // Add desktop resolution + int w, h; + double fps; + + int dispIdx = GetDisplayIndex(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR)); + GetScreenResolution(&w, &h, &fps, dispIdx); + NSString* dispName = screenNameForDisplay(GetDisplayID(dispIdx)); + UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), [dispName UTF8String], 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(); + CDisplaySettings::GetInstance().ApplyCalibrations(); +} + +/* +void* Cocoa_GL_CreateContext(void* pixFmt, void* shareCtx) +{ + if (!pixFmt) + return nil; + + NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt + shareContext:(NSOpenGLContext*)shareCtx]; + + // snipit from SDL_cocoaopengl.m + // + // Wisdom from Apple engineer in reference to UT2003's OpenGL performance: + // "You are blowing a couple of the internal OpenGL function caches. This + // appears to be happening in the VAO case. You can tell OpenGL to up + // the cache size by issuing the following calls right after you create + // the OpenGL context. The default cache size is 16." --ryan. + // + + #ifndef GLI_ARRAY_FUNC_CACHE_MAX + #define GLI_ARRAY_FUNC_CACHE_MAX 284 + #endif + + #ifndef GLI_SUBMIT_FUNC_CACHE_MAX + #define GLI_SUBMIT_FUNC_CACHE_MAX 280 + #endif + + { + long cache_max = 64; + CGLContextObj ctx = (CGLContextObj)[newContext CGLContextObj]; + CGLSetParameter(ctx, (CGLContextParameter)GLI_SUBMIT_FUNC_CACHE_MAX, &cache_max); + CGLSetParameter(ctx, (CGLContextParameter)GLI_ARRAY_FUNC_CACHE_MAX, &cache_max); + } + + // End Wisdom from Apple Engineer section. --ryan. + return newContext; +} +*/ + +NSOpenGLContext* CreateWindowedContext(NSOpenGLContext* shareCtx) +{ + NSOpenGLPixelFormat* pixFmt; + if (getenv("KODI_GL_PROFILE_LEGACY")) + { + NSOpenGLPixelFormatAttribute wattrs[] = { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFANoRecovery, + NSOpenGLPFAAccelerated, + NSOpenGLPFADepthSize, + static_cast(8), + static_cast(0)}; + pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs]; + } + else + { + NSOpenGLPixelFormatAttribute wattrs_gl3[] = { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFAOpenGLProfile, + NSOpenGLProfileVersion3_2Core, + NSOpenGLPFANoRecovery, + NSOpenGLPFAAccelerated, + NSOpenGLPFADepthSize, + static_cast(24), + static_cast(0)}; + pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs_gl3]; + } + + auto newContext = [[NSOpenGLContext alloc] initWithFormat:pixFmt shareContext:shareCtx]; + + if (!newContext) + { + // bah, try again for non-accelerated renderer + NSOpenGLPixelFormatAttribute wattrs2[] = + { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFANoRecovery, + NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8, + (NSOpenGLPixelFormatAttribute)0 + }; + newContext = [[NSOpenGLContext alloc] + initWithFormat:[[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs2] + shareContext:shareCtx]; + } + + return newContext; +} + +NSOpenGLContext* CreateFullScreenContext(int screen_index, NSOpenGLContext* shareCtx) +{ + CGDirectDisplayID displayArray[MAX_DISPLAYS]; + CGDisplayCount numDisplays; + CGDirectDisplayID displayID; + + // Get the list of displays. + CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays); + displayID = displayArray[screen_index]; + + NSOpenGLPixelFormat* pixFmt; + if (getenv("KODI_GL_PROFILE_LEGACY")) + { + NSOpenGLPixelFormatAttribute fsattrs[] = { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFANoRecovery, + NSOpenGLPFAAccelerated, + NSOpenGLPFADepthSize, + static_cast(8), + NSOpenGLPFAScreenMask, + static_cast(CGDisplayIDToOpenGLDisplayMask(displayID)), + static_cast(0)}; + pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs]; + } + else + { + NSOpenGLPixelFormatAttribute fsattrs_gl3[] = { + NSOpenGLPFADoubleBuffer, + NSOpenGLPFANoRecovery, + NSOpenGLPFAAccelerated, + NSOpenGLPFADepthSize, + static_cast(24), + NSOpenGLPFAOpenGLProfile, + NSOpenGLProfileVersion3_2Core, + NSOpenGLPFAScreenMask, + static_cast(CGDisplayIDToOpenGLDisplayMask(displayID)), + static_cast(0)}; + pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs_gl3]; + } + + if (!pixFmt) + return nil; + + auto newContext = [[NSOpenGLContext alloc] initWithFormat:pixFmt shareContext:shareCtx]; + + return newContext; +} + +void CWinSystemOSX::GetScreenResolution(int* w, int* h, double* fps, int screenIdx) +{ + CGDirectDisplayID display_id = (CGDirectDisplayID)GetDisplayID(screenIdx); + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display_id); + *w = CGDisplayModeGetWidth(mode); + *h = CGDisplayModeGetHeight(mode); + *fps = CGDisplayModeGetRefreshRate(mode); + CGDisplayModeRelease(mode); + if ((int)*fps == 0) + { + // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays. + *fps = 60.0; + } +} + +void CWinSystemOSX::EnableVSync(bool enable) +{ + // OpenGL Flush synchronised with vertical retrace + GLint swapInterval = enable ? 1 : 0; + [[NSOpenGLContext currentContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; +} + +bool CWinSystemOSX::SwitchToVideoMode(int width, int height, double refreshrate) +{ + boolean_t match = false; + CGDisplayModeRef dispMode = NULL; + + int screenIdx = GetDisplayIndex(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR)); + + // Figure out the screen size. (default to main screen) + CGDirectDisplayID display_id = GetDisplayID(screenIdx); + + // find mode that matches the desired size, refreshrate + // non interlaced, nonstretched, safe for hardware + dispMode = GetMode(width, height, refreshrate, screenIdx); + + //not found - fallback to bestemdeforparameters + if (!dispMode) + { + dispMode = BestMatchForMode(display_id, 32, width, height, match); + + if (!match) + dispMode = BestMatchForMode(display_id, 16, width, height, match); + + // still no match? fallback to current resolution of the display which HAS to work [tm] + if (!match) + { + int tmpWidth; + int tmpHeight; + double tmpRefresh; + + GetScreenResolution(&tmpWidth, &tmpHeight, &tmpRefresh, screenIdx); + dispMode = GetMode(tmpWidth, tmpHeight, tmpRefresh, screenIdx); + + // no way to get a resolution set + if (!dispMode) + return false; + } + + if (!match) + return false; + } + + // switch mode and return success + CGDisplayCapture(display_id); + CGDisplayConfigRef cfg; + CGBeginDisplayConfiguration(&cfg); + CGConfigureDisplayWithDisplayMode(cfg, display_id, dispMode, nullptr); + CGError err = CGCompleteDisplayConfiguration(cfg, kCGConfigureForAppOnly); + CGDisplayRelease(display_id); + + m_refreshRate = CGDisplayModeGetRefreshRate(dispMode); + + Cocoa_CVDisplayLinkUpdate(); + + return (err == kCGErrorSuccess); +} + +void CWinSystemOSX::FillInVideoModes() +{ + int dispIdx = GetDisplayIndex(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR)); + + // Add full screen settings for additional monitors + int numDisplays = [[NSScreen screens] count]; + + for (int disp = 0; disp < numDisplays; disp++) + { + Boolean stretched; + Boolean interlaced; + Boolean safeForHardware; + Boolean televisionoutput; + int w, h, bitsperpixel; + double refreshrate; + RESOLUTION_INFO res; + + CFArrayRef displayModes = GetAllDisplayModes(GetDisplayID(disp)); + NSString *dispName = screenNameForDisplay(GetDisplayID(disp)); + + CLog::Log(LOGINFO, "Display {} has name {}", disp, [dispName UTF8String]); + + if (NULL == displayModes) + continue; + + for (int i=0; i < CFArrayGetCount(displayModes); ++i) + { + CGDisplayModeRef displayMode = (CGDisplayModeRef)CFArrayGetValueAtIndex(displayModes, i); + + uint32_t flags = CGDisplayModeGetIOFlags(displayMode); + stretched = flags & kDisplayModeStretchedFlag ? true : false; + interlaced = flags & kDisplayModeInterlacedFlag ? true : false; + bitsperpixel = DisplayBitsPerPixelForMode(displayMode); + safeForHardware = flags & kDisplayModeSafetyFlags ? true : false; + televisionoutput = flags & kDisplayModeTelevisionFlag ? true : false; + + if ((bitsperpixel == 32) && + (safeForHardware == YES) && + (stretched == NO) && + (interlaced == NO)) + { + w = CGDisplayModeGetWidth(displayMode); + h = CGDisplayModeGetHeight(displayMode); + refreshrate = CGDisplayModeGetRefreshRate(displayMode); + if ((int)refreshrate == 0) // LCD display? + { + // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays. + refreshrate = 60.0; + } + CLog::Log(LOGINFO, "Found possible resolution for display {} with {} x {} @ {:f} Hz", disp, + w, h, refreshrate); + + // only add the resolution if it belongs to "our" screen + // all others are only logged above... + if (disp == dispIdx) + { + UpdateDesktopResolution(res, (dispName != nil) ? [dispName UTF8String] : "Unknown", w, h, refreshrate, 0); + CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res); + CDisplaySettings::GetInstance().AddResolutionInfo(res); + } + } + } + CFRelease(displayModes); + } +} + +bool CWinSystemOSX::FlushBuffer(void) +{ + if (m_updateGLContext < 5) + { + [m_impl->m_glContext update]; + m_updateGLContext++; + } + + [m_impl->m_glContext flushBuffer]; + + return true; +} + +bool CWinSystemOSX::IsObscured(void) +{ + // check once a second if we are obscured. + auto now_time = std::chrono::steady_clock::now(); + if (m_obscured_timecheck > now_time) + return m_obscured; + else + m_obscured_timecheck = now_time + std::chrono::milliseconds(1000); + + NSOpenGLContext* cur_context = [NSOpenGLContext currentContext]; + NSView* view = [cur_context view]; + if (!view) + { + // sanity check, we should always have a view + m_obscured = true; + return m_obscured; + } + + NSWindow *window = [view window]; + if (!window) + { + // sanity check, we should always have a window + m_obscured = true; + return m_obscured; + } + + if ([window isVisible] == NO) + { + // not visible means the window is not showing. + // this should never really happen as we are always visible + // even when minimized in dock. + m_obscured = true; + return m_obscured; + } + + // check if we are minimized (to an icon in the Dock). + if ([window isMiniaturized] == YES) + { + m_obscured = true; + return m_obscured; + } + + // check if we are showing on the active workspace. + if ([window isOnActiveSpace] == NO) + { + m_obscured = true; + return m_obscured; + } + + // default to false before we start parsing though the windows. + // if we are are obscured by any windows, then set true. + m_obscured = false; + static bool obscureLogged = false; + + CGWindowListOption opts; + opts = kCGWindowListOptionOnScreenAboveWindow | kCGWindowListExcludeDesktopElements; + CFArrayRef windowIDs =CGWindowListCreate(opts, (CGWindowID)[window windowNumber]); + + if (!windowIDs) + return m_obscured; + + CFArrayRef windowDescs = CGWindowListCreateDescriptionFromArray(windowIDs); + if (!windowDescs) + { + CFRelease(windowIDs); + return m_obscured; + } + + CGRect bounds = NSRectToCGRect([window frame]); + // kCGWindowBounds measures the origin as the top-left corner of the rectangle + // relative to the top-left corner of the screen. + // NSWindow’s frame property measures the origin as the bottom-left corner + // of the rectangle relative to the bottom-left corner of the screen. + // convert bounds from NSWindow to CGWindowBounds here. + bounds.origin.y = [[window screen] frame].size.height - bounds.origin.y - bounds.size.height; + + std::vector partialOverlaps; + CRect ourBounds = CGRectToCRect(bounds); + + for (CFIndex idx=0; idx < CFArrayGetCount(windowDescs); idx++) + { + // walk the window list of windows that are above us and are not desktop elements + CFDictionaryRef windowDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(windowDescs, idx); + + // skip the Dock window, it actually covers the entire screen. + CFStringRef ownerName = (CFStringRef)CFDictionaryGetValue(windowDictionary, kCGWindowOwnerName); + if (CFStringCompare(ownerName, CFSTR("Dock"), 0) == kCFCompareEqualTo) + continue; + + // Ignore known brightness tools for dimming the screen. They claim to cover + // the whole XBMC window and therefore would make the framerate limiter + // kicking in. Unfortunately even the alpha of these windows is 1.0 so + // we have to check the ownerName. + if (CFStringCompare(ownerName, CFSTR("Shades"), 0) == kCFCompareEqualTo || + CFStringCompare(ownerName, CFSTR("SmartSaver"), 0) == kCFCompareEqualTo || + CFStringCompare(ownerName, CFSTR("Brightness Slider"), 0) == kCFCompareEqualTo || + CFStringCompare(ownerName, CFSTR("Displaperture"), 0) == kCFCompareEqualTo || + CFStringCompare(ownerName, CFSTR("Dreamweaver"), 0) == kCFCompareEqualTo || + CFStringCompare(ownerName, CFSTR("Window Server"), 0) == kCFCompareEqualTo) + continue; + + CFDictionaryRef rectDictionary = (CFDictionaryRef)CFDictionaryGetValue(windowDictionary, kCGWindowBounds); + if (!rectDictionary) + continue; + + CGRect windowBounds; + if (CGRectMakeWithDictionaryRepresentation(rectDictionary, &windowBounds)) + { + if (CGRectContainsRect(windowBounds, bounds)) + { + // if the windowBounds completely encloses our bounds, we are obscured. + if (!obscureLogged) + { + std::string appName; + if (CDarwinUtils::CFStringRefToUTF8String(ownerName, appName)) + CLog::Log(LOGDEBUG, "WinSystemOSX: Fullscreen window {} obscures Kodi!", appName); + obscureLogged = true; + } + m_obscured = true; + break; + } + + // handle overlapping windows above us that combine + // to obscure by collecting any partial overlaps, + // then subtract them from our bounds and check + // for any remaining area. + CRect intersection = CGRectToCRect(windowBounds); + intersection.Intersect(ourBounds); + if (!intersection.IsEmpty()) + partialOverlaps.push_back(intersection); + } + } + + if (!m_obscured) + { + // if we are here we are not obscured by any fullscreen window - reset flag + // for allowing the logmessage above to show again if this changes. + if (obscureLogged) + obscureLogged = false; + std::vector rects = ourBounds.SubtractRects(partialOverlaps); + // they got us covered + if (rects.empty()) + m_obscured = true; + } + + CFRelease(windowDescs); + CFRelease(windowIDs); + + return m_obscured; +} + +void CWinSystemOSX::NotifyAppFocusChange(bool bGaining) +{ + if (!(m_bFullScreen && bGaining)) + return; + @autoreleasepool + { + // find the window + NSOpenGLContext* context = [NSOpenGLContext currentContext]; + if (context) + { + NSView* view; + + view = [context view]; + if (view) + { + NSWindow* window; + window = [view window]; + if (window) + { + SetMenuBarVisible(false); + [window orderFront:nil]; + } + } + } + } +} + +void CWinSystemOSX::ShowOSMouse(bool show) +{ + SDL_ShowCursor(show ? 1 : 0); +} + +bool CWinSystemOSX::Minimize() +{ + @autoreleasepool + { + [[NSApplication sharedApplication] miniaturizeAll:nil]; + } + return true; +} + +bool CWinSystemOSX::Restore() +{ + @autoreleasepool + { + [[NSApplication sharedApplication] unhide:nil]; + } + return true; +} + +bool CWinSystemOSX::Hide() +{ + @autoreleasepool + { + [[NSApplication sharedApplication] hide:nil]; + } + return true; +} + +void CWinSystemOSX::HandlePossibleRefreshrateChange() +{ + static double oldRefreshRate = m_refreshRate; + Cocoa_CVDisplayLinkUpdate(); + int dummy = 0; + + GetScreenResolution(&dummy, &dummy, &m_refreshRate, m_lastDisplayNr); + + if (oldRefreshRate != m_refreshRate) + { + oldRefreshRate = m_refreshRate; + // send a message so that videoresolution (and refreshrate) + // is changed + CApplicationMessenger::GetInstance().PostMsg(TMSG_VIDEORESIZE, m_SDLSurface->w, m_SDLSurface->h); + } +} + +void CWinSystemOSX::OnMove(int x, int y) +{ + HandlePossibleRefreshrateChange(); +} + +std::unique_ptr CWinSystemOSX::GetOSScreenSaverImpl() +{ + return std::unique_ptr (new COSScreenSaverOSX); +} + +OSXTextInputResponder *g_textInputResponder = nil; + +void CWinSystemOSX::StartTextInput() +{ + NSView *parentView = [[NSApp keyWindow] contentView]; + + /* We only keep one field editor per process, since only the front most + * window can receive text input events, so it make no sense to keep more + * than one copy. When we switched to another window and requesting for + * text input, simply remove the field editor from its superview then add + * it to the front most window's content view */ + if (!g_textInputResponder) { + g_textInputResponder = + [[OSXTextInputResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)]; + } + + if (![[g_textInputResponder superview] isEqual: parentView]) + { +// DLOG(@"add fieldEdit to window contentView"); + [g_textInputResponder removeFromSuperview]; + [parentView addSubview: g_textInputResponder]; + [[NSApp keyWindow] makeFirstResponder: g_textInputResponder]; + } +} +void CWinSystemOSX::StopTextInput() +{ + if (g_textInputResponder) + { + [g_textInputResponder removeFromSuperview]; + g_textInputResponder = nil; + } +} + +void CWinSystemOSX::Register(IDispResource *resource) +{ + CSingleLock lock(m_resourceSection); + m_resources.push_back(resource); +} + +void CWinSystemOSX::Unregister(IDispResource* resource) +{ + CSingleLock lock(m_resourceSection); + std::vector::iterator i = find(m_resources.begin(), m_resources.end(), resource); + if (i != m_resources.end()) + m_resources.erase(i); +} + +bool CWinSystemOSX::Show(bool raise) +{ + @autoreleasepool + { + auto app = [NSApplication sharedApplication]; + if (raise) + { + [app unhide:nil]; + [app activateIgnoringOtherApps:YES]; + [app arrangeInFront:nil]; + } + else + { + [app unhideWithoutActivation]; + } + } + return true; +} + +void CWinSystemOSX::WindowChangedScreen() +{ + // user has moved the window to a + // different screen + NSOpenGLContext* context = [NSOpenGLContext currentContext]; + m_lastDisplayNr = -1; + + // if we are here the user dragged the window to a different + // screen and we return the screen of the window + if (context) + { + NSView* view; + + view = [context view]; + if (view) + { + NSWindow* window; + window = [view window]; + if (window) + { + m_lastDisplayNr = GetDisplayIndex(GetDisplayIDFromScreen([window screen])); + } + } + } + if (m_lastDisplayNr == -1) + m_lastDisplayNr = 0;// default to main screen +} + +void CWinSystemOSX::AnnounceOnLostDevice() +{ + CSingleLock lock(m_resourceSection); + // tell any shared resources + CLog::Log(LOGDEBUG, "CWinSystemOSX::AnnounceOnLostDevice"); + for (std::vector::iterator i = m_resources.begin(); i != m_resources.end(); ++i) + (*i)->OnLostDisplay(); +} + +void CWinSystemOSX::HandleOnResetDevice() +{ + + int delay = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt("videoscreen.delayrefreshchange"); + if (delay > 0) + { + m_delayDispReset = true; + m_dispResetTimer.Set(delay * 100); + } + else + { + AnnounceOnResetDevice(); + } +} + +void CWinSystemOSX::AnnounceOnResetDevice() +{ + double currentFps = m_refreshRate; + int w = 0; + int h = 0; + int currentScreenIdx = m_lastDisplayNr; + // ensure that graphics context knows about the current refreshrate before + // doing the callbacks + GetScreenResolution(&w, &h, ¤tFps, currentScreenIdx); + + CServiceBroker::GetWinSystem()->GetGfxContext().SetFPS(currentFps); + + CSingleLock lock(m_resourceSection); + // tell any shared resources + CLog::Log(LOGDEBUG, "CWinSystemOSX::AnnounceOnResetDevice"); + for (std::vector::iterator i = m_resources.begin(); i != m_resources.end(); ++i) + (*i)->OnResetDisplay(); +} + +void* CWinSystemOSX::GetCGLContextObj() +{ + return [m_impl->m_glContext CGLContextObj]; +} + +NSOpenGLContext* CWinSystemOSX::GetNSOpenGLContext() +{ + return m_impl->m_glContext; +} + +std::string CWinSystemOSX::GetClipboardText(void) +{ + std::string utf8_text; + + const char *szStr = Cocoa_Paste(); + if (szStr) + utf8_text = szStr; + + return utf8_text; +} + +std::unique_ptr CWinSystemOSX::GetVideoSync(void *clock) +{ + std::unique_ptr pVSync(new CVideoSyncOsx(clock)); + return pVSync; +} + +bool CWinSystemOSX::MessagePump() +{ + return m_winEvents->MessagePump(); +} + +void CWinSystemOSX::GetConnectedOutputs(std::vector *outputs) +{ + outputs->push_back("Default"); + + int numDisplays = [[NSScreen screens] count]; + + for (int disp = 0; disp < numDisplays; disp++) + { + NSString *dispName = screenNameForDisplay(GetDisplayID(disp)); + outputs->push_back([dispName UTF8String]); + } +} diff --git a/xbmc/windowing/osx/WinEventsOSX.h b/xbmc/windowing/osx/WinEventsOSX.h index 0a95bf7254..f9f38c3ced 100644 --- a/xbmc/windowing/osx/WinEventsOSX.h +++ b/xbmc/windowing/osx/WinEventsOSX.h @@ -8,9 +8,9 @@ #pragma once -#include "windowing/osx/WinEventsSDL.h" +#include "windowing/WinEvents.h" -class CWinEventsOSX : public CWinEventsSDL +class CWinEventsOSX : public IWinEvents { public: CWinEventsOSX() = default; diff --git a/xbmc/windowing/osx/WinEventsSDL.cpp b/xbmc/windowing/osx/WinEventsSDL.cpp deleted file mode 100644 index 0c83e7fdc5..0000000000 --- a/xbmc/windowing/osx/WinEventsSDL.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2005-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 "WinEventsSDL.h" - -#include "AppInboundProtocol.h" -#include "Application.h" -#include "GUIUserMessages.h" -#include "ServiceBroker.h" -#include "guilib/GUIComponent.h" -#include "guilib/GUIWindowManager.h" -#include "input/InputManager.h" -#include "input/Key.h" -#include "input/mouse/MouseStat.h" -#include "messaging/ApplicationMessenger.h" -#include "settings/DisplaySettings.h" -#include "windowing/WinSystem.h" - -#include "platform/darwin/osx/CocoaInterface.h" - -using namespace KODI::MESSAGING; - -bool CWinEventsSDL::MessagePump() -{ - SDL_Event event; - bool ret = false; - - while (SDL_PollEvent(&event)) - { - switch(event.type) - { - case SDL_QUIT: - if (!g_application.m_bStop) - CApplicationMessenger::GetInstance().PostMsg(TMSG_QUIT); - break; - - case SDL_ACTIVEEVENT: - //If the window was inconified or restored - if( event.active.state & SDL_APPACTIVE ) - { - std::shared_ptr appPort = CServiceBroker::GetAppPort(); - if (appPort) - appPort->SetRenderGUI(event.active.gain != 0); - CServiceBroker::GetWinSystem()->NotifyAppActiveChange(g_application.GetRenderGUI()); - } - else if (event.active.state & SDL_APPINPUTFOCUS) - { - g_application.m_AppFocused = event.active.gain != 0; - std::shared_ptr appPort = CServiceBroker::GetAppPort(); - if (appPort && g_application.m_AppFocused) - appPort->SetRenderGUI(g_application.m_AppFocused); - CServiceBroker::GetWinSystem()->NotifyAppFocusChange(g_application.m_AppFocused); - } - break; - - case SDL_KEYDOWN: - { - // process any platform specific shortcuts before handing off to XBMC - if (ProcessOSXShortcuts(event)) - { - ret = true; - break; - } - - XBMC_Event newEvent = {}; - newEvent.type = XBMC_KEYDOWN; - newEvent.key.keysym.scancode = event.key.keysym.scancode; - newEvent.key.keysym.sym = (XBMCKey) event.key.keysym.sym; - newEvent.key.keysym.unicode = event.key.keysym.unicode; - - // Check if the Windows keys are down because SDL doesn't flag this. - uint16_t mod = event.key.keysym.mod; - uint8_t* keystate = SDL_GetKeyState(NULL); - if (keystate[SDLK_LSUPER] || keystate[SDLK_RSUPER]) - mod |= XBMCKMOD_LSUPER; - newEvent.key.keysym.mod = (XBMCMod) mod; - - // don't handle any more messages in the queue until we've handled keydown, - // if a keyup is in the queue it will reset the keypress before it is handled. - std::shared_ptr appPort = CServiceBroker::GetAppPort(); - if (appPort) - ret |= appPort->OnEvent(newEvent); - break; - } - - case SDL_KEYUP: - { - XBMC_Event newEvent = {}; - newEvent.type = XBMC_KEYUP; - newEvent.key.keysym.scancode = event.key.keysym.scancode; - newEvent.key.keysym.sym = (XBMCKey) event.key.keysym.sym; - newEvent.key.keysym.mod =(XBMCMod) event.key.keysym.mod; - newEvent.key.keysym.unicode = event.key.keysym.unicode; - - std::shared_ptr appPort = CServiceBroker::GetAppPort(); - if (appPort) - ret |= appPort->OnEvent(newEvent); - break; - } - - case SDL_MOUSEBUTTONDOWN: - { - XBMC_Event newEvent = {}; - newEvent.type = XBMC_MOUSEBUTTONDOWN; - newEvent.button.button = event.button.button; - newEvent.button.x = event.button.x; - newEvent.button.y = event.button.y; - - std::shared_ptr appPort = CServiceBroker::GetAppPort(); - if (appPort) - ret |= appPort->OnEvent(newEvent); - break; - } - - case SDL_MOUSEBUTTONUP: - { - XBMC_Event newEvent = {}; - newEvent.type = XBMC_MOUSEBUTTONUP; - newEvent.button.button = event.button.button; - newEvent.button.x = event.button.x; - newEvent.button.y = event.button.y; - - std::shared_ptr appPort = CServiceBroker::GetAppPort(); - if (appPort) - ret |= appPort->OnEvent(newEvent); - break; - } - - case SDL_MOUSEMOTION: - { - if (0 == (SDL_GetAppState() & SDL_APPMOUSEFOCUS)) - { - CServiceBroker::GetInputManager().SetMouseActive(false); - // See CApplication::ProcessSlow() for a description as to why we call Cocoa_HideMouse. - // this is here to restore the pointer when toggling back to window mode from fullscreen. - Cocoa_ShowMouse(); - break; - } - XBMC_Event newEvent = {}; - newEvent.type = XBMC_MOUSEMOTION; - newEvent.motion.x = event.motion.x; - newEvent.motion.y = event.motion.y; - - std::shared_ptr appPort = CServiceBroker::GetAppPort(); - if (appPort) - ret |= appPort->OnEvent(newEvent); - break; - } - case SDL_VIDEORESIZE: - { - // Under newer osx versions sdl is so fucked up that it even fires resize events - // that exceed the screen size (maybe some HiDP incompatibility in old SDL?) - // ensure to ignore those events because it will mess with windowed size - if((event.resize.w > CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iWidth) || - (event.resize.h > CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP).iHeight)) - { - break; - } - XBMC_Event newEvent = {}; - newEvent.type = XBMC_VIDEORESIZE; - newEvent.resize.w = event.resize.w; - newEvent.resize.h = event.resize.h; - std::shared_ptr appPort = CServiceBroker::GetAppPort(); - if (appPort) - ret |= appPort->OnEvent(newEvent); - CServiceBroker::GetGUI()->GetWindowManager().MarkDirty(); - break; - } - case SDL_USEREVENT: - { - XBMC_Event newEvent = {}; - newEvent.type = XBMC_USEREVENT; - newEvent.user.code = event.user.code; - std::shared_ptr appPort = CServiceBroker::GetAppPort(); - if (appPort) - ret |= appPort->OnEvent(newEvent); - break; - } - case SDL_VIDEOEXPOSE: - CServiceBroker::GetGUI()->GetWindowManager().MarkDirty(); - break; - } - memset(&event, 0, sizeof(SDL_Event)); - } - - return ret; -} - -bool CWinEventsSDL::ProcessOSXShortcuts(SDL_Event& event) -{ - static bool shift = false, cmd = false; - - cmd = !!(SDL_GetModState() & (KMOD_LMETA | KMOD_RMETA )); - shift = !!(SDL_GetModState() & (KMOD_LSHIFT | KMOD_RSHIFT)); - - if (cmd && event.key.type == SDL_KEYDOWN) - { - char keysymbol = event.key.keysym.sym; - - // if the unicode is in the ascii range - // use this instead for getting the real - // character based on the used keyboard layout - // see http://lists.libsdl.org/pipermail/sdl-libsdl.org/2004-May/043716.html - bool isControl = (event.key.keysym.mod & KMOD_CTRL) != 0; - if (!isControl && !(event.key.keysym.unicode & 0xff80)) - keysymbol = event.key.keysym.unicode; - - switch(keysymbol) - { - case SDLK_q: // CMD-q to quit - if (!g_application.m_bStop) - CApplicationMessenger::GetInstance().PostMsg(TMSG_QUIT); - return true; - - case SDLK_f: // CMD-Ctrl-f to toggle fullscreen - if (!isControl) - return false; - g_application.OnAction(CAction(ACTION_TOGGLE_FULLSCREEN)); - return true; - - case SDLK_s: // CMD-3 to take a screenshot - g_application.OnAction(CAction(ACTION_TAKE_SCREENSHOT)); - return true; - - case SDLK_h: // CMD-h to hide - CServiceBroker::GetWinSystem()->Hide(); - return true; - - case SDLK_m: // CMD-m to minimize - CApplicationMessenger::GetInstance().PostMsg(TMSG_MINIMIZE); - return true; - - default: - return false; - } - } - - return false; -} diff --git a/xbmc/windowing/osx/WinEventsSDL.h b/xbmc/windowing/osx/WinEventsSDL.h deleted file mode 100644 index 6fd35863be..0000000000 --- a/xbmc/windowing/osx/WinEventsSDL.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2005-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. - */ - -#pragma once - -#include "windowing/WinEvents.h" - -#include - -class CWinEventsSDL : public IWinEvents -{ -public: - bool MessagePump() override; - -private: - static bool ProcessOSXShortcuts(SDL_Event& event); -}; diff --git a/xbmc/windowing/osx/WinSystemOSX.h b/xbmc/windowing/osx/WinSystemOSX.h deleted file mode 100644 index 4622c7d9f5..0000000000 --- a/xbmc/windowing/osx/WinSystemOSX.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2005-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. - */ - -#pragma once - -#include "threads/CriticalSection.h" -#include "threads/SystemClock.h" -#include "threads/Timer.h" -#include "windowing/WinSystem.h" - -#include -#include -#include - -typedef struct SDL_Surface SDL_Surface; - -class IDispResource; -class CWinEventsOSX; -class CWinSystemOSXImpl; -#ifdef __OBJC__ -@class NSOpenGLContext; -#endif - -class CWinSystemOSX : public CWinSystemBase, public ITimerCallback -{ -public: - - CWinSystemOSX(); - ~CWinSystemOSX() override; - - // ITimerCallback interface - void OnTimeout() override; - - // CWinSystemBase - bool InitWindowSystem() override; - bool DestroyWindowSystem() override; - bool CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) override; - bool DestroyWindow() override; - bool ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) override; - bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override; - void UpdateResolutions() override; - void NotifyAppFocusChange(bool bGaining) override; - void ShowOSMouse(bool show) override; - bool Minimize() override; - bool Restore() override; - bool Hide() override; - bool Show(bool raise = true) override; - void OnMove(int x, int y) override; - - std::string GetClipboardText() override; - - void Register(IDispResource *resource) override; - void Unregister(IDispResource *resource) override; - - std::unique_ptr GetVideoSync(void* clock) override; - - void WindowChangedScreen(); - - void AnnounceOnLostDevice(); - void AnnounceOnResetDevice(); - void HandleOnResetDevice(); - void StartLostDeviceTimer(); - void StopLostDeviceTimer(); - - void* GetCGLContextObj(); -#ifdef __OBJC__ - NSOpenGLContext* GetNSOpenGLContext(); -#else - void* GetNSOpenGLContext(); -#endif - void GetConnectedOutputs(std::vector *outputs); - - // winevents override - bool MessagePump() override; - -protected: - std::unique_ptr GetOSScreenSaverImpl() override; - - void HandlePossibleRefreshrateChange(); - void GetScreenResolution(int* w, int* h, double* fps, int screenIdx); - void EnableVSync(bool enable); - bool SwitchToVideoMode(int width, int height, double refreshrate); - void FillInVideoModes(); - bool FlushBuffer(void); - bool IsObscured(void); - void StartTextInput(); - void StopTextInput(); - - std::unique_ptr m_impl; - SDL_Surface* m_SDLSurface; - CWinEventsOSX *m_osx_events; - bool m_obscured; - std::chrono::time_point m_obscured_timecheck; - - bool m_movedToOtherScreen; - int m_lastDisplayNr; - double m_refreshRate; - - CCriticalSection m_resourceSection; - std::vector m_resources; - CTimer m_lostDeviceTimer; - bool m_delayDispReset; - XbmcThreads::EndTime m_dispResetTimer; - int m_updateGLContext = 0; -}; diff --git a/xbmc/windowing/osx/WinSystemOSX.mm b/xbmc/windowing/osx/WinSystemOSX.mm deleted file mode 100644 index 866da1427c..0000000000 --- a/xbmc/windowing/osx/WinSystemOSX.mm +++ /dev/null @@ -1,1845 +0,0 @@ -/* - * Copyright (C) 2005-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 "WinSystemOSX.h" - -#include "AppInboundProtocol.h" -#include "CompileInfo.h" -#include "OSScreenSaverOSX.h" -#include "ServiceBroker.h" -#include "VideoSyncOsx.h" -#include "WinEventsOSX.h" -#include "cores/AudioEngine/AESinkFactory.h" -#include "cores/AudioEngine/Sinks/AESinkDARWINOSX.h" -#include "cores/RetroPlayer/process/osx/RPProcessInfoOSX.h" -#include "cores/RetroPlayer/rendering/VideoRenderers/RPRendererOpenGL.h" -#include "cores/VideoPlayer/DVDCodecs/DVDFactoryCodec.h" -#include "cores/VideoPlayer/DVDCodecs/Video/VTB.h" -#include "cores/VideoPlayer/Process/osx/ProcessInfoOSX.h" -#include "cores/VideoPlayer/VideoRenderers/HwDecRender/RendererVTBGL.h" -#include "cores/VideoPlayer/VideoRenderers/LinuxRendererGL.h" -#include "cores/VideoPlayer/VideoRenderers/RenderFactory.h" -#include "guilib/DispResource.h" -#include "guilib/GUIWindowManager.h" -#include "input/KeyboardStat.h" -#include "messaging/ApplicationMessenger.h" -#include "rendering/gl/ScreenshotSurfaceGL.h" -#include "settings/DisplaySettings.h" -#include "settings/Settings.h" -#include "settings/SettingsComponent.h" -#include "threads/SingleLock.h" -#include "utils/StringUtils.h" -#include "utils/SystemInfo.h" -#include "utils/log.h" -#include "windowing/osx/CocoaDPMSSupport.h" - -#include "platform/darwin/DarwinUtils.h" -#include "platform/darwin/DictionaryUtils.h" -#include "platform/darwin/osx/CocoaInterface.h" -#import "platform/darwin/osx/OSXTextInputResponder.h" -#include "platform/darwin/osx/XBMCHelper.h" - -#include -#include - -#import -#import -#import -#import -#import - -// turn off deprecated warning spew. -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - -using namespace KODI; -using namespace MESSAGING; -using namespace WINDOWING; -using namespace std::chrono_literals; - -//------------------------------------------------------------------------------------------ -// special object-c class for handling the NSWindowDidMoveNotification callback. -@interface windowDidMoveNoteClass : NSObject -{ - void *m_userdata; -} -+ (windowDidMoveNoteClass*) initWith: (void*) userdata; -- (void) windowDidMoveNotification:(NSNotification*) note; -@end - -@implementation windowDidMoveNoteClass -+ (windowDidMoveNoteClass*) initWith: (void*) userdata -{ - windowDidMoveNoteClass *windowDidMove = [windowDidMoveNoteClass new]; - windowDidMove->m_userdata = userdata; - return windowDidMove; -} -- (void) windowDidMoveNotification:(NSNotification*) note -{ - CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata; - if (!winsys) - return; - - NSOpenGLContext* context = [NSOpenGLContext currentContext]; - if (context) - { - if ([context view]) - { - NSPoint window_origin = [[[context view] window] frame].origin; - XBMC_Event newEvent = {}; - newEvent.type = XBMC_VIDEOMOVE; - newEvent.move.x = window_origin.x; - newEvent.move.y = window_origin.y; - std::shared_ptr appPort = CServiceBroker::GetAppPort(); - if (appPort) - appPort->OnEvent(newEvent); - } - } -} -@end -//------------------------------------------------------------------------------------------ -// special object-c class for handling the NSWindowDidReSizeNotification callback. -@interface windowDidReSizeNoteClass : NSObject -{ - void *m_userdata; -} -+ (windowDidReSizeNoteClass*) initWith: (void*) userdata; -- (void) windowDidReSizeNotification:(NSNotification*) note; -@end -@implementation windowDidReSizeNoteClass -+ (windowDidReSizeNoteClass*) initWith: (void*) userdata -{ - windowDidReSizeNoteClass *windowDidReSize = [windowDidReSizeNoteClass new]; - windowDidReSize->m_userdata = userdata; - return windowDidReSize; -} -- (void) windowDidReSizeNotification:(NSNotification*) note -{ - CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata; - if (!winsys) - return; - -} -@end - -//------------------------------------------------------------------------------------------ -// special object-c class for handling the NSWindowDidChangeScreenNotification callback. -@interface windowDidChangeScreenNoteClass : NSObject -{ - void *m_userdata; -} -+ (windowDidChangeScreenNoteClass*) initWith: (void*) userdata; -- (void) windowDidChangeScreenNotification:(NSNotification*) note; -@end -@implementation windowDidChangeScreenNoteClass -+ (windowDidChangeScreenNoteClass*) initWith: (void*) userdata -{ - windowDidChangeScreenNoteClass *windowDidChangeScreen = [windowDidChangeScreenNoteClass new]; - windowDidChangeScreen->m_userdata = userdata; - return windowDidChangeScreen; -} -- (void) windowDidChangeScreenNotification:(NSNotification*) note -{ - CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata; - if (!winsys) - return; - winsys->WindowChangedScreen(); -} -@end -//------------------------------------------------------------------------------------------ - -class CWinSystemOSXImpl -{ -public: - NSOpenGLContext* m_glContext; - static NSOpenGLContext* m_lastOwnedContext; - - windowDidMoveNoteClass* m_windowDidMove; - windowDidReSizeNoteClass* m_windowDidReSize; - windowDidChangeScreenNoteClass* m_windowChangedScreen; -}; - -NSOpenGLContext* CWinSystemOSXImpl::m_lastOwnedContext = nil; - - -#define MAX_DISPLAYS 32 -// if there was a devicelost callback -// but no device reset for 3 secs -// a timeout fires the reset callback -// (for ensuring that e.x. AE isn't stuck) -constexpr auto LOST_DEVICE_TIMEOUT_MS = 3000ms; -static NSWindow* blankingWindows[MAX_DISPLAYS]; - -//------------------------------------------------------------------------------------------ -CRect CGRectToCRect(CGRect cgrect) -{ - CRect crect = CRect( - cgrect.origin.x, - cgrect.origin.y, - cgrect.origin.x + cgrect.size.width, - cgrect.origin.y + cgrect.size.height); - return crect; -} -//--------------------------------------------------------------------------------- -void SetMenuBarVisible(bool visible) -{ - if(visible) - { - [[NSApplication sharedApplication] - setPresentationOptions: NSApplicationPresentationDefault]; - } - else - { - [[NSApplication sharedApplication] - setPresentationOptions: NSApplicationPresentationHideMenuBar | - NSApplicationPresentationHideDock]; - } -} -//--------------------------------------------------------------------------------- -CGDirectDisplayID GetDisplayID(int screen_index) -{ - CGDirectDisplayID displayArray[MAX_DISPLAYS]; - CGDisplayCount numDisplays; - - // Get the list of displays. - CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays); - if (screen_index >= 0 && screen_index < numDisplays) - return(displayArray[screen_index]); - else - return(displayArray[0]); -} - -size_t DisplayBitsPerPixelForMode(CGDisplayModeRef mode) -{ - size_t bitsPerPixel = 0; - - CFStringRef pixEnc = CGDisplayModeCopyPixelEncoding(mode); - if(CFStringCompare(pixEnc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - { - bitsPerPixel = 32; - } - else if(CFStringCompare(pixEnc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - { - bitsPerPixel = 16; - } - else if(CFStringCompare(pixEnc, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) - { - bitsPerPixel = 8; - } - - CFRelease(pixEnc); - - return bitsPerPixel; -} - -CFArrayRef GetAllDisplayModes(CGDirectDisplayID display) -{ - int value = 1; - - CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value); - if (!number) - { - CLog::Log(LOGERROR, "GetAllDisplayModes - could not create Number!"); - return NULL; - } - - CFStringRef key = kCGDisplayShowDuplicateLowResolutionModes; - CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&key, (const void **)&number, 1, NULL, NULL); - CFRelease(number); - - if (!options) - { - CLog::Log(LOGERROR, "GetAllDisplayModes - could not create Dictionary!"); - return NULL; - } - - CFArrayRef displayModes = CGDisplayCopyAllDisplayModes(display, options); - CFRelease(options); - - if (!displayModes) - { - CLog::Log(LOGERROR, "GetAllDisplayModes - no displaymodes found!"); - return NULL; - } - - return displayModes; -} - -// mimic former behavior of deprecated CGDisplayBestModeForParameters -CGDisplayModeRef BestMatchForMode(CGDirectDisplayID display, size_t bitsPerPixel, size_t width, size_t height, boolean_t &match) -{ - - // Get a copy of the current display mode - CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(display); - - // Loop through all display modes to determine the closest match. - // CGDisplayBestModeForParameters is deprecated on 10.6 so we will emulate it's behavior - // Try to find a mode with the requested depth and equal or greater dimensions first. - // If no match is found, try to find a mode with greater depth and same or greater dimensions. - // If still no match is found, just use the current mode. - CFArrayRef allModes = GetAllDisplayModes(display); - - for(int i = 0; i < CFArrayGetCount(allModes); i++) { - CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i); - - if(DisplayBitsPerPixelForMode(mode) != bitsPerPixel) - continue; - - if((CGDisplayModeGetWidth(mode) == width) && (CGDisplayModeGetHeight(mode) == height)) - { - CGDisplayModeRelease(displayMode); // release the copy we got before ... - displayMode = mode; - match = true; - break; - } - } - - // No depth match was found - if(!match) - { - for(int i = 0; i < CFArrayGetCount(allModes); i++) - { - CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(allModes, i); - if(DisplayBitsPerPixelForMode(mode) >= bitsPerPixel) - continue; - - if((CGDisplayModeGetWidth(mode) == width) && (CGDisplayModeGetHeight(mode) == height)) - { - displayMode = mode; - match = true; - break; - } - } - } - - CFRelease(allModes); - - return displayMode; -} - -CGDirectDisplayID GetDisplayIDFromScreen(NSScreen *screen) -{ - NSDictionary* screenInfo = [screen deviceDescription]; - NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"]; - - return (CGDirectDisplayID)[screenID longValue]; -} - -int GetDisplayIndex(CGDirectDisplayID display) -{ - CGDirectDisplayID displayArray[MAX_DISPLAYS]; - CGDisplayCount numDisplays; - - // Get the list of displays. - CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays); - while (numDisplays > 0) - { - if (display == displayArray[--numDisplays]) - return numDisplays; - } - return -1; -} - -void BlankOtherDisplays(int screen_index) -{ - int i; - int numDisplays = [[NSScreen screens] count]; - - // zero out blankingWindows for debugging - for (i=0; i 0) - { - screenName = [localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]]; - } - } - - if (screenName == nil) - { - screenName = [[NSString alloc] initWithFormat:@"%i", displayID]; - } - else - { - // ensure screen name is unique by appending displayid - screenName = [screenName stringByAppendingFormat:@" (%@)", [@(displayID) stringValue]]; - } - - return screenName; -} - -int GetDisplayIndex(const std::string& dispName) -{ - int ret = 0; - - // Add full screen settings for additional monitors - int numDisplays = [[NSScreen screens] count]; - - for (int disp = 0; disp < numDisplays; disp++) - { - NSString *name = screenNameForDisplay(GetDisplayID(disp)); - if ([name UTF8String] == dispName) - { - ret = disp; - break; - } - } - - return ret; -} - -void ShowHideNSWindow(NSWindow *wind, bool show) -{ - if (show) - [wind orderFront:nil]; - else - [wind orderOut:nil]; -} - -static NSWindow *curtainWindow; -void fadeInDisplay(NSScreen *theScreen, double fadeTime) -{ - int fadeSteps = 100; - double fadeInterval = (fadeTime / (double) fadeSteps); - - if (curtainWindow != nil) - { - for (int step = 0; step < fadeSteps; step++) - { - double fade = 1.0 - (step * fadeInterval); - [curtainWindow setAlphaValue:fade]; - - NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval]; - [[NSRunLoop currentRunLoop] runUntilDate:nextDate]; - } - } - [curtainWindow close]; - curtainWindow = nil; - - [NSCursor unhide]; -} - -void fadeOutDisplay(NSScreen *theScreen, double fadeTime) -{ - int fadeSteps = 100; - double fadeInterval = (fadeTime / (double) fadeSteps); - - [NSCursor hide]; - - curtainWindow = [[NSWindow alloc] - initWithContentRect:[theScreen frame] - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:YES - screen:theScreen]; - - [curtainWindow setAlphaValue:0.0]; - [curtainWindow setBackgroundColor:[NSColor blackColor]]; - [curtainWindow setLevel:NSScreenSaverWindowLevel]; - - [curtainWindow makeKeyAndOrderFront:nil]; - [curtainWindow setFrame:[curtainWindow - frameRectForContentRect:[theScreen frame]] - display:YES - animate:NO]; - - for (int step = 0; step < fadeSteps; step++) - { - double fade = step * fadeInterval; - [curtainWindow setAlphaValue:fade]; - - NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval]; - [[NSRunLoop currentRunLoop] runUntilDate:nextDate]; - } -} - -// try to find mode that matches the desired size, refreshrate -// non interlaced, nonstretched, safe for hardware -CGDisplayModeRef GetMode(int width, int height, double refreshrate, int screenIdx) -{ - if ( screenIdx >= (signed)[[NSScreen screens] count]) - return NULL; - - Boolean stretched; - Boolean interlaced; - Boolean safeForHardware; - Boolean televisionoutput; - int w, h, bitsperpixel; - double rate; - RESOLUTION_INFO res; - - CLog::Log(LOGDEBUG, "GetMode looking for suitable mode with {} x {} @ {:f} Hz on display {}", - width, height, refreshrate, screenIdx); - - CFArrayRef displayModes = GetAllDisplayModes(GetDisplayID(screenIdx)); - - if (!displayModes) - return NULL; - - for (int i=0; i < CFArrayGetCount(displayModes); ++i) - { - CGDisplayModeRef displayMode = (CGDisplayModeRef)CFArrayGetValueAtIndex(displayModes, i); - uint32_t flags = CGDisplayModeGetIOFlags(displayMode); - stretched = flags & kDisplayModeStretchedFlag ? true : false; - interlaced = flags & kDisplayModeInterlacedFlag ? true : false; - bitsperpixel = DisplayBitsPerPixelForMode(displayMode); - safeForHardware = flags & kDisplayModeSafetyFlags ? true : false; - televisionoutput = flags & kDisplayModeTelevisionFlag ? true : false; - w = CGDisplayModeGetWidth(displayMode); - h = CGDisplayModeGetHeight(displayMode); - rate = CGDisplayModeGetRefreshRate(displayMode); - - - if ((bitsperpixel == 32) && - (safeForHardware == YES) && - (stretched == NO) && - (interlaced == NO) && - (w == width) && - (h == height) && - (rate == refreshrate || rate == 0)) - { - CLog::Log(LOGDEBUG, "GetMode found a match!"); - return displayMode; - } - } - - CFRelease(displayModes); - CLog::Log(LOGERROR, "GetMode - no match found!"); - return NULL; -} - -//--------------------------------------------------------------------------------- -static void DisplayReconfigured(CGDirectDisplayID display, - CGDisplayChangeSummaryFlags flags, void* userData) -{ - CWinSystemOSX *winsys = (CWinSystemOSX*)userData; - if (!winsys) - return; - - CLog::Log(LOGDEBUG, "CWinSystemOSX::DisplayReconfigured with flags {}", flags); - - // we fire the callbacks on start of configuration - // or when the mode set was finished - // or when we are called with flags == 0 (which is undocumented but seems to happen - // on some macs - we treat it as device reset) - - // first check if we need to call OnLostDevice - if (flags & kCGDisplayBeginConfigurationFlag) - { - // pre/post-reconfiguration changes - RESOLUTION res = CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution(); - if (res == RES_INVALID) - return; - - NSScreen* pScreen = nil; - unsigned int screenIdx = 0; - - if ( screenIdx < [[NSScreen screens] count] ) - { - pScreen = [[NSScreen screens] objectAtIndex:screenIdx]; - } - - // kCGDisplayBeginConfigurationFlag is only fired while the screen is still - // valid - if (pScreen) - { - CGDirectDisplayID xbmc_display = GetDisplayIDFromScreen(pScreen); - if (xbmc_display == display) - { - // we only respond to changes on the display we are running on. - winsys->AnnounceOnLostDevice(); - winsys->StartLostDeviceTimer(); - } - } - } - else // the else case checks if we need to call OnResetDevice - { - // we fire if kCGDisplaySetModeFlag is set or if flags == 0 - // (which is undocumented but seems to happen - // on some macs - we treat it as device reset) - // we also don't check the screen here as we might not even have - // one anymore (e.x. when tv is turned off) - if (flags & kCGDisplaySetModeFlag || flags == 0) - { - winsys->StopLostDeviceTimer(); // no need to timeout - we've got the callback - winsys->HandleOnResetDevice(); - } - } - - if ((flags & kCGDisplayAddFlag) || (flags & kCGDisplayRemoveFlag)) - winsys->UpdateResolutions(); -} - -//------------------------------------------------------------------------------ -NSOpenGLContext* CreateWindowedContext(NSOpenGLContext* shareCtx); -void ResizeWindowInternal(int newWidth, int newHeight, int newLeft, int newTop, NSView* last_view); - -//------------------------------------------------------------------------------ -CWinSystemOSX::CWinSystemOSX() - : CWinSystemBase() - , m_impl{new CWinSystemOSXImpl} - , m_lostDeviceTimer(this) -{ - m_SDLSurface = NULL; - m_osx_events = NULL; - m_obscured = false; - m_obscured_timecheck = std::chrono::steady_clock::now() + std::chrono::milliseconds(1000); - m_lastDisplayNr = -1; - m_movedToOtherScreen = false; - m_refreshRate = 0.0; - m_delayDispReset = false; - - m_winEvents.reset(new CWinEventsOSX()); - - AE::CAESinkFactory::ClearSinks(); - CAESinkDARWINOSX::Register(); - m_dpms = std::make_shared(); -} - -CWinSystemOSX::~CWinSystemOSX() = default; - -void CWinSystemOSX::StartLostDeviceTimer() -{ - if (m_lostDeviceTimer.IsRunning()) - m_lostDeviceTimer.Restart(); - else - m_lostDeviceTimer.Start(LOST_DEVICE_TIMEOUT_MS, false); -} - -void CWinSystemOSX::StopLostDeviceTimer() -{ - m_lostDeviceTimer.Stop(); -} - -void CWinSystemOSX::OnTimeout() -{ - HandleOnResetDevice(); -} - -bool CWinSystemOSX::InitWindowSystem() -{ - CLog::LogF(LOGINFO, "Setup SDL"); - - /* Clean up on exit, exit on window close and interrupt */ - std::atexit(SDL_Quit); - - if (SDL_Init(SDL_INIT_VIDEO) != 0) - { - CLog::LogF(LOGFATAL, "Unable to initialize SDL: {}", SDL_GetError()); - return false; - } - // SDL_Init will install a handler for segfaults, restore the default handler. - signal(SIGSEGV, SIG_DFL); - - SDL_EnableUNICODE(1); - - // set repeat to 10ms to ensure repeat time < frame time - // so that hold times can be reliably detected - SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 10); - - if (!CWinSystemBase::InitWindowSystem()) - return false; - - m_osx_events = new CWinEventsOSX(); - - CGDisplayRegisterReconfigurationCallback(DisplayReconfigured, (void*)this); - - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - auto windowDidMove = [windowDidMoveNoteClass initWith:this]; - [center addObserver:windowDidMove - selector:@selector(windowDidMoveNotification:) - name:NSWindowDidMoveNotification object:nil]; - m_impl->m_windowDidMove = windowDidMove; - - auto windowDidReSize = [windowDidReSizeNoteClass initWith:this]; - [center addObserver:windowDidReSize - selector:@selector(windowDidReSizeNotification:) - name:NSWindowDidResizeNotification object:nil]; - m_impl->m_windowDidReSize = windowDidReSize; - - auto windowDidChangeScreen = [windowDidChangeScreenNoteClass initWith:this]; - [center addObserver:windowDidChangeScreen - selector:@selector(windowDidChangeScreenNotification:) - name:NSWindowDidChangeScreenNotification object:nil]; - m_impl->m_windowChangedScreen = windowDidChangeScreen; - - return true; -} - -bool CWinSystemOSX::DestroyWindowSystem() -{ - NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; - [center removeObserver:m_impl->m_windowDidMove name:NSWindowDidMoveNotification object:nil]; - [center removeObserver:m_impl->m_windowDidReSize name:NSWindowDidResizeNotification object:nil]; - [center removeObserver:m_impl->m_windowChangedScreen - name:NSWindowDidChangeScreenNotification - object:nil]; - - CGDisplayRemoveReconfigurationCallback(DisplayReconfigured, (void*)this); - - delete m_osx_events; - m_osx_events = NULL; - - UnblankDisplays(); - m_impl->m_glContext = nil; - return true; -} - -bool CWinSystemOSX::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) -{ - // force initial window creation to be windowed, if fullscreen, it will switch to it below - // fixes the white screen of death if starting fullscreen and switching to windowed. - RESOLUTION_INFO resInfo = CDisplaySettings::GetInstance().GetResolutionInfo(RES_WINDOW); - m_nWidth = resInfo.iWidth; - m_nHeight = resInfo.iHeight; - m_bFullScreen = false; - - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - - // Enable vertical sync to avoid any tearing. - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); - - m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, SDL_OPENGL | SDL_RESIZABLE); - if (!m_SDLSurface) - return false; - - // the context SDL creates isn't full screen compatible, so we create new one - // first, find the current contect and make sure a view is attached - NSOpenGLContext* cur_context = [NSOpenGLContext currentContext]; - NSView* view = [cur_context view]; - if (!view) - return false; - - if (CDisplaySettings::GetInstance().GetCurrentResolution() != RES_WINDOW) - { - // If we are not starting up windowed, then hide the initial SDL window - // so we do not see it flash before the fade-out and switch to fullscreen. - ShowHideNSWindow([view window], false); - } - - // disassociate view from context - [cur_context clearDrawable]; - - // release the context - if (CWinSystemOSXImpl::m_lastOwnedContext == cur_context) - { - [ NSOpenGLContext clearCurrentContext ]; - [ cur_context clearDrawable ]; - cur_context = nil; - } - - // create a new context - auto new_context = CreateWindowedContext(nil); - if (!new_context) - return false; - - // associate with current view - [new_context setView:view]; - [new_context makeCurrentContext]; - - // set the window title - [[[new_context view] window] - setTitle:[NSString stringWithFormat:@"%s Media Center", CCompileInfo::GetAppName()]]; - - m_impl->m_glContext = new_context; - CWinSystemOSXImpl::m_lastOwnedContext = new_context; - m_bWindowCreated = true; - - // get screen refreshrate - this is needed - // when we startup in windowed mode and don't run through SetFullScreen - int dummy; - GetScreenResolution(&dummy, &dummy, &m_refreshRate, m_lastDisplayNr); - - // register platform dependent objects - CDVDFactoryCodec::ClearHWAccels(); - VTB::CDecoder::Register(); - VIDEOPLAYER::CRendererFactory::ClearRenderer(); - CLinuxRendererGL::Register(); - CRendererVTB::Register(); - VIDEOPLAYER::CProcessInfoOSX::Register(); - RETRO::CRPProcessInfoOSX::Register(); - RETRO::CRPProcessInfoOSX::RegisterRendererFactory(new RETRO::CRendererFactoryOpenGL); - CScreenshotSurfaceGL::Register(); - - return true; -} - -bool CWinSystemOSX::DestroyWindow() -{ - return true; -} - -extern "C" void SDL_SetWidthHeight(int w, int h); -void ResizeWindowInternal(int newWidth, int newHeight, int newLeft, int newTop, NSView* last_view) -{ - if (last_view && [last_view window]) - { - auto size = NSMakeSize(newWidth, newHeight); - NSWindow* lastWindow = [last_view window]; - [lastWindow setContentSize:size]; - [lastWindow update]; - [last_view setFrameSize:size]; - } -} -bool CWinSystemOSX::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) -{ - if (!m_impl->m_glContext) - return false; - - NSOpenGLContext* context = [NSOpenGLContext currentContext]; - NSView* view; - NSWindow* window; - - view = [context view]; - - if (view) - { - // It seems, that in macOS 10.15 this defaults to YES, but we currently do not support - // Retina resolutions properly. Ensure that the view uses a 1 pixel per point framebuffer. - view.wantsBestResolutionOpenGLSurface = NO; - } - - if (view && (newWidth > 0) && (newHeight > 0)) - { - window = [view window]; - if (window) - { - [window setContentSize:NSMakeSize(newWidth, newHeight)]; - [window update]; - [view setFrameSize:NSMakeSize(newWidth, newHeight)]; - [context update]; - // this is needed in case we traverse from fullscreen screen 2 - // to windowed on screen 1 directly where in ScreenChangedNotification - // we don't have a window to get the current screen on - // in that case ResizeWindow is called at a later stage from SetFullScreen(false) - // and we can grab the correct display number here then - m_lastDisplayNr = GetDisplayIndex(GetDisplayIDFromScreen( [window screen] )); - } - } - - // HACK: resize SDL's view manually so that mouse bounds are correctly updated. - // there are two parts to this, the internal SDL (current_video->screen) and - // the cocoa view ( handled in SetFullScreen). - SDL_SetWidthHeight(newWidth, newHeight); - - [context makeCurrentContext]; - - m_nWidth = newWidth; - m_nHeight = newHeight; - m_impl->m_glContext = context; - CServiceBroker::GetWinSystem()->GetGfxContext().SetFPS(m_refreshRate); - - return true; -} - -static bool needtoshowme = true; - -bool CWinSystemOSX::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) -{ - static NSWindow* windowedFullScreenwindow = NULL; - static NSScreen* last_window_screen = NULL; - static NSPoint last_window_origin; - static NSView* last_view = NULL; - static NSSize last_view_size; - static NSPoint last_view_origin; - static NSInteger last_window_level = NSNormalWindowLevel; - bool was_fullscreen = m_bFullScreen; - NSOpenGLContext* cur_context; - - // Fade to black to hide resolution-switching flicker and garbage. - CGDisplayFadeReservationToken fade_token = DisplayFadeToBlack(needtoshowme); - - // If we're already fullscreen then we must be moving to a different display. - // or if we are still on the same display - it might be only a refreshrate/resolution - // change request. - // Recurse to reset fullscreen mode and then continue. - if (was_fullscreen && fullScreen) - { - needtoshowme = false; - ShowHideNSWindow([last_view window], needtoshowme); - RESOLUTION_INFO& window = CDisplaySettings::GetInstance().GetResolutionInfo(RES_WINDOW); - CWinSystemOSX::SetFullScreen(false, window, blankOtherDisplays); - needtoshowme = true; - } - - const std::shared_ptr settings = CServiceBroker::GetSettingsComponent()->GetSettings(); - m_lastDisplayNr = GetDisplayIndex(settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR)); - m_nWidth = res.iWidth; - m_nHeight = res.iHeight; - m_bFullScreen = fullScreen; - - cur_context = [NSOpenGLContext currentContext]; - - //handle resolution/refreshrate switching early here - if (m_bFullScreen) - { - // switch videomode - SwitchToVideoMode(res.iWidth, res.iHeight, static_cast(res.fRefreshRate)); - } - - //no context? done. - if (!cur_context) - { - DisplayFadeFromBlack(fade_token, needtoshowme); - return false; - } - - if (windowedFullScreenwindow != NULL) - { - [windowedFullScreenwindow close]; - windowedFullScreenwindow = nil; - } - - if (m_bFullScreen) - { - // FullScreen Mode - NSOpenGLContext* newContext = NULL; - - // Save info about the windowed context so we can restore it when returning to windowed. - last_view = [cur_context view]; - last_view_size = [last_view frame].size; - last_view_origin = [last_view frame].origin; - last_window_screen = [[last_view window] screen]; - last_window_origin = [[last_view window] frame].origin; - last_window_level = [[last_view window] level]; - - // This is Cocoa Windowed FullScreen Mode - // Get the screen rect of our current display - NSScreen* pScreen = [[NSScreen screens] objectAtIndex:m_lastDisplayNr]; - NSRect screenRect = [pScreen frame]; - - // remove frame origin offset of original display - screenRect.origin = NSZeroPoint; - - // make a new window to act as the windowedFullScreen - windowedFullScreenwindow = [[NSWindow alloc] initWithContentRect:screenRect - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:NO - screen:pScreen]; - - [windowedFullScreenwindow setBackgroundColor:[NSColor blackColor]]; - [windowedFullScreenwindow makeKeyAndOrderFront:nil]; - - // make our window the same level as the rest to enable cmd+tab switching - [windowedFullScreenwindow setLevel:NSNormalWindowLevel]; - // this will make our window topmost and hide all system messages - //[windowedFullScreenwindow setLevel:CGShieldingWindowLevel()]; - - // ...and the original one beneath it and on the same screen. - [[last_view window] setLevel:NSNormalWindowLevel-1]; - [[last_view window] setFrameOrigin:[pScreen frame].origin]; - // expand the mouse bounds in SDL view to fullscreen - [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)]; - [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ]; - - NSView* blankView = [[NSView alloc] init]; - [windowedFullScreenwindow setContentView:blankView]; - [windowedFullScreenwindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)]; - [windowedFullScreenwindow update]; - [blankView setFrameSize:NSMakeSize(m_nWidth, m_nHeight)]; - - // Obtain windowed pixel format and create a new context. - newContext = CreateWindowedContext(cur_context); - [newContext setView:blankView]; - - // Hide the menu bar. - SetMenuBarVisible(false); - - // Blank other displays if requested. - if (blankOtherDisplays) - BlankOtherDisplays(m_lastDisplayNr); - - // Hide the mouse. - [NSCursor hide]; - - // Release old context if we created it. - if (CWinSystemOSXImpl::m_lastOwnedContext == cur_context) - { - [ NSOpenGLContext clearCurrentContext ]; - [ cur_context clearDrawable ]; - } - - // activate context - [newContext makeCurrentContext]; - CWinSystemOSXImpl::m_lastOwnedContext = newContext; - } - else - { - // Windowed Mode - // exit fullscreen - [cur_context clearDrawable]; - - [NSCursor unhide]; - - // Show menubar. - SetMenuBarVisible(true); - - // restore the windowed window level - [[last_view window] setLevel:last_window_level]; - - // Get rid of the new window we created. - if (windowedFullScreenwindow != nil) - { - [windowedFullScreenwindow close]; - windowedFullScreenwindow = nil; - } - - // Unblank. - // Force the unblank when returning from fullscreen, we get called with blankOtherDisplays set false. - //if (blankOtherDisplays) - UnblankDisplays(); - - // create our new context (sharing with the current one) - auto newContext = CreateWindowedContext(cur_context); - if (!newContext) - return false; - - // Assign view from old context, move back to original screen. - [newContext setView:last_view]; - [[last_view window] setFrameOrigin:last_window_origin]; - // return the mouse bounds in SDL view to previous size - [ last_view setFrameSize:last_view_size ]; - [ last_view setFrameOrigin:last_view_origin ]; - // done with restoring windowed window, don't set last_view to NULL as we can lose it under dual displays. - //last_window_screen = NULL; - - // Release the fullscreen context. - if (CWinSystemOSXImpl::m_lastOwnedContext == cur_context) - { - [ NSOpenGLContext clearCurrentContext ]; - [ cur_context clearDrawable ]; - } - - // Activate context. - [newContext makeCurrentContext]; - CWinSystemOSXImpl::m_lastOwnedContext = newContext; - } - - DisplayFadeFromBlack(fade_token, needtoshowme); - - ShowHideNSWindow([last_view window], needtoshowme); - // need to make sure SDL tracks any window size changes - ResizeWindow(m_nWidth, m_nHeight, -1, -1); - ResizeWindowInternal(m_nWidth, m_nHeight, -1, -1, last_view); - // restore origin once again when going to windowed mode - if (!fullScreen) - { - [[last_view window] setFrameOrigin:last_window_origin]; - } - HandlePossibleRefreshrateChange(); - - m_updateGLContext = 0; - return true; -} - -void CWinSystemOSX::UpdateResolutions() -{ - CWinSystemBase::UpdateResolutions(); - - // Add desktop resolution - int w, h; - double fps; - - int dispIdx = GetDisplayIndex(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR)); - GetScreenResolution(&w, &h, &fps, dispIdx); - NSString* dispName = screenNameForDisplay(GetDisplayID(dispIdx)); - UpdateDesktopResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP), [dispName UTF8String], 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(); - CDisplaySettings::GetInstance().ApplyCalibrations(); -} - -/* -void* Cocoa_GL_CreateContext(void* pixFmt, void* shareCtx) -{ - if (!pixFmt) - return nil; - - NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt - shareContext:(NSOpenGLContext*)shareCtx]; - - // snipit from SDL_cocoaopengl.m - // - // Wisdom from Apple engineer in reference to UT2003's OpenGL performance: - // "You are blowing a couple of the internal OpenGL function caches. This - // appears to be happening in the VAO case. You can tell OpenGL to up - // the cache size by issuing the following calls right after you create - // the OpenGL context. The default cache size is 16." --ryan. - // - - #ifndef GLI_ARRAY_FUNC_CACHE_MAX - #define GLI_ARRAY_FUNC_CACHE_MAX 284 - #endif - - #ifndef GLI_SUBMIT_FUNC_CACHE_MAX - #define GLI_SUBMIT_FUNC_CACHE_MAX 280 - #endif - - { - long cache_max = 64; - CGLContextObj ctx = (CGLContextObj)[newContext CGLContextObj]; - CGLSetParameter(ctx, (CGLContextParameter)GLI_SUBMIT_FUNC_CACHE_MAX, &cache_max); - CGLSetParameter(ctx, (CGLContextParameter)GLI_ARRAY_FUNC_CACHE_MAX, &cache_max); - } - - // End Wisdom from Apple Engineer section. --ryan. - return newContext; -} -*/ - -NSOpenGLContext* CreateWindowedContext(NSOpenGLContext* shareCtx) -{ - NSOpenGLPixelFormat* pixFmt; - if (getenv("KODI_GL_PROFILE_LEGACY")) - { - NSOpenGLPixelFormatAttribute wattrs[] = { - NSOpenGLPFADoubleBuffer, - NSOpenGLPFANoRecovery, - NSOpenGLPFAAccelerated, - NSOpenGLPFADepthSize, - static_cast(8), - static_cast(0)}; - pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs]; - } - else - { - NSOpenGLPixelFormatAttribute wattrs_gl3[] = { - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAOpenGLProfile, - NSOpenGLProfileVersion3_2Core, - NSOpenGLPFANoRecovery, - NSOpenGLPFAAccelerated, - NSOpenGLPFADepthSize, - static_cast(24), - static_cast(0)}; - pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs_gl3]; - } - - auto newContext = [[NSOpenGLContext alloc] initWithFormat:pixFmt shareContext:shareCtx]; - - if (!newContext) - { - // bah, try again for non-accelerated renderer - NSOpenGLPixelFormatAttribute wattrs2[] = - { - NSOpenGLPFADoubleBuffer, - NSOpenGLPFANoRecovery, - NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8, - (NSOpenGLPixelFormatAttribute)0 - }; - newContext = [[NSOpenGLContext alloc] - initWithFormat:[[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs2] - shareContext:shareCtx]; - } - - return newContext; -} - -NSOpenGLContext* CreateFullScreenContext(int screen_index, NSOpenGLContext* shareCtx) -{ - CGDirectDisplayID displayArray[MAX_DISPLAYS]; - CGDisplayCount numDisplays; - CGDirectDisplayID displayID; - - // Get the list of displays. - CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays); - displayID = displayArray[screen_index]; - - NSOpenGLPixelFormat* pixFmt; - if (getenv("KODI_GL_PROFILE_LEGACY")) - { - NSOpenGLPixelFormatAttribute fsattrs[] = { - NSOpenGLPFADoubleBuffer, - NSOpenGLPFANoRecovery, - NSOpenGLPFAAccelerated, - NSOpenGLPFADepthSize, - static_cast(8), - NSOpenGLPFAScreenMask, - static_cast(CGDisplayIDToOpenGLDisplayMask(displayID)), - static_cast(0)}; - pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs]; - } - else - { - NSOpenGLPixelFormatAttribute fsattrs_gl3[] = { - NSOpenGLPFADoubleBuffer, - NSOpenGLPFANoRecovery, - NSOpenGLPFAAccelerated, - NSOpenGLPFADepthSize, - static_cast(24), - NSOpenGLPFAOpenGLProfile, - NSOpenGLProfileVersion3_2Core, - NSOpenGLPFAScreenMask, - static_cast(CGDisplayIDToOpenGLDisplayMask(displayID)), - static_cast(0)}; - pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs_gl3]; - } - - if (!pixFmt) - return nil; - - auto newContext = [[NSOpenGLContext alloc] initWithFormat:pixFmt shareContext:shareCtx]; - - return newContext; -} - -void CWinSystemOSX::GetScreenResolution(int* w, int* h, double* fps, int screenIdx) -{ - CGDirectDisplayID display_id = (CGDirectDisplayID)GetDisplayID(screenIdx); - CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display_id); - *w = CGDisplayModeGetWidth(mode); - *h = CGDisplayModeGetHeight(mode); - *fps = CGDisplayModeGetRefreshRate(mode); - CGDisplayModeRelease(mode); - if ((int)*fps == 0) - { - // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays. - *fps = 60.0; - } -} - -void CWinSystemOSX::EnableVSync(bool enable) -{ - // OpenGL Flush synchronised with vertical retrace - GLint swapInterval = enable ? 1 : 0; - [[NSOpenGLContext currentContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; -} - -bool CWinSystemOSX::SwitchToVideoMode(int width, int height, double refreshrate) -{ - boolean_t match = false; - CGDisplayModeRef dispMode = NULL; - - int screenIdx = GetDisplayIndex(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR)); - - // Figure out the screen size. (default to main screen) - CGDirectDisplayID display_id = GetDisplayID(screenIdx); - - // find mode that matches the desired size, refreshrate - // non interlaced, nonstretched, safe for hardware - dispMode = GetMode(width, height, refreshrate, screenIdx); - - //not found - fallback to bestemdeforparameters - if (!dispMode) - { - dispMode = BestMatchForMode(display_id, 32, width, height, match); - - if (!match) - dispMode = BestMatchForMode(display_id, 16, width, height, match); - - // still no match? fallback to current resolution of the display which HAS to work [tm] - if (!match) - { - int tmpWidth; - int tmpHeight; - double tmpRefresh; - - GetScreenResolution(&tmpWidth, &tmpHeight, &tmpRefresh, screenIdx); - dispMode = GetMode(tmpWidth, tmpHeight, tmpRefresh, screenIdx); - - // no way to get a resolution set - if (!dispMode) - return false; - } - - if (!match) - return false; - } - - // switch mode and return success - CGDisplayCapture(display_id); - CGDisplayConfigRef cfg; - CGBeginDisplayConfiguration(&cfg); - CGConfigureDisplayWithDisplayMode(cfg, display_id, dispMode, nullptr); - CGError err = CGCompleteDisplayConfiguration(cfg, kCGConfigureForAppOnly); - CGDisplayRelease(display_id); - - m_refreshRate = CGDisplayModeGetRefreshRate(dispMode); - - Cocoa_CVDisplayLinkUpdate(); - - return (err == kCGErrorSuccess); -} - -void CWinSystemOSX::FillInVideoModes() -{ - int dispIdx = GetDisplayIndex(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR)); - - // Add full screen settings for additional monitors - int numDisplays = [[NSScreen screens] count]; - - for (int disp = 0; disp < numDisplays; disp++) - { - Boolean stretched; - Boolean interlaced; - Boolean safeForHardware; - Boolean televisionoutput; - int w, h, bitsperpixel; - double refreshrate; - RESOLUTION_INFO res; - - CFArrayRef displayModes = GetAllDisplayModes(GetDisplayID(disp)); - NSString *dispName = screenNameForDisplay(GetDisplayID(disp)); - - CLog::Log(LOGINFO, "Display {} has name {}", disp, [dispName UTF8String]); - - if (NULL == displayModes) - continue; - - for (int i=0; i < CFArrayGetCount(displayModes); ++i) - { - CGDisplayModeRef displayMode = (CGDisplayModeRef)CFArrayGetValueAtIndex(displayModes, i); - - uint32_t flags = CGDisplayModeGetIOFlags(displayMode); - stretched = flags & kDisplayModeStretchedFlag ? true : false; - interlaced = flags & kDisplayModeInterlacedFlag ? true : false; - bitsperpixel = DisplayBitsPerPixelForMode(displayMode); - safeForHardware = flags & kDisplayModeSafetyFlags ? true : false; - televisionoutput = flags & kDisplayModeTelevisionFlag ? true : false; - - if ((bitsperpixel == 32) && - (safeForHardware == YES) && - (stretched == NO) && - (interlaced == NO)) - { - w = CGDisplayModeGetWidth(displayMode); - h = CGDisplayModeGetHeight(displayMode); - refreshrate = CGDisplayModeGetRefreshRate(displayMode); - if ((int)refreshrate == 0) // LCD display? - { - // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays. - refreshrate = 60.0; - } - CLog::Log(LOGINFO, "Found possible resolution for display {} with {} x {} @ {:f} Hz", disp, - w, h, refreshrate); - - // only add the resolution if it belongs to "our" screen - // all others are only logged above... - if (disp == dispIdx) - { - UpdateDesktopResolution(res, (dispName != nil) ? [dispName UTF8String] : "Unknown", w, h, refreshrate, 0); - CServiceBroker::GetWinSystem()->GetGfxContext().ResetOverscan(res); - CDisplaySettings::GetInstance().AddResolutionInfo(res); - } - } - } - CFRelease(displayModes); - } -} - -bool CWinSystemOSX::FlushBuffer(void) -{ - if (m_updateGLContext < 5) - { - [m_impl->m_glContext update]; - m_updateGLContext++; - } - - [m_impl->m_glContext flushBuffer]; - - return true; -} - -bool CWinSystemOSX::IsObscured(void) -{ - // check once a second if we are obscured. - auto now_time = std::chrono::steady_clock::now(); - if (m_obscured_timecheck > now_time) - return m_obscured; - else - m_obscured_timecheck = now_time + std::chrono::milliseconds(1000); - - NSOpenGLContext* cur_context = [NSOpenGLContext currentContext]; - NSView* view = [cur_context view]; - if (!view) - { - // sanity check, we should always have a view - m_obscured = true; - return m_obscured; - } - - NSWindow *window = [view window]; - if (!window) - { - // sanity check, we should always have a window - m_obscured = true; - return m_obscured; - } - - if ([window isVisible] == NO) - { - // not visible means the window is not showing. - // this should never really happen as we are always visible - // even when minimized in dock. - m_obscured = true; - return m_obscured; - } - - // check if we are minimized (to an icon in the Dock). - if ([window isMiniaturized] == YES) - { - m_obscured = true; - return m_obscured; - } - - // check if we are showing on the active workspace. - if ([window isOnActiveSpace] == NO) - { - m_obscured = true; - return m_obscured; - } - - // default to false before we start parsing though the windows. - // if we are are obscured by any windows, then set true. - m_obscured = false; - static bool obscureLogged = false; - - CGWindowListOption opts; - opts = kCGWindowListOptionOnScreenAboveWindow | kCGWindowListExcludeDesktopElements; - CFArrayRef windowIDs =CGWindowListCreate(opts, (CGWindowID)[window windowNumber]); - - if (!windowIDs) - return m_obscured; - - CFArrayRef windowDescs = CGWindowListCreateDescriptionFromArray(windowIDs); - if (!windowDescs) - { - CFRelease(windowIDs); - return m_obscured; - } - - CGRect bounds = NSRectToCGRect([window frame]); - // kCGWindowBounds measures the origin as the top-left corner of the rectangle - // relative to the top-left corner of the screen. - // NSWindow’s frame property measures the origin as the bottom-left corner - // of the rectangle relative to the bottom-left corner of the screen. - // convert bounds from NSWindow to CGWindowBounds here. - bounds.origin.y = [[window screen] frame].size.height - bounds.origin.y - bounds.size.height; - - std::vector partialOverlaps; - CRect ourBounds = CGRectToCRect(bounds); - - for (CFIndex idx=0; idx < CFArrayGetCount(windowDescs); idx++) - { - // walk the window list of windows that are above us and are not desktop elements - CFDictionaryRef windowDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(windowDescs, idx); - - // skip the Dock window, it actually covers the entire screen. - CFStringRef ownerName = (CFStringRef)CFDictionaryGetValue(windowDictionary, kCGWindowOwnerName); - if (CFStringCompare(ownerName, CFSTR("Dock"), 0) == kCFCompareEqualTo) - continue; - - // Ignore known brightness tools for dimming the screen. They claim to cover - // the whole XBMC window and therefore would make the framerate limiter - // kicking in. Unfortunately even the alpha of these windows is 1.0 so - // we have to check the ownerName. - if (CFStringCompare(ownerName, CFSTR("Shades"), 0) == kCFCompareEqualTo || - CFStringCompare(ownerName, CFSTR("SmartSaver"), 0) == kCFCompareEqualTo || - CFStringCompare(ownerName, CFSTR("Brightness Slider"), 0) == kCFCompareEqualTo || - CFStringCompare(ownerName, CFSTR("Displaperture"), 0) == kCFCompareEqualTo || - CFStringCompare(ownerName, CFSTR("Dreamweaver"), 0) == kCFCompareEqualTo || - CFStringCompare(ownerName, CFSTR("Window Server"), 0) == kCFCompareEqualTo) - continue; - - CFDictionaryRef rectDictionary = (CFDictionaryRef)CFDictionaryGetValue(windowDictionary, kCGWindowBounds); - if (!rectDictionary) - continue; - - CGRect windowBounds; - if (CGRectMakeWithDictionaryRepresentation(rectDictionary, &windowBounds)) - { - if (CGRectContainsRect(windowBounds, bounds)) - { - // if the windowBounds completely encloses our bounds, we are obscured. - if (!obscureLogged) - { - std::string appName; - if (CDarwinUtils::CFStringRefToUTF8String(ownerName, appName)) - CLog::Log(LOGDEBUG, "WinSystemOSX: Fullscreen window {} obscures Kodi!", appName); - obscureLogged = true; - } - m_obscured = true; - break; - } - - // handle overlapping windows above us that combine - // to obscure by collecting any partial overlaps, - // then subtract them from our bounds and check - // for any remaining area. - CRect intersection = CGRectToCRect(windowBounds); - intersection.Intersect(ourBounds); - if (!intersection.IsEmpty()) - partialOverlaps.push_back(intersection); - } - } - - if (!m_obscured) - { - // if we are here we are not obscured by any fullscreen window - reset flag - // for allowing the logmessage above to show again if this changes. - if (obscureLogged) - obscureLogged = false; - std::vector rects = ourBounds.SubtractRects(partialOverlaps); - // they got us covered - if (rects.empty()) - m_obscured = true; - } - - CFRelease(windowDescs); - CFRelease(windowIDs); - - return m_obscured; -} - -void CWinSystemOSX::NotifyAppFocusChange(bool bGaining) -{ - if (!(m_bFullScreen && bGaining)) - return; - @autoreleasepool - { - // find the window - NSOpenGLContext* context = [NSOpenGLContext currentContext]; - if (context) - { - NSView* view; - - view = [context view]; - if (view) - { - NSWindow* window; - window = [view window]; - if (window) - { - SetMenuBarVisible(false); - [window orderFront:nil]; - } - } - } - } -} - -void CWinSystemOSX::ShowOSMouse(bool show) -{ - SDL_ShowCursor(show ? 1 : 0); -} - -bool CWinSystemOSX::Minimize() -{ - @autoreleasepool - { - [[NSApplication sharedApplication] miniaturizeAll:nil]; - } - return true; -} - -bool CWinSystemOSX::Restore() -{ - @autoreleasepool - { - [[NSApplication sharedApplication] unhide:nil]; - } - return true; -} - -bool CWinSystemOSX::Hide() -{ - @autoreleasepool - { - [[NSApplication sharedApplication] hide:nil]; - } - return true; -} - -void CWinSystemOSX::HandlePossibleRefreshrateChange() -{ - static double oldRefreshRate = m_refreshRate; - Cocoa_CVDisplayLinkUpdate(); - int dummy = 0; - - GetScreenResolution(&dummy, &dummy, &m_refreshRate, m_lastDisplayNr); - - if (oldRefreshRate != m_refreshRate) - { - oldRefreshRate = m_refreshRate; - // send a message so that videoresolution (and refreshrate) - // is changed - CApplicationMessenger::GetInstance().PostMsg(TMSG_VIDEORESIZE, m_SDLSurface->w, m_SDLSurface->h); - } -} - -void CWinSystemOSX::OnMove(int x, int y) -{ - HandlePossibleRefreshrateChange(); -} - -std::unique_ptr CWinSystemOSX::GetOSScreenSaverImpl() -{ - return std::unique_ptr (new COSScreenSaverOSX); -} - -OSXTextInputResponder *g_textInputResponder = nil; - -void CWinSystemOSX::StartTextInput() -{ - NSView *parentView = [[NSApp keyWindow] contentView]; - - /* We only keep one field editor per process, since only the front most - * window can receive text input events, so it make no sense to keep more - * than one copy. When we switched to another window and requesting for - * text input, simply remove the field editor from its superview then add - * it to the front most window's content view */ - if (!g_textInputResponder) { - g_textInputResponder = - [[OSXTextInputResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)]; - } - - if (![[g_textInputResponder superview] isEqual: parentView]) - { -// DLOG(@"add fieldEdit to window contentView"); - [g_textInputResponder removeFromSuperview]; - [parentView addSubview: g_textInputResponder]; - [[NSApp keyWindow] makeFirstResponder: g_textInputResponder]; - } -} -void CWinSystemOSX::StopTextInput() -{ - if (g_textInputResponder) - { - [g_textInputResponder removeFromSuperview]; - g_textInputResponder = nil; - } -} - -void CWinSystemOSX::Register(IDispResource *resource) -{ - CSingleLock lock(m_resourceSection); - m_resources.push_back(resource); -} - -void CWinSystemOSX::Unregister(IDispResource* resource) -{ - CSingleLock lock(m_resourceSection); - std::vector::iterator i = find(m_resources.begin(), m_resources.end(), resource); - if (i != m_resources.end()) - m_resources.erase(i); -} - -bool CWinSystemOSX::Show(bool raise) -{ - @autoreleasepool - { - auto app = [NSApplication sharedApplication]; - if (raise) - { - [app unhide:nil]; - [app activateIgnoringOtherApps:YES]; - [app arrangeInFront:nil]; - } - else - { - [app unhideWithoutActivation]; - } - } - return true; -} - -void CWinSystemOSX::WindowChangedScreen() -{ - // user has moved the window to a - // different screen - NSOpenGLContext* context = [NSOpenGLContext currentContext]; - m_lastDisplayNr = -1; - - // if we are here the user dragged the window to a different - // screen and we return the screen of the window - if (context) - { - NSView* view; - - view = [context view]; - if (view) - { - NSWindow* window; - window = [view window]; - if (window) - { - m_lastDisplayNr = GetDisplayIndex(GetDisplayIDFromScreen([window screen])); - } - } - } - if (m_lastDisplayNr == -1) - m_lastDisplayNr = 0;// default to main screen -} - -void CWinSystemOSX::AnnounceOnLostDevice() -{ - CSingleLock lock(m_resourceSection); - // tell any shared resources - CLog::Log(LOGDEBUG, "CWinSystemOSX::AnnounceOnLostDevice"); - for (std::vector::iterator i = m_resources.begin(); i != m_resources.end(); ++i) - (*i)->OnLostDisplay(); -} - -void CWinSystemOSX::HandleOnResetDevice() -{ - - int delay = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt("videoscreen.delayrefreshchange"); - if (delay > 0) - { - m_delayDispReset = true; - m_dispResetTimer.Set(delay * 100); - } - else - { - AnnounceOnResetDevice(); - } -} - -void CWinSystemOSX::AnnounceOnResetDevice() -{ - double currentFps = m_refreshRate; - int w = 0; - int h = 0; - int currentScreenIdx = m_lastDisplayNr; - // ensure that graphics context knows about the current refreshrate before - // doing the callbacks - GetScreenResolution(&w, &h, ¤tFps, currentScreenIdx); - - CServiceBroker::GetWinSystem()->GetGfxContext().SetFPS(currentFps); - - CSingleLock lock(m_resourceSection); - // tell any shared resources - CLog::Log(LOGDEBUG, "CWinSystemOSX::AnnounceOnResetDevice"); - for (std::vector::iterator i = m_resources.begin(); i != m_resources.end(); ++i) - (*i)->OnResetDisplay(); -} - -void* CWinSystemOSX::GetCGLContextObj() -{ - return [m_impl->m_glContext CGLContextObj]; -} - -NSOpenGLContext* CWinSystemOSX::GetNSOpenGLContext() -{ - return m_impl->m_glContext; -} - -std::string CWinSystemOSX::GetClipboardText(void) -{ - std::string utf8_text; - - const char *szStr = Cocoa_Paste(); - if (szStr) - utf8_text = szStr; - - return utf8_text; -} - -std::unique_ptr CWinSystemOSX::GetVideoSync(void *clock) -{ - std::unique_ptr pVSync(new CVideoSyncOsx(clock)); - return pVSync; -} - -bool CWinSystemOSX::MessagePump() -{ - return m_winEvents->MessagePump(); -} - -void CWinSystemOSX::GetConnectedOutputs(std::vector *outputs) -{ - outputs->push_back("Default"); - - int numDisplays = [[NSScreen screens] count]; - - for (int disp = 0; disp < numDisplays; disp++) - { - NSString *dispName = screenNameForDisplay(GetDisplayID(disp)); - outputs->push_back([dispName UTF8String]); - } -} diff --git a/xbmc/windowing/osx/WinSystemOSXGL.h b/xbmc/windowing/osx/WinSystemOSXGL.h index e40fe1abde..9b5ee5fdca 100644 --- a/xbmc/windowing/osx/WinSystemOSXGL.h +++ b/xbmc/windowing/osx/WinSystemOSXGL.h @@ -8,7 +8,9 @@ #pragma once -#include "WinSystemOSX.h" +#if defined(HAS_SDL) +#include "windowing/osx/SDL/WinSystemOSXSDL.h" +#endif #include "rendering/gl/RenderSystemGL.h" class CWinSystemOSXGL : public CWinSystemOSX, public CRenderSystemGL -- cgit v1.2.3