root/OpenSceneGraph/trunk/src/osgViewer/DarwinUtils.mm @ 11207

Revision 11207, 14.7 kB (checked in by robert, 5 years ago)

Refactored the initialization of OSX windowing settings so that it's done on demand rather than on startup.

  • Property svn:executable set to *
RevLine 
[9879]1/*
2 *  DarwinUtils.cpp
3 *  OpenSceneGraph
4 *
5 *  Created by Stephan Huber on 27.06.08.
6 *  Copyright 2008 Stephan Maximilian Huber, digital mind. All rights reserved.
7 *
8 */
9
10#include <osg/Referenced>
11#include <osg/DeleteHandler>
12#include "DarwinUtils.h"
13#include <Cocoa/Cocoa.h>
14
[10340]15@interface MenubarToggler : NSObject {
16
17}
18
[10622]19-(void) show: (id) data;
20-(void) hide: (id) data;
[10340]21
22@end
23
24@implementation MenubarToggler
25
26
27
[10622]28-(void) hide:(id) data 
[10340]29{
30    OSErr error = SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
31    if (error) {
32        osg::notify(osg::DEBUG_INFO) << "MenubarToggler::hide failed with " << error << std::endl;
33    }
34}
35
36
[10622]37-(void) show:(id) data 
[10340]38{
39    OSErr error = SetSystemUIMode(kUIModeNormal, 0);
40    if (error) {
41        osg::notify(osg::DEBUG_INFO) << "MenubarToggler::show failed with " << error << std::endl;
42    }
43}
44
45
46@end
47
[9879]48namespace osgDarwin {
49
50
51static inline CGRect toCGRect(NSRect nsRect)
52{
[9895]53    CGRect cgRect;
[9879]54
[9895]55    cgRect.origin.x = nsRect.origin.x;
56    cgRect.origin.y = nsRect.origin.y;
57    cgRect.size.width = nsRect.size.width;
58    cgRect.size.height = nsRect.size.height;
[9879]59
[9895]60    return cgRect;
[9879]61}
62
63
64MenubarController::MenubarController()
[9895]65:    osg::Referenced(),
[9879]66    _list(),
67    _menubarShown(false),
68    _mutex()
69{
[9895]70    // the following code will query the system for the available rect on the main-display (typically the displaying showing the menubar + the dock
[9879]71
[9895]72    NSRect rect = [[[NSScreen screens] objectAtIndex: 0] visibleFrame];
73    _availRect = toCGRect(rect);
74   
75    // now we need the rect of the main-display including the menubar and the dock
76    _mainScreenBounds = CGDisplayBounds( CGMainDisplayID() );
[9879]77
78
[9895]79    // NSRect 0/0 is bottom/left, _mainScreenBounds 0/0 is top/left
80    _availRect.origin.y = _mainScreenBounds.size.height - _availRect.size.height - _availRect.origin.y;
81   
82       
83    // hide the menubar initially
84    SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
[9879]85}
86
87
88
89
90MenubarController* MenubarController::instance()
91{
92    static osg::ref_ptr<MenubarController> s_menubar_controller = new MenubarController();
93    return s_menubar_controller.get();
94}
95
96
97void MenubarController::attachWindow(WindowAdapter* win)
98{
99    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
100    _list.push_back(win);
101    update();
102}
103
104
105void MenubarController::detachWindow(osgViewer::GraphicsWindow* win)
106{
107    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
108    for(WindowList::iterator i = _list.begin(); i != _list.end(); ) {
109        if ((*i)->getWindow() == win)
110            i = _list.erase(i);
111        else 
112            ++i;
113    }
114    update();
115}
116
117// iterate through all open windows and check, if they intersect the area occupied by the menubar/dock, and if so, hide the menubar/dock
118
[10340]119
[9879]120void MenubarController::update()
121{
122    unsigned int windowsCoveringMenubarArea = 0;   
123    unsigned int windowsIntersectingMainScreen = 0;
124    for(WindowList::iterator i = _list.begin(); i != _list.end(); ) {
[9895]125        WindowAdapter* wi = (*i).get();
[9879]126        if (wi->valid()) {
127            CGRect windowBounds;
[9895]128            wi->getWindowBounds(windowBounds);
129           
130            if (CGRectIntersectsRect(_mainScreenBounds, windowBounds))
[9879]131            {
132                ++windowsIntersectingMainScreen;
133                // osg::notify(osg::ALWAYS) << "testing rect " << windowBounds.origin.x << "/" << windowBounds.origin.y << " " << windowBounds.size.width << "x" << windowBounds.size.height << std::endl;
[9895]134                // osg::notify(osg::ALWAYS) << "against      " << _availRect.origin.x << "/" << _availRect.origin.y << " " << _availRect.size.width << "x" << _availRect.size.height << std::endl;
[9879]135                // the window intersects the main-screen, does it intersect with the menubar/dock?
136                if (((_availRect.origin.y > _mainScreenBounds.origin.y) && (_availRect.origin.y > windowBounds.origin.y)) ||
137                    ((_availRect.origin.x > _mainScreenBounds.origin.x) && (_availRect.origin.x > windowBounds.origin.x)) ||
138                    ((_availRect.size.width < _mainScreenBounds.size.width) && (_availRect.origin.x + _availRect.size.width < windowBounds.origin.x + windowBounds.size.width)) ||
139                    ((_availRect.size.height < _mainScreenBounds.size.height) && (_availRect.origin.y + _availRect.size.height < windowBounds.origin.y + windowBounds.size.height) ))
140                {
141                    ++windowsCoveringMenubarArea;
142                }
143            }
144           
145            ++i;
146        }
147        else
[10417]148            i = _list.erase(i);
[9879]149    }
150   
[10340]151    // if we use the cocoa implementation then we have a NSRunLoop in place, and so we can use the deferred menubar-toggling which is thread safe
152           
153    #ifdef USE_DARWIN_COCOA_IMPLEMENTATION
154   
155        // SetSystemUIMode is not threadsafe, you'll get crashes if you call this method from other threads
156        // so use a small NSObject to switch the menubar on the main thread via performSelectorOnMainThread
[9879]157       
[10340]158        NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
159        if (windowsCoveringMenubarArea && _menubarShown)
160        {
161           
162            //error = SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
163            MenubarToggler* toggler = [[MenubarToggler alloc] init];
164            [toggler performSelectorOnMainThread: @selector(hide:) withObject:NULL waitUntilDone: YES];
165            [toggler autorelease];
166        }
167        if (!windowsCoveringMenubarArea && !_menubarShown)
168        {
169            //error = SetSystemUIMode(kUIModeNormal, 0);
170            MenubarToggler* toggler = [[MenubarToggler alloc] init];
171            [toggler performSelectorOnMainThread: @selector(show:) withObject:NULL waitUntilDone: YES];
172            [toggler autorelease];
173        }
174        [pool release];
[9879]175   
[10340]176    #else
[9879]177   
[10340]178        OSErr error;
179       
180          // see http://developer.apple.com/technotes/tn2002/tn2062.html for hiding the dock+menubar
181        if (windowsCoveringMenubarArea && _menubarShown)
182        {
183            error = SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
184        }
[10417]185        if (!windowsCoveringMenubarArea && !_menubarShown)
[10340]186        {
187            error = SetSystemUIMode(kUIModeNormal, 0);
188        }
189    #endif
190   
191    _menubarShown = !windowsCoveringMenubarArea;
[9879]192}
193
194
195
196/** Helper method to get a double value out of a CFDictionary */
197static double getDictDouble (CFDictionaryRef refDict, CFStringRef key)
198{
[9895]199    double value;
200    CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
201    if (!number_value) // if can't get a number for the dictionary
202        return -1;  // fail
203    if (!CFNumberGetValue(number_value, kCFNumberDoubleType, &value)) // or if cant convert it
204        return -1; // fail
205    return value; // otherwise return the long value
[9879]206}
207
208/** Helper method to get a long value out of a CFDictionary */
209static long getDictLong(CFDictionaryRef refDict, CFStringRef key)        // const void* key?
210{
[9895]211    long value = 0;
212    CFNumberRef number_value = (CFNumberRef)CFDictionaryGetValue(refDict, key);
213    if (!number_value) // if can't get a number for the dictionary
214        return -1;  // fail
215    if (!CFNumberGetValue(number_value, kCFNumberLongType, &value)) // or if cant convert it
216        return -1; // fail
217    return value;
[9879]218}
219
220
221
222/** ctor, get a list of all attached displays */
223DarwinWindowingSystemInterface::DarwinWindowingSystemInterface() :
[11207]224    _initialized(false),
[9895]225    _displayCount(0),
226    _displayIds(NULL)
[9879]227{
[11207]228}
[9879]229
230/** dtor */
231DarwinWindowingSystemInterface::~DarwinWindowingSystemInterface()
232{
[9895]233    if (osg::Referenced::getDeleteHandler())
234    {
235        osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
236        osg::Referenced::getDeleteHandler()->flushAll();
237    }
[9879]238
[9895]239    if (_displayIds) delete[] _displayIds;
240    _displayIds = NULL;
[9879]241}
242
[11207]243void DarwinWindowingSystemInterface::_init()
244{
245    if (_initialized) return;
246
247    ProcessSerialNumber sn = { 0, kCurrentProcess };
248    TransformProcessType(&sn,kProcessTransformToForegroundApplication);
249    SetFrontProcess(&sn);
250
251    if( CGGetActiveDisplayList( 0, NULL, &_displayCount ) != CGDisplayNoErr )
252    {
253        osg::notify(osg::WARN) << "DarwinWindowingSystemInterface: could not get # of screens" << std::endl;
254        _displayCount = 0;
255
256        _initialized = true;
257        return;
258    }
259
260    _displayIds = new CGDirectDisplayID[_displayCount];
261
262    if( CGGetActiveDisplayList( _displayCount, _displayIds, &_displayCount ) != CGDisplayNoErr )
263    {
264        osg::notify(osg::WARN) << "DarwinWindowingSystemInterface: CGGetActiveDisplayList failed" << std::endl;
265    }
266
267    _initialized = true;
268}
269
[9879]270/** @return a CGDirectDisplayID for a ScreenIdentifier */
[11207]271CGDirectDisplayID DarwinWindowingSystemInterface::getDisplayID(const osg::GraphicsContext::ScreenIdentifier& si)
272{
273    _init();
274
275    if (_displayCount==0)
276    {
277        osg::notify(osg::WARN) << "DarwinWindowingSystemInterface::getDisplayID(..) no valid screens available returning 0 instead." << std::endl;
278        return 0;
279    }
280
[9895]281    if (si.screenNum < static_cast<int>(_displayCount))
[11207]282    {
[9895]283        return _displayIds[si.screenNum];
[11207]284    }
285    else
286    {
287        osg::notify(osg::WARN) << "DarwinWindowingSystemInterface::getDisplayID(..) invalid screen # " << si.screenNum << ", returning main-screen instead." << std::endl;
[9895]288        return _displayIds[0];
289    }
[9879]290}
291
292/** @return count of attached screens */
293unsigned int DarwinWindowingSystemInterface::getNumScreens(const osg::GraphicsContext::ScreenIdentifier& si)
294{
[11207]295    _init();
296
[9895]297    return _displayCount;
[9879]298}
299
300void DarwinWindowingSystemInterface::getScreenSettings(const osg::GraphicsContext::ScreenIdentifier& si, osg::GraphicsContext::ScreenSettings & resolution)
301{
[11207]302    _init();
303
304    if (_displayCount==0)
305    {
306        resolution.width = 0;
307        resolution.height = 0;
308        resolution.colorDepth = 0;
309        resolution.refreshRate = 0;
310        return;
311    }
312
[9895]313    CGDirectDisplayID id = getDisplayID(si);
314    resolution.width = CGDisplayPixelsWide(id);
315    resolution.height = CGDisplayPixelsHigh(id);
316    resolution.colorDepth = CGDisplayBitsPerPixel(id);
317    resolution.refreshRate = getDictDouble (CGDisplayCurrentMode(id), kCGDisplayRefreshRate);        // Not tested
318    if (resolution.refreshRate<0) resolution.refreshRate = 0;
[9879]319}
320
321
[11207]322void DarwinWindowingSystemInterface::enumerateScreenSettings(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, osg::GraphicsContext::ScreenSettingsList & resolutionList)
323{
324    _init();
[9879]325
[11207]326    // Warning! This method has not been tested.
327    resolutionList.clear();
[9879]328
[11207]329    if (_displayCount==0)
330    {
331        return;
332    }
[9879]333
[11207]334    CGDirectDisplayID displayid = getDisplayID(screenIdentifier);
335    CFArrayRef availableModes = CGDisplayAvailableModes(displayid);
336    unsigned int numberOfAvailableModes = CFArrayGetCount(availableModes);
337    for (unsigned int i=0; i<numberOfAvailableModes; ++i) {
338        // look at each mode in the available list
339        CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex(availableModes, i);
340        osg::GraphicsContext::ScreenSettings tmpSR;
341
342        long width = getDictLong(mode, kCGDisplayWidth);
343        tmpSR.width = width<=0 ? 0 : width;
344        long height = getDictLong(mode, kCGDisplayHeight);
345        tmpSR.height = height<=0 ? 0 : height;
346        long rate = getDictLong(mode, kCGDisplayRefreshRate);
347        tmpSR.refreshRate = rate<=0 ? 0 : rate;
348        long depth = getDictLong(mode, kCGDisplayBitsPerPixel);
349        tmpSR.colorDepth = depth<=0 ? 0 : depth;
350
351        resolutionList.push_back(tmpSR);
[9879]352    }
[11207]353}
[9879]354
355/** return the top left coord of a specific screen in global screen space */
[11207]356void DarwinWindowingSystemInterface::getScreenTopLeft(const osg::GraphicsContext::ScreenIdentifier& si, int& x, int& y)
357{
358    _init();
359
360    if (_displayCount==0)
361    {
362        x = 0;
363        y = 0;
364        return;
365    }
366
[9895]367    CGRect bounds = CGDisplayBounds( getDisplayID(si) );
368    x = static_cast<int>(bounds.origin.x);
369    y = static_cast<int>(bounds.origin.y);
370   
371    // osg::notify(osg::DEBUG_INFO) << "topleft of screen " << si.screenNum <<" " << bounds.origin.x << "/" << bounds.origin.y << std::endl;
[9879]372}
373
374
[10208]375bool DarwinWindowingSystemInterface::setScreenSettings(const osg::GraphicsContext::ScreenIdentifier &si, const osg::GraphicsContext::ScreenSettings & settings)
376{
377    bool result = setScreenResolutionImpl(si, settings.width, settings.height);
378    if (result)
[11207]379    {
[10208]380        setScreenRefreshRateImpl(si, settings.refreshRate);
[11207]381    }
382
[10208]383    return result;
384}
[9879]385
[10208]386
387
[9879]388/** implementation of setScreenResolution */
[10208]389bool DarwinWindowingSystemInterface::setScreenResolutionImpl(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, unsigned int width, unsigned int height)
[9879]390{
[11207]391    _init();
392
393    if (_displayCount==0)
394    {
395        return false;
396    }
397
[10622]398    CGDirectDisplayID displayid = getDisplayID(screenIdentifier);
[9895]399   
400    // add next line and on following line replace hard coded depth and refresh rate
[10622]401    CGRefreshRate refresh =  getDictDouble (CGDisplayCurrentMode(displayid), kCGDisplayRefreshRate); 
[9895]402    CFDictionaryRef display_mode_values =
403        CGDisplayBestModeForParametersAndRefreshRate(
[10622]404                        displayid,
405                        CGDisplayBitsPerPixel(displayid),
[9895]406                        width, height, 
407                        refresh, 
408                        NULL);
[9879]409
[9895]410                                     
[10622]411    CGDisplaySwitchToMode(displayid, display_mode_values);   
[9895]412    return true;
[9879]413}
414
415/** implementation of setScreenRefreshRate */
[11207]416bool DarwinWindowingSystemInterface::setScreenRefreshRateImpl(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, double refreshRate)
417{
418    _init();
419
420    if (_displayCount==0)
421    {
422        return false;
423    }
424
[9895]425    boolean_t  success(false);
426    unsigned width, height;
427    getScreenResolution(screenIdentifier, width, height);
428   
[10622]429    CGDirectDisplayID displayid = getDisplayID(screenIdentifier);
[9895]430   
431    // add next line and on following line replace hard coded depth and refresh rate
432    CFDictionaryRef display_mode_values =
433        CGDisplayBestModeForParametersAndRefreshRate(
[10622]434                        displayid,
435                        CGDisplayBitsPerPixel(displayid),
[9895]436                        width, height, 
437                        refreshRate, 
438                        &success);
[9879]439
[9895]440                                     
441    if (success)
[10622]442        CGDisplaySwitchToMode(displayid, display_mode_values);   
[9895]443       
444    return (success != 0);
[9879]445}
446
447
448unsigned int DarwinWindowingSystemInterface::getScreenContaining(int x, int y, int w, int h)
449{
[11207]450    _init();
451
452    if (_displayCount==0)
453    {
454        return 0;
455    }
456
[9895]457    CGRect rect = CGRectMake(x,y,w,h);
458    for(unsigned int i = 0; i < _displayCount; ++i) {
459        CGRect bounds = CGDisplayBounds( getDisplayID(i) );
460        if (CGRectIntersectsRect(bounds, rect)) {
461            return i;
462        }
463    }
464   
465    return 0;
[9879]466}
467
[9895]468}
Note: See TracBrowser for help on using the browser.