Merge pull request #4196 from arnova/sub_fallback
[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 extern "C" void SDL_SetWidthHeight(int w, int h);
704 bool CWinSystemOSX::ResizeWindowInternal(int newWidth, int newHeight, int newLeft, int newTop, void *additional)
705 {
706   bool ret = ResizeWindow(newWidth, newHeight, newLeft, newTop);
707
708   if( DarwinIsMavericks() )
709   {
710     NSView * last_view = (NSView *)additional;
711     if (last_view && [last_view window])
712     {
713       NSWindow* lastWindow = [last_view window];
714       [lastWindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)];
715       [lastWindow update];
716       [last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight)];
717     }
718   }
719   return ret;
720 }
721 bool CWinSystemOSX::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
722 {
723   if (!m_glContext)
724     return false;
725
726   NSOpenGLContext* context = [NSOpenGLContext currentContext];
727   NSView* view;
728   NSWindow* window;
729
730   view = [context view];
731   if (view && (newWidth > 0) && (newHeight > 0))
732   {
733     window = [view window];
734     if (window)
735     {
736       [window setContentSize:NSMakeSize(newWidth, newHeight)];
737       [window update];
738       [view setFrameSize:NSMakeSize(newWidth, newHeight)];
739       [context update];
740     }
741   }
742
743   // HACK: resize SDL's view manually so that mouse bounds are correctly updated.
744   // there are two parts to this, the internal SDL (current_video->screen) and
745   // the cocoa view ( handled in SetFullScreen).
746   SDL_SetWidthHeight(newWidth, newHeight);
747
748   [context makeCurrentContext];
749
750   m_nWidth = newWidth;
751   m_nHeight = newHeight;
752   m_glContext = context;
753
754   return true;
755 }
756
757 static bool needtoshowme = true;
758
759 bool CWinSystemOSX::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
760 {
761   static NSWindow* windowedFullScreenwindow = NULL;
762   static NSScreen* last_window_screen = NULL;
763   static NSPoint last_window_origin;
764   static NSView* last_view = NULL;
765   static NSSize last_view_size;
766   static NSPoint last_view_origin;
767   static NSInteger last_window_level = NSNormalWindowLevel;
768   bool was_fullscreen = m_bFullScreen;
769   NSOpenGLContext* cur_context;
770   
771   if (m_lastDisplayNr == -1)
772     m_lastDisplayNr = res.iScreen;
773
774   // Fade to black to hide resolution-switching flicker and garbage.
775   CGDisplayFadeReservationToken fade_token = DisplayFadeToBlack(needtoshowme);
776
777   // If we're already fullscreen then we must be moving to a different display.
778   // or if we are still on the same display - it might be only a refreshrate/resolution
779   // change request.
780   // Recurse to reset fullscreen mode and then continue.
781   if (was_fullscreen && fullScreen)
782   {
783     needtoshowme = false;
784     ShowHideNSWindow([last_view window], needtoshowme);
785     RESOLUTION_INFO& window = CDisplaySettings::Get().GetResolutionInfo(RES_WINDOW);
786     CWinSystemOSX::SetFullScreen(false, window, blankOtherDisplays);
787     needtoshowme = true;
788   }
789
790   m_nWidth      = res.iWidth;
791   m_nHeight     = res.iHeight;
792   m_bFullScreen = fullScreen;
793
794   cur_context = [NSOpenGLContext currentContext];
795   
796   //handle resolution/refreshrate switching early here
797   if (m_bFullScreen)
798   {
799     if (m_can_display_switch)
800     {
801       // switch videomode
802       SwitchToVideoMode(res.iWidth, res.iHeight, res.fRefreshRate, res.iScreen);
803       m_lastDisplayNr = res.iScreen;
804     }
805   }
806
807   //no context? done.
808   if (!cur_context)
809   {
810     DisplayFadeFromBlack(fade_token, needtoshowme);
811     return false;
812   }
813
814   if (windowedFullScreenwindow != NULL)
815   {
816     [windowedFullScreenwindow close];
817     if ([windowedFullScreenwindow isReleasedWhenClosed] == NO)
818       [windowedFullScreenwindow release];
819     windowedFullScreenwindow = NULL;
820   }
821
822   if (m_bFullScreen)
823   {
824     // FullScreen Mode
825     NSOpenGLContext* newContext = NULL;
826
827     // Save info about the windowed context so we can restore it when returning to windowed.
828     last_view = [cur_context view];
829     last_view_size = [last_view frame].size;
830     last_view_origin = [last_view frame].origin;
831     last_window_screen = [[last_view window] screen];
832     last_window_origin = [[last_view window] frame].origin;
833     last_window_level = [[last_view window] level];
834
835     if (CSettings::Get().GetBool("videoscreen.fakefullscreen"))
836     {
837       // This is Cocca Windowed FullScreen Mode
838       // Get the screen rect of our current display
839       NSScreen* pScreen = [[NSScreen screens] objectAtIndex:res.iScreen];
840       NSRect    screenRect = [pScreen frame];
841
842       // remove frame origin offset of orginal display
843       screenRect.origin = NSZeroPoint;
844
845       // make a new window to act as the windowedFullScreen
846       windowedFullScreenwindow = [[NSWindow alloc] initWithContentRect:screenRect
847         styleMask:NSBorderlessWindowMask
848         backing:NSBackingStoreBuffered
849         defer:NO
850         screen:pScreen];
851
852       [windowedFullScreenwindow setBackgroundColor:[NSColor blackColor]];
853       [windowedFullScreenwindow makeKeyAndOrderFront:nil];
854
855       // make our window the same level as the rest to enable cmd+tab switching
856       [windowedFullScreenwindow setLevel:NSNormalWindowLevel];
857       // this will make our window topmost and hide all system messages
858       //[windowedFullScreenwindow setLevel:CGShieldingWindowLevel()];
859
860       // ...and the original one beneath it and on the same screen.
861       [[last_view window] setLevel:NSNormalWindowLevel-1];
862       [[last_view window] setFrameOrigin:[pScreen frame].origin];
863       // expand the mouse bounds in SDL view to fullscreen
864       [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)];
865       [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ];
866
867       NSView* blankView = [[NSView alloc] init];
868       [windowedFullScreenwindow setContentView:blankView];
869       [windowedFullScreenwindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)];
870       [windowedFullScreenwindow update];
871       [blankView setFrameSize:NSMakeSize(m_nWidth, m_nHeight)];
872
873       // Obtain windowed pixel format and create a new context.
874       newContext = (NSOpenGLContext*)CreateWindowedContext((void* )cur_context);
875       [newContext setView:blankView];
876
877       // Hide the menu bar.
878       if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || DarwinIsMavericks() )
879         SetMenuBarVisible(false);
880
881       // Blank other displays if requested.
882       if (blankOtherDisplays)
883         BlankOtherDisplays(res.iScreen);
884     }
885     else
886     {
887       // hide the window
888       [[last_view window] setFrameOrigin:[last_window_screen frame].origin];
889       // expand the mouse bounds in SDL view to fullscreen
890       [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)];
891       [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ];
892
893       // This is OpenGL FullScreen Mode
894       // create our new context (sharing with the current one)
895       newContext = (NSOpenGLContext*)CreateFullScreenContext(res.iScreen, (void*)cur_context);
896       if (!newContext)
897         return false;
898
899       // clear the current context
900       [NSOpenGLContext clearCurrentContext];
901
902       // set fullscreen
903       [newContext setFullScreen];
904
905       // Capture the display before going fullscreen.
906       if (blankOtherDisplays == true)
907         CGCaptureAllDisplays();
908       else
909         CGDisplayCapture(GetDisplayID(res.iScreen));
910
911       // If we don't hide menu bar, it will get events and interrupt the program.
912       if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || DarwinIsMavericks() )
913         SetMenuBarVisible(false);
914     }
915
916     // Hide the mouse.
917     [NSCursor hide];
918
919     // Release old context if we created it.
920     if (m_lastOwnedContext == cur_context)
921     {
922       [ NSOpenGLContext clearCurrentContext ];
923       [ cur_context clearDrawable ];
924       [ cur_context release ];
925     }
926
927     // activate context
928     [newContext makeCurrentContext];
929     m_lastOwnedContext = newContext;
930   }
931   else
932   {
933     // Windowed Mode
934     // exit fullscreen
935     [cur_context clearDrawable];
936
937     [NSCursor unhide];
938
939     // Show menubar.
940     if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay || DarwinIsMavericks() )
941       SetMenuBarVisible(true);
942
943     if (CSettings::Get().GetBool("videoscreen.fakefullscreen"))
944     {
945       // restore the windowed window level
946       [[last_view window] setLevel:last_window_level];
947
948       // Get rid of the new window we created.
949       if (windowedFullScreenwindow != NULL)
950       {
951         [windowedFullScreenwindow close];
952         if ([windowedFullScreenwindow isReleasedWhenClosed] == NO)
953           [windowedFullScreenwindow release];
954         windowedFullScreenwindow = NULL;
955       }
956
957       // Unblank.
958       // Force the unblank when returning from fullscreen, we get called with blankOtherDisplays set false.
959       //if (blankOtherDisplays)
960         UnblankDisplays();
961     }
962     else
963     {
964       // release displays
965       CGReleaseAllDisplays();
966     }
967
968     // create our new context (sharing with the current one)
969     NSOpenGLContext* newContext = (NSOpenGLContext*)CreateWindowedContext((void* )cur_context);
970     if (!newContext)
971       return false;
972
973     // Assign view from old context, move back to original screen.
974     [newContext setView:last_view];
975     [[last_view window] setFrameOrigin:last_window_origin];
976     // return the mouse bounds in SDL view to prevous size
977     [ last_view setFrameSize:last_view_size ];
978     [ last_view setFrameOrigin:last_view_origin ];
979     // done with restoring windowed window, don't set last_view to NULL as we can lose it under dual displays.
980     //last_window_screen = NULL;
981
982     // Release the fullscreen context.
983     if (m_lastOwnedContext == cur_context)
984     {
985       [ NSOpenGLContext clearCurrentContext ];
986       [ cur_context clearDrawable ];
987       [ cur_context release ];
988     }
989
990     // Activate context.
991     [newContext makeCurrentContext];
992     m_lastOwnedContext = newContext;
993   }
994
995   DisplayFadeFromBlack(fade_token, needtoshowme);
996
997   ShowHideNSWindow([last_view window], needtoshowme);
998   // need to make sure SDL tracks any window size changes
999   ResizeWindowInternal(m_nWidth, m_nHeight, -1, -1, last_view);
1000
1001   return true;
1002 }
1003
1004 void CWinSystemOSX::UpdateResolutions()
1005 {
1006   CWinSystemBase::UpdateResolutions();
1007
1008   // Add desktop resolution
1009   int w, h;
1010   double fps;
1011
1012   // first screen goes into the current desktop mode
1013   GetScreenResolution(&w, &h, &fps, 0);
1014   UpdateDesktopResolution(CDisplaySettings::Get().GetResolutionInfo(RES_DESKTOP), 0, w, h, fps);
1015
1016   // see resolution.h enum RESOLUTION for how the resolutions
1017   // have to appear in the resolution info vector in CDisplaySettings
1018   // add the desktop resolutions of the other screens
1019   for(int i = 1; i < GetNumScreens(); i++)
1020   {
1021     RESOLUTION_INFO res;
1022     // get current resolution of screen i
1023     GetScreenResolution(&w, &h, &fps, i);
1024     UpdateDesktopResolution(res, i, w, h, fps);
1025     CDisplaySettings::Get().AddResolutionInfo(res);
1026   }
1027
1028   if (m_can_display_switch)
1029   {
1030     // now just fill in the possible reolutions for the attached screens
1031     // and push to the resolution info vector
1032     FillInVideoModes();
1033   }
1034 }
1035
1036 /*
1037 void* Cocoa_GL_CreateContext(void* pixFmt, void* shareCtx)
1038 {
1039   if (!pixFmt)
1040     return nil;
1041
1042   NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1043     shareContext:(NSOpenGLContext*)shareCtx];
1044
1045   // snipit from SDL_cocoaopengl.m
1046   //
1047   // Wisdom from Apple engineer in reference to UT2003's OpenGL performance:
1048   //  "You are blowing a couple of the internal OpenGL function caches. This
1049   //  appears to be happening in the VAO case.  You can tell OpenGL to up
1050   //  the cache size by issuing the following calls right after you create
1051   //  the OpenGL context.  The default cache size is 16."    --ryan.
1052   //
1053
1054   #ifndef GLI_ARRAY_FUNC_CACHE_MAX
1055   #define GLI_ARRAY_FUNC_CACHE_MAX 284
1056   #endif
1057
1058   #ifndef GLI_SUBMIT_FUNC_CACHE_MAX
1059   #define GLI_SUBMIT_FUNC_CACHE_MAX 280
1060   #endif
1061
1062   {
1063       long cache_max = 64;
1064       CGLContextObj ctx = (CGLContextObj)[newContext CGLContextObj];
1065       CGLSetParameter(ctx, (CGLContextParameter)GLI_SUBMIT_FUNC_CACHE_MAX, &cache_max);
1066       CGLSetParameter(ctx, (CGLContextParameter)GLI_ARRAY_FUNC_CACHE_MAX, &cache_max);
1067   }
1068
1069   // End Wisdom from Apple Engineer section. --ryan.
1070   return newContext;
1071 }
1072 */
1073
1074 void* CWinSystemOSX::CreateWindowedContext(void* shareCtx)
1075 {
1076   NSOpenGLContext* newContext = NULL;
1077
1078   NSOpenGLPixelFormatAttribute wattrs[] =
1079   {
1080     NSOpenGLPFADoubleBuffer,
1081     NSOpenGLPFAWindow,
1082     NSOpenGLPFANoRecovery,
1083     NSOpenGLPFAAccelerated,
1084     NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1085     (NSOpenGLPixelFormatAttribute)0
1086   };
1087
1088   NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs];
1089
1090   newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1091     shareContext:(NSOpenGLContext*)shareCtx];
1092   [pixFmt release];
1093
1094   if (!newContext)
1095   {
1096     // bah, try again for non-accelerated renderer
1097     NSOpenGLPixelFormatAttribute wattrs2[] =
1098     {
1099       NSOpenGLPFADoubleBuffer,
1100       NSOpenGLPFAWindow,
1101       NSOpenGLPFANoRecovery,
1102       NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1103       (NSOpenGLPixelFormatAttribute)0
1104     };
1105     NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs2];
1106
1107     newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1108       shareContext:(NSOpenGLContext*)shareCtx];
1109     [pixFmt release];
1110   }
1111
1112   return newContext;
1113 }
1114
1115 void* CWinSystemOSX::CreateFullScreenContext(int screen_index, void* shareCtx)
1116 {
1117   CGDirectDisplayID displayArray[MAX_DISPLAYS];
1118   CGDisplayCount    numDisplays;
1119   CGDirectDisplayID displayID;
1120
1121   // Get the list of displays.
1122   CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
1123   displayID = displayArray[screen_index];
1124
1125   NSOpenGLPixelFormatAttribute fsattrs[] =
1126   {
1127     NSOpenGLPFADoubleBuffer,
1128     NSOpenGLPFAFullScreen,
1129     NSOpenGLPFANoRecovery,
1130     NSOpenGLPFAAccelerated,
1131     NSOpenGLPFADepthSize,  (NSOpenGLPixelFormatAttribute)8,
1132     NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(displayID),
1133     (NSOpenGLPixelFormatAttribute)0
1134   };
1135
1136   NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs];
1137   if (!pixFmt)
1138     return nil;
1139
1140   NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1141     shareContext:(NSOpenGLContext*)shareCtx];
1142   [pixFmt release];
1143
1144   return newContext;
1145 }
1146
1147 void CWinSystemOSX::GetScreenResolution(int* w, int* h, double* fps, int screenIdx)
1148 {
1149   // Figure out the screen size. (default to main screen)
1150   if (screenIdx >= GetNumScreens())
1151     return;
1152   CGDirectDisplayID display_id = (CGDirectDisplayID)GetDisplayID(screenIdx);
1153
1154   NSOpenGLContext* context = [NSOpenGLContext currentContext];
1155   if (context)
1156   {
1157     NSView* view;
1158
1159     view = [context view];
1160     if (view)
1161     {
1162       NSWindow* window;
1163       window = [view window];
1164       if (window)
1165         display_id = GetDisplayIDFromScreen( [window screen] );
1166     }
1167   }
1168   CGDisplayModeRef mode  = CGDisplayCopyDisplayMode(display_id);
1169   *w = CGDisplayModeGetWidth(mode);
1170   *h = CGDisplayModeGetHeight(mode);
1171   *fps = CGDisplayModeGetRefreshRate(mode);
1172   CGDisplayModeRelease(mode);
1173   if ((int)*fps == 0)
1174   {
1175     // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
1176     *fps = 60.0;
1177   }
1178 }
1179
1180 void CWinSystemOSX::EnableVSync(bool enable)
1181 {
1182   // OpenGL Flush synchronised with vertical retrace
1183   GLint swapInterval = enable ? 1 : 0;
1184   [[NSOpenGLContext currentContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
1185 }
1186
1187 bool CWinSystemOSX::SwitchToVideoMode(int width, int height, double refreshrate, int screenIdx)
1188 {
1189   // SwitchToVideoMode will not return until the display has actually switched over.
1190   // This can take several seconds.
1191   if( screenIdx >= GetNumScreens())
1192     return false;
1193
1194   boolean_t match = false;
1195   CFDictionaryRef dispMode = NULL;
1196   // Figure out the screen size. (default to main screen)
1197   CGDirectDisplayID display_id = GetDisplayID(screenIdx);
1198
1199   // find mode that matches the desired size, refreshrate
1200   // non interlaced, nonstretched, safe for hardware
1201   dispMode = GetMode(width, height, refreshrate, screenIdx);
1202
1203   //not found - fallback to bestemdeforparameters
1204   if (!dispMode)
1205   {
1206     dispMode = CGDisplayBestModeForParameters(display_id, 32, width, height, &match);
1207
1208     if (!match)
1209       dispMode = CGDisplayBestModeForParameters(display_id, 16, width, height, &match);
1210
1211     if (!match)
1212       return false;
1213   }
1214
1215   // switch mode and return success
1216   CGDisplayCapture(display_id);
1217   CGDisplayConfigRef cfg;
1218   CGBeginDisplayConfiguration(&cfg);
1219   // we don't need to do this, we are already faded.
1220   //CGConfigureDisplayFadeEffect(cfg, 0.3f, 0.5f, 0, 0, 0);
1221   CGConfigureDisplayMode(cfg, display_id, dispMode);
1222   CGError err = CGCompleteDisplayConfiguration(cfg, kCGConfigureForAppOnly);
1223   CGDisplayRelease(display_id);
1224
1225   Cocoa_CVDisplayLinkUpdate();
1226
1227   return (err == kCGErrorSuccess);
1228 }
1229
1230 void CWinSystemOSX::FillInVideoModes()
1231 {
1232   // Add full screen settings for additional monitors
1233   int numDisplays = [[NSScreen screens] count];
1234
1235   for (int disp = 0; disp < numDisplays; disp++)
1236   {
1237     Boolean stretched;
1238     Boolean interlaced;
1239     Boolean safeForHardware;
1240     Boolean televisionoutput;
1241     int w, h, bitsperpixel;
1242     double refreshrate;
1243     RESOLUTION_INFO res;
1244
1245     CFArrayRef displayModes = CGDisplayAvailableModes(GetDisplayID(disp));
1246     NSString *dispName = screenNameForDisplay(GetDisplayID(disp));
1247     CLog::Log(LOGNOTICE, "Display %i has name %s", disp, [dispName UTF8String]);
1248
1249     if (NULL == displayModes)
1250       continue;
1251
1252     for (int i=0; i < CFArrayGetCount(displayModes); ++i)
1253     {
1254       CFDictionaryRef displayMode = (CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i);
1255
1256       stretched = GetDictionaryBoolean(displayMode, kCGDisplayModeIsStretched);
1257       interlaced = GetDictionaryBoolean(displayMode, kCGDisplayModeIsInterlaced);
1258       bitsperpixel = GetDictionaryInt(displayMode, kCGDisplayBitsPerPixel);
1259       safeForHardware = GetDictionaryBoolean(displayMode, kCGDisplayModeIsSafeForHardware);
1260       televisionoutput = GetDictionaryBoolean(displayMode, kCGDisplayModeIsTelevisionOutput);
1261
1262       if ((bitsperpixel == 32)      &&
1263           (safeForHardware == YES)  &&
1264           (stretched == NO)         &&
1265           (interlaced == NO))
1266       {
1267         w = GetDictionaryInt(displayMode, kCGDisplayWidth);
1268         h = GetDictionaryInt(displayMode, kCGDisplayHeight);
1269         refreshrate = GetDictionaryDouble(displayMode, kCGDisplayRefreshRate);
1270         if ((int)refreshrate == 0)  // LCD display?
1271         {
1272           // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
1273           refreshrate = 60.0;
1274         }
1275         CLog::Log(LOGNOTICE, "Found possible resolution for display %d with %d x %d @ %f Hz\n", disp, w, h, refreshrate);
1276
1277         UpdateDesktopResolution(res, disp, w, h, refreshrate);
1278
1279         // overwrite the mode str because  UpdateDesktopResolution adds a
1280         // "Full Screen". Since the current resolution is there twice
1281         // this would lead to 2 identical resolution entrys in the guisettings.xml.
1282         // That would cause problems with saving screen overscan calibration
1283         // because the wrong entry is picked on load.
1284         // So we just use UpdateDesktopResolutions for the current DESKTOP_RESOLUTIONS
1285         // in UpdateResolutions. And on all othere resolutions make a unique
1286         // mode str by doing it without appending "Full Screen".
1287         // this is what linux does - though it feels that there shouldn't be
1288         // the same resolution twice... - thats why i add a FIXME here.
1289         res.strMode = StringUtils::Format("%dx%d @ %.2f", w, h, refreshrate);
1290         g_graphicsContext.ResetOverscan(res);
1291         CDisplaySettings::Get().AddResolutionInfo(res);
1292       }
1293     }
1294   }
1295 }
1296
1297 bool CWinSystemOSX::FlushBuffer(void)
1298 {
1299   [ (NSOpenGLContext*)m_glContext flushBuffer ];
1300
1301   return true;
1302 }
1303
1304 bool CWinSystemOSX::IsObscured(void)
1305 {
1306   // check once a second if we are obscured.
1307   unsigned int now_time = XbmcThreads::SystemClockMillis();
1308   if (m_obscured_timecheck > now_time)
1309     return m_obscured;
1310   else
1311     m_obscured_timecheck = now_time + 1000;
1312
1313   NSOpenGLContext* cur_context = [NSOpenGLContext currentContext];
1314   NSView* view = [cur_context view];
1315   if (!view)
1316   {
1317     // sanity check, we should always have a view
1318     m_obscured = true;
1319     return m_obscured;
1320   }
1321
1322   NSWindow *window = [view window];
1323   if (!window)
1324   {
1325     // sanity check, we should always have a window
1326     m_obscured = true;
1327     return m_obscured;
1328   }
1329
1330   if ([window isVisible] == NO)
1331   {
1332     // not visable means the window is not showing.
1333     // this should never really happen as we are always visable
1334     // even when minimized in dock.
1335     m_obscured = true;
1336     return m_obscured;
1337   }
1338
1339   // check if we are minimized (to an icon in the Dock).
1340   if ([window isMiniaturized] == YES)
1341   {
1342     m_obscured = true;
1343     return m_obscured;
1344   }
1345
1346   // check if we are showing on the active workspace.
1347   if ([window isOnActiveSpace] == NO)
1348   {
1349     m_obscured = true;
1350     return m_obscured;
1351   }
1352
1353   // default to false before we start parsing though the windows.
1354   // if we are are obscured by any windows, then set true.
1355   m_obscured = false;
1356   static bool obscureLogged = false;
1357
1358   CGWindowListOption opts;
1359   opts = kCGWindowListOptionOnScreenAboveWindow | kCGWindowListExcludeDesktopElements;
1360   CFArrayRef windowIDs =CGWindowListCreate(opts, (CGWindowID)[window windowNumber]);  
1361
1362   if (!windowIDs)
1363     return m_obscured;
1364
1365   CFArrayRef windowDescs = CGWindowListCreateDescriptionFromArray(windowIDs);
1366   if (!windowDescs)
1367   {
1368     CFRelease(windowIDs);
1369     return m_obscured;
1370   }
1371
1372   CGRect bounds = NSRectToCGRect([window frame]);
1373   // kCGWindowBounds measures the origin as the top-left corner of the rectangle
1374   //  relative to the top-left corner of the screen.
1375   // NSWindow’s frame property measures the origin as the bottom-left corner
1376   //  of the rectangle relative to the bottom-left corner of the screen.
1377   // convert bounds from NSWindow to CGWindowBounds here.
1378   bounds.origin.y = [[window screen] frame].size.height - bounds.origin.y - bounds.size.height;
1379
1380   std::vector<CRect> partialOverlaps;
1381   CRect ourBounds = CGRectToCRect(bounds);
1382
1383   for (CFIndex idx=0; idx < CFArrayGetCount(windowDescs); idx++)
1384   {
1385     // walk the window list of windows that are above us and are not desktop elements
1386     CFDictionaryRef windowDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(windowDescs, idx);
1387
1388     // skip the Dock window, it actually covers the entire screen.
1389     CFStringRef ownerName = (CFStringRef)CFDictionaryGetValue(windowDictionary, kCGWindowOwnerName);
1390     if (CFStringCompare(ownerName, CFSTR("Dock"), 0) == kCFCompareEqualTo)
1391       continue;
1392
1393     // Ignore known brightness tools for dimming the screen. They claim to cover
1394     // the whole XBMC window and therefore would make the framerate limiter
1395     // kicking in. Unfortunatly even the alpha of these windows is 1.0 so
1396     // we have to check the ownerName.
1397     if (CFStringCompare(ownerName, CFSTR("Shades"), 0)            == kCFCompareEqualTo ||
1398         CFStringCompare(ownerName, CFSTR("SmartSaver"), 0)        == kCFCompareEqualTo ||
1399         CFStringCompare(ownerName, CFSTR("Brightness Slider"), 0) == kCFCompareEqualTo ||
1400         CFStringCompare(ownerName, CFSTR("Displaperture"), 0)     == kCFCompareEqualTo ||
1401         CFStringCompare(ownerName, CFSTR("Dreamweaver"), 0)       == kCFCompareEqualTo)
1402       continue;
1403
1404     CFDictionaryRef rectDictionary = (CFDictionaryRef)CFDictionaryGetValue(windowDictionary, kCGWindowBounds);
1405     if (!rectDictionary)
1406       continue;
1407
1408     CGRect windowBounds;
1409     if (CGRectMakeWithDictionaryRepresentation(rectDictionary, &windowBounds))
1410     {
1411       if (CGRectContainsRect(windowBounds, bounds))
1412       {
1413         // if the windowBounds completely encloses our bounds, we are obscured.
1414         if (!obscureLogged)
1415         {
1416           std::string appName;
1417           if (DarwinCFStringRefToUTF8String(ownerName, appName))
1418             CLog::Log(LOGDEBUG, "WinSystemOSX: Fullscreen window %s obscures XBMC!", appName.c_str());
1419           obscureLogged = true;
1420         }
1421         m_obscured = true;
1422         break;
1423       }
1424
1425       // handle overlaping windows above us that combine
1426       // to obscure by collecting any partial overlaps,
1427       // then subtract them from our bounds and check
1428       // for any remaining area.
1429       CRect intersection = CGRectToCRect(windowBounds);
1430       intersection.Intersect(ourBounds);
1431       if (!intersection.IsEmpty())
1432         partialOverlaps.push_back(intersection);
1433     }
1434   }
1435
1436   if (!m_obscured)
1437   {
1438     // if we are here we are not obscured by any fullscreen window - reset flag
1439     // for allowing the logmessage above to show again if this changes.
1440     if (obscureLogged)
1441       obscureLogged = false;
1442     std::vector<CRect> rects = ourBounds.SubtractRects(partialOverlaps);
1443     // they got us covered
1444     if (rects.size() == 0)
1445       m_obscured = true;
1446   }
1447
1448   CFRelease(windowDescs);
1449   CFRelease(windowIDs);
1450
1451   return m_obscured;
1452 }
1453
1454 void CWinSystemOSX::NotifyAppFocusChange(bool bGaining)
1455 {
1456   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1457
1458   if (m_bFullScreen && bGaining)
1459   {
1460     // find the window
1461     NSOpenGLContext* context = [NSOpenGLContext currentContext];
1462     if (context)
1463     {
1464       NSView* view;
1465
1466       view = [context view];
1467       if (view)
1468       {
1469         NSWindow* window;
1470         window = [view window];
1471         if (window)
1472         {
1473           // find the screenID
1474           NSDictionary* screenInfo = [[window screen] deviceDescription];
1475           NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
1476           if ((CGDirectDisplayID)[screenID longValue] == kCGDirectMainDisplay || DarwinIsMavericks() )
1477           {
1478             SetMenuBarVisible(false);
1479           }
1480           [window orderFront:nil];
1481         }
1482       }
1483     }
1484   }
1485   [pool release];
1486 }
1487
1488 void CWinSystemOSX::ShowOSMouse(bool show)
1489 {
1490   SDL_ShowCursor(show ? 1 : 0);
1491 }
1492
1493 bool CWinSystemOSX::Minimize()
1494 {
1495   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1496
1497   [[NSApplication sharedApplication] miniaturizeAll:nil];
1498
1499   [pool release];
1500   return true;
1501 }
1502
1503 bool CWinSystemOSX::Restore()
1504 {
1505   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1506
1507   [[NSApplication sharedApplication] unhide:nil];
1508
1509   [pool release];
1510   return true;
1511 }
1512
1513 bool CWinSystemOSX::Hide()
1514 {
1515   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1516
1517   [[NSApplication sharedApplication] hide:nil];
1518
1519   [pool release];
1520   return true;
1521 }
1522
1523 void CWinSystemOSX::OnMove(int x, int y)
1524 {
1525   Cocoa_CVDisplayLinkUpdate();
1526 }
1527
1528 void CWinSystemOSX::EnableSystemScreenSaver(bool bEnable)
1529 {
1530   // see Technical Q&A QA1340
1531   static IOPMAssertionID assertionID = 0;
1532
1533   if (!bEnable)
1534   {
1535     if (assertionID == 0)
1536     {
1537       CFStringRef reasonForActivity= CFSTR("XBMC requested disable system screen saver");
1538       IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
1539         kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
1540     }
1541     UpdateSystemActivity(UsrActivity);
1542   }
1543   else if (assertionID != 0)
1544   {
1545     IOPMAssertionRelease(assertionID);
1546     assertionID = 0;
1547   }
1548
1549   m_use_system_screensaver = bEnable;
1550 }
1551
1552 bool CWinSystemOSX::IsSystemScreenSaverEnabled()
1553 {
1554   return m_use_system_screensaver;
1555 }
1556
1557 void CWinSystemOSX::ResetOSScreensaver()
1558 {
1559   // allow os screensaver only if we are fullscreen
1560   EnableSystemScreenSaver(!m_bFullScreen);
1561 }
1562
1563 bool CWinSystemOSX::EnableFrameLimiter()
1564 {
1565   return IsObscured();
1566 }
1567
1568 void CWinSystemOSX::EnableTextInput(bool bEnable)
1569 {
1570   if (bEnable)
1571     StartTextInput();
1572   else
1573     StopTextInput();
1574 }
1575
1576 OSXTextInputResponder *g_textInputResponder = nil;
1577
1578 bool CWinSystemOSX::IsTextInputEnabled()
1579 {
1580   return g_textInputResponder != nil && [[g_textInputResponder superview] isEqual: [[NSApp keyWindow] contentView]];
1581 }
1582
1583 void CWinSystemOSX::StartTextInput()
1584 {
1585   NSView *parentView = [[NSApp keyWindow] contentView];
1586
1587   /* We only keep one field editor per process, since only the front most
1588    * window can receive text input events, so it make no sense to keep more
1589    * than one copy. When we switched to another window and requesting for
1590    * text input, simply remove the field editor from its superview then add
1591    * it to the front most window's content view */
1592   if (!g_textInputResponder) {
1593     g_textInputResponder =
1594     [[OSXTextInputResponder alloc] initWithFrame: NSMakeRect(0.0, 0.0, 0.0, 0.0)];
1595   }
1596
1597   if (![[g_textInputResponder superview] isEqual: parentView])
1598   {
1599 //    DLOG(@"add fieldEdit to window contentView");
1600     [g_textInputResponder removeFromSuperview];
1601     [parentView addSubview: g_textInputResponder];
1602     [[NSApp keyWindow] makeFirstResponder: g_textInputResponder];
1603   }
1604 }
1605 void CWinSystemOSX::StopTextInput()
1606 {
1607   if (g_textInputResponder) {
1608     [g_textInputResponder removeFromSuperview];
1609     [g_textInputResponder release];
1610     g_textInputResponder = nil;
1611   }
1612 }
1613
1614 void CWinSystemOSX::Register(IDispResource *resource)
1615 {
1616   CSingleLock lock(m_resourceSection);
1617   m_resources.push_back(resource);
1618 }
1619
1620 void CWinSystemOSX::Unregister(IDispResource* resource)
1621 {
1622   CSingleLock lock(m_resourceSection);
1623   std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
1624   if (i != m_resources.end())
1625     m_resources.erase(i);
1626 }
1627
1628 bool CWinSystemOSX::Show(bool raise)
1629 {
1630   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1631
1632   if (raise)
1633   {
1634     [[NSApplication sharedApplication] unhide:nil];
1635     [[NSApplication sharedApplication] activateIgnoringOtherApps: YES];
1636     [[NSApplication sharedApplication] arrangeInFront:nil];
1637   }
1638   else
1639   {
1640     [[NSApplication sharedApplication] unhideWithoutActivation];
1641   }
1642
1643   [pool release];
1644   return true;
1645 }
1646
1647 int CWinSystemOSX::GetNumScreens()
1648 {
1649   int numDisplays = [[NSScreen screens] count];
1650   return(numDisplays);
1651 }
1652
1653 int CWinSystemOSX::GetCurrentScreen()
1654 {
1655   NSOpenGLContext* context = [NSOpenGLContext currentContext];
1656   
1657   // if user hasn't moved us in windowed mode - return the
1658   // last display we were fullscreened at
1659   if (!m_movedToOtherScreen)
1660     return m_lastDisplayNr;
1661   
1662   // if we are here the user dragged the window to a different
1663   // screen and we return the screen of the window
1664   if (context)
1665   {
1666     NSView* view;
1667
1668     view = [context view];
1669     if (view)
1670     {
1671       NSWindow* window;
1672       window = [view window];
1673       if (window)
1674       {
1675         m_movedToOtherScreen = false;
1676         return GetDisplayIndex(GetDisplayIDFromScreen( [window screen] ));
1677       }
1678         
1679     }
1680   }
1681   return 0;
1682 }
1683
1684 void CWinSystemOSX::WindowChangedScreen()
1685 {
1686   // user has moved the window to a
1687   // different screen
1688   m_movedToOtherScreen = true;
1689 }
1690
1691 void CWinSystemOSX::CheckDisplayChanging(u_int32_t flags)
1692 {
1693   if (flags)
1694   {
1695     CSingleLock lock(m_resourceSection);
1696     // tell any shared resources
1697     if (flags & kCGDisplayBeginConfigurationFlag)
1698     {
1699       CLog::Log(LOGDEBUG, "CWinSystemOSX::CheckDisplayChanging:OnLostDevice");
1700       for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
1701         (*i)->OnLostDevice();
1702     }
1703     if (flags & kCGDisplaySetModeFlag)
1704     {
1705       CLog::Log(LOGDEBUG, "CWinSystemOSX::CheckDisplayChanging:OnResetDevice");
1706       for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
1707         (*i)->OnResetDevice();
1708     }
1709   }
1710 }
1711
1712 void* CWinSystemOSX::GetCGLContextObj()
1713 {
1714   return [(NSOpenGLContext*)m_glContext CGLContextObj];
1715 }
1716
1717 std::string CWinSystemOSX::GetClipboardText(void)
1718 {
1719   std::string utf8_text;
1720
1721   const char *szStr = Cocoa_Paste();
1722   if (szStr)
1723     utf8_text = szStr;
1724
1725   return utf8_text;
1726 }
1727
1728 #endif