[cstdstring] demise Format, replacing with StringUtils::Format
[vuplus_xbmc] / xbmc / windowing / osx / WinSystemOSX.mm
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #if defined(TARGET_DARWIN_OSX)
22
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"
42 #undef BOOL
43
44 #import <SDL/SDL_video.h>
45 #import <SDL/SDL_events.h>
46
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"
52
53 // turn off deprecated warning spew.
54 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
55
56 //------------------------------------------------------------------------------------------
57 // special object-c class for handling the inhibit display NSTimer callback.
58 @interface windowInhibitScreenSaverClass : NSObject
59 - (void) updateSystemActivity: (NSTimer*)timer;
60 @end
61
62 @implementation windowInhibitScreenSaverClass
63 -(void) updateSystemActivity: (NSTimer*)timer
64 {
65   UpdateSystemActivity(UsrActivity);
66 }
67 @end
68
69 //------------------------------------------------------------------------------------------
70 // special object-c class for handling the NSWindowDidMoveNotification callback.
71 @interface windowDidMoveNoteClass : NSObject
72 {
73   void *m_userdata;
74 }
75 + initWith: (void*) userdata;
76 -  (void) windowDidMoveNotification:(NSNotification*) note;
77 @end
78
79 @implementation windowDidMoveNoteClass
80 + initWith: (void*) userdata;
81 {
82     windowDidMoveNoteClass *windowDidMove = [windowDidMoveNoteClass new];
83     windowDidMove->m_userdata = userdata;
84     return [windowDidMove autorelease];
85 }
86 -  (void) windowDidMoveNotification:(NSNotification*) note;
87 {
88   CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
89         if (!winsys)
90     return;
91
92   NSOpenGLContext* context = [NSOpenGLContext currentContext];
93   if (context)
94   {
95     if ([context view])
96     {
97       NSPoint window_origin = [[[context view] window] frame].origin;
98       XBMC_Event newEvent;
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);
104     }
105   }
106 }
107 @end
108 //------------------------------------------------------------------------------------------
109 // special object-c class for handling the NSWindowDidReSizeNotification callback.
110 @interface windowDidReSizeNoteClass : NSObject
111 {
112   void *m_userdata;
113 }
114 + initWith: (void*) userdata;
115 - (void) windowDidReSizeNotification:(NSNotification*) note;
116 @end
117 @implementation windowDidReSizeNoteClass
118 + initWith: (void*) userdata;
119 {
120     windowDidReSizeNoteClass *windowDidReSize = [windowDidReSizeNoteClass new];
121     windowDidReSize->m_userdata = userdata;
122     return [windowDidReSize autorelease];
123 }
124 - (void) windowDidReSizeNotification:(NSNotification*) note;
125 {
126   CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
127         if (!winsys)
128     return;
129   /* placeholder, do not uncomment or you will SDL recurse into death
130   NSOpenGLContext* context = [NSOpenGLContext currentContext];
131   if (context)
132   {
133     if ([context view])
134     {
135       NSSize view_size = [[context view] frame].size;
136       XBMC_Event newEvent;
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)
142       {
143         g_application.OnEvent(newEvent);
144         g_windowManager.MarkDirty();
145       }
146     }
147   }
148   */
149 }
150 @end
151
152 //------------------------------------------------------------------------------------------
153 // special object-c class for handling the NSWindowDidChangeScreenNotification callback.
154 @interface windowDidChangeScreenNoteClass : NSObject
155 {
156   void *m_userdata;
157 }
158 + initWith: (void*) userdata;
159 - (void) windowDidChangeScreenNotification:(NSNotification*) note;
160 @end
161 @implementation windowDidChangeScreenNoteClass
162 + initWith: (void*) userdata;
163 {
164     windowDidChangeScreenNoteClass *windowDidChangeScreen = [windowDidChangeScreenNoteClass new];
165     windowDidChangeScreen->m_userdata = userdata;
166     return [windowDidChangeScreen autorelease];
167 }
168 - (void) windowDidChangeScreenNotification:(NSNotification*) note;
169 {
170   CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
171         if (!winsys)
172     return;
173   winsys->WindowChangedScreen();
174 }
175 @end
176 //------------------------------------------------------------------------------------------
177
178
179 #define MAX_DISPLAYS 32
180 static NSWindow* blankingWindows[MAX_DISPLAYS];
181
182 void* CWinSystemOSX::m_lastOwnedContext = 0;
183
184 //------------------------------------------------------------------------------------------
185 CRect CGRectToCRect(CGRect cgrect)
186 {
187   CRect crect = CRect(
188     cgrect.origin.x,
189     cgrect.origin.y,
190     cgrect.origin.x + cgrect.size.width,
191     cgrect.origin.y + cgrect.size.height);
192   return crect;
193 }
194
195 //------------------------------------------------------------------------------------------
196 Boolean GetDictionaryBoolean(CFDictionaryRef theDict, const void* key)
197 {
198         // get a boolean from the dictionary
199         Boolean value = false;
200         CFBooleanRef boolRef;
201         boolRef = (CFBooleanRef)CFDictionaryGetValue(theDict, key);
202         if (boolRef != NULL)
203                 value = CFBooleanGetValue(boolRef);
204         return value;
205 }
206 //------------------------------------------------------------------------------------------
207 long GetDictionaryLong(CFDictionaryRef theDict, const void* key)
208 {
209         // get a long from the dictionary
210         long value = 0;
211         CFNumberRef numRef;
212         numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
213         if (numRef != NULL)
214                 CFNumberGetValue(numRef, kCFNumberLongType, &value);
215         return value;
216 }
217 //------------------------------------------------------------------------------------------
218 int GetDictionaryInt(CFDictionaryRef theDict, const void* key)
219 {
220         // get a long from the dictionary
221         int value = 0;
222         CFNumberRef numRef;
223         numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
224         if (numRef != NULL)
225                 CFNumberGetValue(numRef, kCFNumberIntType, &value);
226         return value;
227 }
228 //------------------------------------------------------------------------------------------
229 float GetDictionaryFloat(CFDictionaryRef theDict, const void* key)
230 {
231         // get a long from the dictionary
232         int value = 0;
233         CFNumberRef numRef;
234         numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
235         if (numRef != NULL)
236                 CFNumberGetValue(numRef, kCFNumberFloatType, &value);
237         return value;
238 }
239 //------------------------------------------------------------------------------------------
240 double GetDictionaryDouble(CFDictionaryRef theDict, const void* key)
241 {
242         // get a long from the dictionary
243         double value = 0.0;
244         CFNumberRef numRef;
245         numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
246         if (numRef != NULL)
247                 CFNumberGetValue(numRef, kCFNumberDoubleType, &value);
248         return value;
249 }
250
251 //---------------------------------------------------------------------------------
252 void SetMenuBarVisible(bool visible)
253 {
254   if(visible)
255   {
256     [[NSApplication sharedApplication]
257       setPresentationOptions:   NSApplicationPresentationDefault];
258   }
259   else
260   {
261     [[NSApplication sharedApplication]
262       setPresentationOptions:   NSApplicationPresentationHideMenuBar |
263                                 NSApplicationPresentationHideDock];
264   }
265 }
266 //---------------------------------------------------------------------------------
267 CGDirectDisplayID GetDisplayID(int screen_index)
268 {
269   CGDirectDisplayID displayArray[MAX_DISPLAYS];
270   CGDisplayCount    numDisplays;
271
272   // Get the list of displays.
273   CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
274   return(displayArray[screen_index]);
275 }
276
277 CGDirectDisplayID GetDisplayIDFromScreen(NSScreen *screen)
278 {
279   NSDictionary* screenInfo = [screen deviceDescription];
280   NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
281
282   return (CGDirectDisplayID)[screenID longValue];
283 }
284
285 int GetDisplayIndex(CGDirectDisplayID display)
286 {
287   CGDirectDisplayID displayArray[MAX_DISPLAYS];
288   CGDisplayCount    numDisplays;
289
290   // Get the list of displays.
291   CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
292   while (numDisplays > 0)
293   {
294     if (display == displayArray[--numDisplays])
295           return numDisplays;
296   }
297   return -1;
298 }
299
300 void BlankOtherDisplays(int screen_index)
301 {
302   int i;
303   int numDisplays = [[NSScreen screens] count];
304
305   // zero out blankingWindows for debugging
306   for (i=0; i<MAX_DISPLAYS; i++)
307   {
308     blankingWindows[i] = 0;
309   }
310
311   // Blank.
312   for (i=0; i<numDisplays; i++)
313   {
314     if (i != screen_index)
315     {
316       // Get the size.
317       NSScreen* pScreen = [[NSScreen screens] objectAtIndex:i];
318       NSRect    screenRect = [pScreen frame];
319
320       // Build a blanking window.
321       screenRect.origin = NSZeroPoint;
322       blankingWindows[i] = [[NSWindow alloc] initWithContentRect:screenRect
323         styleMask:NSBorderlessWindowMask
324         backing:NSBackingStoreBuffered
325         defer:NO
326         screen:pScreen];
327
328       [blankingWindows[i] setBackgroundColor:[NSColor blackColor]];
329       [blankingWindows[i] setLevel:CGShieldingWindowLevel()];
330       [blankingWindows[i] makeKeyAndOrderFront:nil];
331     }
332   }
333 }
334
335 void UnblankDisplays(void)
336 {
337   int numDisplays = [[NSScreen screens] count];
338   int i = 0;
339
340   for (i=0; i<numDisplays; i++)
341   {
342     if (blankingWindows[i] != 0)
343     {
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;
349     }
350   }
351 }
352
353 CGDisplayFadeReservationToken DisplayFadeToBlack(bool fade)
354 {
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);
359
360   return(fade_token);
361 }
362
363 void DisplayFadeFromBlack(CGDisplayFadeReservationToken fade_token, bool fade)
364 {
365   if (fade_token != kCGDisplayFadeReservationInvalidToken)
366   {
367     if (fade)
368       CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
369     CGReleaseDisplayFadeReservation(fade_token);
370   }
371 }
372
373 NSString* screenNameForDisplay(CGDirectDisplayID displayID)
374 {
375   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
376
377   NSString *screenName = nil;
378
379   NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName);
380   NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
381
382   if ([localizedNames count] > 0) {
383       screenName = [[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] retain];
384   }
385
386   [deviceInfo release];
387   [pool release];
388
389   return [screenName autorelease];
390 }
391
392 void ShowHideNSWindow(NSWindow *wind, bool show)
393 {
394   if (show)
395     [wind orderFront:nil];
396   else
397     [wind orderOut:nil];
398 }
399
400 static NSWindow *curtainWindow;
401 void fadeInDisplay(NSScreen *theScreen, double fadeTime)
402 {
403   int     fadeSteps     = 100;
404   double  fadeInterval  = (fadeTime / (double) fadeSteps);
405
406   if (curtainWindow != nil)
407   {
408     for (int step = 0; step < fadeSteps; step++)
409     {
410       double fade = 1.0 - (step * fadeInterval);
411       [curtainWindow setAlphaValue:fade];
412
413       NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
414       [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
415     }
416   }
417   [curtainWindow close];
418   curtainWindow = nil;
419
420   [NSCursor unhide];
421 }
422
423 void fadeOutDisplay(NSScreen *theScreen, double fadeTime)
424 {
425   int     fadeSteps     = 100;
426   double  fadeInterval  = (fadeTime / (double) fadeSteps);
427
428   [NSCursor hide];
429
430   curtainWindow = [[NSWindow alloc]
431     initWithContentRect:[theScreen frame]
432     styleMask:NSBorderlessWindowMask
433     backing:NSBackingStoreBuffered
434     defer:YES
435     screen:theScreen];
436
437   [curtainWindow setAlphaValue:0.0];
438   [curtainWindow setBackgroundColor:[NSColor blackColor]];
439   [curtainWindow setLevel:NSScreenSaverWindowLevel];
440
441   [curtainWindow makeKeyAndOrderFront:nil];
442   [curtainWindow setFrame:[curtainWindow
443     frameRectForContentRect:[theScreen frame]]
444     display:YES
445     animate:NO];
446
447   for (int step = 0; step < fadeSteps; step++)
448   {
449     double fade = step * fadeInterval;
450     [curtainWindow setAlphaValue:fade];
451
452     NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
453     [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
454   }
455 }
456
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)
460 {
461   if ( screenIdx >= (signed)[[NSScreen screens] count])
462     return NULL;
463
464   Boolean stretched;
465   Boolean interlaced;
466   Boolean safeForHardware;
467   Boolean televisionoutput;
468   int w, h, bitsperpixel;
469   double rate;
470   RESOLUTION_INFO res;
471
472   CLog::Log(LOGDEBUG, "GetMode looking for suitable mode with %d x %d @ %f Hz on display %d\n", width, height, refreshrate, screenIdx);
473
474   CFArrayRef displayModes = CGDisplayAvailableModes(GetDisplayID(screenIdx));
475
476   if (NULL == displayModes)
477   {
478     CLog::Log(LOGERROR, "GetMode - no displaymodes found!");
479     return NULL;
480   }
481
482   for (int i=0; i < CFArrayGetCount(displayModes); ++i)
483   {
484     CFDictionaryRef displayMode = (CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i);
485
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);
494
495
496     if ((bitsperpixel == 32)      &&
497         (safeForHardware == YES)  &&
498         (stretched == NO)         &&
499         (interlaced == NO)        &&
500         (w == width)              &&
501         (h == height)             &&
502         (rate == refreshrate || rate == 0))
503     {
504       CLog::Log(LOGDEBUG, "GetMode found a match!");
505       return displayMode;
506     }
507   }
508   CLog::Log(LOGERROR, "GetMode - no match found!");
509   return NULL;
510 }
511
512 //---------------------------------------------------------------------------------
513 static void DisplayReconfigured(CGDirectDisplayID display,
514   CGDisplayChangeSummaryFlags flags, void* userData)
515 {
516   CWinSystemOSX *winsys = (CWinSystemOSX*)userData;
517         if (!winsys)
518     return;
519
520   if (flags & kCGDisplaySetModeFlag || flags & kCGDisplayBeginConfigurationFlag)
521   {
522     // pre/post-reconfiguration changes
523     RESOLUTION res = g_graphicsContext.GetVideoResolution();
524     if (res == RES_INVALID)
525       return;
526
527     NSScreen* pScreen = nil;
528     unsigned int screenIdx = CDisplaySettings::Get().GetResolutionInfo(res).iScreen;
529
530     if ( screenIdx < [[NSScreen screens] count] )
531     {
532         pScreen = [[NSScreen screens] objectAtIndex:screenIdx];
533     }
534
535     if (pScreen)
536     {
537       CGDirectDisplayID xbmc_display = GetDisplayIDFromScreen(pScreen);
538       if (xbmc_display == display)
539       {
540         // we only respond to changes on the display we are running on.
541         CLog::Log(LOGDEBUG, "CWinSystemOSX::DisplayReconfigured");
542         winsys->CheckDisplayChanging(flags);
543       }
544     }
545   }
546 }
547
548 //---------------------------------------------------------------------------------
549 //---------------------------------------------------------------------------------
550 CWinSystemOSX::CWinSystemOSX() : CWinSystemBase()
551 {
552   m_eWindowSystem = WINDOW_SYSTEM_OSX;
553   m_glContext = 0;
554   m_SDLSurface = NULL;
555   m_osx_events = NULL;
556   m_obscured   = false;
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;
563 }
564
565 CWinSystemOSX::~CWinSystemOSX()
566 {
567 };
568
569 bool CWinSystemOSX::InitWindowSystem()
570 {
571   SDL_EnableUNICODE(1);
572
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);
576
577   if (!CWinSystemBase::InitWindowSystem())
578     return false;
579
580   m_osx_events = new CWinEventsOSX();
581
582   if (m_can_display_switch)
583     CGDisplayRegisterReconfigurationCallback(DisplayReconfigured, (void*)this);
584
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;
592
593
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;
600
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;
607
608   return true;
609 }
610
611 bool CWinSystemOSX::DestroyWindowSystem()
612 {
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];  
617
618   if (m_can_display_switch)
619     CGDisplayRemoveReconfigurationCallback(DisplayReconfigured, (void*)this);
620
621   delete m_osx_events;
622   m_osx_events = NULL;
623
624   UnblankDisplays();
625   if (m_glContext)
626   {
627     NSOpenGLContext* oldContext = (NSOpenGLContext*)m_glContext;
628     [oldContext release];
629     m_glContext = NULL;
630   }
631   return true;
632 }
633
634 bool CWinSystemOSX::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction)
635 {
636   m_nWidth  = res.iWidth;
637   m_nHeight = res.iHeight;
638   m_bFullScreen = fullScreen;
639
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);
645
646   // Enable vertical sync to avoid any tearing.
647   SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
648
649   m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, SDL_OPENGL | SDL_RESIZABLE);
650   if (!m_SDLSurface)
651     return false;
652
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];
657   if (!view)
658     return false;
659
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);
664
665   // disassociate view from context
666   [cur_context clearDrawable];
667
668   // release the context
669   if (m_lastOwnedContext == cur_context)
670   {
671     [ NSOpenGLContext clearCurrentContext ];
672     [ cur_context clearDrawable ];
673     [ cur_context release ];
674   }
675
676   // create a new context
677   NSOpenGLContext* new_context = (NSOpenGLContext*)CreateWindowedContext(nil);
678   if (!new_context)
679     return false;
680
681   // associate with current view
682   [new_context setView:view];
683   [new_context makeCurrentContext];
684
685   // set the window title
686   NSString *string;
687   string = [ [ NSString alloc ] initWithUTF8String:"XBMC Media Center" ];
688   [ [ [new_context view] window] setTitle:string ];
689   [ string release ];
690
691   m_glContext = new_context;
692   m_lastOwnedContext = new_context;
693   m_bWindowCreated = true;
694
695   return true;
696 }
697
698 bool CWinSystemOSX::DestroyWindow()
699 {
700   return true;
701 }
702
703 bool isMavericks()
704 {
705   static int isMavericks = -1;
706
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)
712   {
713     CLog::Log(LOGDEBUG, "Detected Mavericks - enable windowing fixups.");
714     isMavericks = [NSProcessInfo instancesRespondToSelector:@selector(beginActivityWithOptions:reason:)] == TRUE ? 1 : 0;
715   }
716   return isMavericks == 1;
717 }
718
719 extern "C" void SDL_SetWidthHeight(int w, int h);
720 bool CWinSystemOSX::ResizeWindowInternal(int newWidth, int newHeight, int newLeft, int newTop, void *additional)
721 {
722   bool ret = ResizeWindow(newWidth, newHeight, newLeft, newTop);
723
724   if( isMavericks() )
725   {
726     NSView * last_view = (NSView *)additional;
727     if (last_view && [last_view window])
728     {
729       NSWindow* lastWindow = [last_view window];
730       [lastWindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)];
731       [lastWindow update];
732       [last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight)];
733     }
734   }
735   return ret;
736 }
737 bool CWinSystemOSX::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
738 {
739   if (!m_glContext)
740     return false;
741
742   NSOpenGLContext* context = [NSOpenGLContext currentContext];
743   NSView* view;
744   NSWindow* window;
745
746   view = [context view];
747   if (view && (newWidth > 0) && (newHeight > 0))
748   {
749     window = [view window];
750     if (window)
751     {
752       [window setContentSize:NSMakeSize(newWidth, newHeight)];
753       [window update];
754       [view setFrameSize:NSMakeSize(newWidth, newHeight)];
755       [context update];
756     }
757   }
758
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);
763
764   [context makeCurrentContext];
765
766   m_nWidth = newWidth;
767   m_nHeight = newHeight;
768   m_glContext = context;
769
770   return true;
771 }
772
773 static bool needtoshowme = true;
774
775 bool CWinSystemOSX::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
776 {
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;
786   
787   if (m_lastDisplayNr == -1)
788     m_lastDisplayNr = res.iScreen;
789
790   // Fade to black to hide resolution-switching flicker and garbage.
791   CGDisplayFadeReservationToken fade_token = DisplayFadeToBlack(needtoshowme);
792
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
795   // change request.
796   // Recurse to reset fullscreen mode and then continue.
797   if (was_fullscreen && fullScreen)
798   {
799     needtoshowme = false;
800     ShowHideNSWindow([last_view window], needtoshowme);
801     RESOLUTION_INFO& window = CDisplaySettings::Get().GetResolutionInfo(RES_WINDOW);
802     CWinSystemOSX::SetFullScreen(false, window, blankOtherDisplays);
803     needtoshowme = true;
804   }
805
806   m_nWidth      = res.iWidth;
807   m_nHeight     = res.iHeight;
808   m_bFullScreen = fullScreen;
809
810   cur_context = [NSOpenGLContext currentContext];
811   
812   //handle resolution/refreshrate switching early here
813   if (m_bFullScreen)
814   {
815     if (m_can_display_switch)
816     {
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);
822
823       // switch videomode
824       SwitchToVideoMode(res.iWidth, res.iHeight, res.fRefreshRate, res.iScreen);
825       m_lastDisplayNr = res.iScreen;
826     }
827   }
828
829   //no context? done.
830   if (!cur_context)
831   {
832     DisplayFadeFromBlack(fade_token, needtoshowme);
833     return false;
834   }
835
836   if (windowedFullScreenwindow != NULL)
837   {
838     [windowedFullScreenwindow close];
839     if ([windowedFullScreenwindow isReleasedWhenClosed] == NO)
840       [windowedFullScreenwindow release];
841     windowedFullScreenwindow = NULL;
842   }
843
844   if (m_bFullScreen)
845   {
846     // FullScreen Mode
847     NSOpenGLContext* newContext = NULL;
848
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];
856
857     if (CSettings::Get().GetBool("videoscreen.fakefullscreen"))
858     {
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];
863
864       // remove frame origin offset of orginal display
865       screenRect.origin = NSZeroPoint;
866
867       // make a new window to act as the windowedFullScreen
868       windowedFullScreenwindow = [[NSWindow alloc] initWithContentRect:screenRect
869         styleMask:NSBorderlessWindowMask
870         backing:NSBackingStoreBuffered
871         defer:NO
872         screen:pScreen];
873
874       [windowedFullScreenwindow setBackgroundColor:[NSColor blackColor]];
875       [windowedFullScreenwindow makeKeyAndOrderFront:nil];
876
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()];
881
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) ];
888
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)];
894
895       // Obtain windowed pixel format and create a new context.
896       newContext = (NSOpenGLContext*)CreateWindowedContext((void* )cur_context);
897       [newContext setView:blankView];
898
899       // Hide the menu bar.
900       if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || isMavericks() )
901         SetMenuBarVisible(false);
902
903       // Blank other displays if requested.
904       if (blankOtherDisplays)
905         BlankOtherDisplays(res.iScreen);
906     }
907     else
908     {
909       // hide the window
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) ];
914
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);
918       if (!newContext)
919         return false;
920
921       // clear the current context
922       [NSOpenGLContext clearCurrentContext];
923
924       // set fullscreen
925       [newContext setFullScreen];
926
927       // Capture the display before going fullscreen.
928       if (blankOtherDisplays == true)
929         CGCaptureAllDisplays();
930       else
931         CGDisplayCapture(GetDisplayID(res.iScreen));
932
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);
936     }
937
938     // Hide the mouse.
939     [NSCursor hide];
940
941     // Release old context if we created it.
942     if (m_lastOwnedContext == cur_context)
943     {
944       [ NSOpenGLContext clearCurrentContext ];
945       [ cur_context clearDrawable ];
946       [ cur_context release ];
947     }
948
949     // activate context
950     [newContext makeCurrentContext];
951     m_lastOwnedContext = newContext;
952   }
953   else
954   {
955     // Windowed Mode
956     // exit fullscreen
957     [cur_context clearDrawable];
958
959     [NSCursor unhide];
960
961     // Show menubar.
962     if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || isMavericks() )
963       SetMenuBarVisible(true);
964
965     if (CSettings::Get().GetBool("videoscreen.fakefullscreen"))
966     {
967       // restore the windowed window level
968       [[last_view window] setLevel:last_window_level];
969
970       // Get rid of the new window we created.
971       if (windowedFullScreenwindow != NULL)
972       {
973         [windowedFullScreenwindow close];
974         if ([windowedFullScreenwindow isReleasedWhenClosed] == NO)
975           [windowedFullScreenwindow release];
976         windowedFullScreenwindow = NULL;
977       }
978
979       // Unblank.
980       // Force the unblank when returning from fullscreen, we get called with blankOtherDisplays set false.
981       //if (blankOtherDisplays)
982         UnblankDisplays();
983     }
984     else
985     {
986       // release displays
987       CGReleaseAllDisplays();
988     }
989
990     // create our new context (sharing with the current one)
991     NSOpenGLContext* newContext = (NSOpenGLContext*)CreateWindowedContext((void* )cur_context);
992     if (!newContext)
993       return false;
994
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;
1003
1004     // Release the fullscreen context.
1005     if (m_lastOwnedContext == cur_context)
1006     {
1007       [ NSOpenGLContext clearCurrentContext ];
1008       [ cur_context clearDrawable ];
1009       [ cur_context release ];
1010     }
1011
1012     // Activate context.
1013     [newContext makeCurrentContext];
1014     m_lastOwnedContext = newContext;
1015   }
1016
1017   DisplayFadeFromBlack(fade_token, needtoshowme);
1018
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);
1022
1023   return true;
1024 }
1025
1026 void CWinSystemOSX::UpdateResolutions()
1027 {
1028   CWinSystemBase::UpdateResolutions();
1029
1030   // Add desktop resolution
1031   int w, h;
1032   double fps;
1033
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);
1037
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++)
1042   {
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);
1048   }
1049
1050   if (m_can_display_switch)
1051   {
1052     // now just fill in the possible reolutions for the attached screens
1053     // and push to the resolution info vector
1054     FillInVideoModes();
1055   }
1056 }
1057
1058 /*
1059 void* Cocoa_GL_CreateContext(void* pixFmt, void* shareCtx)
1060 {
1061   if (!pixFmt)
1062     return nil;
1063
1064   NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1065     shareContext:(NSOpenGLContext*)shareCtx];
1066
1067   // snipit from SDL_cocoaopengl.m
1068   //
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.
1074   //
1075
1076   #ifndef GLI_ARRAY_FUNC_CACHE_MAX
1077   #define GLI_ARRAY_FUNC_CACHE_MAX 284
1078   #endif
1079
1080   #ifndef GLI_SUBMIT_FUNC_CACHE_MAX
1081   #define GLI_SUBMIT_FUNC_CACHE_MAX 280
1082   #endif
1083
1084   {
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);
1089   }
1090
1091   // End Wisdom from Apple Engineer section. --ryan.
1092   return newContext;
1093 }
1094 */
1095
1096 void* CWinSystemOSX::CreateWindowedContext(void* shareCtx)
1097 {
1098   NSOpenGLContext* newContext = NULL;
1099
1100   NSOpenGLPixelFormatAttribute wattrs[] =
1101   {
1102     NSOpenGLPFADoubleBuffer,
1103     NSOpenGLPFAWindow,
1104     NSOpenGLPFANoRecovery,
1105     NSOpenGLPFAAccelerated,
1106     NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1107     (NSOpenGLPixelFormatAttribute)0
1108   };
1109
1110   NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs];
1111
1112   newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1113     shareContext:(NSOpenGLContext*)shareCtx];
1114   [pixFmt release];
1115
1116   if (!newContext)
1117   {
1118     // bah, try again for non-accelerated renderer
1119     NSOpenGLPixelFormatAttribute wattrs2[] =
1120     {
1121       NSOpenGLPFADoubleBuffer,
1122       NSOpenGLPFAWindow,
1123       NSOpenGLPFANoRecovery,
1124       NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1125       (NSOpenGLPixelFormatAttribute)0
1126     };
1127     NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs2];
1128
1129     newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1130       shareContext:(NSOpenGLContext*)shareCtx];
1131     [pixFmt release];
1132   }
1133
1134   return newContext;
1135 }
1136
1137 void* CWinSystemOSX::CreateFullScreenContext(int screen_index, void* shareCtx)
1138 {
1139   CGDirectDisplayID displayArray[MAX_DISPLAYS];
1140   CGDisplayCount    numDisplays;
1141   CGDirectDisplayID displayID;
1142
1143   // Get the list of displays.
1144   CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
1145   displayID = displayArray[screen_index];
1146
1147   NSOpenGLPixelFormatAttribute fsattrs[] =
1148   {
1149     NSOpenGLPFADoubleBuffer,
1150     NSOpenGLPFAFullScreen,
1151     NSOpenGLPFANoRecovery,
1152     NSOpenGLPFAAccelerated,
1153     NSOpenGLPFADepthSize,  (NSOpenGLPixelFormatAttribute)8,
1154     NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(displayID),
1155     (NSOpenGLPixelFormatAttribute)0
1156   };
1157
1158   NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs];
1159   if (!pixFmt)
1160     return nil;
1161
1162   NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1163     shareContext:(NSOpenGLContext*)shareCtx];
1164   [pixFmt release];
1165
1166   return newContext;
1167 }
1168
1169 void CWinSystemOSX::GetScreenResolution(int* w, int* h, double* fps, int screenIdx)
1170 {
1171   // Figure out the screen size. (default to main screen)
1172   if (screenIdx >= GetNumScreens())
1173     return;
1174   CGDirectDisplayID display_id = (CGDirectDisplayID)GetDisplayID(screenIdx);
1175
1176   NSOpenGLContext* context = [NSOpenGLContext currentContext];
1177   if (context)
1178   {
1179     NSView* view;
1180
1181     view = [context view];
1182     if (view)
1183     {
1184       NSWindow* window;
1185       window = [view window];
1186       if (window)
1187         display_id = GetDisplayIDFromScreen( [window screen] );
1188     }
1189   }
1190   CGDisplayModeRef mode  = CGDisplayCopyDisplayMode(display_id);
1191   *w = CGDisplayModeGetWidth(mode);
1192   *h = CGDisplayModeGetHeight(mode);
1193   *fps = CGDisplayModeGetRefreshRate(mode);
1194   CGDisplayModeRelease(mode);
1195   if ((int)*fps == 0)
1196   {
1197     // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
1198     *fps = 60.0;
1199   }
1200 }
1201
1202 void CWinSystemOSX::EnableVSync(bool enable)
1203 {
1204   // OpenGL Flush synchronised with vertical retrace
1205   GLint swapInterval = enable ? 1 : 0;
1206   [[NSOpenGLContext currentContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
1207 }
1208
1209 bool CWinSystemOSX::SwitchToVideoMode(int width, int height, double refreshrate, int screenIdx)
1210 {
1211   // SwitchToVideoMode will not return until the display has actually switched over.
1212   // This can take several seconds.
1213   if( screenIdx >= GetNumScreens())
1214     return false;
1215
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);
1220
1221   // find mode that matches the desired size, refreshrate
1222   // non interlaced, nonstretched, safe for hardware
1223   dispMode = GetMode(width, height, refreshrate, screenIdx);
1224
1225   //not found - fallback to bestemdeforparameters
1226   if (!dispMode)
1227   {
1228     dispMode = CGDisplayBestModeForParameters(display_id, 32, width, height, &match);
1229
1230     if (!match)
1231       dispMode = CGDisplayBestModeForParameters(display_id, 16, width, height, &match);
1232
1233     if (!match)
1234       return false;
1235   }
1236
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);
1246
1247   Cocoa_CVDisplayLinkUpdate();
1248
1249   return (err == kCGErrorSuccess);
1250 }
1251
1252 void CWinSystemOSX::FillInVideoModes()
1253 {
1254   // Add full screen settings for additional monitors
1255   int numDisplays = [[NSScreen screens] count];
1256
1257   for (int disp = 0; disp < numDisplays; disp++)
1258   {
1259     Boolean stretched;
1260     Boolean interlaced;
1261     Boolean safeForHardware;
1262     Boolean televisionoutput;
1263     int w, h, bitsperpixel;
1264     double refreshrate;
1265     RESOLUTION_INFO res;
1266
1267     CFArrayRef displayModes = CGDisplayAvailableModes(GetDisplayID(disp));
1268     NSString *dispName = screenNameForDisplay(GetDisplayID(disp));
1269     CLog::Log(LOGNOTICE, "Display %i has name %s", disp, [dispName UTF8String]);
1270
1271     if (NULL == displayModes)
1272       continue;
1273
1274     for (int i=0; i < CFArrayGetCount(displayModes); ++i)
1275     {
1276       CFDictionaryRef displayMode = (CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i);
1277
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);
1283
1284       if ((bitsperpixel == 32)      &&
1285           (safeForHardware == YES)  &&
1286           (stretched == NO)         &&
1287           (interlaced == NO))
1288       {
1289         w = GetDictionaryInt(displayMode, kCGDisplayWidth);
1290         h = GetDictionaryInt(displayMode, kCGDisplayHeight);
1291         refreshrate = GetDictionaryDouble(displayMode, kCGDisplayRefreshRate);
1292         if ((int)refreshrate == 0)  // LCD display?
1293         {
1294           // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
1295           refreshrate = 60.0;
1296         }
1297         CLog::Log(LOGNOTICE, "Found possible resolution for display %d with %d x %d @ %f Hz\n", disp, w, h, refreshrate);
1298
1299         UpdateDesktopResolution(res, disp, w, h, refreshrate);
1300
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);
1314       }
1315     }
1316   }
1317 }
1318
1319 bool CWinSystemOSX::FlushBuffer(void)
1320 {
1321   [ (NSOpenGLContext*)m_glContext flushBuffer ];
1322
1323   return true;
1324 }
1325
1326 bool CWinSystemOSX::IsObscured(void)
1327 {
1328   // check once a second if we are obscured.
1329   unsigned int now_time = XbmcThreads::SystemClockMillis();
1330   if (m_obscured_timecheck > now_time)
1331     return m_obscured;
1332   else
1333     m_obscured_timecheck = now_time + 1000;
1334
1335   NSOpenGLContext* cur_context = [NSOpenGLContext currentContext];
1336   NSView* view = [cur_context view];
1337   if (!view)
1338   {
1339     // sanity check, we should always have a view
1340     m_obscured = true;
1341     return m_obscured;
1342   }
1343
1344   NSWindow *window = [view window];
1345   if (!window)
1346   {
1347     // sanity check, we should always have a window
1348     m_obscured = true;
1349     return m_obscured;
1350   }
1351
1352   if ([window isVisible] == NO)
1353   {
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.
1357     m_obscured = true;
1358     return m_obscured;
1359   }
1360
1361   // check if we are minimized (to an icon in the Dock).
1362   if ([window isMiniaturized] == YES)
1363   {
1364     m_obscured = true;
1365     return m_obscured;
1366   }
1367
1368   // check if we are showing on the active workspace.
1369   if ([window isOnActiveSpace] == NO)
1370   {
1371     m_obscured = true;
1372     return m_obscured;
1373   }
1374
1375   // default to false before we start parsing though the windows.
1376   // if we are are obscured by any windows, then set true.
1377   m_obscured = false;
1378   static bool obscureLogged = false;
1379
1380   CGWindowListOption opts;
1381   opts = kCGWindowListOptionOnScreenAboveWindow | kCGWindowListExcludeDesktopElements;
1382   CFArrayRef windowIDs =CGWindowListCreate(opts, (CGWindowID)[window windowNumber]);  
1383
1384   if (!windowIDs)
1385     return m_obscured;
1386
1387   CFArrayRef windowDescs = CGWindowListCreateDescriptionFromArray(windowIDs);
1388   if (!windowDescs)
1389   {
1390     CFRelease(windowIDs);
1391     return m_obscured;
1392   }
1393
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;
1401
1402   std::vector<CRect> partialOverlaps;
1403   CRect ourBounds = CGRectToCRect(bounds);
1404
1405   for (CFIndex idx=0; idx < CFArrayGetCount(windowDescs); idx++)
1406   {
1407     // walk the window list of windows that are above us and are not desktop elements
1408     CFDictionaryRef windowDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(windowDescs, idx);
1409
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)
1413       continue;
1414
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)
1424       continue;
1425
1426     CFDictionaryRef rectDictionary = (CFDictionaryRef)CFDictionaryGetValue(windowDictionary, kCGWindowBounds);
1427     if (!rectDictionary)
1428       continue;
1429
1430     CGRect windowBounds;
1431     if (CGRectMakeWithDictionaryRepresentation(rectDictionary, &windowBounds))
1432     {
1433       if (CGRectContainsRect(windowBounds, bounds))
1434       {
1435         // if the windowBounds completely encloses our bounds, we are obscured.
1436         if (!obscureLogged)
1437         {
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;
1442         }
1443         m_obscured = true;
1444         break;
1445       }
1446
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);
1455     }
1456   }
1457
1458   if (!m_obscured)
1459   {
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.
1462     if (obscureLogged)
1463       obscureLogged = false;
1464     std::vector<CRect> rects = ourBounds.SubtractRects(partialOverlaps);
1465     // they got us covered
1466     if (rects.size() == 0)
1467       m_obscured = true;
1468   }
1469
1470   CFRelease(windowDescs);
1471   CFRelease(windowIDs);
1472
1473   return m_obscured;
1474 }
1475
1476 void CWinSystemOSX::NotifyAppFocusChange(bool bGaining)
1477 {
1478   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1479
1480   if (m_bFullScreen && bGaining)
1481   {
1482     // find the window
1483     NSOpenGLContext* context = [NSOpenGLContext currentContext];
1484     if (context)
1485     {
1486       NSView* view;
1487
1488       view = [context view];
1489       if (view)
1490       {
1491         NSWindow* window;
1492         window = [view window];
1493         if (window)
1494         {
1495           // find the screenID
1496           NSDictionary* screenInfo = [[window screen] deviceDescription];
1497           NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
1498           if ((CGDirectDisplayID)[screenID longValue] == kCGDirectMainDisplay || isMavericks() )
1499           {
1500             SetMenuBarVisible(false);
1501           }
1502           [window orderFront:nil];
1503         }
1504       }
1505     }
1506   }
1507   [pool release];
1508 }
1509
1510 void CWinSystemOSX::ShowOSMouse(bool show)
1511 {
1512   SDL_ShowCursor(show ? 1 : 0);
1513 }
1514
1515 bool CWinSystemOSX::Minimize()
1516 {
1517   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1518
1519   [[NSApplication sharedApplication] miniaturizeAll:nil];
1520
1521   [pool release];
1522   return true;
1523 }
1524
1525 bool CWinSystemOSX::Restore()
1526 {
1527   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1528
1529   [[NSApplication sharedApplication] unhide:nil];
1530
1531   [pool release];
1532   return true;
1533 }
1534
1535 bool CWinSystemOSX::Hide()
1536 {
1537   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1538
1539   [[NSApplication sharedApplication] hide:nil];
1540
1541   [pool release];
1542   return true;
1543 }
1544
1545 void CWinSystemOSX::OnMove(int x, int y)
1546 {
1547   Cocoa_CVDisplayLinkUpdate();
1548 }
1549
1550 void CWinSystemOSX::EnableSystemScreenSaver(bool bEnable)
1551 {
1552   // see Technical Q&A QA1340
1553   static IOPMAssertionID assertionID = 0;
1554
1555   if (!bEnable)
1556   {
1557     if (assertionID == 0)
1558     {
1559       CFStringRef reasonForActivity= CFSTR("XBMC requested disable system screen saver");
1560       IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
1561         kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
1562     }
1563     UpdateSystemActivity(UsrActivity);
1564   }
1565   else if (assertionID != 0)
1566   {
1567     IOPMAssertionRelease(assertionID);
1568     assertionID = 0;
1569   }
1570
1571   m_use_system_screensaver = bEnable;
1572 }
1573
1574 bool CWinSystemOSX::IsSystemScreenSaverEnabled()
1575 {
1576   return m_use_system_screensaver;
1577 }
1578
1579 void CWinSystemOSX::ResetOSScreensaver()
1580 {
1581   // allow os screensaver only if we are fullscreen
1582   EnableSystemScreenSaver(!m_bFullScreen);
1583 }
1584
1585 bool CWinSystemOSX::EnableFrameLimiter()
1586 {
1587   return IsObscured();
1588 }
1589
1590 void CWinSystemOSX::EnableTextInput(bool bEnable)
1591 {
1592   if (bEnable)
1593     StartTextInput();
1594   else
1595     StopTextInput();
1596 }
1597
1598 OSXTextInputResponder *g_textInputResponder = nil;
1599
1600 bool CWinSystemOSX::IsTextInputEnabled()
1601 {
1602   return g_textInputResponder != nil && [[g_textInputResponder superview] isEqual: [[NSApp keyWindow] contentView]];
1603 }
1604
1605 void CWinSystemOSX::StartTextInput()
1606 {
1607   NSView *parentView = [[NSApp keyWindow] contentView];
1608
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)];
1617   }
1618
1619   if (![[g_textInputResponder superview] isEqual: parentView])
1620   {
1621 //    DLOG(@"add fieldEdit to window contentView");
1622     [g_textInputResponder removeFromSuperview];
1623     [parentView addSubview: g_textInputResponder];
1624     [[NSApp keyWindow] makeFirstResponder: g_textInputResponder];
1625   }
1626 }
1627 void CWinSystemOSX::StopTextInput()
1628 {
1629   if (g_textInputResponder) {
1630     [g_textInputResponder removeFromSuperview];
1631     [g_textInputResponder release];
1632     g_textInputResponder = nil;
1633   }
1634 }
1635
1636 void CWinSystemOSX::Register(IDispResource *resource)
1637 {
1638   CSingleLock lock(m_resourceSection);
1639   m_resources.push_back(resource);
1640 }
1641
1642 void CWinSystemOSX::Unregister(IDispResource* resource)
1643 {
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);
1648 }
1649
1650 bool CWinSystemOSX::Show(bool raise)
1651 {
1652   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1653
1654   if (raise)
1655   {
1656     [[NSApplication sharedApplication] unhide:nil];
1657     [[NSApplication sharedApplication] activateIgnoringOtherApps: YES];
1658     [[NSApplication sharedApplication] arrangeInFront:nil];
1659   }
1660   else
1661   {
1662     [[NSApplication sharedApplication] unhideWithoutActivation];
1663   }
1664
1665   [pool release];
1666   return true;
1667 }
1668
1669 int CWinSystemOSX::GetNumScreens()
1670 {
1671   int numDisplays = [[NSScreen screens] count];
1672   return(numDisplays);
1673 }
1674
1675 int CWinSystemOSX::GetCurrentScreen()
1676 {
1677   NSOpenGLContext* context = [NSOpenGLContext currentContext];
1678   
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;
1683   
1684   // if we are here the user dragged the window to a different
1685   // screen and we return the screen of the window
1686   if (context)
1687   {
1688     NSView* view;
1689
1690     view = [context view];
1691     if (view)
1692     {
1693       NSWindow* window;
1694       window = [view window];
1695       if (window)
1696       {
1697         m_movedToOtherScreen = false;
1698         return GetDisplayIndex(GetDisplayIDFromScreen( [window screen] ));
1699       }
1700         
1701     }
1702   }
1703   return 0;
1704 }
1705
1706 void CWinSystemOSX::WindowChangedScreen()
1707 {
1708   // user has moved the window to a
1709   // different screen
1710   m_movedToOtherScreen = true;
1711 }
1712
1713 void CWinSystemOSX::CheckDisplayChanging(u_int32_t flags)
1714 {
1715   if (flags)
1716   {
1717     CSingleLock lock(m_resourceSection);
1718     // tell any shared resources
1719     if (flags & kCGDisplayBeginConfigurationFlag)
1720     {
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();
1724     }
1725     if (flags & kCGDisplaySetModeFlag)
1726     {
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();
1730     }
1731   }
1732 }
1733
1734 void* CWinSystemOSX::GetCGLContextObj()
1735 {
1736   return [(NSOpenGLContext*)m_glContext CGLContextObj];
1737 }
1738
1739 std::string CWinSystemOSX::GetClipboardText(void)
1740 {
1741   std::string utf8_text;
1742
1743   const char *szStr = Cocoa_Paste();
1744   if (szStr)
1745     utf8_text = szStr;
1746
1747   return utf8_text;
1748 }
1749
1750 #endif