2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #if defined(TARGET_DARWIN_OSX)
23 //hack around problem with xbmc's typedef int BOOL
24 // and obj-c's typedef unsigned char BOOL
25 #define BOOL XBMC_BOOL
26 #include "WinSystemOSX.h"
27 #include "WinEventsOSX.h"
28 #include "Application.h"
29 #include "guilib/DispResource.h"
30 #include "guilib/GUIWindowManager.h"
31 #include "settings/DisplaySettings.h"
32 #include "settings/Settings.h"
33 #include "settings/DisplaySettings.h"
34 #include "input/KeyboardStat.h"
35 #include "threads/SingleLock.h"
36 #include "utils/log.h"
37 #include "utils/StringUtils.h"
38 #include "osx/XBMCHelper.h"
39 #include "utils/SystemInfo.h"
40 #include "osx/CocoaInterface.h"
41 #include "osx/DarwinUtils.h"
44 #import <SDL/SDL_video.h>
45 #import <SDL/SDL_events.h>
47 #import <Cocoa/Cocoa.h>
48 #import <QuartzCore/QuartzCore.h>
49 #import <IOKit/pwr_mgt/IOPMLib.h>
50 #import <IOKit/graphics/IOGraphicsLib.h>
51 #import "osx/OSXTextInputResponder.h"
53 // turn off deprecated warning spew.
54 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
56 //------------------------------------------------------------------------------------------
57 // special object-c class for handling the inhibit display NSTimer callback.
58 @interface windowInhibitScreenSaverClass : NSObject
59 - (void) updateSystemActivity: (NSTimer*)timer;
62 @implementation windowInhibitScreenSaverClass
63 -(void) updateSystemActivity: (NSTimer*)timer
65 UpdateSystemActivity(UsrActivity);
69 //------------------------------------------------------------------------------------------
70 // special object-c class for handling the NSWindowDidMoveNotification callback.
71 @interface windowDidMoveNoteClass : NSObject
75 + initWith: (void*) userdata;
76 - (void) windowDidMoveNotification:(NSNotification*) note;
79 @implementation windowDidMoveNoteClass
80 + initWith: (void*) userdata;
82 windowDidMoveNoteClass *windowDidMove = [windowDidMoveNoteClass new];
83 windowDidMove->m_userdata = userdata;
84 return [windowDidMove autorelease];
86 - (void) windowDidMoveNotification:(NSNotification*) note;
88 CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
92 NSOpenGLContext* context = [NSOpenGLContext currentContext];
97 NSPoint window_origin = [[[context view] window] frame].origin;
99 memset(&newEvent, 0, sizeof(newEvent));
100 newEvent.type = XBMC_VIDEOMOVE;
101 newEvent.move.x = window_origin.x;
102 newEvent.move.y = window_origin.y;
103 g_application.OnEvent(newEvent);
108 //------------------------------------------------------------------------------------------
109 // special object-c class for handling the NSWindowDidReSizeNotification callback.
110 @interface windowDidReSizeNoteClass : NSObject
114 + initWith: (void*) userdata;
115 - (void) windowDidReSizeNotification:(NSNotification*) note;
117 @implementation windowDidReSizeNoteClass
118 + initWith: (void*) userdata;
120 windowDidReSizeNoteClass *windowDidReSize = [windowDidReSizeNoteClass new];
121 windowDidReSize->m_userdata = userdata;
122 return [windowDidReSize autorelease];
124 - (void) windowDidReSizeNotification:(NSNotification*) note;
126 CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
129 /* placeholder, do not uncomment or you will SDL recurse into death
130 NSOpenGLContext* context = [NSOpenGLContext currentContext];
135 NSSize view_size = [[context view] frame].size;
137 memset(&newEvent, 0, sizeof(newEvent));
138 newEvent.type = XBMC_VIDEORESIZE;
139 newEvent.resize.w = view_size.width;
140 newEvent.resize.h = view_size.height;
141 if (newEvent.resize.w * newEvent.resize.h)
143 g_application.OnEvent(newEvent);
144 g_windowManager.MarkDirty();
152 //------------------------------------------------------------------------------------------
153 // special object-c class for handling the NSWindowDidChangeScreenNotification callback.
154 @interface windowDidChangeScreenNoteClass : NSObject
158 + initWith: (void*) userdata;
159 - (void) windowDidChangeScreenNotification:(NSNotification*) note;
161 @implementation windowDidChangeScreenNoteClass
162 + initWith: (void*) userdata;
164 windowDidChangeScreenNoteClass *windowDidChangeScreen = [windowDidChangeScreenNoteClass new];
165 windowDidChangeScreen->m_userdata = userdata;
166 return [windowDidChangeScreen autorelease];
168 - (void) windowDidChangeScreenNotification:(NSNotification*) note;
170 CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
173 winsys->WindowChangedScreen();
176 //------------------------------------------------------------------------------------------
179 #define MAX_DISPLAYS 32
180 static NSWindow* blankingWindows[MAX_DISPLAYS];
182 void* CWinSystemOSX::m_lastOwnedContext = 0;
184 //------------------------------------------------------------------------------------------
185 CRect CGRectToCRect(CGRect cgrect)
190 cgrect.origin.x + cgrect.size.width,
191 cgrect.origin.y + cgrect.size.height);
195 //------------------------------------------------------------------------------------------
196 Boolean GetDictionaryBoolean(CFDictionaryRef theDict, const void* key)
198 // get a boolean from the dictionary
199 Boolean value = false;
200 CFBooleanRef boolRef;
201 boolRef = (CFBooleanRef)CFDictionaryGetValue(theDict, key);
203 value = CFBooleanGetValue(boolRef);
206 //------------------------------------------------------------------------------------------
207 long GetDictionaryLong(CFDictionaryRef theDict, const void* key)
209 // get a long from the dictionary
212 numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
214 CFNumberGetValue(numRef, kCFNumberLongType, &value);
217 //------------------------------------------------------------------------------------------
218 int GetDictionaryInt(CFDictionaryRef theDict, const void* key)
220 // get a long from the dictionary
223 numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
225 CFNumberGetValue(numRef, kCFNumberIntType, &value);
228 //------------------------------------------------------------------------------------------
229 float GetDictionaryFloat(CFDictionaryRef theDict, const void* key)
231 // get a long from the dictionary
234 numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
236 CFNumberGetValue(numRef, kCFNumberFloatType, &value);
239 //------------------------------------------------------------------------------------------
240 double GetDictionaryDouble(CFDictionaryRef theDict, const void* key)
242 // get a long from the dictionary
245 numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
247 CFNumberGetValue(numRef, kCFNumberDoubleType, &value);
251 //---------------------------------------------------------------------------------
252 void SetMenuBarVisible(bool visible)
256 [[NSApplication sharedApplication]
257 setPresentationOptions: NSApplicationPresentationDefault];
261 [[NSApplication sharedApplication]
262 setPresentationOptions: NSApplicationPresentationHideMenuBar |
263 NSApplicationPresentationHideDock];
266 //---------------------------------------------------------------------------------
267 CGDirectDisplayID GetDisplayID(int screen_index)
269 CGDirectDisplayID displayArray[MAX_DISPLAYS];
270 CGDisplayCount numDisplays;
272 // Get the list of displays.
273 CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
274 return(displayArray[screen_index]);
277 CGDirectDisplayID GetDisplayIDFromScreen(NSScreen *screen)
279 NSDictionary* screenInfo = [screen deviceDescription];
280 NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
282 return (CGDirectDisplayID)[screenID longValue];
285 int GetDisplayIndex(CGDirectDisplayID display)
287 CGDirectDisplayID displayArray[MAX_DISPLAYS];
288 CGDisplayCount numDisplays;
290 // Get the list of displays.
291 CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
292 while (numDisplays > 0)
294 if (display == displayArray[--numDisplays])
300 void BlankOtherDisplays(int screen_index)
303 int numDisplays = [[NSScreen screens] count];
305 // zero out blankingWindows for debugging
306 for (i=0; i<MAX_DISPLAYS; i++)
308 blankingWindows[i] = 0;
312 for (i=0; i<numDisplays; i++)
314 if (i != screen_index)
317 NSScreen* pScreen = [[NSScreen screens] objectAtIndex:i];
318 NSRect screenRect = [pScreen frame];
320 // Build a blanking window.
321 screenRect.origin = NSZeroPoint;
322 blankingWindows[i] = [[NSWindow alloc] initWithContentRect:screenRect
323 styleMask:NSBorderlessWindowMask
324 backing:NSBackingStoreBuffered
328 [blankingWindows[i] setBackgroundColor:[NSColor blackColor]];
329 [blankingWindows[i] setLevel:CGShieldingWindowLevel()];
330 [blankingWindows[i] makeKeyAndOrderFront:nil];
335 void UnblankDisplays(void)
337 int numDisplays = [[NSScreen screens] count];
340 for (i=0; i<numDisplays; i++)
342 if (blankingWindows[i] != 0)
344 // Get rid of the blanking windows we created.
345 [blankingWindows[i] close];
346 if ([blankingWindows[i] isReleasedWhenClosed] == NO)
347 [blankingWindows[i] release];
348 blankingWindows[i] = 0;
353 CGDisplayFadeReservationToken DisplayFadeToBlack(bool fade)
355 // Fade to black to hide resolution-switching flicker and garbage.
356 CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
357 if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess && fade)
358 CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
363 void DisplayFadeFromBlack(CGDisplayFadeReservationToken fade_token, bool fade)
365 if (fade_token != kCGDisplayFadeReservationInvalidToken)
368 CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
369 CGReleaseDisplayFadeReservation(fade_token);
373 NSString* screenNameForDisplay(CGDirectDisplayID displayID)
375 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
377 NSString *screenName = nil;
379 NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName);
380 NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
382 if ([localizedNames count] > 0) {
383 screenName = [[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] retain];
386 [deviceInfo release];
389 return [screenName autorelease];
392 void ShowHideNSWindow(NSWindow *wind, bool show)
395 [wind orderFront:nil];
400 static NSWindow *curtainWindow;
401 void fadeInDisplay(NSScreen *theScreen, double fadeTime)
404 double fadeInterval = (fadeTime / (double) fadeSteps);
406 if (curtainWindow != nil)
408 for (int step = 0; step < fadeSteps; step++)
410 double fade = 1.0 - (step * fadeInterval);
411 [curtainWindow setAlphaValue:fade];
413 NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
414 [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
417 [curtainWindow close];
423 void fadeOutDisplay(NSScreen *theScreen, double fadeTime)
426 double fadeInterval = (fadeTime / (double) fadeSteps);
430 curtainWindow = [[NSWindow alloc]
431 initWithContentRect:[theScreen frame]
432 styleMask:NSBorderlessWindowMask
433 backing:NSBackingStoreBuffered
437 [curtainWindow setAlphaValue:0.0];
438 [curtainWindow setBackgroundColor:[NSColor blackColor]];
439 [curtainWindow setLevel:NSScreenSaverWindowLevel];
441 [curtainWindow makeKeyAndOrderFront:nil];
442 [curtainWindow setFrame:[curtainWindow
443 frameRectForContentRect:[theScreen frame]]
447 for (int step = 0; step < fadeSteps; step++)
449 double fade = step * fadeInterval;
450 [curtainWindow setAlphaValue:fade];
452 NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
453 [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
457 // try to find mode that matches the desired size, refreshrate
458 // non interlaced, nonstretched, safe for hardware
459 CFDictionaryRef GetMode(int width, int height, double refreshrate, int screenIdx)
461 if ( screenIdx >= (signed)[[NSScreen screens] count])
466 Boolean safeForHardware;
467 Boolean televisionoutput;
468 int w, h, bitsperpixel;
472 CLog::Log(LOGDEBUG, "GetMode looking for suitable mode with %d x %d @ %f Hz on display %d\n", width, height, refreshrate, screenIdx);
474 CFArrayRef displayModes = CGDisplayAvailableModes(GetDisplayID(screenIdx));
476 if (NULL == displayModes)
478 CLog::Log(LOGERROR, "GetMode - no displaymodes found!");
482 for (int i=0; i < CFArrayGetCount(displayModes); ++i)
484 CFDictionaryRef displayMode = (CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i);
486 stretched = GetDictionaryBoolean(displayMode, kCGDisplayModeIsStretched);
487 interlaced = GetDictionaryBoolean(displayMode, kCGDisplayModeIsInterlaced);
488 bitsperpixel = GetDictionaryInt(displayMode, kCGDisplayBitsPerPixel);
489 safeForHardware = GetDictionaryBoolean(displayMode, kCGDisplayModeIsSafeForHardware);
490 televisionoutput = GetDictionaryBoolean(displayMode, kCGDisplayModeIsTelevisionOutput);
491 w = GetDictionaryInt(displayMode, kCGDisplayWidth);
492 h = GetDictionaryInt(displayMode, kCGDisplayHeight);
493 rate = GetDictionaryDouble(displayMode, kCGDisplayRefreshRate);
496 if ((bitsperpixel == 32) &&
497 (safeForHardware == YES) &&
499 (interlaced == NO) &&
502 (rate == refreshrate || rate == 0))
504 CLog::Log(LOGDEBUG, "GetMode found a match!");
508 CLog::Log(LOGERROR, "GetMode - no match found!");
512 //---------------------------------------------------------------------------------
513 static void DisplayReconfigured(CGDirectDisplayID display,
514 CGDisplayChangeSummaryFlags flags, void* userData)
516 CWinSystemOSX *winsys = (CWinSystemOSX*)userData;
520 if (flags & kCGDisplaySetModeFlag || flags & kCGDisplayBeginConfigurationFlag)
522 // pre/post-reconfiguration changes
523 RESOLUTION res = g_graphicsContext.GetVideoResolution();
524 if (res == RES_INVALID)
527 NSScreen* pScreen = nil;
528 unsigned int screenIdx = CDisplaySettings::Get().GetResolutionInfo(res).iScreen;
530 if ( screenIdx < [[NSScreen screens] count] )
532 pScreen = [[NSScreen screens] objectAtIndex:screenIdx];
537 CGDirectDisplayID xbmc_display = GetDisplayIDFromScreen(pScreen);
538 if (xbmc_display == display)
540 // we only respond to changes on the display we are running on.
541 CLog::Log(LOGDEBUG, "CWinSystemOSX::DisplayReconfigured");
542 winsys->CheckDisplayChanging(flags);
548 //---------------------------------------------------------------------------------
549 //---------------------------------------------------------------------------------
550 CWinSystemOSX::CWinSystemOSX() : CWinSystemBase()
552 m_eWindowSystem = WINDOW_SYSTEM_OSX;
557 m_obscured_timecheck = XbmcThreads::SystemClockMillis() + 1000;
558 m_use_system_screensaver = true;
559 // check runtime, we only allow this on 10.5+
560 m_can_display_switch = (floor(NSAppKitVersionNumber) >= 949);
561 m_lastDisplayNr = -1;
562 m_movedToOtherScreen = false;
565 CWinSystemOSX::~CWinSystemOSX()
569 bool CWinSystemOSX::InitWindowSystem()
571 SDL_EnableUNICODE(1);
573 // set repeat to 10ms to ensure repeat time < frame time
574 // so that hold times can be reliably detected
575 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 10);
577 if (!CWinSystemBase::InitWindowSystem())
580 m_osx_events = new CWinEventsOSX();
582 if (m_can_display_switch)
583 CGDisplayRegisterReconfigurationCallback(DisplayReconfigured, (void*)this);
585 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
586 windowDidMoveNoteClass *windowDidMove;
587 windowDidMove = [windowDidMoveNoteClass initWith: this];
588 [center addObserver:windowDidMove
589 selector:@selector(windowDidMoveNotification:)
590 name:NSWindowDidMoveNotification object:nil];
591 m_windowDidMove = windowDidMove;
594 windowDidReSizeNoteClass *windowDidReSize;
595 windowDidReSize = [windowDidReSizeNoteClass initWith: this];
596 [center addObserver:windowDidReSize
597 selector:@selector(windowDidReSizeNotification:)
598 name:NSWindowDidResizeNotification object:nil];
599 m_windowDidReSize = windowDidReSize;
601 windowDidChangeScreenNoteClass *windowDidChangeScreen;
602 windowDidChangeScreen = [windowDidChangeScreenNoteClass initWith: this];
603 [center addObserver:windowDidChangeScreen
604 selector:@selector(windowDidChangeScreenNotification:)
605 name:NSWindowDidChangeScreenNotification object:nil];
606 m_windowChangedScreen = windowDidChangeScreen;
611 bool CWinSystemOSX::DestroyWindowSystem()
613 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
614 [center removeObserver:(windowDidMoveNoteClass*)m_windowDidMove name:NSWindowDidMoveNotification object:nil];
615 [center removeObserver:(windowDidReSizeNoteClass*)m_windowDidReSize name:NSWindowDidResizeNotification object:nil];
616 [center removeObserver:(windowDidChangeScreenNoteClass*)m_windowChangedScreen name:NSWindowDidChangeScreenNotification object:nil];
618 if (m_can_display_switch)
619 CGDisplayRemoveReconfigurationCallback(DisplayReconfigured, (void*)this);
627 NSOpenGLContext* oldContext = (NSOpenGLContext*)m_glContext;
628 [oldContext release];
634 bool CWinSystemOSX::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction)
636 m_nWidth = res.iWidth;
637 m_nHeight = res.iHeight;
638 m_bFullScreen = fullScreen;
640 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
641 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
642 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
643 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
644 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
646 // Enable vertical sync to avoid any tearing.
647 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
649 m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, SDL_OPENGL | SDL_RESIZABLE);
653 // the context SDL creates isn't full screen compatible, so we create new one
654 // first, find the current contect and make sure a view is attached
655 NSOpenGLContext* cur_context = [NSOpenGLContext currentContext];
656 NSView* view = [cur_context view];
660 // if we are not starting up windowed, then hide the initial SDL window
661 // so we do not see it flash before the fade-out and switch to fullscreen.
662 if (CDisplaySettings::Get().GetCurrentResolution() != RES_WINDOW)
663 ShowHideNSWindow([view window], false);
665 // disassociate view from context
666 [cur_context clearDrawable];
668 // release the context
669 if (m_lastOwnedContext == cur_context)
671 [ NSOpenGLContext clearCurrentContext ];
672 [ cur_context clearDrawable ];
673 [ cur_context release ];
676 // create a new context
677 NSOpenGLContext* new_context = (NSOpenGLContext*)CreateWindowedContext(nil);
681 // associate with current view
682 [new_context setView:view];
683 [new_context makeCurrentContext];
685 // set the window title
687 string = [ [ NSString alloc ] initWithUTF8String:"XBMC Media Center" ];
688 [ [ [new_context view] window] setTitle:string ];
691 m_glContext = new_context;
692 m_lastOwnedContext = new_context;
693 m_bWindowCreated = true;
698 bool CWinSystemOSX::DestroyWindow()
705 static int isMavericks = -1;
707 // there is no NSAppKitVersionNumber10_9 out there anywhere
708 // so we detect mavericks by one of these newly added app nap
709 // methods - and fix the ugly mouse rect problem which was hitting
710 // us when mavericks came out
711 if (isMavericks == -1)
713 CLog::Log(LOGDEBUG, "Detected Mavericks - enable windowing fixups.");
714 isMavericks = [NSProcessInfo instancesRespondToSelector:@selector(beginActivityWithOptions:reason:)] == TRUE ? 1 : 0;
716 return isMavericks == 1;
719 extern "C" void SDL_SetWidthHeight(int w, int h);
720 bool CWinSystemOSX::ResizeWindowInternal(int newWidth, int newHeight, int newLeft, int newTop, void *additional)
722 bool ret = ResizeWindow(newWidth, newHeight, newLeft, newTop);
726 NSView * last_view = (NSView *)additional;
727 if (last_view && [last_view window])
729 NSWindow* lastWindow = [last_view window];
730 [lastWindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)];
732 [last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight)];
737 bool CWinSystemOSX::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
742 NSOpenGLContext* context = [NSOpenGLContext currentContext];
746 view = [context view];
747 if (view && (newWidth > 0) && (newHeight > 0))
749 window = [view window];
752 [window setContentSize:NSMakeSize(newWidth, newHeight)];
754 [view setFrameSize:NSMakeSize(newWidth, newHeight)];
759 // HACK: resize SDL's view manually so that mouse bounds are correctly updated.
760 // there are two parts to this, the internal SDL (current_video->screen) and
761 // the cocoa view ( handled in SetFullScreen).
762 SDL_SetWidthHeight(newWidth, newHeight);
764 [context makeCurrentContext];
767 m_nHeight = newHeight;
768 m_glContext = context;
773 static bool needtoshowme = true;
775 bool CWinSystemOSX::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
777 static NSWindow* windowedFullScreenwindow = NULL;
778 static NSScreen* last_window_screen = NULL;
779 static NSPoint last_window_origin;
780 static NSView* last_view = NULL;
781 static NSSize last_view_size;
782 static NSPoint last_view_origin;
783 static NSInteger last_window_level = NSNormalWindowLevel;
784 bool was_fullscreen = m_bFullScreen;
785 NSOpenGLContext* cur_context;
787 if (m_lastDisplayNr == -1)
788 m_lastDisplayNr = res.iScreen;
790 // Fade to black to hide resolution-switching flicker and garbage.
791 CGDisplayFadeReservationToken fade_token = DisplayFadeToBlack(needtoshowme);
793 // If we're already fullscreen then we must be moving to a different display.
794 // or if we are still on the same display - it might be only a refreshrate/resolution
796 // Recurse to reset fullscreen mode and then continue.
797 if (was_fullscreen && fullScreen)
799 needtoshowme = false;
800 ShowHideNSWindow([last_view window], needtoshowme);
801 RESOLUTION_INFO& window = CDisplaySettings::Get().GetResolutionInfo(RES_WINDOW);
802 CWinSystemOSX::SetFullScreen(false, window, blankOtherDisplays);
806 m_nWidth = res.iWidth;
807 m_nHeight = res.iHeight;
808 m_bFullScreen = fullScreen;
810 cur_context = [NSOpenGLContext currentContext];
812 //handle resolution/refreshrate switching early here
815 if (m_can_display_switch)
817 // send pre-configuration change now and do not
818 // wait for switch videomode callback. This gives just
819 // a little more advanced notice of the display pre-change.
820 if (CSettings::Get().GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF)
821 CheckDisplayChanging(kCGDisplayBeginConfigurationFlag);
824 SwitchToVideoMode(res.iWidth, res.iHeight, res.fRefreshRate, res.iScreen);
825 m_lastDisplayNr = res.iScreen;
832 DisplayFadeFromBlack(fade_token, needtoshowme);
836 if (windowedFullScreenwindow != NULL)
838 [windowedFullScreenwindow close];
839 if ([windowedFullScreenwindow isReleasedWhenClosed] == NO)
840 [windowedFullScreenwindow release];
841 windowedFullScreenwindow = NULL;
847 NSOpenGLContext* newContext = NULL;
849 // Save info about the windowed context so we can restore it when returning to windowed.
850 last_view = [cur_context view];
851 last_view_size = [last_view frame].size;
852 last_view_origin = [last_view frame].origin;
853 last_window_screen = [[last_view window] screen];
854 last_window_origin = [[last_view window] frame].origin;
855 last_window_level = [[last_view window] level];
857 if (CSettings::Get().GetBool("videoscreen.fakefullscreen"))
859 // This is Cocca Windowed FullScreen Mode
860 // Get the screen rect of our current display
861 NSScreen* pScreen = [[NSScreen screens] objectAtIndex:res.iScreen];
862 NSRect screenRect = [pScreen frame];
864 // remove frame origin offset of orginal display
865 screenRect.origin = NSZeroPoint;
867 // make a new window to act as the windowedFullScreen
868 windowedFullScreenwindow = [[NSWindow alloc] initWithContentRect:screenRect
869 styleMask:NSBorderlessWindowMask
870 backing:NSBackingStoreBuffered
874 [windowedFullScreenwindow setBackgroundColor:[NSColor blackColor]];
875 [windowedFullScreenwindow makeKeyAndOrderFront:nil];
877 // make our window the same level as the rest to enable cmd+tab switching
878 [windowedFullScreenwindow setLevel:NSNormalWindowLevel];
879 // this will make our window topmost and hide all system messages
880 //[windowedFullScreenwindow setLevel:CGShieldingWindowLevel()];
882 // ...and the original one beneath it and on the same screen.
883 [[last_view window] setLevel:NSNormalWindowLevel-1];
884 [[last_view window] setFrameOrigin:[pScreen frame].origin];
885 // expand the mouse bounds in SDL view to fullscreen
886 [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)];
887 [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ];
889 NSView* blankView = [[NSView alloc] init];
890 [windowedFullScreenwindow setContentView:blankView];
891 [windowedFullScreenwindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)];
892 [windowedFullScreenwindow update];
893 [blankView setFrameSize:NSMakeSize(m_nWidth, m_nHeight)];
895 // Obtain windowed pixel format and create a new context.
896 newContext = (NSOpenGLContext*)CreateWindowedContext((void* )cur_context);
897 [newContext setView:blankView];
899 // Hide the menu bar.
900 if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || isMavericks() )
901 SetMenuBarVisible(false);
903 // Blank other displays if requested.
904 if (blankOtherDisplays)
905 BlankOtherDisplays(res.iScreen);
910 [[last_view window] setFrameOrigin:[last_window_screen frame].origin];
911 // expand the mouse bounds in SDL view to fullscreen
912 [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)];
913 [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ];
915 // This is OpenGL FullScreen Mode
916 // create our new context (sharing with the current one)
917 newContext = (NSOpenGLContext*)CreateFullScreenContext(res.iScreen, (void*)cur_context);
921 // clear the current context
922 [NSOpenGLContext clearCurrentContext];
925 [newContext setFullScreen];
927 // Capture the display before going fullscreen.
928 if (blankOtherDisplays == true)
929 CGCaptureAllDisplays();
931 CGDisplayCapture(GetDisplayID(res.iScreen));
933 // If we don't hide menu bar, it will get events and interrupt the program.
934 if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || isMavericks() )
935 SetMenuBarVisible(false);
941 // Release old context if we created it.
942 if (m_lastOwnedContext == cur_context)
944 [ NSOpenGLContext clearCurrentContext ];
945 [ cur_context clearDrawable ];
946 [ cur_context release ];
950 [newContext makeCurrentContext];
951 m_lastOwnedContext = newContext;
957 [cur_context clearDrawable];
962 if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || isMavericks() )
963 SetMenuBarVisible(true);
965 if (CSettings::Get().GetBool("videoscreen.fakefullscreen"))
967 // restore the windowed window level
968 [[last_view window] setLevel:last_window_level];
970 // Get rid of the new window we created.
971 if (windowedFullScreenwindow != NULL)
973 [windowedFullScreenwindow close];
974 if ([windowedFullScreenwindow isReleasedWhenClosed] == NO)
975 [windowedFullScreenwindow release];
976 windowedFullScreenwindow = NULL;
980 // Force the unblank when returning from fullscreen, we get called with blankOtherDisplays set false.
981 //if (blankOtherDisplays)
987 CGReleaseAllDisplays();
990 // create our new context (sharing with the current one)
991 NSOpenGLContext* newContext = (NSOpenGLContext*)CreateWindowedContext((void* )cur_context);
995 // Assign view from old context, move back to original screen.
996 [newContext setView:last_view];
997 [[last_view window] setFrameOrigin:last_window_origin];
998 // return the mouse bounds in SDL view to prevous size
999 [ last_view setFrameSize:last_view_size ];
1000 [ last_view setFrameOrigin:last_view_origin ];
1001 // done with restoring windowed window, don't set last_view to NULL as we can lose it under dual displays.
1002 //last_window_screen = NULL;
1004 // Release the fullscreen context.
1005 if (m_lastOwnedContext == cur_context)
1007 [ NSOpenGLContext clearCurrentContext ];
1008 [ cur_context clearDrawable ];
1009 [ cur_context release ];
1012 // Activate context.
1013 [newContext makeCurrentContext];
1014 m_lastOwnedContext = newContext;
1017 DisplayFadeFromBlack(fade_token, needtoshowme);
1019 ShowHideNSWindow([last_view window], needtoshowme);
1020 // need to make sure SDL tracks any window size changes
1021 ResizeWindowInternal(m_nWidth, m_nHeight, -1, -1, last_view);
1026 void CWinSystemOSX::UpdateResolutions()
1028 CWinSystemBase::UpdateResolutions();
1030 // Add desktop resolution
1034 // first screen goes into the current desktop mode
1035 GetScreenResolution(&w, &h, &fps, 0);
1036 UpdateDesktopResolution(CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP), 0, w, h, fps);
1038 // see resolution.h enum RESOLUTION for how the resolutions
1039 // have to appear in the resolution info vector in CDisplaySettings
1040 // add the desktop resolutions of the other screens
1041 for(int i = 1; i < GetNumScreens(); i++)
1043 RESOLUTION_INFO res;
1044 // get current resolution of screen i
1045 GetScreenResolution(&w, &h, &fps, i);
1046 UpdateDesktopResolution(res, i, w, h, fps);
1047 CDisplaySettings::Get().AddResolutionInfo(res);
1050 if (m_can_display_switch)
1052 // now just fill in the possible reolutions for the attached screens
1053 // and push to the resolution info vector
1059 void* Cocoa_GL_CreateContext(void* pixFmt, void* shareCtx)
1064 NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1065 shareContext:(NSOpenGLContext*)shareCtx];
1067 // snipit from SDL_cocoaopengl.m
1069 // Wisdom from Apple engineer in reference to UT2003's OpenGL performance:
1070 // "You are blowing a couple of the internal OpenGL function caches. This
1071 // appears to be happening in the VAO case. You can tell OpenGL to up
1072 // the cache size by issuing the following calls right after you create
1073 // the OpenGL context. The default cache size is 16." --ryan.
1076 #ifndef GLI_ARRAY_FUNC_CACHE_MAX
1077 #define GLI_ARRAY_FUNC_CACHE_MAX 284
1080 #ifndef GLI_SUBMIT_FUNC_CACHE_MAX
1081 #define GLI_SUBMIT_FUNC_CACHE_MAX 280
1085 long cache_max = 64;
1086 CGLContextObj ctx = (CGLContextObj)[newContext CGLContextObj];
1087 CGLSetParameter(ctx, (CGLContextParameter)GLI_SUBMIT_FUNC_CACHE_MAX, &cache_max);
1088 CGLSetParameter(ctx, (CGLContextParameter)GLI_ARRAY_FUNC_CACHE_MAX, &cache_max);
1091 // End Wisdom from Apple Engineer section. --ryan.
1096 void* CWinSystemOSX::CreateWindowedContext(void* shareCtx)
1098 NSOpenGLContext* newContext = NULL;
1100 NSOpenGLPixelFormatAttribute wattrs[] =
1102 NSOpenGLPFADoubleBuffer,
1104 NSOpenGLPFANoRecovery,
1105 NSOpenGLPFAAccelerated,
1106 NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1107 (NSOpenGLPixelFormatAttribute)0
1110 NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs];
1112 newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1113 shareContext:(NSOpenGLContext*)shareCtx];
1118 // bah, try again for non-accelerated renderer
1119 NSOpenGLPixelFormatAttribute wattrs2[] =
1121 NSOpenGLPFADoubleBuffer,
1123 NSOpenGLPFANoRecovery,
1124 NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1125 (NSOpenGLPixelFormatAttribute)0
1127 NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs2];
1129 newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1130 shareContext:(NSOpenGLContext*)shareCtx];
1137 void* CWinSystemOSX::CreateFullScreenContext(int screen_index, void* shareCtx)
1139 CGDirectDisplayID displayArray[MAX_DISPLAYS];
1140 CGDisplayCount numDisplays;
1141 CGDirectDisplayID displayID;
1143 // Get the list of displays.
1144 CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
1145 displayID = displayArray[screen_index];
1147 NSOpenGLPixelFormatAttribute fsattrs[] =
1149 NSOpenGLPFADoubleBuffer,
1150 NSOpenGLPFAFullScreen,
1151 NSOpenGLPFANoRecovery,
1152 NSOpenGLPFAAccelerated,
1153 NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1154 NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(displayID),
1155 (NSOpenGLPixelFormatAttribute)0
1158 NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs];
1162 NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1163 shareContext:(NSOpenGLContext*)shareCtx];
1169 void CWinSystemOSX::GetScreenResolution(int* w, int* h, double* fps, int screenIdx)
1171 // Figure out the screen size. (default to main screen)
1172 if (screenIdx >= GetNumScreens())
1174 CGDirectDisplayID display_id = (CGDirectDisplayID)GetDisplayID(screenIdx);
1176 NSOpenGLContext* context = [NSOpenGLContext currentContext];
1181 view = [context view];
1185 window = [view window];
1187 display_id = GetDisplayIDFromScreen( [window screen] );
1190 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display_id);
1191 *w = CGDisplayModeGetWidth(mode);
1192 *h = CGDisplayModeGetHeight(mode);
1193 *fps = CGDisplayModeGetRefreshRate(mode);
1194 CGDisplayModeRelease(mode);
1197 // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
1202 void CWinSystemOSX::EnableVSync(bool enable)
1204 // OpenGL Flush synchronised with vertical retrace
1205 GLint swapInterval = enable ? 1 : 0;
1206 [[NSOpenGLContext currentContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
1209 bool CWinSystemOSX::SwitchToVideoMode(int width, int height, double refreshrate, int screenIdx)
1211 // SwitchToVideoMode will not return until the display has actually switched over.
1212 // This can take several seconds.
1213 if( screenIdx >= GetNumScreens())
1216 boolean_t match = false;
1217 CFDictionaryRef dispMode = NULL;
1218 // Figure out the screen size. (default to main screen)
1219 CGDirectDisplayID display_id = GetDisplayID(screenIdx);
1221 // find mode that matches the desired size, refreshrate
1222 // non interlaced, nonstretched, safe for hardware
1223 dispMode = GetMode(width, height, refreshrate, screenIdx);
1225 //not found - fallback to bestemdeforparameters
1228 dispMode = CGDisplayBestModeForParameters(display_id, 32, width, height, &match);
1231 dispMode = CGDisplayBestModeForParameters(display_id, 16, width, height, &match);
1237 // switch mode and return success
1238 CGDisplayCapture(display_id);
1239 CGDisplayConfigRef cfg;
1240 CGBeginDisplayConfiguration(&cfg);
1241 // we don't need to do this, we are already faded.
1242 //CGConfigureDisplayFadeEffect(cfg, 0.3f, 0.5f, 0, 0, 0);
1243 CGConfigureDisplayMode(cfg, display_id, dispMode);
1244 CGError err = CGCompleteDisplayConfiguration(cfg, kCGConfigureForAppOnly);
1245 CGDisplayRelease(display_id);
1247 Cocoa_CVDisplayLinkUpdate();
1249 return (err == kCGErrorSuccess);
1252 void CWinSystemOSX::FillInVideoModes()
1254 // Add full screen settings for additional monitors
1255 int numDisplays = [[NSScreen screens] count];
1257 for (int disp = 0; disp < numDisplays; disp++)
1261 Boolean safeForHardware;
1262 Boolean televisionoutput;
1263 int w, h, bitsperpixel;
1265 RESOLUTION_INFO res;
1267 CFArrayRef displayModes = CGDisplayAvailableModes(GetDisplayID(disp));
1268 NSString *dispName = screenNameForDisplay(GetDisplayID(disp));
1269 CLog::Log(LOGNOTICE, "Display %i has name %s", disp, [dispName UTF8String]);
1271 if (NULL == displayModes)
1274 for (int i=0; i < CFArrayGetCount(displayModes); ++i)
1276 CFDictionaryRef displayMode = (CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i);
1278 stretched = GetDictionaryBoolean(displayMode, kCGDisplayModeIsStretched);
1279 interlaced = GetDictionaryBoolean(displayMode, kCGDisplayModeIsInterlaced);
1280 bitsperpixel = GetDictionaryInt(displayMode, kCGDisplayBitsPerPixel);
1281 safeForHardware = GetDictionaryBoolean(displayMode, kCGDisplayModeIsSafeForHardware);
1282 televisionoutput = GetDictionaryBoolean(displayMode, kCGDisplayModeIsTelevisionOutput);
1284 if ((bitsperpixel == 32) &&
1285 (safeForHardware == YES) &&
1286 (stretched == NO) &&
1289 w = GetDictionaryInt(displayMode, kCGDisplayWidth);
1290 h = GetDictionaryInt(displayMode, kCGDisplayHeight);
1291 refreshrate = GetDictionaryDouble(displayMode, kCGDisplayRefreshRate);
1292 if ((int)refreshrate == 0) // LCD display?
1294 // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
1297 CLog::Log(LOGNOTICE, "Found possible resolution for display %d with %d x %d @ %f Hz\n", disp, w, h, refreshrate);
1299 UpdateDesktopResolution(res, disp, w, h, refreshrate);
1301 // overwrite the mode str because UpdateDesktopResolution adds a
1302 // "Full Screen". Since the current resolution is there twice
1303 // this would lead to 2 identical resolution entrys in the guisettings.xml.
1304 // That would cause problems with saving screen overscan calibration
1305 // because the wrong entry is picked on load.
1306 // So we just use UpdateDesktopResolutions for the current DESKTOP_RESOLUTIONS
1307 // in UpdateResolutions. And on all othere resolutions make a unique
1308 // mode str by doing it without appending "Full Screen".
1309 // this is what linux does - though it feels that there shouldn't be
1310 // the same resolution twice... - thats why i add a FIXME here.
1311 res.strMode = StringUtils::Format("%dx%d @ %.2f", w, h, refreshrate);
1312 g_graphicsContext.ResetOverscan(res);
1313 CDisplaySettings::Get().AddResolutionInfo(res);
1319 bool CWinSystemOSX::FlushBuffer(void)
1321 [ (NSOpenGLContext*)m_glContext flushBuffer ];
1326 bool CWinSystemOSX::IsObscured(void)
1328 // check once a second if we are obscured.
1329 unsigned int now_time = XbmcThreads::SystemClockMillis();
1330 if (m_obscured_timecheck > now_time)
1333 m_obscured_timecheck = now_time + 1000;
1335 NSOpenGLContext* cur_context = [NSOpenGLContext currentContext];
1336 NSView* view = [cur_context view];
1339 // sanity check, we should always have a view
1344 NSWindow *window = [view window];
1347 // sanity check, we should always have a window
1352 if ([window isVisible] == NO)
1354 // not visable means the window is not showing.
1355 // this should never really happen as we are always visable
1356 // even when minimized in dock.
1361 // check if we are minimized (to an icon in the Dock).
1362 if ([window isMiniaturized] == YES)
1368 // check if we are showing on the active workspace.
1369 if ([window isOnActiveSpace] == NO)
1375 // default to false before we start parsing though the windows.
1376 // if we are are obscured by any windows, then set true.
1378 static bool obscureLogged = false;
1380 CGWindowListOption opts;
1381 opts = kCGWindowListOptionOnScreenAboveWindow | kCGWindowListExcludeDesktopElements;
1382 CFArrayRef windowIDs =CGWindowListCreate(opts, (CGWindowID)[window windowNumber]);
1387 CFArrayRef windowDescs = CGWindowListCreateDescriptionFromArray(windowIDs);
1390 CFRelease(windowIDs);
1394 CGRect bounds = NSRectToCGRect([window frame]);
1395 // kCGWindowBounds measures the origin as the top-left corner of the rectangle
1396 // relative to the top-left corner of the screen.
1397 // NSWindow’s frame property measures the origin as the bottom-left corner
1398 // of the rectangle relative to the bottom-left corner of the screen.
1399 // convert bounds from NSWindow to CGWindowBounds here.
1400 bounds.origin.y = [[window screen] frame].size.height - bounds.origin.y - bounds.size.height;
1402 std::vector<CRect> partialOverlaps;
1403 CRect ourBounds = CGRectToCRect(bounds);
1405 for (CFIndex idx=0; idx < CFArrayGetCount(windowDescs); idx++)
1407 // walk the window list of windows that are above us and are not desktop elements
1408 CFDictionaryRef windowDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(windowDescs, idx);
1410 // skip the Dock window, it actually covers the entire screen.
1411 CFStringRef ownerName = (CFStringRef)CFDictionaryGetValue(windowDictionary, kCGWindowOwnerName);
1412 if (CFStringCompare(ownerName, CFSTR("Dock"), 0) == kCFCompareEqualTo)
1415 // Ignore known brightness tools for dimming the screen. They claim to cover
1416 // the whole XBMC window and therefore would make the framerate limiter
1417 // kicking in. Unfortunatly even the alpha of these windows is 1.0 so
1418 // we have to check the ownerName.
1419 if (CFStringCompare(ownerName, CFSTR("Shades"), 0) == kCFCompareEqualTo ||
1420 CFStringCompare(ownerName, CFSTR("SmartSaver"), 0) == kCFCompareEqualTo ||
1421 CFStringCompare(ownerName, CFSTR("Brightness Slider"), 0) == kCFCompareEqualTo ||
1422 CFStringCompare(ownerName, CFSTR("Displaperture"), 0) == kCFCompareEqualTo ||
1423 CFStringCompare(ownerName, CFSTR("Dreamweaver"), 0) == kCFCompareEqualTo)
1426 CFDictionaryRef rectDictionary = (CFDictionaryRef)CFDictionaryGetValue(windowDictionary, kCGWindowBounds);
1427 if (!rectDictionary)
1430 CGRect windowBounds;
1431 if (CGRectMakeWithDictionaryRepresentation(rectDictionary, &windowBounds))
1433 if (CGRectContainsRect(windowBounds, bounds))
1435 // if the windowBounds completely encloses our bounds, we are obscured.
1438 std::string appName;
1439 if (DarwinCFStringRefToUTF8String(ownerName, appName))
1440 CLog::Log(LOGDEBUG, "WinSystemOSX: Fullscreen window %s obscures XBMC!", appName.c_str());
1441 obscureLogged = true;
1447 // handle overlaping windows above us that combine
1448 // to obscure by collecting any partial overlaps,
1449 // then subtract them from our bounds and check
1450 // for any remaining area.
1451 CRect intersection = CGRectToCRect(windowBounds);
1452 intersection.Intersect(ourBounds);
1453 if (!intersection.IsEmpty())
1454 partialOverlaps.push_back(intersection);
1460 // if we are here we are not obscured by any fullscreen window - reset flag
1461 // for allowing the logmessage above to show again if this changes.
1463 obscureLogged = false;
1464 std::vector<CRect> rects = ourBounds.SubtractRects(partialOverlaps);
1465 // they got us covered
1466 if (rects.size() == 0)
1470 CFRelease(windowDescs);
1471 CFRelease(windowIDs);
1476 void CWinSystemOSX::NotifyAppFocusChange(bool bGaining)
1478 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1480 if (m_bFullScreen && bGaining)
1483 NSOpenGLContext* context = [NSOpenGLContext currentContext];
1488 view = [context view];
1492 window = [view window];
1495 // find the screenID
1496 NSDictionary* screenInfo = [[window screen] deviceDescription];
1497 NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
1498 if ((CGDirectDisplayID)[screenID longValue] == kCGDirectMainDisplay || isMavericks() )
1500 SetMenuBarVisible(false);
1502 [window orderFront:nil];
1510 void CWinSystemOSX::ShowOSMouse(bool show)
1512 SDL_ShowCursor(show ? 1 : 0);
1515 bool CWinSystemOSX::Minimize()
1517 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1519 [[NSApplication sharedApplication] miniaturizeAll:nil];
1525 bool CWinSystemOSX::Restore()
1527 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1529 [[NSApplication sharedApplication] unhide:nil];
1535 bool CWinSystemOSX::Hide()
1537 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1539 [[NSApplication sharedApplication] hide:nil];
1545 void CWinSystemOSX::OnMove(int x, int y)
1547 Cocoa_CVDisplayLinkUpdate();
1550 void CWinSystemOSX::EnableSystemScreenSaver(bool bEnable)
1552 // see Technical Q&A QA1340
1553 static IOPMAssertionID assertionID = 0;
1557 if (assertionID == 0)
1559 CFStringRef reasonForActivity= CFSTR("XBMC requested disable system screen saver");
1560 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
1561 kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
1563 UpdateSystemActivity(UsrActivity);
1565 else if (assertionID != 0)
1567 IOPMAssertionRelease(assertionID);
1571 m_use_system_screensaver = bEnable;
1574 bool CWinSystemOSX::IsSystemScreenSaverEnabled()
1576 return m_use_system_screensaver;
1579 void CWinSystemOSX::ResetOSScreensaver()
1581 // allow os screensaver only if we are fullscreen
1582 EnableSystemScreenSaver(!m_bFullScreen);
1585 bool CWinSystemOSX::EnableFrameLimiter()
1587 return IsObscured();
1590 void CWinSystemOSX::EnableTextInput(bool bEnable)
1598 OSXTextInputResponder *g_textInputResponder = nil;
1600 bool CWinSystemOSX::IsTextInputEnabled()
1602 return g_textInputResponder != nil && [[g_textInputResponder superview] isEqual: [[NSApp keyWindow] contentView]];
1605 void CWinSystemOSX::StartTextInput()
1607 NSView *parentView = [[NSApp keyWindow] contentView];
1609 /* We only keep one field editor per process, since only the front most
1610 * window can receive text input events, so it make no sense to keep more
1611 * than one copy. When we switched to another window and requesting for
1612 * text input, simply remove the field editor from its superview then add
1613 * it to the front most window's content view */
1614 if (!g_textInputResponder) {
1615 g_textInputResponder =
1616 [[OSXTextInputResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)];
1619 if (![[g_textInputResponder superview] isEqual: parentView])
1621 // DLOG(@"add fieldEdit to window contentView");
1622 [g_textInputResponder removeFromSuperview];
1623 [parentView addSubview: g_textInputResponder];
1624 [[NSApp keyWindow] makeFirstResponder: g_textInputResponder];
1627 void CWinSystemOSX::StopTextInput()
1629 if (g_textInputResponder) {
1630 [g_textInputResponder removeFromSuperview];
1631 [g_textInputResponder release];
1632 g_textInputResponder = nil;
1636 void CWinSystemOSX::Register(IDispResource *resource)
1638 CSingleLock lock(m_resourceSection);
1639 m_resources.push_back(resource);
1642 void CWinSystemOSX::Unregister(IDispResource* resource)
1644 CSingleLock lock(m_resourceSection);
1645 std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
1646 if (i != m_resources.end())
1647 m_resources.erase(i);
1650 bool CWinSystemOSX::Show(bool raise)
1652 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1656 [[NSApplication sharedApplication] unhide:nil];
1657 [[NSApplication sharedApplication] activateIgnoringOtherApps: YES];
1658 [[NSApplication sharedApplication] arrangeInFront:nil];
1662 [[NSApplication sharedApplication] unhideWithoutActivation];
1669 int CWinSystemOSX::GetNumScreens()
1671 int numDisplays = [[NSScreen screens] count];
1672 return(numDisplays);
1675 int CWinSystemOSX::GetCurrentScreen()
1677 NSOpenGLContext* context = [NSOpenGLContext currentContext];
1679 // if user hasn't moved us in windowed mode - return the
1680 // last display we were fullscreened at
1681 if (!m_movedToOtherScreen)
1682 return m_lastDisplayNr;
1684 // if we are here the user dragged the window to a different
1685 // screen and we return the screen of the window
1690 view = [context view];
1694 window = [view window];
1697 m_movedToOtherScreen = false;
1698 return GetDisplayIndex(GetDisplayIDFromScreen( [window screen] ));
1706 void CWinSystemOSX::WindowChangedScreen()
1708 // user has moved the window to a
1710 m_movedToOtherScreen = true;
1713 void CWinSystemOSX::CheckDisplayChanging(u_int32_t flags)
1717 CSingleLock lock(m_resourceSection);
1718 // tell any shared resources
1719 if (flags & kCGDisplayBeginConfigurationFlag)
1721 CLog::Log(LOGDEBUG, "CWinSystemOSX::CheckDisplayChanging:OnLostDevice");
1722 for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
1723 (*i)->OnLostDevice();
1725 if (flags & kCGDisplaySetModeFlag)
1727 CLog::Log(LOGDEBUG, "CWinSystemOSX::CheckDisplayChanging:OnResetDevice");
1728 for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
1729 (*i)->OnResetDevice();
1734 void* CWinSystemOSX::GetCGLContextObj()
1736 return [(NSOpenGLContext*)m_glContext CGLContextObj];
1739 std::string CWinSystemOSX::GetClipboardText(void)
1741 std::string utf8_text;
1743 const char *szStr = Cocoa_Paste();