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

Revision 10417, 13.7 kB (checked in by robert, 5 years ago)

From Stephan Huber, fixes to Cocoa support

  • 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
19-(void) show: (ID) data;
20-(void) hide: (ID) data;
21
22@end
23
24@implementation MenubarToggler
25
26
27
28-(void) hide:(ID) data 
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
37-(void) show:(ID) data 
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() :
[9895]224    _displayCount(0),
225    _displayIds(NULL)
[9879]226{
[9895]227    ProcessSerialNumber sn = { 0, kCurrentProcess };
228    TransformProcessType(&sn,kProcessTransformToForegroundApplication);
229    SetFrontProcess(&sn);
230   
231    if( CGGetActiveDisplayList( 0, NULL, &_displayCount ) != CGDisplayNoErr )
232        osg::notify(osg::WARN) << "DarwinWindowingSystemInterface: could not get # of screens" << std::endl;
233       
234    _displayIds = new CGDirectDisplayID[_displayCount];
235    if( CGGetActiveDisplayList( _displayCount, _displayIds, &_displayCount ) != CGDisplayNoErr )
236        osg::notify(osg::WARN) << "DarwinWindowingSystemInterface: CGGetActiveDisplayList failed" << std::endl;
237   
238    }
[9879]239
240/** dtor */
241DarwinWindowingSystemInterface::~DarwinWindowingSystemInterface()
242{
[9895]243    if (osg::Referenced::getDeleteHandler())
244    {
245        osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
246        osg::Referenced::getDeleteHandler()->flushAll();
247    }
[9879]248
[9895]249    if (_displayIds) delete[] _displayIds;
250    _displayIds = NULL;
[9879]251}
252
253/** @return a CGDirectDisplayID for a ScreenIdentifier */
254CGDirectDisplayID DarwinWindowingSystemInterface::getDisplayID(const osg::GraphicsContext::ScreenIdentifier& si) {
[9895]255    if (si.screenNum < static_cast<int>(_displayCount))
256        return _displayIds[si.screenNum];
257    else {
258        osg::notify(osg::WARN) << "GraphicsWindowCarbon :: invalid screen # " << si.screenNum << ", returning main-screen instead" << std::endl;
259        return _displayIds[0];
260    }
[9879]261}
262
263/** @return count of attached screens */
264unsigned int DarwinWindowingSystemInterface::getNumScreens(const osg::GraphicsContext::ScreenIdentifier& si)
265{
[9895]266    return _displayCount;
[9879]267}
268
269void DarwinWindowingSystemInterface::getScreenSettings(const osg::GraphicsContext::ScreenIdentifier& si, osg::GraphicsContext::ScreenSettings & resolution)
270{
[9895]271    CGDirectDisplayID id = getDisplayID(si);
272    resolution.width = CGDisplayPixelsWide(id);
273    resolution.height = CGDisplayPixelsHigh(id);
274    resolution.colorDepth = CGDisplayBitsPerPixel(id);
275    resolution.refreshRate = getDictDouble (CGDisplayCurrentMode(id), kCGDisplayRefreshRate);        // Not tested
276    if (resolution.refreshRate<0) resolution.refreshRate = 0;
[9879]277}
278
279
280void DarwinWindowingSystemInterface::enumerateScreenSettings(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, osg::GraphicsContext::ScreenSettingsList & resolutionList) {
281        // Warning! This method has not been tested.
282        resolutionList.clear();
283
284        CGDirectDisplayID displayID = getDisplayID(screenIdentifier);
285        CFArrayRef availableModes = CGDisplayAvailableModes(displayID);
286        unsigned int numberOfAvailableModes = CFArrayGetCount(availableModes);
287        for (unsigned int i=0; i<numberOfAvailableModes; ++i) {
288            // look at each mode in the available list
289            CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex(availableModes, i);
290            osg::GraphicsContext::ScreenSettings tmpSR;
291
292            long width = getDictLong(mode, kCGDisplayWidth);
293            tmpSR.width = width<=0 ? 0 : width;
294            long height = getDictLong(mode, kCGDisplayHeight);
295            tmpSR.height = height<=0 ? 0 : height;
296            long rate = getDictLong(mode, kCGDisplayRefreshRate);
297            tmpSR.refreshRate = rate<=0 ? 0 : rate;
298            long depth = getDictLong(mode, kCGDisplayBitsPerPixel);
299            tmpSR.colorDepth = depth<=0 ? 0 : depth;
300
301            resolutionList.push_back(tmpSR);
302        }
303    }
304
305/** return the top left coord of a specific screen in global screen space */
306void DarwinWindowingSystemInterface::getScreenTopLeft(const osg::GraphicsContext::ScreenIdentifier& si, int& x, int& y) {
[9895]307    CGRect bounds = CGDisplayBounds( getDisplayID(si) );
308    x = static_cast<int>(bounds.origin.x);
309    y = static_cast<int>(bounds.origin.y);
310   
311    // osg::notify(osg::DEBUG_INFO) << "topleft of screen " << si.screenNum <<" " << bounds.origin.x << "/" << bounds.origin.y << std::endl;
[9879]312}
313
314
[10208]315bool DarwinWindowingSystemInterface::setScreenSettings(const osg::GraphicsContext::ScreenIdentifier &si, const osg::GraphicsContext::ScreenSettings & settings)
316{
317    bool result = setScreenResolutionImpl(si, settings.width, settings.height);
318    if (result)
319        setScreenRefreshRateImpl(si, settings.refreshRate);
320   
321    return result;
322}
[9879]323
[10208]324
325
[9879]326/** implementation of setScreenResolution */
[10208]327bool DarwinWindowingSystemInterface::setScreenResolutionImpl(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, unsigned int width, unsigned int height)
[9879]328{
[9895]329    CGDirectDisplayID displayID = getDisplayID(screenIdentifier);
330   
331    // add next line and on following line replace hard coded depth and refresh rate
332    CGRefreshRate refresh =  getDictDouble (CGDisplayCurrentMode(displayID), kCGDisplayRefreshRate); 
333    CFDictionaryRef display_mode_values =
334        CGDisplayBestModeForParametersAndRefreshRate(
335                        displayID,
336                        CGDisplayBitsPerPixel(displayID),
337                        width, height, 
338                        refresh, 
339                        NULL);
[9879]340
[9895]341                                     
342    CGDisplaySwitchToMode(displayID, display_mode_values);   
343    return true;
[9879]344}
345
346/** implementation of setScreenRefreshRate */
[10208]347bool DarwinWindowingSystemInterface::setScreenRefreshRateImpl(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, double refreshRate) {
[9895]348   
349    boolean_t  success(false);
350    unsigned width, height;
351    getScreenResolution(screenIdentifier, width, height);
352   
353    CGDirectDisplayID displayID = getDisplayID(screenIdentifier);
354   
355    // add next line and on following line replace hard coded depth and refresh rate
356    CFDictionaryRef display_mode_values =
357        CGDisplayBestModeForParametersAndRefreshRate(
358                        displayID,
359                        CGDisplayBitsPerPixel(displayID),
360                        width, height, 
361                        refreshRate, 
362                        &success);
[9879]363
[9895]364                                     
365    if (success)
366        CGDisplaySwitchToMode(displayID, display_mode_values);   
367       
368    return (success != 0);
[9879]369}
370
371
372unsigned int DarwinWindowingSystemInterface::getScreenContaining(int x, int y, int w, int h)
373{
[9895]374    CGRect rect = CGRectMake(x,y,w,h);
375    for(unsigned int i = 0; i < _displayCount; ++i) {
376        CGRect bounds = CGDisplayBounds( getDisplayID(i) );
377        if (CGRectIntersectsRect(bounds, rect)) {
378            return i;
379        }
380    }
381   
382    return 0;
[9879]383}
384
385
386
387
388
389
[9895]390}
Note: See TracBrowser for help on using the browser.