[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / windowing / osx / WinSystemOSX.mm
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.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/Settings.h"
32 #include "settings/GUISettings.h"
33 #include "input/KeyboardStat.h"
34 #include "threads/SingleLock.h"
35 #include "utils/log.h"
36 #include "osx/XBMCHelper.h"
37 #include "utils/SystemInfo.h"
38 #include "osx/CocoaInterface.h"
39 #include "osx/DarwinUtils.h"
40 #undef BOOL
41
42 #import <SDL/SDL_video.h>
43 #import <SDL/SDL_events.h>
44
45 #import <Cocoa/Cocoa.h>
46 #import <QuartzCore/QuartzCore.h>
47 #import <IOKit/pwr_mgt/IOPMLib.h>
48 #import <IOKit/graphics/IOGraphicsLib.h>
49
50 // turn off deprecated warning spew.
51 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
52
53 //------------------------------------------------------------------------------------------
54 // special object-c class for handling the inhibit display NSTimer callback.
55 @interface windowInhibitScreenSaverClass : NSObject
56 - (void) updateSystemActivity: (NSTimer*)timer;
57 @end
58
59 @implementation windowInhibitScreenSaverClass
60 -(void) updateSystemActivity: (NSTimer*)timer
61 {
62   UpdateSystemActivity(UsrActivity);
63 }
64 @end
65
66 //------------------------------------------------------------------------------------------
67 // special object-c class for handling the NSWindowDidMoveNotification callback.
68 @interface windowDidMoveNoteClass : NSObject
69 {
70   void *m_userdata;
71 }
72 + initWith: (void*) userdata;
73 -  (void) windowDidMoveNotification:(NSNotification*) note;
74 @end
75
76 @implementation windowDidMoveNoteClass
77 + initWith: (void*) userdata;
78 {
79     windowDidMoveNoteClass *windowDidMove = [windowDidMoveNoteClass new];
80     windowDidMove->m_userdata = userdata;
81     return [windowDidMove autorelease];
82 }
83 -  (void) windowDidMoveNotification:(NSNotification*) note;
84 {
85   CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
86         if (!winsys)
87     return;
88
89   NSOpenGLContext* context = [NSOpenGLContext currentContext];
90   if (context)
91   {
92     if ([context view])
93     {
94       NSPoint window_origin = [[[context view] window] frame].origin;
95       XBMC_Event newEvent;
96       memset(&newEvent, 0, sizeof(newEvent));
97       newEvent.type = XBMC_VIDEOMOVE;
98       newEvent.move.x = window_origin.x;
99       newEvent.move.y = window_origin.y;
100       g_application.OnEvent(newEvent);
101     }
102   }
103 }
104 @end
105 //------------------------------------------------------------------------------------------
106 // special object-c class for handling the NSWindowDidReSizeNotification callback.
107 @interface windowDidReSizeNoteClass : NSObject
108 {
109   void *m_userdata;
110 }
111 + initWith: (void*) userdata;
112 - (void) windowDidReSizeNotification:(NSNotification*) note;
113 @end
114 @implementation windowDidReSizeNoteClass
115 + initWith: (void*) userdata;
116 {
117     windowDidReSizeNoteClass *windowDidReSize = [windowDidReSizeNoteClass new];
118     windowDidReSize->m_userdata = userdata;
119     return [windowDidReSize autorelease];
120 }
121 - (void) windowDidReSizeNotification:(NSNotification*) note;
122 {
123   CWinSystemOSX *winsys = (CWinSystemOSX*)m_userdata;
124         if (!winsys)
125     return;
126   /* placeholder, do not uncomment or you will SDL recurse into death
127   NSOpenGLContext* context = [NSOpenGLContext currentContext];
128   if (context)
129   {
130     if ([context view])
131     {
132       NSSize view_size = [[context view] frame].size;
133       XBMC_Event newEvent;
134       memset(&newEvent, 0, sizeof(newEvent));
135       newEvent.type = XBMC_VIDEORESIZE;
136       newEvent.resize.w = view_size.width;
137       newEvent.resize.h = view_size.height;
138       if (newEvent.resize.w * newEvent.resize.h)
139       {
140         g_application.OnEvent(newEvent);
141         g_windowManager.MarkDirty();
142       }
143     }
144   }
145   */
146 }
147 @end
148 //------------------------------------------------------------------------------------------
149
150
151 #define MAX_DISPLAYS 32
152 static NSWindow* blankingWindows[MAX_DISPLAYS];
153
154 void* CWinSystemOSX::m_lastOwnedContext = 0;
155
156 //------------------------------------------------------------------------------------------
157 CRect CGRectToCRect(CGRect cgrect)
158 {
159   CRect crect = CRect(
160     cgrect.origin.x,
161     cgrect.origin.y,
162     cgrect.origin.x + cgrect.size.width,
163     cgrect.origin.y + cgrect.size.height);
164   return crect;
165 }
166
167 //------------------------------------------------------------------------------------------
168 Boolean GetDictionaryBoolean(CFDictionaryRef theDict, const void* key)
169 {
170         // get a boolean from the dictionary
171         Boolean value = false;
172         CFBooleanRef boolRef;
173         boolRef = (CFBooleanRef)CFDictionaryGetValue(theDict, key);
174         if (boolRef != NULL)
175                 value = CFBooleanGetValue(boolRef);
176         return value;
177 }
178 //------------------------------------------------------------------------------------------
179 long GetDictionaryLong(CFDictionaryRef theDict, const void* key)
180 {
181         // get a long from the dictionary
182         long value = 0;
183         CFNumberRef numRef;
184         numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
185         if (numRef != NULL)
186                 CFNumberGetValue(numRef, kCFNumberLongType, &value);
187         return value;
188 }
189 //------------------------------------------------------------------------------------------
190 int GetDictionaryInt(CFDictionaryRef theDict, const void* key)
191 {
192         // get a long from the dictionary
193         int value = 0;
194         CFNumberRef numRef;
195         numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
196         if (numRef != NULL)
197                 CFNumberGetValue(numRef, kCFNumberIntType, &value);
198         return value;
199 }
200 //------------------------------------------------------------------------------------------
201 float GetDictionaryFloat(CFDictionaryRef theDict, const void* key)
202 {
203         // get a long from the dictionary
204         int value = 0;
205         CFNumberRef numRef;
206         numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
207         if (numRef != NULL)
208                 CFNumberGetValue(numRef, kCFNumberFloatType, &value);
209         return value;
210 }
211 //------------------------------------------------------------------------------------------
212 double GetDictionaryDouble(CFDictionaryRef theDict, const void* key)
213 {
214         // get a long from the dictionary
215         double value = 0.0;
216         CFNumberRef numRef;
217         numRef = (CFNumberRef)CFDictionaryGetValue(theDict, key);
218         if (numRef != NULL)
219                 CFNumberGetValue(numRef, kCFNumberDoubleType, &value);
220         return value;
221 }
222
223 //---------------------------------------------------------------------------------
224 void SetMenuBarVisible(bool visible)
225 {
226   if(visible)
227   {
228     [[NSApplication sharedApplication]
229       setPresentationOptions:   NSApplicationPresentationDefault];
230   }
231   else
232   {
233     [[NSApplication sharedApplication]
234       setPresentationOptions:   NSApplicationPresentationHideMenuBar |
235                                 NSApplicationPresentationHideDock];
236   }
237 }
238 //---------------------------------------------------------------------------------
239 CGDirectDisplayID GetDisplayID(int screen_index)
240 {
241   CGDirectDisplayID displayArray[MAX_DISPLAYS];
242   CGDisplayCount    numDisplays;
243
244   // Get the list of displays.
245   CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
246   return(displayArray[screen_index]);
247 }
248
249 CGDirectDisplayID GetDisplayIDFromScreen(NSScreen *screen)
250 {
251   NSDictionary* screenInfo = [screen deviceDescription];
252   NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
253
254   return (CGDirectDisplayID)[screenID longValue];
255 }
256
257 int GetDisplayIndex(CGDirectDisplayID display)
258 {
259   CGDirectDisplayID displayArray[MAX_DISPLAYS];
260   CGDisplayCount    numDisplays;
261
262   // Get the list of displays.
263   CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
264   while (numDisplays > 0)
265   {
266     if (display == displayArray[--numDisplays])
267           return numDisplays;
268   }
269   return -1;
270 }
271
272 void BlankOtherDisplays(int screen_index)
273 {
274   int i;
275   int numDisplays = [[NSScreen screens] count];
276
277   // zero out blankingWindows for debugging
278   for (i=0; i<MAX_DISPLAYS; i++)
279   {
280     blankingWindows[i] = 0;
281   }
282
283   // Blank.
284   for (i=0; i<numDisplays; i++)
285   {
286     if (i != screen_index)
287     {
288       // Get the size.
289       NSScreen* pScreen = [[NSScreen screens] objectAtIndex:i];
290       NSRect    screenRect = [pScreen frame];
291
292       // Build a blanking window.
293       screenRect.origin = NSZeroPoint;
294       blankingWindows[i] = [[NSWindow alloc] initWithContentRect:screenRect
295         styleMask:NSBorderlessWindowMask
296         backing:NSBackingStoreBuffered
297         defer:NO
298         screen:pScreen];
299
300       [blankingWindows[i] setBackgroundColor:[NSColor blackColor]];
301       [blankingWindows[i] setLevel:CGShieldingWindowLevel()];
302       [blankingWindows[i] makeKeyAndOrderFront:nil];
303     }
304   }
305 }
306
307 void UnblankDisplays(void)
308 {
309   int numDisplays = [[NSScreen screens] count];
310   int i = 0;
311
312   for (i=0; i<numDisplays; i++)
313   {
314     if (blankingWindows[i] != 0)
315     {
316       // Get rid of the blanking windows we created.
317       [blankingWindows[i] close];
318       if ([blankingWindows[i] isReleasedWhenClosed] == NO)
319         [blankingWindows[i] release];
320       blankingWindows[i] = 0;
321     }
322   }
323 }
324
325 CGDisplayFadeReservationToken DisplayFadeToBlack(bool fade)
326 {
327   // Fade to black to hide resolution-switching flicker and garbage.
328   CGDisplayFadeReservationToken fade_token = kCGDisplayFadeReservationInvalidToken;
329   if (CGAcquireDisplayFadeReservation (5, &fade_token) == kCGErrorSuccess && fade)
330     CGDisplayFade(fade_token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE);
331
332   return(fade_token);
333 }
334
335 void DisplayFadeFromBlack(CGDisplayFadeReservationToken fade_token, bool fade)
336 {
337   if (fade_token != kCGDisplayFadeReservationInvalidToken)
338   {
339     if (fade)
340       CGDisplayFade(fade_token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE);
341     CGReleaseDisplayFadeReservation(fade_token);
342   }
343 }
344
345 NSString* screenNameForDisplay(CGDirectDisplayID displayID)
346 {
347   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
348
349   NSString *screenName = nil;
350
351   NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName);
352   NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
353
354   if ([localizedNames count] > 0) {
355       screenName = [[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] retain];
356   }
357
358   [deviceInfo release];
359   [pool release];
360
361   return [screenName autorelease];
362 }
363
364 void ShowHideNSWindow(NSWindow *wind, bool show)
365 {
366   if (show)
367     [wind orderFront:nil];
368   else
369     [wind orderOut:nil];
370 }
371
372 static NSWindow *curtainWindow;
373 void fadeInDisplay(NSScreen *theScreen, double fadeTime)
374 {
375   int     fadeSteps     = 100;
376   double  fadeInterval  = (fadeTime / (double) fadeSteps);
377
378   if (curtainWindow != nil)
379   {
380     for (int step = 0; step < fadeSteps; step++)
381     {
382       double fade = 1.0 - (step * fadeInterval);
383       [curtainWindow setAlphaValue:fade];
384
385       NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
386       [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
387     }
388   }
389   [curtainWindow close];
390   curtainWindow = nil;
391
392   [NSCursor unhide];
393 }
394
395 void fadeOutDisplay(NSScreen *theScreen, double fadeTime)
396 {
397   int     fadeSteps     = 100;
398   double  fadeInterval  = (fadeTime / (double) fadeSteps);
399
400   [NSCursor hide];
401
402   curtainWindow = [[NSWindow alloc]
403     initWithContentRect:[theScreen frame]
404     styleMask:NSBorderlessWindowMask
405     backing:NSBackingStoreBuffered
406     defer:YES
407     screen:theScreen];
408
409   [curtainWindow setAlphaValue:0.0];
410   [curtainWindow setBackgroundColor:[NSColor blackColor]];
411   [curtainWindow setLevel:NSScreenSaverWindowLevel];
412
413   [curtainWindow makeKeyAndOrderFront:nil];
414   [curtainWindow setFrame:[curtainWindow
415     frameRectForContentRect:[theScreen frame]]
416     display:YES
417     animate:NO];
418
419   for (int step = 0; step < fadeSteps; step++)
420   {
421     double fade = step * fadeInterval;
422     [curtainWindow setAlphaValue:fade];
423
424     NSDate *nextDate = [NSDate dateWithTimeIntervalSinceNow:fadeInterval];
425     [[NSRunLoop currentRunLoop] runUntilDate:nextDate];
426   }
427 }
428
429 // try to find mode that matches the desired size, refreshrate
430 // non interlaced, nonstretched, safe for hardware
431 CFDictionaryRef GetMode(int width, int height, double refreshrate, int screenIdx)
432 {
433   if ( screenIdx >= (signed)[[NSScreen screens] count])
434     return NULL;
435
436   Boolean stretched;
437   Boolean interlaced;
438   Boolean safeForHardware;
439   Boolean televisionoutput;
440   int w, h, bitsperpixel;
441   double rate;
442   RESOLUTION_INFO res;
443
444   CLog::Log(LOGDEBUG, "GetMode looking for suitable mode with %d x %d @ %f Hz on display %d\n", width, height, refreshrate, screenIdx);
445
446   CFArrayRef displayModes = CGDisplayAvailableModes(GetDisplayID(screenIdx));
447
448   if (NULL == displayModes)
449   {
450     CLog::Log(LOGERROR, "GetMode - no displaymodes found!");
451     return NULL;
452   }
453
454   for (int i=0; i < CFArrayGetCount(displayModes); ++i)
455   {
456     CFDictionaryRef displayMode = (CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i);
457
458     stretched = GetDictionaryBoolean(displayMode, kCGDisplayModeIsStretched);
459     interlaced = GetDictionaryBoolean(displayMode, kCGDisplayModeIsInterlaced);
460     bitsperpixel = GetDictionaryInt(displayMode, kCGDisplayBitsPerPixel);
461     safeForHardware = GetDictionaryBoolean(displayMode, kCGDisplayModeIsSafeForHardware);
462     televisionoutput = GetDictionaryBoolean(displayMode, kCGDisplayModeIsTelevisionOutput);
463     w = GetDictionaryInt(displayMode, kCGDisplayWidth);
464     h = GetDictionaryInt(displayMode, kCGDisplayHeight);
465     rate = GetDictionaryDouble(displayMode, kCGDisplayRefreshRate);
466
467
468     if ((bitsperpixel == 32)      &&
469         (safeForHardware == YES)  &&
470         (stretched == NO)         &&
471         (interlaced == NO)        &&
472         (w == width)              &&
473         (h == height)             &&
474         (rate == refreshrate || rate == 0))
475     {
476       CLog::Log(LOGDEBUG, "GetMode found a match!");
477       return displayMode;
478     }
479   }
480   CLog::Log(LOGERROR, "GetMode - no match found!");
481   return NULL;
482 }
483
484 //---------------------------------------------------------------------------------
485 static void DisplayReconfigured(CGDirectDisplayID display,
486   CGDisplayChangeSummaryFlags flags, void* userData)
487 {
488   CWinSystemOSX *winsys = (CWinSystemOSX*)userData;
489         if (!winsys)
490     return;
491
492   if (flags & kCGDisplaySetModeFlag || flags & kCGDisplayBeginConfigurationFlag)
493   {
494     // pre/post-reconfiguration changes
495     RESOLUTION res = g_graphicsContext.GetVideoResolution();
496     if (res == RES_INVALID)
497       return;
498
499     NSScreen* pScreen = nil;
500     unsigned int screenIdx = g_settings.m_ResInfo[res].iScreen;
501
502     if ( screenIdx < [[NSScreen screens] count] )
503     {
504         pScreen = [[NSScreen screens] objectAtIndex:screenIdx];
505     }
506
507     if (pScreen)
508     {
509       CGDirectDisplayID xbmc_display = GetDisplayIDFromScreen(pScreen);
510       if (xbmc_display == display)
511       {
512         // we only respond to changes on the display we are running on.
513         CLog::Log(LOGDEBUG, "CWinSystemOSX::DisplayReconfigured");
514         winsys->CheckDisplayChanging(flags);
515       }
516     }
517   }
518 }
519
520 //---------------------------------------------------------------------------------
521 //---------------------------------------------------------------------------------
522 CWinSystemOSX::CWinSystemOSX() : CWinSystemBase()
523 {
524   m_eWindowSystem = WINDOW_SYSTEM_OSX;
525   m_glContext = 0;
526   m_SDLSurface = NULL;
527   m_osx_events = NULL;
528   m_obscured   = false;
529   m_obscured_timecheck = XbmcThreads::SystemClockMillis() + 1000;
530   m_use_system_screensaver = true;
531   // check runtime, we only allow this on 10.5+
532   m_can_display_switch = (floor(NSAppKitVersionNumber) >= 949);
533 }
534
535 CWinSystemOSX::~CWinSystemOSX()
536 {
537 };
538
539 bool CWinSystemOSX::InitWindowSystem()
540 {
541   SDL_EnableUNICODE(1);
542
543   // set repeat to 10ms to ensure repeat time < frame time
544   // so that hold times can be reliably detected
545   SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, 10);
546
547   if (!CWinSystemBase::InitWindowSystem())
548     return false;
549
550   m_osx_events = new CWinEventsOSX();
551
552   if (m_can_display_switch)
553     CGDisplayRegisterReconfigurationCallback(DisplayReconfigured, (void*)this);
554
555   NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
556   windowDidMoveNoteClass *windowDidMove;
557   windowDidMove = [windowDidMoveNoteClass initWith: this];
558   [center addObserver:windowDidMove
559     selector:@selector(windowDidMoveNotification:)
560     name:NSWindowDidMoveNotification object:nil];
561   m_windowDidMove = windowDidMove;
562
563
564   windowDidReSizeNoteClass *windowDidReSize;
565   windowDidReSize = [windowDidReSizeNoteClass initWith: this];
566   [center addObserver:windowDidReSize
567     selector:@selector(windowDidReSizeNotification:)
568     name:NSWindowDidResizeNotification object:nil];
569   m_windowDidReSize = windowDidReSize;
570
571   return true;
572 }
573
574 bool CWinSystemOSX::DestroyWindowSystem()
575 {
576   NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
577   [center removeObserver:(windowDidMoveNoteClass*)m_windowDidMove name:NSWindowDidMoveNotification object:nil];
578   [center removeObserver:(windowDidReSizeNoteClass*)m_windowDidReSize name:NSWindowDidResizeNotification object:nil];
579
580   if (m_can_display_switch)
581     CGDisplayRemoveReconfigurationCallback(DisplayReconfigured, (void*)this);
582
583   delete m_osx_events;
584   m_osx_events = NULL;
585
586   UnblankDisplays();
587   if (m_glContext)
588   {
589     NSOpenGLContext* oldContext = (NSOpenGLContext*)m_glContext;
590     [oldContext release];
591     m_glContext = NULL;
592   }
593   return true;
594 }
595
596 bool CWinSystemOSX::CreateNewWindow(const CStdString& name, bool fullScreen, RESOLUTION_INFO& res, PHANDLE_EVENT_FUNC userFunction)
597 {
598   m_nWidth  = res.iWidth;
599   m_nHeight = res.iHeight;
600   m_bFullScreen = fullScreen;
601
602   SDL_GL_SetAttribute(SDL_GL_RED_SIZE,   8);
603   SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
604   SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,  8);
605   SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
606   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
607
608   // Enable vertical sync to avoid any tearing.
609   SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
610
611   m_SDLSurface = SDL_SetVideoMode(m_nWidth, m_nHeight, 0, SDL_OPENGL | SDL_RESIZABLE);
612   if (!m_SDLSurface)
613     return false;
614
615   // the context SDL creates isn't full screen compatible, so we create new one
616   // first, find the current contect and make sure a view is attached
617   NSOpenGLContext* cur_context = [NSOpenGLContext currentContext];
618   NSView* view = [cur_context view];
619   if (!view)
620     return false;
621
622   // if we are not starting up windowed, then hide the initial SDL window
623   // so we do not see it flash before the fade-out and switch to fullscreen.
624   if (g_guiSettings.m_LookAndFeelResolution != RES_WINDOW)
625     ShowHideNSWindow([view window], false);
626
627   // disassociate view from context
628   [cur_context clearDrawable];
629
630   // release the context
631   if (m_lastOwnedContext == cur_context)
632   {
633     [ NSOpenGLContext clearCurrentContext ];
634     [ cur_context clearDrawable ];
635     [ cur_context release ];
636   }
637
638   // create a new context
639   NSOpenGLContext* new_context = (NSOpenGLContext*)CreateWindowedContext(nil);
640   if (!new_context)
641     return false;
642
643   // associate with current view
644   [new_context setView:view];
645   [new_context makeCurrentContext];
646
647   // set the window title
648   NSString *string;
649   string = [ [ NSString alloc ] initWithUTF8String:"XBMC Media Center" ];
650   [ [ [new_context view] window] setTitle:string ];
651   [ string release ];
652
653   m_glContext = new_context;
654   m_lastOwnedContext = new_context;
655   m_bWindowCreated = true;
656
657   return true;
658 }
659
660 bool CWinSystemOSX::DestroyWindow()
661 {
662   return true;
663 }
664
665 extern "C" void SDL_SetWidthHeight(int w, int h);
666 bool CWinSystemOSX::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop)
667 {
668   if (!m_glContext)
669     return false;
670
671   NSOpenGLContext* context = [NSOpenGLContext currentContext];
672   NSView* view;
673   NSWindow* window;
674
675   view = [context view];
676   if (view && (newWidth > 0) && (newHeight > 0))
677   {
678     window = [view window];
679     if (window)
680     {
681       [window setContentSize:NSMakeSize(newWidth, newHeight)];
682       [window update];
683       [view setFrameSize:NSMakeSize(newWidth, newHeight)];
684       [context update];
685     }
686   }
687
688   // HACK: resize SDL's view manually so that mouse bounds are correctly updated.
689   // there are two parts to this, the internal SDL (current_video->screen) and
690   // the cocoa view ( handled in SetFullScreen).
691   SDL_SetWidthHeight(newWidth, newHeight);
692
693   [context makeCurrentContext];
694
695   m_nWidth = newWidth;
696   m_nHeight = newHeight;
697   m_glContext = context;
698
699   return true;
700 }
701
702 static bool needtoshowme = true;
703
704 bool CWinSystemOSX::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays)
705 {
706   static NSWindow* windowedFullScreenwindow = NULL;
707   static NSScreen* last_window_screen = NULL;
708   static NSPoint last_window_origin;
709   static NSView* last_view = NULL;
710   static NSSize last_view_size;
711   static NSPoint last_view_origin;
712   bool was_fullscreen = m_bFullScreen;
713   static int lastDisplayNr = res.iScreen;
714   NSOpenGLContext* cur_context;
715
716   // Fade to black to hide resolution-switching flicker and garbage.
717   CGDisplayFadeReservationToken fade_token = DisplayFadeToBlack(needtoshowme);
718
719   // If we're already fullscreen then we must be moving to a different display.
720   // or if we are still on the same display - it might be only a refreshrate/resolution
721   // change request.
722   // Recurse to reset fullscreen mode and then continue.
723   if (was_fullscreen && fullScreen && lastDisplayNr != res.iScreen)
724   {
725     needtoshowme = false;
726     ShowHideNSWindow([last_view window], needtoshowme);
727     RESOLUTION_INFO& window = g_settings.m_ResInfo[RES_WINDOW];
728     CWinSystemOSX::SetFullScreen(false, window, blankOtherDisplays);
729     needtoshowme = true;
730   }
731
732   m_nWidth      = res.iWidth;
733   m_nHeight     = res.iHeight;
734   m_bFullScreen = fullScreen;
735
736   cur_context = [NSOpenGLContext currentContext];
737   
738   //handle resolution/refreshrate switching early here
739   if (m_bFullScreen)
740   {
741     if (m_can_display_switch)
742     {
743       // send pre-configuration change now and do not
744       //  wait for switch videomode callback. This gives just
745       //  a little more advanced notice of the display pre-change.
746       if (g_guiSettings.GetInt("videoplayer.adjustrefreshrate") != ADJUST_REFRESHRATE_OFF)
747         CheckDisplayChanging(kCGDisplayBeginConfigurationFlag);
748
749       // switch videomode
750       SwitchToVideoMode(res.iWidth, res.iHeight, res.fRefreshRate, res.iScreen);
751       lastDisplayNr = res.iScreen;
752     }
753   }
754
755   //no context? done.
756   if (!cur_context)
757   {
758     DisplayFadeFromBlack(fade_token, needtoshowme);
759     return false;
760   }
761
762   if (windowedFullScreenwindow != NULL)
763   {
764     [windowedFullScreenwindow close];
765     if ([windowedFullScreenwindow isReleasedWhenClosed] == NO)
766       [windowedFullScreenwindow release];
767     windowedFullScreenwindow = NULL;
768   }
769
770   if (m_bFullScreen)
771   {
772     // FullScreen Mode
773     NSOpenGLContext* newContext = NULL;
774
775     // Save info about the windowed context so we can restore it when returning to windowed.
776     last_view = [cur_context view];
777     last_view_size = [last_view frame].size;
778     last_view_origin = [last_view frame].origin;
779     last_window_screen = [[last_view window] screen];
780     last_window_origin = [[last_view window] frame].origin;
781
782     if (g_guiSettings.GetBool("videoscreen.fakefullscreen"))
783     {
784       // This is Cocca Windowed FullScreen Mode
785       // Get the screen rect of our current display
786       NSScreen* pScreen = [[NSScreen screens] objectAtIndex:res.iScreen];
787       NSRect    screenRect = [pScreen frame];
788
789       // remove frame origin offset of orginal display
790       screenRect.origin = NSZeroPoint;
791
792       // make a new window to act as the windowedFullScreen
793       windowedFullScreenwindow = [[NSWindow alloc] initWithContentRect:screenRect
794         styleMask:NSBorderlessWindowMask
795         backing:NSBackingStoreBuffered
796         defer:NO
797         screen:pScreen];
798
799       [windowedFullScreenwindow setBackgroundColor:[NSColor blackColor]];
800       [windowedFullScreenwindow makeKeyAndOrderFront:nil];
801
802       // make our window the same level as the rest to enable cmd+tab switching
803       [windowedFullScreenwindow setLevel:NSNormalWindowLevel];
804       // this will make our window topmost and hide all system messages
805       //[windowedFullScreenwindow setLevel:CGShieldingWindowLevel()];
806
807       // ...and the original one beneath it and on the same screen.
808       [[last_view window] setLevel:NSNormalWindowLevel-1];
809       [[last_view window] setFrameOrigin:[pScreen frame].origin];
810       // expand the mouse bounds in SDL view to fullscreen
811       [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)];
812       [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ];
813
814       NSView* blankView = [[NSView alloc] init];
815       [windowedFullScreenwindow setContentView:blankView];
816       [windowedFullScreenwindow setContentSize:NSMakeSize(m_nWidth, m_nHeight)];
817       [windowedFullScreenwindow update];
818       [blankView setFrameSize:NSMakeSize(m_nWidth, m_nHeight)];
819
820       // Obtain windowed pixel format and create a new context.
821       newContext = (NSOpenGLContext*)CreateWindowedContext((void* )cur_context);
822       [newContext setView:blankView];
823
824       // Hide the menu bar.
825       if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay)
826         SetMenuBarVisible(false);
827
828       // Blank other displays if requested.
829       if (blankOtherDisplays)
830         BlankOtherDisplays(res.iScreen);
831     }
832     else
833     {
834       // hide the window
835       [[last_view window] setFrameOrigin:[last_window_screen frame].origin];
836       // expand the mouse bounds in SDL view to fullscreen
837       [ last_view setFrameOrigin:NSMakePoint(0.0, 0.0)];
838       [ last_view setFrameSize:NSMakeSize(m_nWidth, m_nHeight) ];
839
840       // This is OpenGL FullScreen Mode
841       // create our new context (sharing with the current one)
842       newContext = (NSOpenGLContext*)CreateFullScreenContext(res.iScreen, (void*)cur_context);
843       if (!newContext)
844         return false;
845
846       // clear the current context
847       [NSOpenGLContext clearCurrentContext];
848
849       // set fullscreen
850       [newContext setFullScreen];
851
852       // Capture the display before going fullscreen.
853       if (blankOtherDisplays == true)
854         CGCaptureAllDisplays();
855       else
856         CGDisplayCapture(GetDisplayID(res.iScreen));
857
858       // If we don't hide menu bar, it will get events and interrupt the program.
859       if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay)
860         SetMenuBarVisible(false);
861     }
862
863     // Hide the mouse.
864     [NSCursor hide];
865
866     // Release old context if we created it.
867     if (m_lastOwnedContext == cur_context)
868     {
869       [ NSOpenGLContext clearCurrentContext ];
870       [ cur_context clearDrawable ];
871       [ cur_context release ];
872     }
873
874     // activate context
875     [newContext makeCurrentContext];
876     m_lastOwnedContext = newContext;
877   }
878   else
879   {
880     // Windowed Mode
881     // exit fullscreen
882     [cur_context clearDrawable];
883
884     [NSCursor unhide];
885
886     // Show menubar.
887     if (GetDisplayID(res.iScreen) == kCGDirectMainDisplay)
888       SetMenuBarVisible(true);
889
890     if (g_guiSettings.GetBool("videoscreen.fakefullscreen"))
891     {
892       // restore the windowed window level
893       [[last_view window] setLevel:NSNormalWindowLevel];
894
895       // Get rid of the new window we created.
896       if (windowedFullScreenwindow != NULL)
897       {
898         [windowedFullScreenwindow close];
899         if ([windowedFullScreenwindow isReleasedWhenClosed] == NO)
900           [windowedFullScreenwindow release];
901         windowedFullScreenwindow = NULL;
902       }
903
904       // Unblank.
905       // Force the unblank when returning from fullscreen, we get called with blankOtherDisplays set false.
906       //if (blankOtherDisplays)
907         UnblankDisplays();
908     }
909     else
910     {
911       // release displays
912       CGReleaseAllDisplays();
913     }
914
915     // create our new context (sharing with the current one)
916     NSOpenGLContext* newContext = (NSOpenGLContext*)CreateWindowedContext((void* )cur_context);
917     if (!newContext)
918       return false;
919
920     // Assign view from old context, move back to original screen.
921     [newContext setView:last_view];
922     [[last_view window] setFrameOrigin:last_window_origin];
923     // return the mouse bounds in SDL view to prevous size
924     [ last_view setFrameSize:last_view_size ];
925     [ last_view setFrameOrigin:last_view_origin ];
926     // done with restoring windowed window, don't set last_view to NULL as we can lose it under dual displays.
927     //last_window_screen = NULL;
928
929     // Release the fullscreen context.
930     if (m_lastOwnedContext == cur_context)
931     {
932       [ NSOpenGLContext clearCurrentContext ];
933       [ cur_context clearDrawable ];
934       [ cur_context release ];
935     }
936
937     // Activate context.
938     [newContext makeCurrentContext];
939     m_lastOwnedContext = newContext;
940   }
941
942   DisplayFadeFromBlack(fade_token, needtoshowme);
943
944   ShowHideNSWindow([last_view window], needtoshowme);
945   // need to make sure SDL tracks any window size changes
946   ResizeWindow(m_nWidth, m_nHeight, -1, -1);
947
948   return true;
949 }
950
951 void CWinSystemOSX::UpdateResolutions()
952 {
953   CWinSystemBase::UpdateResolutions();
954
955   // Add desktop resolution
956   int w, h;
957   double fps;
958
959   // first screen goes into the current desktop mode
960   GetScreenResolution(&w, &h, &fps, 0);
961   UpdateDesktopResolution(g_settings.m_ResInfo[RES_DESKTOP], 0, w, h, fps);
962
963   // see resolution.h enum RESOLUTION for how the resolutions
964   // have to appear in the g_settings.m_ResInfo vector
965   // add the desktop resolutions of the other screens
966   for(int i = 1; i < GetNumScreens(); i++)
967   {
968     RESOLUTION_INFO res;
969     // get current resolution of screen i
970     GetScreenResolution(&w, &h, &fps, i);
971     UpdateDesktopResolution(res, i, w, h, fps);
972     g_settings.m_ResInfo.push_back(res);
973   }
974
975   if (m_can_display_switch)
976   {
977     // now just fill in the possible reolutions for the attached screens
978     // and push to the m_ResInfo vector
979     FillInVideoModes();
980   }
981 }
982
983 /*
984 void* Cocoa_GL_CreateContext(void* pixFmt, void* shareCtx)
985 {
986   if (!pixFmt)
987     return nil;
988
989   NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
990     shareContext:(NSOpenGLContext*)shareCtx];
991
992   // snipit from SDL_cocoaopengl.m
993   //
994   // Wisdom from Apple engineer in reference to UT2003's OpenGL performance:
995   //  "You are blowing a couple of the internal OpenGL function caches. This
996   //  appears to be happening in the VAO case.  You can tell OpenGL to up
997   //  the cache size by issuing the following calls right after you create
998   //  the OpenGL context.  The default cache size is 16."    --ryan.
999   //
1000
1001   #ifndef GLI_ARRAY_FUNC_CACHE_MAX
1002   #define GLI_ARRAY_FUNC_CACHE_MAX 284
1003   #endif
1004
1005   #ifndef GLI_SUBMIT_FUNC_CACHE_MAX
1006   #define GLI_SUBMIT_FUNC_CACHE_MAX 280
1007   #endif
1008
1009   {
1010       long cache_max = 64;
1011       CGLContextObj ctx = (CGLContextObj)[newContext CGLContextObj];
1012       CGLSetParameter(ctx, (CGLContextParameter)GLI_SUBMIT_FUNC_CACHE_MAX, &cache_max);
1013       CGLSetParameter(ctx, (CGLContextParameter)GLI_ARRAY_FUNC_CACHE_MAX, &cache_max);
1014   }
1015
1016   // End Wisdom from Apple Engineer section. --ryan.
1017   return newContext;
1018 }
1019 */
1020
1021 void* CWinSystemOSX::CreateWindowedContext(void* shareCtx)
1022 {
1023   NSOpenGLContext* newContext = NULL;
1024
1025   NSOpenGLPixelFormatAttribute wattrs[] =
1026   {
1027     NSOpenGLPFADoubleBuffer,
1028     NSOpenGLPFAWindow,
1029     NSOpenGLPFANoRecovery,
1030     NSOpenGLPFAAccelerated,
1031     NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1032     (NSOpenGLPixelFormatAttribute)0
1033   };
1034
1035   NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs];
1036
1037   newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1038     shareContext:(NSOpenGLContext*)shareCtx];
1039   [pixFmt release];
1040
1041   if (!newContext)
1042   {
1043     // bah, try again for non-accelerated renderer
1044     NSOpenGLPixelFormatAttribute wattrs2[] =
1045     {
1046       NSOpenGLPFADoubleBuffer,
1047       NSOpenGLPFAWindow,
1048       NSOpenGLPFANoRecovery,
1049       NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)8,
1050       (NSOpenGLPixelFormatAttribute)0
1051     };
1052     NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:wattrs2];
1053
1054     newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1055       shareContext:(NSOpenGLContext*)shareCtx];
1056     [pixFmt release];
1057   }
1058
1059   return newContext;
1060 }
1061
1062 void* CWinSystemOSX::CreateFullScreenContext(int screen_index, void* shareCtx)
1063 {
1064   CGDirectDisplayID displayArray[MAX_DISPLAYS];
1065   CGDisplayCount    numDisplays;
1066   CGDirectDisplayID displayID;
1067
1068   // Get the list of displays.
1069   CGGetActiveDisplayList(MAX_DISPLAYS, displayArray, &numDisplays);
1070   displayID = displayArray[screen_index];
1071
1072   NSOpenGLPixelFormatAttribute fsattrs[] =
1073   {
1074     NSOpenGLPFADoubleBuffer,
1075     NSOpenGLPFAFullScreen,
1076     NSOpenGLPFANoRecovery,
1077     NSOpenGLPFAAccelerated,
1078     NSOpenGLPFADepthSize,  (NSOpenGLPixelFormatAttribute)8,
1079     NSOpenGLPFAScreenMask, (NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(displayID),
1080     (NSOpenGLPixelFormatAttribute)0
1081   };
1082
1083   NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:fsattrs];
1084   if (!pixFmt)
1085     return nil;
1086
1087   NSOpenGLContext* newContext = [[NSOpenGLContext alloc] initWithFormat:(NSOpenGLPixelFormat*)pixFmt
1088     shareContext:(NSOpenGLContext*)shareCtx];
1089   [pixFmt release];
1090
1091   return newContext;
1092 }
1093
1094 void CWinSystemOSX::GetScreenResolution(int* w, int* h, double* fps, int screenIdx)
1095 {
1096   // Figure out the screen size. (default to main screen)
1097   if (screenIdx >= GetNumScreens())
1098     return;
1099   CGDirectDisplayID display_id = (CGDirectDisplayID)GetDisplayID(screenIdx);
1100
1101   NSOpenGLContext* context = [NSOpenGLContext currentContext];
1102   if (context)
1103   {
1104     NSView* view;
1105
1106     view = [context view];
1107     if (view)
1108     {
1109       NSWindow* window;
1110       window = [view window];
1111       if (window)
1112         display_id = GetDisplayIDFromScreen( [window screen] );
1113     }
1114   }
1115   CGDisplayModeRef mode  = CGDisplayCopyDisplayMode(display_id);
1116   *w = CGDisplayModeGetWidth(mode);
1117   *h = CGDisplayModeGetHeight(mode);
1118   *fps = CGDisplayModeGetRefreshRate(mode);
1119   CGDisplayModeRelease(mode);
1120   if ((int)*fps == 0)
1121   {
1122     // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
1123     *fps = 60.0;
1124   }
1125 }
1126
1127 void CWinSystemOSX::EnableVSync(bool enable)
1128 {
1129   // OpenGL Flush synchronised with vertical retrace
1130   GLint swapInterval = enable ? 1 : 0;
1131   [[NSOpenGLContext currentContext] setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
1132 }
1133
1134 bool CWinSystemOSX::SwitchToVideoMode(int width, int height, double refreshrate, int screenIdx)
1135 {
1136   // SwitchToVideoMode will not return until the display has actually switched over.
1137   // This can take several seconds.
1138   if( screenIdx >= GetNumScreens())
1139     return false;
1140
1141   boolean_t match = false;
1142   CFDictionaryRef dispMode = NULL;
1143   // Figure out the screen size. (default to main screen)
1144   CGDirectDisplayID display_id = GetDisplayID(screenIdx);
1145
1146   // find mode that matches the desired size, refreshrate
1147   // non interlaced, nonstretched, safe for hardware
1148   dispMode = GetMode(width, height, refreshrate, screenIdx);
1149
1150   //not found - fallback to bestemdeforparameters
1151   if (!dispMode)
1152   {
1153     dispMode = CGDisplayBestModeForParameters(display_id, 32, width, height, &match);
1154
1155     if (!match)
1156       dispMode = CGDisplayBestModeForParameters(display_id, 16, width, height, &match);
1157
1158     if (!match)
1159       return false;
1160   }
1161
1162   // switch mode and return success
1163   CGDisplayCapture(display_id);
1164   CGDisplayConfigRef cfg;
1165   CGBeginDisplayConfiguration(&cfg);
1166   // we don't need to do this, we are already faded.
1167   //CGConfigureDisplayFadeEffect(cfg, 0.3f, 0.5f, 0, 0, 0);
1168   CGConfigureDisplayMode(cfg, display_id, dispMode);
1169   CGError err = CGCompleteDisplayConfiguration(cfg, kCGConfigureForAppOnly);
1170   CGDisplayRelease(display_id);
1171
1172   Cocoa_CVDisplayLinkUpdate();
1173
1174   return (err == kCGErrorSuccess);
1175 }
1176
1177 void CWinSystemOSX::FillInVideoModes()
1178 {
1179   // Add full screen settings for additional monitors
1180   int numDisplays = [[NSScreen screens] count];
1181
1182   for (int disp = 0; disp < numDisplays; disp++)
1183   {
1184     Boolean stretched;
1185     Boolean interlaced;
1186     Boolean safeForHardware;
1187     Boolean televisionoutput;
1188     int w, h, bitsperpixel;
1189     double refreshrate;
1190     RESOLUTION_INFO res;
1191
1192     CFArrayRef displayModes = CGDisplayAvailableModes(GetDisplayID(disp));
1193     NSString *dispName = screenNameForDisplay(GetDisplayID(disp));
1194     CLog::Log(LOGNOTICE, "Display %i has name %s", disp, [dispName UTF8String]);
1195
1196     if (NULL == displayModes)
1197       continue;
1198
1199     for (int i=0; i < CFArrayGetCount(displayModes); ++i)
1200     {
1201       CFDictionaryRef displayMode = (CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i);
1202
1203       stretched = GetDictionaryBoolean(displayMode, kCGDisplayModeIsStretched);
1204       interlaced = GetDictionaryBoolean(displayMode, kCGDisplayModeIsInterlaced);
1205       bitsperpixel = GetDictionaryInt(displayMode, kCGDisplayBitsPerPixel);
1206       safeForHardware = GetDictionaryBoolean(displayMode, kCGDisplayModeIsSafeForHardware);
1207       televisionoutput = GetDictionaryBoolean(displayMode, kCGDisplayModeIsTelevisionOutput);
1208
1209       if ((bitsperpixel == 32)      &&
1210           (safeForHardware == YES)  &&
1211           (stretched == NO)         &&
1212           (interlaced == NO))
1213       {
1214         w = GetDictionaryInt(displayMode, kCGDisplayWidth);
1215         h = GetDictionaryInt(displayMode, kCGDisplayHeight);
1216         refreshrate = GetDictionaryDouble(displayMode, kCGDisplayRefreshRate);
1217         if ((int)refreshrate == 0)  // LCD display?
1218         {
1219           // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays.
1220           refreshrate = 60.0;
1221         }
1222         CLog::Log(LOGNOTICE, "Found possible resolution for display %d with %d x %d @ %f Hz\n", disp, w, h, refreshrate);
1223
1224         UpdateDesktopResolution(res, disp, w, h, refreshrate);
1225
1226         // overwrite the mode str because  UpdateDesktopResolution adds a
1227         // "Full Screen". Since the current resolution is there twice
1228         // this would lead to 2 identical resolution entrys in the guisettings.xml.
1229         // That would cause problems with saving screen overscan calibration
1230         // because the wrong entry is picked on load.
1231         // So we just use UpdateDesktopResolutions for the current DESKTOP_RESOLUTIONS
1232         // in UpdateResolutions. And on all othere resolutions make a unique
1233         // mode str by doing it without appending "Full Screen".
1234         // this is what linux does - though it feels that there shouldn't be
1235         // the same resolution twice... - thats why i add a FIXME here.
1236         res.strMode.Format("%dx%d @ %.2f", w, h, refreshrate);
1237         g_graphicsContext.ResetOverscan(res);
1238         g_settings.m_ResInfo.push_back(res);
1239       }
1240     }
1241   }
1242 }
1243
1244 bool CWinSystemOSX::FlushBuffer(void)
1245 {
1246   [ (NSOpenGLContext*)m_glContext flushBuffer ];
1247
1248   return true;
1249 }
1250
1251 bool CWinSystemOSX::IsObscured(void)
1252 {
1253   // check once a second if we are obscured.
1254   unsigned int now_time = XbmcThreads::SystemClockMillis();
1255   if (m_obscured_timecheck > now_time)
1256     return m_obscured;
1257   else
1258     m_obscured_timecheck = now_time + 1000;
1259
1260   NSOpenGLContext* cur_context = [NSOpenGLContext currentContext];
1261   NSView* view = [cur_context view];
1262   if (!view)
1263   {
1264     // sanity check, we should always have a view
1265     m_obscured = true;
1266     return m_obscured;
1267   }
1268
1269   NSWindow *window = [view window];
1270   if (!window)
1271   {
1272     // sanity check, we should always have a window
1273     m_obscured = true;
1274     return m_obscured;
1275   }
1276
1277   if ([window isVisible] == NO)
1278   {
1279     // not visable means the window is not showing.
1280     // this should never really happen as we are always visable
1281     // even when minimized in dock.
1282     m_obscured = true;
1283     return m_obscured;
1284   }
1285
1286   // check if we are minimized (to an icon in the Dock).
1287   if ([window isMiniaturized] == YES)
1288   {
1289     m_obscured = true;
1290     return m_obscured;
1291   }
1292
1293   // check if we are showing on the active workspace.
1294   if ([window isOnActiveSpace] == NO)
1295   {
1296     m_obscured = true;
1297     return m_obscured;
1298   }
1299
1300   // default to false before we start parsing though the windows.
1301   // if we are are obscured by any windows, then set true.
1302   m_obscured = false;
1303   static bool obscureLogged = false;
1304
1305   CGWindowListOption opts;
1306   opts = kCGWindowListOptionOnScreenAboveWindow | kCGWindowListExcludeDesktopElements;
1307   CFArrayRef windowIDs =CGWindowListCreate(opts, (CGWindowID)[window windowNumber]);  
1308
1309   if (!windowIDs)
1310     return m_obscured;
1311
1312   CFArrayRef windowDescs = CGWindowListCreateDescriptionFromArray(windowIDs);
1313   if (!windowDescs)
1314   {
1315     CFRelease(windowIDs);
1316     return m_obscured;
1317   }
1318
1319   CGRect bounds = NSRectToCGRect([window frame]);
1320   // kCGWindowBounds measures the origin as the top-left corner of the rectangle
1321   //  relative to the top-left corner of the screen.
1322   // NSWindow’s frame property measures the origin as the bottom-left corner
1323   //  of the rectangle relative to the bottom-left corner of the screen.
1324   // convert bounds from NSWindow to CGWindowBounds here.
1325   bounds.origin.y = [[window screen] frame].size.height - bounds.origin.y - bounds.size.height;
1326
1327   std::vector<CRect> partialOverlaps;
1328   CRect ourBounds = CGRectToCRect(bounds);
1329
1330   for (CFIndex idx=0; idx < CFArrayGetCount(windowDescs); idx++)
1331   {
1332     // walk the window list of windows that are above us and are not desktop elements
1333     CFDictionaryRef windowDictionary = (CFDictionaryRef)CFArrayGetValueAtIndex(windowDescs, idx);
1334
1335     // skip the Dock window, it actually covers the entire screen.
1336     CFStringRef ownerName = (CFStringRef)CFDictionaryGetValue(windowDictionary, kCGWindowOwnerName);
1337     if (CFStringCompare(ownerName, CFSTR("Dock"), 0) == kCFCompareEqualTo)
1338       continue;
1339
1340     // Ignore known brightness tools for dimming the screen. They claim to cover
1341     // the whole XBMC window and therefore would make the framerate limiter
1342     // kicking in. Unfortunatly even the alpha of these windows is 1.0 so
1343     // we have to check the ownerName.
1344     if (CFStringCompare(ownerName, CFSTR("Shades"), 0)            == kCFCompareEqualTo ||
1345         CFStringCompare(ownerName, CFSTR("SmartSaver"), 0)        == kCFCompareEqualTo ||
1346         CFStringCompare(ownerName, CFSTR("Brightness Slider"), 0) == kCFCompareEqualTo ||
1347         CFStringCompare(ownerName, CFSTR("Displaperture"), 0)     == kCFCompareEqualTo ||
1348         CFStringCompare(ownerName, CFSTR("Dreamweaver"), 0)       == kCFCompareEqualTo)
1349       continue;
1350
1351     CFDictionaryRef rectDictionary = (CFDictionaryRef)CFDictionaryGetValue(windowDictionary, kCGWindowBounds);
1352     if (!rectDictionary)
1353       continue;
1354
1355     CGRect windowBounds;
1356     if (CGRectMakeWithDictionaryRepresentation(rectDictionary, &windowBounds))
1357     {
1358       if (CGRectContainsRect(windowBounds, bounds))
1359       {
1360         // if the windowBounds completely encloses our bounds, we are obscured.
1361         if (!obscureLogged)
1362         {
1363           std::string appName;
1364           if (DarwinCFStringRefToString(ownerName, appName))
1365             CLog::Log(LOGDEBUG, "WinSystemOSX: Fullscreen window %s obscures XBMC!", appName.c_str());
1366           obscureLogged = true;
1367         }
1368         m_obscured = true;
1369         break;
1370       }
1371
1372       // handle overlaping windows above us that combine
1373       // to obscure by collecting any partial overlaps,
1374       // then subtract them from our bounds and check
1375       // for any remaining area.
1376       CRect intersection = CGRectToCRect(windowBounds);
1377       intersection.Intersect(ourBounds);
1378       if (!intersection.IsEmpty())
1379         partialOverlaps.push_back(intersection);
1380     }
1381   }
1382
1383   if (!m_obscured)
1384   {
1385     // if we are here we are not obscured by any fullscreen window - reset flag
1386     // for allowing the logmessage above to show again if this changes.
1387     if (obscureLogged)
1388       obscureLogged = false;
1389     std::vector<CRect> rects = ourBounds.SubtractRects(partialOverlaps);
1390     // they got us covered
1391     if (rects.size() == 0)
1392       m_obscured = true;
1393   }
1394
1395   CFRelease(windowDescs);
1396   CFRelease(windowIDs);
1397
1398   return m_obscured;
1399 }
1400
1401 void CWinSystemOSX::NotifyAppFocusChange(bool bGaining)
1402 {
1403   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1404
1405   if (m_bFullScreen && bGaining)
1406   {
1407     // find the window
1408     NSOpenGLContext* context = [NSOpenGLContext currentContext];
1409     if (context)
1410     {
1411       NSView* view;
1412
1413       view = [context view];
1414       if (view)
1415       {
1416         NSWindow* window;
1417         window = [view window];
1418         if (window)
1419         {
1420           // find the screenID
1421           NSDictionary* screenInfo = [[window screen] deviceDescription];
1422           NSNumber* screenID = [screenInfo objectForKey:@"NSScreenNumber"];
1423           if ((CGDirectDisplayID)[screenID longValue] == kCGDirectMainDisplay)
1424           {
1425             SetMenuBarVisible(false);
1426           }
1427           [window orderFront:nil];
1428         }
1429       }
1430     }
1431   }
1432   [pool release];
1433 }
1434
1435 void CWinSystemOSX::ShowOSMouse(bool show)
1436 {
1437   SDL_ShowCursor(show ? 1 : 0);
1438 }
1439
1440 bool CWinSystemOSX::Minimize()
1441 {
1442   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1443
1444   [[NSApplication sharedApplication] miniaturizeAll:nil];
1445
1446   [pool release];
1447   return true;
1448 }
1449
1450 bool CWinSystemOSX::Restore()
1451 {
1452   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1453
1454   [[NSApplication sharedApplication] unhide:nil];
1455
1456   [pool release];
1457   return true;
1458 }
1459
1460 bool CWinSystemOSX::Hide()
1461 {
1462   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1463
1464   [[NSApplication sharedApplication] hide:nil];
1465
1466   [pool release];
1467   return true;
1468 }
1469
1470 void CWinSystemOSX::OnMove(int x, int y)
1471 {
1472   Cocoa_CVDisplayLinkUpdate();
1473 }
1474
1475 void CWinSystemOSX::EnableSystemScreenSaver(bool bEnable)
1476 {
1477   // see Technical Q&A QA1340
1478   static IOPMAssertionID assertionID = 0;
1479
1480   if (!bEnable)
1481   {
1482     if (assertionID == 0)
1483     {
1484       CFStringRef reasonForActivity= CFSTR("XBMC requested disable system screen saver");
1485       IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
1486         kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
1487     }
1488   }
1489   else if (assertionID != 0)
1490   {
1491     IOPMAssertionRelease(assertionID);
1492     assertionID = 0;
1493   }
1494
1495   m_use_system_screensaver = bEnable;
1496 }
1497
1498 bool CWinSystemOSX::IsSystemScreenSaverEnabled()
1499 {
1500   return m_use_system_screensaver;
1501 }
1502
1503 void CWinSystemOSX::ResetOSScreensaver()
1504 {
1505   // allow os screensaver only if we are fullscreen
1506   EnableSystemScreenSaver(!m_bFullScreen);
1507 }
1508
1509 bool CWinSystemOSX::EnableFrameLimiter()
1510 {
1511   return IsObscured();
1512 }
1513
1514 void CWinSystemOSX::Register(IDispResource *resource)
1515 {
1516   CSingleLock lock(m_resourceSection);
1517   m_resources.push_back(resource);
1518 }
1519
1520 void CWinSystemOSX::Unregister(IDispResource* resource)
1521 {
1522   CSingleLock lock(m_resourceSection);
1523   std::vector<IDispResource*>::iterator i = find(m_resources.begin(), m_resources.end(), resource);
1524   if (i != m_resources.end())
1525     m_resources.erase(i);
1526 }
1527
1528 bool CWinSystemOSX::Show(bool raise)
1529 {
1530   NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1531
1532   if (raise)
1533   {
1534     [[NSApplication sharedApplication] unhide:nil];
1535     [[NSApplication sharedApplication] activateIgnoringOtherApps: YES];
1536     [[NSApplication sharedApplication] arrangeInFront:nil];
1537   }
1538   else
1539   {
1540     [[NSApplication sharedApplication] unhideWithoutActivation];
1541   }
1542
1543   [pool release];
1544   return true;
1545 }
1546
1547 int CWinSystemOSX::GetNumScreens()
1548 {
1549   int numDisplays = [[NSScreen screens] count];
1550   return(numDisplays);
1551 }
1552
1553 void CWinSystemOSX::CheckDisplayChanging(u_int32_t flags)
1554 {
1555   if (flags)
1556   {
1557     CSingleLock lock(m_resourceSection);
1558     // tell any shared resources
1559     if (flags & kCGDisplayBeginConfigurationFlag)
1560     {
1561       CLog::Log(LOGDEBUG, "CWinSystemOSX::CheckDisplayChanging:OnLostDevice");
1562       for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
1563         (*i)->OnLostDevice();
1564     }
1565     if (flags & kCGDisplaySetModeFlag)
1566     {
1567       CLog::Log(LOGDEBUG, "CWinSystemOSX::CheckDisplayChanging:OnResetDevice");
1568       for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); i++)
1569         (*i)->OnResetDevice();
1570     }
1571   }
1572 }
1573
1574 void* CWinSystemOSX::GetCGLContextObj()
1575 {
1576   return [(NSOpenGLContext*)m_glContext CGLContextObj];
1577 }
1578
1579 #endif