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