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

Revision 10208, 12.0 kB (checked in by robert, 5 years ago)

From Stephan Huber, "attached you'll find some bugfixes and enhancements for the Cocoa
implementation of GraoicsWindowCocoa?:

Enhancements/Bugfixes:

+ now it's possible to integrate osgViewer better into existing
cocoa-applications:
* create one or more NSOpenGLView(s) and add these to your window(s)
* create one or more NSWindows
* disable the integrated event-polling of osgViewer, and let the work be
done by Cocoa / NSApplicationRun. You'll have to run the osgViewer's
runloop in a separate thread

+ missing menu-event-handling implemented

+ added NSAutoReleasePools where necessary, this fixes some memory-leaks
+ fixed some crashes and thread-issues"

  • 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
15namespace osgDarwin {
16
17
18static inline CGRect toCGRect(NSRect nsRect)
19{
[9895]20    CGRect cgRect;
[9879]21
[9895]22    cgRect.origin.x = nsRect.origin.x;
23    cgRect.origin.y = nsRect.origin.y;
24    cgRect.size.width = nsRect.size.width;
25    cgRect.size.height = nsRect.size.height;
[9879]26
[9895]27    return cgRect;
[9879]28}
29
30
31MenubarController::MenubarController()
[9895]32:    osg::Referenced(),
[9879]33    _list(),
34    _menubarShown(false),
35    _mutex()
36{
[9895]37    // the following code will query the system for the available rect on the main-display (typically the displaying showing the menubar + the dock
[9879]38
[9895]39    NSRect rect = [[[NSScreen screens] objectAtIndex: 0] visibleFrame];
40    _availRect = toCGRect(rect);
41   
42    // now we need the rect of the main-display including the menubar and the dock
43    _mainScreenBounds = CGDisplayBounds( CGMainDisplayID() );
[9879]44
45
[9895]46    // NSRect 0/0 is bottom/left, _mainScreenBounds 0/0 is top/left
47    _availRect.origin.y = _mainScreenBounds.size.height - _availRect.size.height - _availRect.origin.y;
48   
49       
50    // hide the menubar initially
51    SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
[9879]52}
53
54
55
56
57MenubarController* MenubarController::instance()
58{
59    static osg::ref_ptr<MenubarController> s_menubar_controller = new MenubarController();
60    return s_menubar_controller.get();
61}
62
63
64void MenubarController::attachWindow(WindowAdapter* win)
65{
66    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
67    _list.push_back(win);
68    update();
69}
70
71
72void MenubarController::detachWindow(osgViewer::GraphicsWindow* win)
73{
74    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
75    for(WindowList::iterator i = _list.begin(); i != _list.end(); ) {
76        if ((*i)->getWindow() == win)
77            i = _list.erase(i);
78        else 
79            ++i;
80    }
81    update();
82}
83
84// iterate through all open windows and check, if they intersect the area occupied by the menubar/dock, and if so, hide the menubar/dock
85
86void MenubarController::update()
87{
88    OSErr error(noErr);
89    unsigned int windowsCoveringMenubarArea = 0;   
90    unsigned int windowsIntersectingMainScreen = 0;
91    for(WindowList::iterator i = _list.begin(); i != _list.end(); ) {
[9895]92        WindowAdapter* wi = (*i).get();
[9879]93        if (wi->valid()) {
94            CGRect windowBounds;
[9895]95            wi->getWindowBounds(windowBounds);
96           
97            if (CGRectIntersectsRect(_mainScreenBounds, windowBounds))
[9879]98            {
99                ++windowsIntersectingMainScreen;
100                // osg::notify(osg::ALWAYS) << "testing rect " << windowBounds.origin.x << "/" << windowBounds.origin.y << " " << windowBounds.size.width << "x" << windowBounds.size.height << std::endl;
[9895]101                // osg::notify(osg::ALWAYS) << "against      " << _availRect.origin.x << "/" << _availRect.origin.y << " " << _availRect.size.width << "x" << _availRect.size.height << std::endl;
[9879]102                // the window intersects the main-screen, does it intersect with the menubar/dock?
103                if (((_availRect.origin.y > _mainScreenBounds.origin.y) && (_availRect.origin.y > windowBounds.origin.y)) ||
104                    ((_availRect.origin.x > _mainScreenBounds.origin.x) && (_availRect.origin.x > windowBounds.origin.x)) ||
105                    ((_availRect.size.width < _mainScreenBounds.size.width) && (_availRect.origin.x + _availRect.size.width < windowBounds.origin.x + windowBounds.size.width)) ||
106                    ((_availRect.size.height < _mainScreenBounds.size.height) && (_availRect.origin.y + _availRect.size.height < windowBounds.origin.y + windowBounds.size.height) ))
107                {
108                    ++windowsCoveringMenubarArea;
109                }
110            }
111           
112            ++i;
113        }
114        else
115            i= _list.erase(i);
116    }
117   
118    // see http://developer.apple.com/technotes/tn2002/tn2062.html for hiding the dock+menubar
119       
120    if (windowsCoveringMenubarArea && _menubarShown)
121        error = SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
122   
123    if (!windowsCoveringMenubarArea && !_menubarShown)
124        error = SetSystemUIMode(kUIModeNormal, 0);
125        _menubarShown = !windowsCoveringMenubarArea;
126   
127    // osg::notify(osg::DEBUG_INFO) << "MenubarController:: " << windowsCoveringMenubarArea << " windows covering the menubar/dock area, " << windowsIntersectingMainScreen << " intersecting mainscreen" << std::endl;
128}
129
130
131
132/** Helper method to get a double value out of a CFDictionary */
133static double getDictDouble (CFDictionaryRef refDict, CFStringRef key)
134{
[9895]135    double value;
136    CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
137    if (!number_value) // if can't get a number for the dictionary
138        return -1;  // fail
139    if (!CFNumberGetValue(number_value, kCFNumberDoubleType, &value)) // or if cant convert it
140        return -1; // fail
141    return value; // otherwise return the long value
[9879]142}
143
144/** Helper method to get a long value out of a CFDictionary */
145static long getDictLong(CFDictionaryRef refDict, CFStringRef key)        // const void* key?
146{
[9895]147    long value = 0;
148    CFNumberRef number_value = (CFNumberRef)CFDictionaryGetValue(refDict, key);
149    if (!number_value) // if can't get a number for the dictionary
150        return -1;  // fail
151    if (!CFNumberGetValue(number_value, kCFNumberLongType, &value)) // or if cant convert it
152        return -1; // fail
153    return value;
[9879]154}
155
156
157
158/** ctor, get a list of all attached displays */
159DarwinWindowingSystemInterface::DarwinWindowingSystemInterface() :
[9895]160    _displayCount(0),
161    _displayIds(NULL)
[9879]162{
[9895]163    ProcessSerialNumber sn = { 0, kCurrentProcess };
164    TransformProcessType(&sn,kProcessTransformToForegroundApplication);
165    SetFrontProcess(&sn);
166   
167    if( CGGetActiveDisplayList( 0, NULL, &_displayCount ) != CGDisplayNoErr )
168        osg::notify(osg::WARN) << "DarwinWindowingSystemInterface: could not get # of screens" << std::endl;
169       
170    _displayIds = new CGDirectDisplayID[_displayCount];
171    if( CGGetActiveDisplayList( _displayCount, _displayIds, &_displayCount ) != CGDisplayNoErr )
172        osg::notify(osg::WARN) << "DarwinWindowingSystemInterface: CGGetActiveDisplayList failed" << std::endl;
173   
174    }
[9879]175
176/** dtor */
177DarwinWindowingSystemInterface::~DarwinWindowingSystemInterface()
178{
[9895]179    if (osg::Referenced::getDeleteHandler())
180    {
181        osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
182        osg::Referenced::getDeleteHandler()->flushAll();
183    }
[9879]184
[9895]185    if (_displayIds) delete[] _displayIds;
186    _displayIds = NULL;
[9879]187}
188
189/** @return a CGDirectDisplayID for a ScreenIdentifier */
190CGDirectDisplayID DarwinWindowingSystemInterface::getDisplayID(const osg::GraphicsContext::ScreenIdentifier& si) {
[9895]191    if (si.screenNum < static_cast<int>(_displayCount))
192        return _displayIds[si.screenNum];
193    else {
194        osg::notify(osg::WARN) << "GraphicsWindowCarbon :: invalid screen # " << si.screenNum << ", returning main-screen instead" << std::endl;
195        return _displayIds[0];
196    }
[9879]197}
198
199/** @return count of attached screens */
200unsigned int DarwinWindowingSystemInterface::getNumScreens(const osg::GraphicsContext::ScreenIdentifier& si)
201{
[9895]202    return _displayCount;
[9879]203}
204
205void DarwinWindowingSystemInterface::getScreenSettings(const osg::GraphicsContext::ScreenIdentifier& si, osg::GraphicsContext::ScreenSettings & resolution)
206{
[9895]207    CGDirectDisplayID id = getDisplayID(si);
208    resolution.width = CGDisplayPixelsWide(id);
209    resolution.height = CGDisplayPixelsHigh(id);
210    resolution.colorDepth = CGDisplayBitsPerPixel(id);
211    resolution.refreshRate = getDictDouble (CGDisplayCurrentMode(id), kCGDisplayRefreshRate);        // Not tested
212    if (resolution.refreshRate<0) resolution.refreshRate = 0;
[9879]213}
214
215
216void DarwinWindowingSystemInterface::enumerateScreenSettings(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, osg::GraphicsContext::ScreenSettingsList & resolutionList) {
217        // Warning! This method has not been tested.
218        resolutionList.clear();
219
220        CGDirectDisplayID displayID = getDisplayID(screenIdentifier);
221        CFArrayRef availableModes = CGDisplayAvailableModes(displayID);
222        unsigned int numberOfAvailableModes = CFArrayGetCount(availableModes);
223        for (unsigned int i=0; i<numberOfAvailableModes; ++i) {
224            // look at each mode in the available list
225            CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex(availableModes, i);
226            osg::GraphicsContext::ScreenSettings tmpSR;
227
228            long width = getDictLong(mode, kCGDisplayWidth);
229            tmpSR.width = width<=0 ? 0 : width;
230            long height = getDictLong(mode, kCGDisplayHeight);
231            tmpSR.height = height<=0 ? 0 : height;
232            long rate = getDictLong(mode, kCGDisplayRefreshRate);
233            tmpSR.refreshRate = rate<=0 ? 0 : rate;
234            long depth = getDictLong(mode, kCGDisplayBitsPerPixel);
235            tmpSR.colorDepth = depth<=0 ? 0 : depth;
236
237            resolutionList.push_back(tmpSR);
238        }
239    }
240
241/** return the top left coord of a specific screen in global screen space */
242void DarwinWindowingSystemInterface::getScreenTopLeft(const osg::GraphicsContext::ScreenIdentifier& si, int& x, int& y) {
[9895]243    CGRect bounds = CGDisplayBounds( getDisplayID(si) );
244    x = static_cast<int>(bounds.origin.x);
245    y = static_cast<int>(bounds.origin.y);
246   
247    // osg::notify(osg::DEBUG_INFO) << "topleft of screen " << si.screenNum <<" " << bounds.origin.x << "/" << bounds.origin.y << std::endl;
[9879]248}
249
250
[10208]251bool DarwinWindowingSystemInterface::setScreenSettings(const osg::GraphicsContext::ScreenIdentifier &si, const osg::GraphicsContext::ScreenSettings & settings)
252{
253    bool result = setScreenResolutionImpl(si, settings.width, settings.height);
254    if (result)
255        setScreenRefreshRateImpl(si, settings.refreshRate);
256   
257    return result;
258}
[9879]259
[10208]260
261
[9879]262/** implementation of setScreenResolution */
[10208]263bool DarwinWindowingSystemInterface::setScreenResolutionImpl(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, unsigned int width, unsigned int height)
[9879]264{
[9895]265    CGDirectDisplayID displayID = getDisplayID(screenIdentifier);
266   
267    // add next line and on following line replace hard coded depth and refresh rate
268    CGRefreshRate refresh =  getDictDouble (CGDisplayCurrentMode(displayID), kCGDisplayRefreshRate); 
269    CFDictionaryRef display_mode_values =
270        CGDisplayBestModeForParametersAndRefreshRate(
271                        displayID,
272                        CGDisplayBitsPerPixel(displayID),
273                        width, height, 
274                        refresh, 
275                        NULL);
[9879]276
[9895]277                                     
278    CGDisplaySwitchToMode(displayID, display_mode_values);   
279    return true;
[9879]280}
281
282/** implementation of setScreenRefreshRate */
[10208]283bool DarwinWindowingSystemInterface::setScreenRefreshRateImpl(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, double refreshRate) {
[9895]284   
285    boolean_t  success(false);
286    unsigned width, height;
287    getScreenResolution(screenIdentifier, width, height);
288   
289    CGDirectDisplayID displayID = getDisplayID(screenIdentifier);
290   
291    // add next line and on following line replace hard coded depth and refresh rate
292    CFDictionaryRef display_mode_values =
293        CGDisplayBestModeForParametersAndRefreshRate(
294                        displayID,
295                        CGDisplayBitsPerPixel(displayID),
296                        width, height, 
297                        refreshRate, 
298                        &success);
[9879]299
[9895]300                                     
301    if (success)
302        CGDisplaySwitchToMode(displayID, display_mode_values);   
303       
304    return (success != 0);
[9879]305}
306
307
308unsigned int DarwinWindowingSystemInterface::getScreenContaining(int x, int y, int w, int h)
309{
[9895]310    CGRect rect = CGRectMake(x,y,w,h);
311    for(unsigned int i = 0; i < _displayCount; ++i) {
312        CGRect bounds = CGDisplayBounds( getDisplayID(i) );
313        if (CGRectIntersectsRect(bounds, rect)) {
314            return i;
315        }
316    }
317   
318    return 0;
[9879]319}
320
321
322
323
324
325
[9895]326}
Note: See TracBrowser for help on using the browser.