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 "osx/XBMCHelper.h"
38 #include "utils/SystemInfo.h"
39 #include "osx/CocoaInterface.h"
40 #include "osx/DarwinUtils.h"
43 #import <SDL/SDL_video.h>
44 #import <SDL/SDL_events.h>
46 #import <Cocoa/Cocoa.h>
47 #import <QuartzCore/QuartzCore.h>
48 #import <IOKit/pwr_mgt/IOPMLib.h>
49 #import <IOKit/graphics/IOGraphicsLib.h>
50 #import "osx/OSXTextInputResponder.h"
52 // turn off deprecated warning spew.
53 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
55 //------------------------------------------------------------------------------------------
56 // special object-c class for handling the inhibit display NSTimer callback.
57 @interface windowInhibitScreenSaverClass : NSObject
58 - (void) updateSystemActivity: (NSTimer*)timer;
61 @implementation windowInhibitScreenSaverClass
62 -(void) updateSystemActivity: (NSTimer*)timer
64 UpdateSystemActivity(UsrActivity);
68 //------------------------------------------------------------------------------------------
69 // special object-c class for handling the NSWindowDidMoveNotification callback.
70 @interface windowDidMoveNoteClass : NSObject
74 + initWith: (void*) userdata;
75 - (void) windowDidMoveNotification:(NSNotification*) note;
78 @implementation windowDidMoveNoteClass
79 + initWith: (void*) userdata;
81 windowDidMoveNoteClass *windowDidMove = [windowDidMoveNoteClass new];
82 windowDidMove->m_userdata = userdata;
83 return [windowDidMove autorelease];
85 - (void) windowDidMoveNotification:(NSNotification*) note;
87 CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
91 NSOpenGLContext* context = [NSOpenGLContext currentContext];
96 NSPoint window_origin = [[[context view] window] frame].origin;
98 memset(&newEvent, 0, sizeof(newEvent));
99 newEvent.type = XBMC_VIDEOMOVE;
100 newEvent.move.x = window_origin.x;
101 newEvent.move.y = window_origin.y;
102 g_application.OnEvent(newEvent);
107 //------------------------------------------------------------------------------------------
108 // special object-c class for handling the NSWindowDidReSizeNotification callback.
109 @interface windowDidReSizeNoteClass : NSObject
113 + initWith: (void*) userdata;
114 - (void) windowDidReSizeNotification:(NSNotification*) note;
116 @implementation windowDidReSizeNoteClass
117 + initWith: (void*) userdata;
119 windowDidReSizeNoteClass *windowDidReSize = [windowDidReSizeNoteClass new];
120 windowDidReSize->m_userdata = userdata;
121 return [windowDidReSize autorelease];
123 - (void) windowDidReSizeNotification:(NSNotification*) note;
125 CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
128 /* placeholder, do not uncomment or you will SDL recurse into death
129 NSOpenGLContext* context = [NSOpenGLContext currentContext];
134 NSSize view_size = [[context view] frame].size;
136 memset(&newEvent, 0, sizeof(newEvent));
137 newEvent.type = XBMC_VIDEORESIZE;
138 newEvent.resize.w = view_size.width;
139 newEvent.resize.h = view_size.height;
140 if (newEvent.resize.w * newEvent.resize.h)
142 g_application.OnEvent(newEvent);
143 g_windowManager.MarkDirty();
151 //------------------------------------------------------------------------------------------
152 // special object-c class for handling the NSWindowDidChangeScreenNotification callback.
153 @interface windowDidChangeScreenNoteClass : NSObject
157 + initWith: (void*) userdata;
158 - (void) windowDidChangeScreenNotification:(NSNotification*) note;
160 @implementation windowDidChangeScreenNoteClass
161 + initWith: (void*) userdata;
163 windowDidChangeScreenNoteClass *windowDidChangeScreen = [windowDidChangeScreenNoteClass new];
164 windowDidChangeScreen->m_userdata = userdata;
165 return [windowDidChangeScreen autorelease];
167 - (void) windowDidChangeScreenNotification:(NSNotification*) note;
169 CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
172 winsys->WindowChangedScreen();
175 //------------------------------------------------------------------------------------------
178 #define MAX_DISPLAYS 32
179 static NSWindow* blankingWindows[MAX_DISPLAYS];
181 void* CWinSystemOSX::m_lastOwnedContext = 0;
183 //------------------------------------------------------------------------------------------
184 CRect CGRectToCRect(CGRect cgrect)
189 cgrect.origin.x + cgrect.size.width,
190 cgrect.origin.y + cgrect.size.height);
194 //------------------------------------------------------------------------------------------
195 Boolean GetDictionaryBoolean(CFDictionaryRef theDict, const void* key)
197 // get a boolean from the dictionary
198 Boolean value = false;
199 CFBooleanRef boolRef;
200 boolRef = (CFBooleanRef)CFDictionaryGetValue(theDict, key);
202 value = CFBooleanGetValue(boolRef);
205 //------------------------------------------------------------------------------------------
206 long GetDictionaryLong(CFDictionaryRef theDict, const void* key)
208 // get a long from the dictionary
211 numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
213 CFNumberGetValue(numRef, kCFNumberLongType, &value);
216 //------------------------------------------------------------------------------------------
217 int GetDictionaryInt(CFDictionaryRef theDict, const void* key)
219 // get a long from the dictionary
222 numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
224 CFNumberGetValue(numRef, kCFNumberIntType, &value);
227 //------------------------------------------------------------------------------------------
228 float GetDictionaryFloat(CFDictionaryRef theDict, const void* key)
230 // get a long from the dictionary
233 numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
235 CFNumberGetValue(numRef, kCFNumberFloatType, &value);
238 //------------------------------------------------------------------------------------------
239 double GetDictionaryDouble(CFDictionaryRef theDict, const void* key)
241 // get a long from the dictionary
244 numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
246 CFNumberGetValue(numRef, kCFNumberDoubleType, &value);
250 //---------------------------------------------------------------------------------
251 void SetMenuBarVisible(bool visible)
255 [[NSApplication sharedApplication]
256 setPresentationOptions: NSApplicationPresentationDefault];
260 [[NSApplication sharedApplication]
261 setPresentationOptions: NSApplicationPresentationHideMenuBar |
262 NSApplicationPresentationHideDock];
265 //---------------------------------------------------------------------------------
266 CGDirectDisplayID GetDisplayID(int screen_index)
268 CGDirectDisplayID displayArray[MAX_DISPLAYS];
269 CGDisplayCount numDisplays;
271 // Get the list of displays.
272 CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
273 return(displayArray[screen_index]);
276 CGDirectDisplayID GetDisplayIDFromScreen(NSScreen *screen)
278 NSDictionary* screenInfo = [screen deviceDescription];
279 NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
281 return (CGDirectDisplayID)[screenID longValue];
284 int GetDisplayIndex(CGDirectDisplayID display)
286 CGDirectDisplayID displayArray[MAX_DISPLAYS];
287 CGDisplayCount numDisplays;
289 // Get the list of displays.
290 CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
291 while (numDisplays > 0)
293 if (display == displayArray[--numDisplays])
299 void BlankOtherDisplays(int screen_index)
302 int numDisplays = [[NSScreen screens] count];
304 // zero out blankingWindows for debugging
305 for (i=0; i<MAX_DISPLAYS; i++)
307 blankingWindows[i] = 0;
311 for (i=0; i<numDisplays; i++)
313 if (i != screen_index)
316 NSScreen* pScreen = [[NSScreen screens] objectAtIndex:i];
317 NSRect screenRect = [pScreen frame];
319 // Build a blanking window.
320 screenRect.origin = NSZeroPoint;
321 blankingWindows[i] = [[NSWindow alloc] initWithContentRect:screenRect
322 styleMask:NSBorderlessWindowMask
323 backing:NSBackingStoreBuffered
327 [blankingWindows[i] setBackgroundColor:[NSColor blackColor]];
328 [blankingWindows[i] setLevel:CGShieldingWindowLevel()];
329 [blankingWindows[i] makeKeyAndOrderFront:nil];
334 void UnblankDisplays(void)
336 int numDisplays = [[NSScreen screens] count];
339 for (i=0; i<numDisplays; i++)
341 if (blankingWindows[i] != 0)
343 // Get rid of the blanking windows we created.
344 [blankingWindows[i] close];
345 if ([blankingWindows[i] isReleasedWhenClosed] == NO)
346 [blankingWindows[i] release];
347 blankingWindows[i] = 0;
352 CGDisplayFadeReservationToken DisplayFadeToBlack(bool fade)
354 // Fade to black to hide resolution-switching flicker and garbage.
355 CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
356 if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess && fade)
357 CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
362 void DisplayFadeFromBlack(CGDisplayFadeReservationToken fade_token, bool fade)
364 if (fade_token != kCGDisplayFadeReservationInvalidToken)
367 CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
368 CGReleaseDisplayFadeReservation(fade_token);
372 NSString* screenNameForDisplay(CGDirectDisplayID displayID)
374 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
376 NSString *screenName = nil;
378 NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName);
379 NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
381 if ([localizedNames count] > 0) {
382 screenName = [[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] retain];
385 [deviceInfo release];
388 return [screenName autorelease];
391 void ShowHideNSWindow(NSWindow *wind, bool show)
394 [wind orderFront:nil];
399 static NSWindow *curtainWindow;
400 void fadeInDisplay(NSScreen *theScreen, double fadeTime)
403 double fadeInterval = (fadeTime / (double) fadeSteps);
405 if (curtainWindow != nil)
407 for (int step = 0; step < fadeSteps; step++)
409 double fade = 1.0 - (step * fadeInterval);
410 [curtainWindow setAlphaValue:fade];
412 NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
413 [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
416 [curtainWindow close];
422 void fadeOutDisplay(NSScreen *theScreen, double fadeTime)
425 double fadeInterval = (fadeTime / (double) fadeSteps);
429 curtainWindow = [[NSWindow alloc]
430 initWithContentRect:[theScreen frame]
431 styleMask:NSBorderlessWindowMask
432 backing:NSBackingStoreBuffered
436 [curtainWindow setAlphaValue:0.0];
437 [curtainWindow setBackgroundColor:[NSColor blackColor]];
438 [curtainWindow setLevel:NSScreenSaverWindowLevel];
440 [curtainWindow makeKeyAndOrderFront:nil];
441 [curtainWindow setFrame:[curtainWindow
442 frameRectForContentRect:[theScreen frame]]
446 for (int step = 0; step < fadeSteps; step++)
448 double fade = step * fadeInterval;
449 [curtainWindow setAlphaValue:fade];
451 NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
452 [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
456 // try to find mode that matches the desired size, refreshrate
457 // non interlaced, nonstretched, safe for hardware
458 CFDictionaryRef GetMode(int width, int height, double refreshrate, int screenIdx)
460 if ( screenIdx >= (signed)[[NSScreen screens] count])
465 Boolean safeForHardware;
466 Boolean televisionoutput;
467 int w, h, bitsperpixel;
471 CLog::Log(LOGDEBUG, "GetMode looking for suitable mode with %d x %d @ %f Hz on display %d\n", width, height, refreshrate, screenIdx);
473 CFArrayRef displayModes = CGDisplayAvailableModes(GetDisplayID(screenIdx));
475 if (NULL == displayModes)
477 CLog::Log(LOGERROR, "GetMode - no displaymodes found!");
481 for (int i=0; i < CFArrayGetCount(displayModes); ++i)
483 CFDictionaryRef displayMode = (CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i);
485 stretched = GetDictionaryBoolean(displayMode, kCGDisplayModeIsStretched);
486 interlaced = GetDictionaryBoolean(displayMode, kCGDisplayModeIsInterlaced);
487 bitsperpixel = GetDictionaryInt(displayMode, kCGDisplayBitsPerPixel);
488 safeForHardware = GetDictionaryBoolean(displayMode, kCGDisplayModeIsSafeForHardware);
489 televisionoutput = GetDictionaryBoolean(displayMode, kCGDisplayModeIsTelevisionOutput);
490 w = GetDictionaryInt(displayMode, kCGDisplayWidth);
491 h = GetDictionaryInt(displayMode, kCGDisplayHeight);
492 rate = GetDictionaryDouble(displayMode, kCGDisplayRefreshRate);
495 if ((bitsperpixel == 32) &&
496 (safeForHardware == YES) &&
498 (interlaced == NO) &&
501 (rate == refreshrate || rate == 0))
503 CLog::Log(LOGDEBUG, "GetMode found a match!");
507 CLog::Log(LOGERROR, "GetMode - no match found!");
511 //---------------------------------------------------------------------------------
512 static void DisplayReconfigured(CGDirectDisplayID display,
513 CGDisplayChangeSummaryFlags flags, void* userData)
515 CWinSystemOSX *winsys = (CWinSystemOSX*)userData;
519 if (flags & kCGDisplaySetModeFlag || flags & kCGDisplayBeginConfigurationFlag)
521 // pre/post-reconfiguration changes
522 RESOLUTION res = g_graphicsContext.GetVideoResolution();
523 if (res == RES_INVALID)
526 NSScreen* pScreen = nil;
527 unsigned int screenIdx = CDisplaySettings::Get().GetResolutionInfo(res).iScreen;
529 if ( screenIdx < [[NSScreen screens] count] )
531 pScreen = [[NSScreen screens] objectAtIndex:screenIdx];
536 CGDirectDisplayID xbmc_display = GetDisplayIDFromScreen(pScreen);
537 if (xbmc_display == display)
539 // we only respond to changes on the display we are running on.
540 CLog::Log(LOGDEBUG, "CWinSystemOSX::DisplayReconfigured");
541 winsys->CheckDisplayChanging(flags);
547 //---------------------------------------------------------------------------------
548 //---------------------------------------------------------------------------------
549 CWinSystemOSX::CWinSystemOSX() : CWinSystemBase()
551 m_eWindowSystem = WINDOW_SYSTEM_OSX;
556 m_obscured_timecheck = XbmcThreads::SystemClockMillis() + 1000;
557 m_use_system_screensaver = true;
558 // check runtime, we only allow this on 10.5+
559 m_can_display_switch = (floor(NSAppKitVersionNumber) >= 949);
560 m_lastDisplayNr = -1;
561 m_movedToOtherScreen = false;
564 CWinSystemOSX::~CWinSystemOSX()
568 bool CWinSystemOSX::InitWindowSystem()
570 SDL_EnableUNICODE(1);
572 // set repeat to 10ms to ensure repeat time < frame time
573 // so that hold times can be reliably detected
574 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 10);
576 if (!CWinSystemBase::InitWindowSystem())
579 m_osx_events = new CWinEventsOSX();
581 if (m_can_display_switch)
582 CGDisplayRegisterReconfigurationCallback(DisplayReconfigured, (void*)this);
584 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
585 windowDidMoveNoteClass *windowDidMove;
586 windowDidMove = [windowDidMoveNoteClass initWith: this];
587 [center addObserver:windowDidMove
588 selector:@selector(windowDidMoveNotification:)
589 name:NSWindowDidMoveNotification object:nil];
590 m_windowDidMove = windowDidMove;
593 windowDidReSizeNoteClass *windowDidReSize;
594 windowDidReSize = [windowDidReSizeNoteClass initWith: this];
595 [center addObserver:windowDidReSize
596 selector:@selector(windowDidReSizeNotification:)
597 name:NSWindowDidResizeNotification object:nil];
598 m_windowDidReSize = windowDidReSize;
600 windowDidChangeScreenNoteClass *windowDidChangeScreen;
601 windowDidChangeScreen = [windowDidChangeScreenNoteClass initWith: this];
602 [center addObserver:windowDidChangeScreen
603 selector:@selector(windowDidChangeScreenNotification:)
604 name:NSWindowDidChangeScreenNotification object:nil];
605 m_windowChangedScreen = windowDidChangeScreen;
610 bool CWinSystemOSX::DestroyWindowSystem()
612 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
613 [center removeObserver:(windowDidMoveNoteClass*)m_windowDidMove name:NSWindowDidMoveNotification object:nil];
614 [center removeObserver:(windowDidReSizeNoteClass*)m_windowDidReSize name:NSWindowDidResizeNotification object:nil];
615 [center removeObserver:(windowDidChangeScreenNoteClass*)m_windowChangedScreen name:NSWindowDidChangeScreenNotification object:nil];
617 if (m_can_display_switch)
618 CGDisplayRemoveReconfigurationCallback(DisplayReconfigured, (void*)this);
626 NSOpenGLContext* oldContext = (NSOpenGLContext*)m_glContext;
627 [oldContext release];
633 bool CWinSystemOSX::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction)
635 m_nWidth = res.iWidth;
636 m_nHeight = res.iHeight;
637 m_bFullScreen = fullScreen;
639 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
640 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
641 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
642 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
643 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
645 // Enable vertical sync to avoid any tearing.
646 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
648 m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, SDL_OPENGL | SDL_RESIZABLE);
652 // the context SDL creates isn't full screen compatible, so we create new one
653 // first, find the current contect and make sure a view is attached
654 NSOpenGLContext* cur_context = [NSOpenGLContext currentContext];
655 NSView* view = [cur_context view];
659 // if we are not starting up windowed, then hide the initial SDL window
660 // so we do not see it flash before the fade-out and switch to fullscreen.
661 if (CDisplaySettings::Get().GetCurrentResolution() != RES_WINDOW)
662 ShowHideNSWindow([view window], false);
664 // disassociate view from context
665 [cur_context clearDrawable];
667 // release the context
668 if (m_lastOwnedContext == cur_context)
670 [ NSOpenGLContext clearCurrentContext ];
671 [ cur_context clearDrawable ];
672 [ cur_context release ];
675 // create a new context
676 NSOpenGLContext* new_context = (NSOpenGLContext*)CreateWindowedContext(nil);
680 // associate with current view
681 [new_context setView:view];
682 [new_context makeCurrentContext];
684 // set the window title
686 string = [ [ NSString alloc ] initWithUTF8String:"XBMC Media Center" ];
687 [ [ [new_context view] window] setTitle:string ];
690 m_glContext = new_context;
691 m_lastOwnedContext = new_context;
692 m_bWindowCreated = true;
697 bool CWinSystemOSX::DestroyWindow()
704 static int isMavericks = -1;
706 // there is no NSAppKitVersionNumber10_9 out there anywhere
707 // so we detect mavericks by one of these newly added app nap
708 // methods - and fix the ugly mouse rect problem which was hitting
709 // us when mavericks came out
710 if (isMavericks == -1)
712 CLog::Log(LOGDEBUG, "Detected Mavericks - enable windowing fixups.");
713 isMavericks = [NSProcessInfo instancesRespondToSelector:@selector(beginActivityWithOptions:reason:)] == TRUE ? 1 : 0;
715 return isMavericks == 1;
718 extern "C" void SDL_SetWidthHeight(int w, int h);
719 bool CWinSystemOSX::ResizeWindowInternal(int newWidth, int newHeight, int newLeft, int newTop, void *additional)
721 bool ret = ResizeWindow(newWidth, newHeight, newLeft, newTop);
725 NSView * last_view = (NSView *)additional;
726 if (last_view && [last_view window])
728 NSWindow* lastWindow = [last_view window];
729 [lastWindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)];
731 [last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight)];
736 bool CWinSystemOSX::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
741 NSOpenGLContext* context = [NSOpenGLContext currentContext];
745 view = [context view];
746 if (view && (newWidth > 0) && (newHeight > 0))
748 window = [view window];
751 [window setContentSize:NSMakeSize(newWidth, newHeight)];
753 [view setFrameSize:NSMakeSize(newWidth, newHeight)];
758 // HACK: resize SDL's view manually so that mouse bounds are correctly updated.
759 // there are two parts to this, the internal SDL (current_video->screen) and
760 // the cocoa view ( handled in SetFullScreen).
761 SDL_SetWidthHeight(newWidth, newHeight);
763 [context makeCurrentContext];
766 m_nHeight = newHeight;
767 m_glContext = context;
772 static bool needtoshowme = true;
774 bool CWinSystemOSX::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
776 static NSWindow* windowedFullScreenwindow = NULL;
777 static NSScreen* last_window_screen = NULL;
778 static NSPoint last_window_origin;
779 static NSView* last_view = NULL;
780 static NSSize last_view_size;
781 static NSPoint last_view_origin;
782 static NSInteger last_window_level = NSNormalWindowLevel;
783 bool was_fullscreen = m_bFullScreen;
784 NSOpenGLContext* cur_context;
786 if (m_lastDisplayNr == -1)
787 m_lastDisplayNr = res.iScreen;
789 // Fade to black to hide resolution-switching flicker and garbage.
790 CGDisplayFadeReservationToken fade_token = DisplayFadeToBlack(needtoshowme);
792 // If we're already fullscreen then we must be moving to a different display.
793 // or if we are still on the same display - it might be only a refreshrate/resolution
795 // Recurse to reset fullscreen mode and then continue.
796 if (was_fullscreen && fullScreen)
798 needtoshowme = false;
799 ShowHideNSWindow([last_view window], needtoshowme);
800 RESOLUTION_INFO& window = CDisplaySettings::Get().GetResolutionInfo(RES_WINDOW);
801 CWinSystemOSX::SetFullScreen(false, window, blankOtherDisplays);
805 m_nWidth = res.iWidth;
806 m_nHeight = res.iHeight;
807 m_bFullScreen = fullScreen;
809 cur_context = [NSOpenGLContext currentContext];
811 //handle resolution/refreshrate switching early here
814 if (m_can_display_switch)
816 // send pre-configuration change now and do not
817 // wait for switch videomode callback. This gives just
818 // a little more advanced notice of the display pre-change.
819 if (CSettings::Get().GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF)
820 CheckDisplayChanging(kCGDisplayBeginConfigurationFlag);
823 SwitchToVideoMode(res.iWidth, res.iHeight, res.fRefreshRate, res.iScreen);
824 m_lastDisplayNr = res.iScreen;
831 DisplayFadeFromBlack(fade_token, needtoshowme);
835 if (windowedFullScreenwindow != NULL)
837 [windowedFullScreenwindow close];
838 if ([windowedFullScreenwindow isReleasedWhenClosed] == NO)
839 [windowedFullScreenwindow release];
840 windowedFullScreenwindow = NULL;
846 NSOpenGLContext* newContext = NULL;
848 // Save info about the windowed context so we can restore it when returning to windowed.
849 last_view = [cur_context view];
850 last_view_size = [last_view frame].size;
851 last_view_origin = [last_view frame].origin;
852 last_window_screen = [[last_view window] screen];
853 last_window_origin = [[last_view window] frame].origin;
854 last_window_level = [[last_view window] level];
856 if (CSettings::Get().GetBool("videoscreen.fakefullscreen"))
858 // This is Cocca Windowed FullScreen Mode
859 // Get the screen rect of our current display
860 NSScreen* pScreen = [[NSScreen screens] objectAtIndex:res.iScreen];
861 NSRect screenRect = [pScreen frame];
863 // remove frame origin offset of orginal display
864 screenRect.origin = NSZeroPoint;
866 // make a new window to act as the windowedFullScreen
867 windowedFullScreenwindow = [[NSWindow alloc] initWithContentRect:screenRect
868 styleMask:NSBorderlessWindowMask
869 backing:NSBackingStoreBuffered
873 [windowedFullScreenwindow setBackgroundColor:[NSColor blackColor]];
874 [windowedFullScreenwindow makeKeyAndOrderFront:nil];
876 // make our window the same level as the rest to enable cmd+tab switching
877 [windowedFullScreenwindow setLevel:NSNormalWindowLevel];
878 // this will make our window topmost and hide all system messages
879 //[windowedFullScreenwindow setLevel:CGShieldingWindowLevel()];
881 // ...and the original one beneath it and on the same screen.
882 [[last_view window] setLevel:NSNormalWindowLevel-1];
883 [[last_view window] setFrameOrigin:[pScreen frame].origin];
884 // expand the mouse bounds in SDL view to fullscreen
885 [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)];
886 [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ];
888 NSView* blankView = [[NSView alloc] init];
889 [windowedFullScreenwindow setContentView:blankView];
890 [windowedFullScreenwindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)];
891 [windowedFullScreenwindow update];
892 [blankView setFrameSize:NSMakeSize(m_nWidth, m_nHeight)];
894 // Obtain windowed pixel format and create a new context.
895 newContext = (NSOpenGLContext*)CreateWindowedContext((void* )cur_context);
896 [newContext setView:blankView];
898 // Hide the menu bar.
899 if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || isMavericks() )
900 SetMenuBarVisible(false);
902 // Blank other displays if requested.
903 if (blankOtherDisplays)
904 BlankOtherDisplays(res.iScreen);
909 [[last_view window] setFrameOrigin:[last_window_screen frame].origin];
910 // expand the mouse bounds in SDL view to fullscreen
911 [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)];
912 [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ];
914 // This is OpenGL FullScreen Mode
915 // create our new context (sharing with the current one)
916 newContext = (NSOpenGLContext*)CreateFullScreenContext(res.iScreen, (void*)cur_context);
920 // clear the current context
921 [NSOpenGLContext clearCurrentContext];
924 [newContext setFullScreen];
926 // Capture the display before going fullscreen.
927 if (blankOtherDisplays == true)
928 CGCaptureAllDisplays();
930 CGDisplayCapture(GetDisplayID(res.iScreen));
932 // If we don't hide menu bar, it will get events and interrupt the program.
933 if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || isMavericks() )
934 SetMenuBarVisible(false);
940 // Release old context if we created it.
941 if (m_lastOwnedContext == cur_context)
943 [ NSOpenGLContext clearCurrentContext ];
944 [ cur_context clearDrawable ];
945 [ cur_context release ];
949 [newContext makeCurrentContext];
950 m_lastOwnedContext = newContext;
956 [cur_context clearDrawable];
961 if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || isMavericks() )
962 SetMenuBarVisible(true);
964 if (CSettings::Get().GetBool("videoscreen.fakefullscreen"))
966 // restore the windowed window level
967 [[last_view window] setLevel:last_window_level];
969 // Get rid of the new window we created.
970 if (windowedFullScreenwindow != NULL)
972 [windowedFullScreenwindow close];
973 if ([windowedFullScreenwindow isReleasedWhenClosed] == NO)
974 [windowedFullScreenwindow release];
975 windowedFullScreenwindow = NULL;
979 // Force the unblank when returning from fullscreen, we get called with blankOtherDisplays set false.
980 //if (blankOtherDisplays)
986 CGReleaseAllDisplays();
989 // create our new context (sharing with the current one)
990 NSOpenGLContext* newContext = (NSOpenGLContext*)CreateWindowedContext((void* )cur_context);
994 // Assign view from old context, move back to original screen.
995 [newContext setView:last_view];
996 [[last_view window] setFrameOrigin:last_window_origin];
997 // return the mouse bounds in SDL view to prevous size
998 [ last_view setFrameSize:last_view_size ];
999 [ last_view setFrameOrigin:last_view_origin ];
1000 // done with restoring windowed window, don't set last_view to NULL as we can lose it under dual displays.
1001 //last_window_screen = NULL;
1003 // Release the fullscreen context.
1004 if (m_lastOwnedContext == cur_context)
1006 [ NSOpenGLContext clearCurrentContext ];
1007 [ cur_context clearDrawable ];
1008 [ cur_context release ];
1011 // Activate context.
1012 [newContext makeCurrentContext];
1013 m_lastOwnedContext = newContext;
1016 DisplayFadeFromBlack(fade_token, needtoshowme);
1018 ShowHideNSWindow([last_view window], needtoshowme);
1019 // need to make sure SDL tracks any window size changes
1020 ResizeWindowInternal(m_nWidth, m_nHeight, -1, -1, last_view);
1025 void CWinSystemOSX::UpdateResolutions()
1027 CWinSystemBase::UpdateResolutions();
1029 // Add desktop resolution
1033 // first screen goes into the current desktop mode
1034 GetScreenResolution(&w, &h, &fps, 0);
1035 UpdateDesktopResolution(CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP), 0, w, h, fps);
1037 // see resolution.h enum RESOLUTION for how the resolutions
1038 // have to appear in the resolution info vector in CDisplaySettings
1039 // add the desktop resolutions of the other screens
1040 for(int i = 1; i < GetNumScreens(); i++)
1042 RESOLUTION_INFO res;
1043 // get current resolution of screen i
1044 GetScreenResolution(&w, &h, &fps, i);
1045 UpdateDesktopResolution(res, i, w, h, fps);
1046 CDisplaySettings::Get().AddResolutionInfo(res);
1049 if (m_can_display_switch)
1051 // now just fill in the possible reolutions for the attached screens
1052 // and push to the resolution info vector
1058 void* Cocoa_GL_CreateContext(void* pixFmt, void* shareCtx)
1063 NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1064 shareContext:(NSOpenGLContext*)shareCtx];
1066 // snipit from SDL_cocoaopengl.m
1068 // Wisdom from Apple engineer in reference to UT2003's OpenGL performance:
1069 // "You are blowing a couple of the internal OpenGL function caches. This
1070 // appears to be happening in the VAO case. You can tell OpenGL to up
1071 // the cache size by issuing the following calls right after you create
1072 // the OpenGL context. The default cache size is 16." --ryan.
1075 #ifndef GLI_ARRAY_FUNC_CACHE_MAX
1076 #define GLI_ARRAY_FUNC_CACHE_MAX 284
1079 #ifndef GLI_SUBMIT_FUNC_CACHE_MAX
1080 #define GLI_SUBMIT_FUNC_CACHE_MAX 280
1084 long cache_max = 64;
1085 CGLContextObj ctx = (CGLContextObj)[newContext CGLContextObj];
1086 CGLSetParameter(ctx, (CGLContextParameter)GLI_SUBMIT_FUNC_CACHE_MAX, &cache_max);
1087 CGLSetParameter(ctx, (CGLContextParameter)GLI_ARRAY_FUNC_CACHE_MAX, &cache_max);
1090 // End Wisdom from Apple Engineer section. --ryan.
1095 void* CWinSystemOSX::CreateWindowedContext(void* shareCtx)
1097 NSOpenGLContext* newContext = NULL;
1099 NSOpenGLPixelFormatAttribute wattrs[] =
1101 NSOpenGLPFADoubleBuffer,
1103 NSOpenGLPFANoRecovery,
1104 NSOpenGLPFAAccelerated,
1105 NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1106 (NSOpenGLPixelFormatAttribute)0
1109 NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs];
1111 newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1112 shareContext:(NSOpenGLContext*)shareCtx];
1117 // bah, try again for non-accelerated renderer
1118 NSOpenGLPixelFormatAttribute wattrs2[] =
1120 NSOpenGLPFADoubleBuffer,
1122 NSOpenGLPFANoRecovery,
1123 NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1124 (NSOpenGLPixelFormatAttribute)0
1126 NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs2];
1128 newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1129 shareContext:(NSOpenGLContext*)shareCtx];
1136 void* CWinSystemOSX::CreateFullScreenContext(int screen_index, void* shareCtx)
1138 CGDirectDisplayID displayArray[MAX_DISPLAYS];
1139 CGDisplayCount numDisplays;
1140 CGDirectDisplayID displayID;
1142 // Get the list of displays.
1143 CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
1144 displayID = displayArray[screen_index];
1146 NSOpenGLPixelFormatAttribute fsattrs[] =
1148 NSOpenGLPFADoubleBuffer,
1149 NSOpenGLPFAFullScreen,
1150 NSOpenGLPFANoRecovery,
1151 NSOpenGLPFAAccelerated,
1152 NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1153 NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(displayID),
1154 (NSOpenGLPixelFormatAttribute)0
1157 NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs];
1161 NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1162 shareContext:(NSOpenGLContext*)shareCtx];
1168 void CWinSystemOSX::GetScreenResolution(int* w, int* h, double* fps, int screenIdx)
1170 // Figure out the screen size. (default to main screen)
1171 if (screenIdx >= GetNumScreens())
1173 CGDirectDisplayID display_id = (CGDirectDisplayID)GetDisplayID(screenIdx);
1175 NSOpenGLContext* context = [NSOpenGLContext currentContext];
1180 view = [context view];
1184 window = [view window];
1186 display_id = GetDisplayIDFromScreen( [window screen] );
1189 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display_id);
1190 *w = CGDisplayModeGetWidth(mode);
1191 *h = CGDisplayModeGetHeight(mode);
1192 *fps = CGDisplayModeGetRefreshRate(mode);
1193 CGDisplayModeRelease(mode);
1196 // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
1201 void CWinSystemOSX::EnableVSync(bool enable)
1203 // OpenGL Flush synchronised with vertical retrace
1204 GLint swapInterval = enable ? 1 : 0;
1205 [[NSOpenGLContext currentContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
1208 bool CWinSystemOSX::SwitchToVideoMode(int width, int height, double refreshrate, int screenIdx)
1210 // SwitchToVideoMode will not return until the display has actually switched over.
1211 // This can take several seconds.
1212 if( screenIdx >= GetNumScreens())
1215 boolean_t match = false;
1216 CFDictionaryRef dispMode = NULL;
1217 // Figure out the screen size. (default to main screen)
1218 CGDirectDisplayID display_id = GetDisplayID(screenIdx);
1220 // find mode that matches the desired size, refreshrate
1221 // non interlaced, nonstretched, safe for hardware
1222 dispMode = GetMode(width, height, refreshrate, screenIdx);
1224 //not found - fallback to bestemdeforparameters
1227 dispMode = CGDisplayBestModeForParameters(display_id, 32, width, height, &match);
1230 dispMode = CGDisplayBestModeForParameters(display_id, 16, width, height, &match);
1236 // switch mode and return success
1237 CGDisplayCapture(display_id);
1238 CGDisplayConfigRef cfg;
1239 CGBeginDisplayConfiguration(&cfg);
1240 // we don't need to do this, we are already faded.
1241 //CGConfigureDisplayFadeEffect(cfg, 0.3f, 0.5f, 0, 0, 0);
1242 CGConfigureDisplayMode(cfg, display_id, dispMode);
1243 CGError err = CGCompleteDisplayConfiguration(cfg, kCGConfigureForAppOnly);
1244 CGDisplayRelease(display_id);
1246 Cocoa_CVDisplayLinkUpdate();
1248 return (err == kCGErrorSuccess);
1251 void CWinSystemOSX::FillInVideoModes()
1253 // Add full screen settings for additional monitors
1254 int numDisplays = [[NSScreen screens] count];
1256 for (int disp = 0; disp < numDisplays; disp++)
1260 Boolean safeForHardware;
1261 Boolean televisionoutput;
1262 int w, h, bitsperpixel;
1264 RESOLUTION_INFO res;
1266 CFArrayRef displayModes = CGDisplayAvailableModes(GetDisplayID(disp));
1267 NSString *dispName = screenNameForDisplay(GetDisplayID(disp));
1268 CLog::Log(LOGNOTICE, "Display %i has name %s", disp, [dispName UTF8String]);
1270 if (NULL == displayModes)
1273 for (int i=0; i < CFArrayGetCount(displayModes); ++i)
1275 CFDictionaryRef displayMode = (CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i);
1277 stretched = GetDictionaryBoolean(displayMode, kCGDisplayModeIsStretched);
1278 interlaced = GetDictionaryBoolean(displayMode, kCGDisplayModeIsInterlaced);
1279 bitsperpixel = GetDictionaryInt(displayMode, kCGDisplayBitsPerPixel);
1280 safeForHardware = GetDictionaryBoolean(displayMode, kCGDisplayModeIsSafeForHardware);
1281 televisionoutput = GetDictionaryBoolean(displayMode, kCGDisplayModeIsTelevisionOutput);
1283 if ((bitsperpixel == 32) &&
1284 (safeForHardware == YES) &&
1285 (stretched == NO) &&
1288 w = GetDictionaryInt(displayMode, kCGDisplayWidth);
1289 h = GetDictionaryInt(displayMode, kCGDisplayHeight);
1290 refreshrate = GetDictionaryDouble(displayMode, kCGDisplayRefreshRate);
1291 if ((int)refreshrate == 0) // LCD display?
1293 // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
1296 CLog::Log(LOGNOTICE, "Found possible resolution for display %d with %d x %d @ %f Hz\n", disp, w, h, refreshrate);
1298 UpdateDesktopResolution(res, disp, w, h, refreshrate);
1300 // overwrite the mode str because UpdateDesktopResolution adds a
1301 // "Full Screen". Since the current resolution is there twice
1302 // this would lead to 2 identical resolution entrys in the guisettings.xml.
1303 // That would cause problems with saving screen overscan calibration
1304 // because the wrong entry is picked on load.
1305 // So we just use UpdateDesktopResolutions for the current DESKTOP_RESOLUTIONS
1306 // in UpdateResolutions. And on all othere resolutions make a unique
1307 // mode str by doing it without appending "Full Screen".
1308 // this is what linux does - though it feels that there shouldn't be
1309 // the same resolution twice... - thats why i add a FIXME here.
1310 res.strMode.Format("%dx%d @ %.2f", w, h, refreshrate);
1311 g_graphicsContext.ResetOverscan(res);
1312 CDisplaySettings::Get().AddResolutionInfo(res);
1318 bool CWinSystemOSX::FlushBuffer(void)
1320 [ (NSOpenGLContext*)m_glContext flushBuffer ];
1325 bool CWinSystemOSX::IsObscured(void)
1327 // check once a second if we are obscured.
1328 unsigned int now_time = XbmcThreads::SystemClockMillis();
1329 if (m_obscured_timecheck > now_time)
1332 m_obscured_timecheck = now_time + 1000;
1334 NSOpenGLContext* cur_context = [NSOpenGLContext currentContext];
1335 NSView* view = [cur_context view];
1338 // sanity check, we should always have a view
1343 NSWindow *window = [view window];
1346 // sanity check, we should always have a window
1351 if ([window isVisible] == NO)
1353 // not visable means the window is not showing.
1354 // this should never really happen as we are always visable
1355 // even when minimized in dock.
1360 // check if we are minimized (to an icon in the Dock).
1361 if ([window isMiniaturized] == YES)
1367 // check if we are showing on the active workspace.
1368 if ([window isOnActiveSpace] == NO)
1374 // default to false before we start parsing though the windows.
1375 // if we are are obscured by any windows, then set true.
1377 static bool obscureLogged = false;
1379 CGWindowListOption opts;
1380 opts = kCGWindowListOptionOnScreenAboveWindow | kCGWindowListExcludeDesktopElements;
1381 CFArrayRef windowIDs =CGWindowListCreate(opts, (CGWindowID)[window windowNumber]);
1386 CFArrayRef windowDescs = CGWindowListCreateDescriptionFromArray(windowIDs);
1389 CFRelease(windowIDs);
1393 CGRect bounds = NSRectToCGRect([window frame]);
1394 // kCGWindowBounds measures the origin as the top-left corner of the rectangle
1395 // relative to the top-left corner of the screen.
1396 // NSWindow’s frame property measures the origin as the bottom-left corner
1397 // of the rectangle relative to the bottom-left corner of the screen.
1398 // convert bounds from NSWindow to CGWindowBounds here.
1399 bounds.origin.y = [[window screen] frame].size.height - bounds.origin.y - bounds.size.height;
1401 std::vector<CRect> partialOverlaps;
1402 CRect ourBounds = CGRectToCRect(bounds);
1404 for (CFIndex idx=0; idx < CFArrayGetCount(windowDescs); idx++)
1406 // walk the window list of windows that are above us and are not desktop elements
1407 CFDictionaryRef windowDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(windowDescs, idx);
1409 // skip the Dock window, it actually covers the entire screen.
1410 CFStringRef ownerName = (CFStringRef)CFDictionaryGetValue(windowDictionary, kCGWindowOwnerName);
1411 if (CFStringCompare(ownerName, CFSTR("Dock"), 0) == kCFCompareEqualTo)
1414 // Ignore known brightness tools for dimming the screen. They claim to cover
1415 // the whole XBMC window and therefore would make the framerate limiter
1416 // kicking in. Unfortunatly even the alpha of these windows is 1.0 so
1417 // we have to check the ownerName.
1418 if (CFStringCompare(ownerName, CFSTR("Shades"), 0) == kCFCompareEqualTo ||
1419 CFStringCompare(ownerName, CFSTR("SmartSaver"), 0) == kCFCompareEqualTo ||
1420 CFStringCompare(ownerName, CFSTR("Brightness Slider"), 0) == kCFCompareEqualTo ||
1421 CFStringCompare(ownerName, CFSTR("Displaperture"), 0) == kCFCompareEqualTo ||
1422 CFStringCompare(ownerName, CFSTR("Dreamweaver"), 0) == kCFCompareEqualTo)
1425 CFDictionaryRef rectDictionary = (CFDictionaryRef)CFDictionaryGetValue(windowDictionary, kCGWindowBounds);
1426 if (!rectDictionary)
1429 CGRect windowBounds;
1430 if (CGRectMakeWithDictionaryRepresentation(rectDictionary, &windowBounds))
1432 if (CGRectContainsRect(windowBounds, bounds))
1434 // if the windowBounds completely encloses our bounds, we are obscured.
1437 std::string appName;
1438 if (DarwinCFStringRefToUTF8String(ownerName, appName))
1439 CLog::Log(LOGDEBUG, "WinSystemOSX: Fullscreen window %s obscures XBMC!", appName.c_str());
1440 obscureLogged = true;
1446 // handle overlaping windows above us that combine
1447 // to obscure by collecting any partial overlaps,
1448 // then subtract them from our bounds and check
1449 // for any remaining area.
1450 CRect intersection = CGRectToCRect(windowBounds);
1451 intersection.Intersect(ourBounds);
1452 if (!intersection.IsEmpty())
1453 partialOverlaps.push_back(intersection);
1459 // if we are here we are not obscured by any fullscreen window - reset flag
1460 // for allowing the logmessage above to show again if this changes.
1462 obscureLogged = false;
1463 std::vector<CRect> rects = ourBounds.SubtractRects(partialOverlaps);
1464 // they got us covered
1465 if (rects.size() == 0)
1469 CFRelease(windowDescs);
1470 CFRelease(windowIDs);
1475 void CWinSystemOSX::NotifyAppFocusChange(bool bGaining)
1477 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1479 if (m_bFullScreen && bGaining)
1482 NSOpenGLContext* context = [NSOpenGLContext currentContext];
1487 view = [context view];
1491 window = [view window];
1494 // find the screenID
1495 NSDictionary* screenInfo = [[window screen] deviceDescription];
1496 NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
1497 if ((CGDirectDisplayID)[screenID longValue] == kCGDirectMainDisplay || isMavericks() )
1499 SetMenuBarVisible(false);
1501 [window orderFront:nil];
1509 void CWinSystemOSX::ShowOSMouse(bool show)
1511 SDL_ShowCursor(show ? 1 : 0);
1514 bool CWinSystemOSX::Minimize()
1516 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1518 [[NSApplication sharedApplication] miniaturizeAll:nil];
1524 bool CWinSystemOSX::Restore()
1526 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1528 [[NSApplication sharedApplication] unhide:nil];
1534 bool CWinSystemOSX::Hide()
1536 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1538 [[NSApplication sharedApplication] hide:nil];
1544 void CWinSystemOSX::OnMove(int x, int y)
1546 Cocoa_CVDisplayLinkUpdate();
1549 void CWinSystemOSX::EnableSystemScreenSaver(bool bEnable)
1551 // see Technical Q&A QA1340
1552 static IOPMAssertionID assertionID = 0;
1556 if (assertionID == 0)
1558 CFStringRef reasonForActivity= CFSTR("XBMC requested disable system screen saver");
1559 IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
1560 kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
1562 UpdateSystemActivity(UsrActivity);
1564 else if (assertionID != 0)
1566 IOPMAssertionRelease(assertionID);
1570 m_use_system_screensaver = bEnable;
1573 bool CWinSystemOSX::IsSystemScreenSaverEnabled()
1575 return m_use_system_screensaver;
1578 void CWinSystemOSX::ResetOSScreensaver()
1580 // allow os screensaver only if we are fullscreen
1581 EnableSystemScreenSaver(!m_bFullScreen);
1584 bool CWinSystemOSX::EnableFrameLimiter()
1586 return IsObscured();
1589 void CWinSystemOSX::EnableTextInput(bool bEnable)
1597 OSXTextInputResponder *g_textInputResponder = nil;
1599 bool CWinSystemOSX::IsTextInputEnabled()
1601 return g_textInputResponder != nil && [[g_textInputResponder superview] isEqual: [[NSApp keyWindow] contentView]];
1604 void CWinSystemOSX::StartTextInput()
1606 NSView *parentView = [[NSApp keyWindow] contentView];
1608 /* We only keep one field editor per process, since only the front most
1609 * window can receive text input events, so it make no sense to keep more
1610 * than one copy. When we switched to another window and requesting for
1611 * text input, simply remove the field editor from its superview then add
1612 * it to the front most window's content view */
1613 if (!g_textInputResponder) {
1614 g_textInputResponder =
1615 [[OSXTextInputResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)];
1618 if (![[g_textInputResponder superview] isEqual: parentView])
1620 // DLOG(@"add fieldEdit to window contentView");
1621 [g_textInputResponder removeFromSuperview];
1622 [parentView addSubview: g_textInputResponder];
1623 [[NSApp keyWindow] makeFirstResponder: g_textInputResponder];
1626 void CWinSystemOSX::StopTextInput()
1628 if (g_textInputResponder) {
1629 [g_textInputResponder removeFromSuperview];
1630 [g_textInputResponder release];
1631 g_textInputResponder = nil;
1635 void CWinSystemOSX::Register(IDispResource *resource)
1637 CSingleLock lock(m_resourceSection);
1638 m_resources.push_back(resource);
1641 void CWinSystemOSX::Unregister(IDispResource* resource)
1643 CSingleLock lock(m_resourceSection);
1644 std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
1645 if (i != m_resources.end())
1646 m_resources.erase(i);
1649 bool CWinSystemOSX::Show(bool raise)
1651 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1655 [[NSApplication sharedApplication] unhide:nil];
1656 [[NSApplication sharedApplication] activateIgnoringOtherApps: YES];
1657 [[NSApplication sharedApplication] arrangeInFront:nil];
1661 [[NSApplication sharedApplication] unhideWithoutActivation];
1668 int CWinSystemOSX::GetNumScreens()
1670 int numDisplays = [[NSScreen screens] count];
1671 return(numDisplays);
1674 int CWinSystemOSX::GetCurrentScreen()
1676 NSOpenGLContext* context = [NSOpenGLContext currentContext];
1678 // if user hasn't moved us in windowed mode - return the
1679 // last display we were fullscreened at
1680 if (!m_movedToOtherScreen)
1681 return m_lastDisplayNr;
1683 // if we are here the user dragged the window to a different
1684 // screen and we return the screen of the window
1689 view = [context view];
1693 window = [view window];
1696 m_movedToOtherScreen = false;
1697 return GetDisplayIndex(GetDisplayIDFromScreen( [window screen] ));
1705 void CWinSystemOSX::WindowChangedScreen()
1707 // user has moved the window to a
1709 m_movedToOtherScreen = true;
1712 void CWinSystemOSX::CheckDisplayChanging(u_int32_t flags)
1716 CSingleLock lock(m_resourceSection);
1717 // tell any shared resources
1718 if (flags & kCGDisplayBeginConfigurationFlag)
1720 CLog::Log(LOGDEBUG, "CWinSystemOSX::CheckDisplayChanging:OnLostDevice");
1721 for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
1722 (*i)->OnLostDevice();
1724 if (flags & kCGDisplaySetModeFlag)
1726 CLog::Log(LOGDEBUG, "CWinSystemOSX::CheckDisplayChanging:OnResetDevice");
1727 for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
1728 (*i)->OnResetDevice();
1733 void* CWinSystemOSX::GetCGLContextObj()
1735 return [(NSOpenGLContext*)m_glContext CGLContextObj];
1738 std::string CWinSystemOSX::GetClipboardText(void)
1740 std::string utf8_text;
1742 const char *szStr = Cocoa_Paste();