Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
GLUTView.m
/* Copyright (c) Dietmar Planitzer, 1998, 2002 - 2003 */ |
/* This program is freely distributable without licensing fees |
and is provided without guarantee or warrantee expressed or |
implied. This program is -not- in the public domain. */ |
#import "GLUTView.h" |
#import "GLUTWindow.h" |
#import "GLUTMenu.h" |
@interface GLUTView(GLUTPrivate) |
- (void)_commonReshape; |
- (void)_updateTrackingRects: (NSNotification *)notification; |
- (NSCursor *)_inheritedNativeCursor; |
- (void)_recursiveInvalidateCursorRectsWithWindow: (GLUTWindow *)aWindow; |
- (void)_recursiveCopyPixelsTo: (NSBitmapImageRep *)bitmap sourceRect: (NSRect)srcRect baseView: (NSView *)bView; |
- (void)_recursiveMarkHidden; |
- (void)_updateComputedVisibility; |
- (void)evaluateVisibility; |
- (NSArray *)_orderedSiblings; |
@end |
static GLUTView * __glutVisibilityUpdateList = NULL; |
static GLUTView * __glutVisibilityUpdateTail = NULL; |
#pragma mark - |
@implementation GLUTView |
/* Designated initializer */ |
- (id)initWithFrame: (NSRect)frameRect pixelFormat: (NSOpenGLPixelFormat *)pixelFormat |
windowID: (int)winid treatAsSingle: (BOOL)treatAsSingle isSubwindow: (BOOL)isSub |
fullscreenStereo: (BOOL)pfStereo isVBLSynced: (BOOL)isVBLSync |
{ |
if((self = [super initWithFrame: frameRect]) != nil) { |
_openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; |
if(!_openGLContext) { |
[self release]; |
return nil; |
} |
[self setAutoresizingMask: (NSViewHeightSizable | NSViewWidthSizable)]; |
[self setAutoresizesSubviews: NO]; |
[self setPostsBoundsChangedNotifications: NO]; |
[self setPostsFrameChangedNotifications: NO]; |
[self allocateGState]; // make -lockFocus 2x faster... |
/* This list contains ALL subviews of a GLUTView including |
hidden views - which are NOT part of -subviews. This is |
necessary so that we can re-insert a hidden view at the |
correct place in the sibling list. I.e. the view just |
below us might have been killed while we were hidden. |
We track such changes via this list and are thus able to |
re-display the hidden view in the correct position once |
glutShowWindow is called on the hidden view again. */ |
__glutInitList(&_allChildrens); |
_siblings.obj = self; |
_cursorID = GLUT_CURSOR_INHERIT; |
_visState = GLUT_UNKNOWN_VISIBILITY; |
_flags.forceReshape = YES; |
_flags.isVisibilityUpdateAllowed = [NSApp isRunning]; |
_flags.isSubwindow = isSub; |
_flags.isShown = NO; |
_flags.treatAsSingle = treatAsSingle; |
_winid = winid; |
_displayFunc = __glutDefaultDisplay; |
_reshapeFunc = __glutDefaultReshape; |
_wmCloseFunc = __glutDefaultWMClose; |
_quadObj = NULL; |
_newVisState = GLUT_UNKNOWN_VISIBILITY; |
_eventMask = 0; |
_curEventMask = 0; |
_isFullscreenStereo = pfStereo; |
if (isVBLSync) |
_isVBLSync = 1; |
else |
_isVBLSync = 0; |
[[self openGLContext] makeCurrentContext]; |
if(_flags.treatAsSingle) { |
/* We do this because either the window really is single |
buffered (in which case this is redundant, but harmless, |
because this is the initial single-buffered context |
state); or we are treating a double buffered window as a |
single-buffered window because the system does not appear |
to export any suitable single- buffered visuals (in which |
the following are necessary). */ |
glDrawBuffer(GL_FRONT); |
glReadBuffer(GL_FRONT); |
} |
[[self openGLContext] setValues:&_isVBLSync forParameter:NSOpenGLCPSwapInterval]; |
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_surfaceNeedsUpdate:) name:NSViewGlobalFrameDidChangeNotification object:self]; |
return self; |
} |
return nil; |
} |
- (void)dealloc |
{ |
int i, winid = [self windowID]; |
GLUTNode * curChild = _allChildrens.head.succ; |
/* Recursively destroy any children. */ |
while(curChild) { |
GLUTNode * tmp = curChild->succ; |
[(id)(curChild->obj) release]; |
curChild = tmp; |
} |
/* Unbind if bound to this window. */ |
if(self == __glutCurrentView) { |
UNMAKE_CURRENT(); |
__glutCurrentView = nil; |
} |
for(i = 0; i < GLUT_MAX_MENUS; i++) |
[_menu[i] release]; |
[self releaseGState]; |
[_nativeCursor release]; |
if(_trackingRectTag) |
[self removeTrackingRect: _trackingRectTag]; |
if(_quadObj) |
gluDeleteQuadric(_quadObj); |
/* NULLing the __glutWindowList helps detect is a window |
instance has been destroyed, given a window number. */ |
__glutViewList[winid - 1] = NULL; |
/* Cleanup data structures that might contain window. */ |
__glutPurgeWorkEvents(winid); |
if(self == __glutGameModeWindow) { |
/* Destroying the game mode window should implicitly |
have GLUT leave game mode. */ |
__glutGameModeWindow = nil; |
__glutDestoryingGameMode = false; |
[[self openGLContext] clearDrawable]; |
__glutCloseDownGameMode(); |
} |
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewGlobalFrameDidChangeNotification object:self]; |
// If our context is current, clear it so no one can do anything bad any more. |
if([NSOpenGLContext currentContext] == _openGLContext) |
[NSOpenGLContext clearCurrentContext]; |
[_openGLContext release]; |
[super dealloc]; |
} |
- (void)finalize |
{ |
int i, winid = [self windowID]; |
GLUTNode * curChild = _allChildrens.head.succ; |
/* Recursively destroy any children. */ |
while(curChild) { |
GLUTNode * tmp = curChild->succ; |
[(id)(curChild->obj) release]; |
curChild = tmp; |
} |
/* Unbind if bound to this window. */ |
if(self == __glutCurrentView) { |
UNMAKE_CURRENT(); |
__glutCurrentView = nil; |
} |
for(i = 0; i < GLUT_MAX_MENUS; i++) |
[_menu[i] release]; |
[self releaseGState]; |
if(_trackingRectTag) |
[self removeTrackingRect: _trackingRectTag]; |
if(_quadObj) |
gluDeleteQuadric(_quadObj); |
/* NULLing the __glutWindowList helps detect is a window |
instance has been destroyed, given a window number. */ |
__glutViewList[winid - 1] = NULL; |
/* Cleanup data structures that might contain window. */ |
__glutPurgeWorkEvents(winid); |
if(self == __glutGameModeWindow) { |
/* Destroying the game mode window should implicitly |
have GLUT leave game mode. */ |
__glutGameModeWindow = nil; |
__glutDestoryingGameMode = false; |
[[self openGLContext] clearDrawable]; |
__glutCloseDownGameMode(); |
} |
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSViewGlobalFrameDidChangeNotification object:self]; |
if([NSOpenGLContext currentContext] == _openGLContext) |
[NSOpenGLContext clearCurrentContext]; |
[super finalize]; |
} |
///////////////////////////////////////////// |
#pragma mark - |
#pragma mark NSView overrides |
#pragma mark - |
- (BOOL)isOpaque |
{ |
return YES; |
} |
- (void)lockFocus |
{ |
NSOpenGLContext* context = _openGLContext; |
// make sure we are ready to draw |
// |
[super lockFocus]; |
// when we are about to draw, make sure we are linked to the view and |
// |
if (!_isFullscreenStereo && ([context view] != self)) { |
[context setView:self]; |
} |
// make us the current OpenGL context |
// |
[context makeCurrentContext]; |
} |
- (void)makeCurrentGLUTView |
{ |
NSOpenGLContext* context = _openGLContext; |
if(_isFullscreenStereo && (__glutGameModeWindow == self) && !_inFullScreen) |
{ |
[context setFullScreen]; |
_inFullScreen = YES; |
} |
[context makeCurrentContext]; |
} |
- (void)resignCurrentGLUTView |
{ |
NSOpenGLContext* context = _openGLContext; |
if(_isFullscreenStereo && (__glutGameModeWindow != self) && _inFullScreen) |
{ |
[context clearDrawable]; |
_inFullScreen = NO; |
} |
[NSOpenGLContext clearCurrentContext]; |
} |
- (NSOpenGLContext *)openGLContext |
{ |
return _openGLContext; |
} |
- (void)update |
{ |
if ([_openGLContext view] == self) { |
[_openGLContext update]; |
} |
} |
- (void) _surfaceNeedsUpdate:(NSNotification*)notification |
{ |
[self update]; |
} |
///////////////////////////////////////////// |
#pragma mark - |
#pragma mark Accessors |
#pragma mark - |
- (NSPoint)windowPosition |
{ |
NSPoint pt; |
GLUTWindow * window = (GLUTWindow *) [self window]; |
if(!_flags.isSubwindow) { |
unsigned int mask = [window styleMask]; |
NSRect rect = [NSWindow contentRectForFrameRect: [window frame] styleMask: mask]; |
pt = rect.origin; |
pt.y = __glutScreenHeight - (pt.y + rect.size.height); |
} else { |
pt = [self convertPoint: [self bounds].origin toView: nil]; |
pt = [window convertBaseToScreen: pt]; |
} |
return pt; |
} |
- (BOOL)isSubwindow |
{ |
return _flags.isSubwindow; |
} |
/* Return window size in local coordinates. */ |
- (NSSize)windowSize |
{ |
return [self bounds].size; |
} |
- (int)visibilityState |
{ |
return _visState; |
} |
- (BOOL)isDamaged |
{ |
return _flags.isDamaged; |
} |
- (BOOL)isShown |
{ |
return _flags.isShown; |
} |
- (void)setShown: (BOOL)flag |
{ |
_flags.isShown = flag; |
} |
- (BOOL)isTreatAsSingle |
{ |
return _flags.treatAsSingle; |
} |
- (int)windowID |
{ |
return _winid; |
} |
- (int)parentWindowID |
{ |
if(_flags.isSubwindow) { |
GLUTView * parentWindow = (GLUTView *)[self superview]; |
if(parentWindow == nil) |
parentWindow = _savedSuperview; |
return [parentWindow windowID]; |
} |
return 0; |
} |
/* Return the real number of childrens including hidden ones */ |
- (unsigned)numberOfChildrens |
{ |
GLUTNode * tail = &_allChildrens.tail; |
GLUTNode * node = _allChildrens.head.succ; |
unsigned i = 0; |
while(node != tail) { |
i++; |
node = node->succ; |
} |
return i; |
} |
- (BOOL)ignoreKeyRepeats |
{ |
return _flags.ignoreKeyRepeats; |
} |
- (void)setIgnoreKeyRepeats: (BOOL)yesno |
{ |
_flags.ignoreKeyRepeats = yesno; |
} |
- (NSTimeInterval)joystickPollInterval |
{ |
return _pollInterval; |
} |
- (int)eventMask |
{ |
return _eventMask; |
} |
- (void)setEventMask: (int)mask |
{ |
_eventMask = mask; |
} |
- (BOOL)isFullscreenStereo |
{ |
return _isFullscreenStereo; |
} |
- (BOOL)isVBLSync |
{ |
return _isVBLSync; |
} |
///////////////////////////////////////////// |
#pragma mark - |
#pragma mark Callbacks |
#pragma mark - |
- (void)setPassiveMotionCallback: (GLUTmotionCB)func { _passiveMotionFunc = func; } |
- (void)setEntryCallback: (GLUTentryCB)func { _entryFunc = func; } |
- (void)setKeyDownCallback: (GLUTkeyboardCB)func { _keyDownFunc = func; } |
- (void)setKeyUpCallback: (GLUTkeyboardCB)func { _keyUpFunc = func; } |
- (void)setMouseCallback: (GLUTmouseCB)func { _mouseFunc = func; } |
- (void)setMotionCallback: (GLUTpassiveCB)func { _motionFunc = func; } |
- (void)setSpecialDownCallback: (GLUTspecialCB)func { _specialFunc = func; } |
- (void)setSpecialUpCallback: (GLUTspecialCB)func { _specialUpFunc = func; } |
- (void)setDisplayCallback: (GLUTdisplayCB)func |
{ |
if(!func) { |
__glutFatalError("NULL display callback not allowed in GLUT 3.0; update your code."); |
} |
_displayFunc = func; |
} |
- (void)setReshapeCallback: (GLUTreshapeCB)func |
{ |
_reshapeFunc = (func) ? func : __glutDefaultReshape; |
} |
- (void)setWindowStatusCallback: (GLUTwindowStatusCB)func |
{ |
_windowStatusFunc = func; |
if(!_windowStatusFunc) { |
/* Make state invalid. */ |
_visState = GLUT_UNKNOWN_VISIBILITY; |
} |
} |
- (void)setSpaceballMotionCallback: (GLUTspaceMotionCB)func |
{ |
_spaceballMotionFunc = func; |
if (_spaceballMotionFunc) |
if (!_spaceballTimer) |
_spaceballTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 |
target: self |
selector: @selector(processSpaceball:) |
userInfo: 0 |
repeats: YES]; |
else |
[_spaceballTimer setFireDate: [NSDate dateWithTimeIntervalSinceNow:0.01]]; |
else if (!_spaceballMotionFunc && !_spaceballRotateFunc && !_spaceballButtonFunc) { // if no spaceball functions turn off timer |
[_spaceballTimer invalidate]; |
_spaceballTimer = nil; |
} |
} |
- (void)setSpaceballRotateCallback: (GLUTspaceRotateCB)func |
{ |
_spaceballRotateFunc = func; |
if (_spaceballRotateFunc) |
if (!_spaceballTimer) |
_spaceballTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 |
target: self |
selector: @selector(processSpaceball:) |
userInfo: 0 |
repeats: YES]; |
else |
[_spaceballTimer setFireDate: [NSDate dateWithTimeIntervalSinceNow:0.01]]; |
else if (!_spaceballMotionFunc && !_spaceballRotateFunc && !_spaceballButtonFunc) { // if no spaceball functions turn off timer |
[_spaceballTimer invalidate]; |
_spaceballTimer = nil; |
} |
} |
- (void)setSpaceballButtonCallback: (GLUTspaceButtonCB)func |
{ |
_spaceballButtonFunc = func; |
if (_spaceballButtonFunc) |
if (!_spaceballTimer) |
_spaceballTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 |
target: self |
selector: @selector(processSpaceball:) |
userInfo: 0 |
repeats: YES]; |
else |
[_joyTimer setFireDate: [NSDate dateWithTimeIntervalSinceNow:0.01]]; |
else if (!_spaceballMotionFunc && !_spaceballRotateFunc && !_spaceballButtonFunc) { // if no spaceball functions turn off timer |
[_spaceballTimer invalidate]; |
_spaceballTimer = nil; |
} |
} |
- (void)setButtonBoxCallback: (GLUTbuttonBoxCB)func { _buttonBoxFunc = func; } |
- (void)setDialCallback: (GLUTdialsCB)func { _dialFunc = func; } |
- (void)setTabletMotionCallback: (GLUTtabletMotionCB)func { _tabletMotionFunc = func; } |
- (void)setTabletButtonCallback: (GLUTtabletButtonCB)func { _tabletButtonFunc = func; } |
- (void)setJoystickCallback: (GLUTjoystickCB)func pollInterval: (NSTimeInterval)delay |
{ |
_joystickFunc = func; // set joystick function always |
if ((delay > 0) && _joystickFunc) |
_pollInterval = delay; |
else |
_pollInterval = 0; |
if (_pollInterval) { |
if (!_joyTimer) |
_joyTimer = [NSTimer scheduledTimerWithTimeInterval: delay |
target: self |
selector: @selector(processJoystick:) |
userInfo: 0 |
repeats: YES]; |
else |
[_joyTimer setFireDate: [NSDate dateWithTimeIntervalSinceNow:_pollInterval]]; |
} else { |
[_joyTimer invalidate]; |
_joyTimer = nil; |
} |
} |
- (void)setVisibilityCallback: (GLUTvisibilityCB)func { _visibilityFunc = func; } |
- (GLUTvisibilityCB)visibilityCallback { return _visibilityFunc; } |
- (void)setWMCloseCallback: (GLUTwmcloseCB)func |
{ |
/* WM close func is only relevant for top-level windows on MacOS X */ |
if(!_flags.isSubwindow) { |
_wmCloseFunc = func; |
/* Enabled/disable window close button */ |
if(_wmCloseFunc == __glutDefaultWMClose) |
[[[self window] standardWindowButton: NSWindowCloseButton] setEnabled: NO]; |
else |
[[[self window] standardWindowButton: NSWindowCloseButton] setEnabled: YES]; |
} |
} |
- (GLUTwmcloseCB)wmCloseCallback { return _wmCloseFunc; } |
- (void)setFortranCallback: (int)which callback: (void *)func |
{ |
switch (which) { |
case GLUT_FCB_DISPLAY: |
_fdisplayFunc = (GLUTdisplayFCB) func; |
break; |
case GLUT_FCB_WMCLOSE: |
_fwmcloseFunc = (GLUTwmcloseFCB) func; |
break; |
case GLUT_FCB_RESHAPE: |
_freshapeFunc = (GLUTreshapeFCB) func; |
break; |
case GLUT_FCB_MOUSE: |
_fmouseFunc = (GLUTmouseFCB) func; |
break; |
case GLUT_FCB_MOTION: |
_fmotionFunc = (GLUTmotionFCB) func; |
break; |
case GLUT_FCB_PASSIVE: |
_fpassiveMotionFunc = (GLUTpassiveFCB) func; |
break; |
case GLUT_FCB_ENTRY: |
_fentryFunc = (GLUTentryFCB) func; |
break; |
case GLUT_FCB_KEYBOARD: |
_fkeyDownFunc = (GLUTkeyboardFCB) func; |
break; |
case GLUT_FCB_KEYBOARD_UP: |
_fkeyUpFunc = (GLUTkeyboardFCB) func; |
break; |
case GLUT_FCB_WINDOW_STATUS: |
_fwindowStatusFunc = (GLUTwindowStatusFCB) func; |
break; |
case GLUT_FCB_VISIBILITY: |
_fvisibilityFunc = (GLUTvisibilityFCB) func; |
break; |
case GLUT_FCB_SPECIAL: |
_fspecialFunc = (GLUTspecialFCB) func; |
break; |
case GLUT_FCB_SPECIAL_UP: |
_fspecialUpFunc = (GLUTspecialFCB) func; |
break; |
case GLUT_FCB_BUTTON_BOX: |
_fbuttonBoxFunc = (GLUTbuttonBoxFCB) func; |
break; |
case GLUT_FCB_DIALS: |
_fdialFunc = (GLUTdialsFCB) func; |
break; |
case GLUT_FCB_SPACE_MOTION: |
_fspaceballMotionFunc = (GLUTspaceMotionFCB) func; |
break; |
case GLUT_FCB_SPACE_ROTATE: |
_fspaceballRotateFunc = (GLUTspaceRotateFCB) func; |
break; |
case GLUT_FCB_SPACE_BUTTON: |
_fspaceballButtonFunc = (GLUTspaceButtonFCB) func; |
break; |
case GLUT_FCB_TABLET_MOTION: |
_ftabletMotionFunc = (GLUTtabletMotionFCB) func; |
break; |
case GLUT_FCB_TABLET_BUTTON: |
_ftabletButtonFunc = (GLUTtabletButtonFCB) func; |
break; |
case GLUT_FCB_JOYSTICK: |
_fjoystickFunc = (GLUTjoystickFCB) func; |
break; |
} |
} |
- (void *)getFortranCallback: (int)which; |
{ |
switch (which) { |
case GLUT_FCB_DISPLAY: |
return (void*) _fdisplayFunc; |
break; |
case GLUT_FCB_WMCLOSE: |
return (void*) _fwmcloseFunc; |
break; |
case GLUT_FCB_RESHAPE: |
return (void*) _freshapeFunc; |
break; |
case GLUT_FCB_MOUSE: |
return (void*) _fmouseFunc; |
break; |
case GLUT_FCB_MOTION: |
return (void*) _fmotionFunc; |
break; |
case GLUT_FCB_PASSIVE: |
return (void*) _fpassiveMotionFunc; |
break; |
case GLUT_FCB_ENTRY: |
return (void*) _fentryFunc; |
break; |
case GLUT_FCB_KEYBOARD: |
return (void*) _fkeyDownFunc; |
break; |
case GLUT_FCB_KEYBOARD_UP: |
return (void*) _fkeyUpFunc; |
break; |
case GLUT_FCB_WINDOW_STATUS: |
return (void*) _fwindowStatusFunc; |
break; |
case GLUT_FCB_VISIBILITY: |
return (void*) _fvisibilityFunc; |
break; |
case GLUT_FCB_SPECIAL: |
return (void*) _fspecialFunc; |
break; |
case GLUT_FCB_SPECIAL_UP: |
return (void*) _fspecialUpFunc; |
break; |
case GLUT_FCB_BUTTON_BOX: |
return (void*) _fbuttonBoxFunc; |
break; |
case GLUT_FCB_DIALS: |
return (void*) _fdialFunc; |
break; |
case GLUT_FCB_SPACE_MOTION: |
return (void*) _fspaceballMotionFunc; |
break; |
case GLUT_FCB_SPACE_ROTATE: |
return (void*) _fspaceballRotateFunc; |
break; |
case GLUT_FCB_SPACE_BUTTON: |
return (void*) _fspaceballButtonFunc; |
break; |
case GLUT_FCB_TABLET_MOTION: |
return (void*) _ftabletMotionFunc; |
break; |
case GLUT_FCB_TABLET_BUTTON: |
return (void*) _ftabletButtonFunc; |
break; |
case GLUT_FCB_JOYSTICK: |
return (void*) _fjoystickFunc; |
break; |
default: |
return nil; |
break; |
} |
} |
///////////////////////////////////////////// |
#pragma mark - |
#pragma mark Cursor |
#pragma mark - |
- (int)cursor |
{ |
return _cursorID; |
} |
- (NSCursor *)_inheritedNativeCursor |
{ |
if(!_nativeCursor) { |
if(_flags.isSubwindow) |
return [(GLUTView *)[self superview] _inheritedNativeCursor]; |
else |
return [NSCursor arrowCursor]; |
} |
return _nativeCursor; |
} |
- (void)resetCursorRects |
{ |
if(!_nativeCursor) { |
if(_cursorID == GLUT_CURSOR_INHERIT) { |
_nativeCursor = [[self _inheritedNativeCursor] retain]; |
} else { |
_nativeCursor = [__glutGetNativeCursor(_cursorID) retain]; |
} |
} |
[self addCursorRect: [self visibleRect] cursor: _nativeCursor]; |
} |
- (void)_recursiveInvalidateCursorRectsWithWindow: (GLUTWindow *)aWindow |
{ |
NSArray * childrens = [self subviews]; |
unsigned int i, count = [childrens count]; |
for(i = 0; i < count; i++) { |
GLUTView * view = (GLUTView *)[childrens objectAtIndex: i]; |
if(view->_cursorID == GLUT_CURSOR_INHERIT) { |
[view->_nativeCursor release]; |
view->_nativeCursor = nil; |
[aWindow invalidateCursorRectsForView: view]; |
[view _recursiveInvalidateCursorRectsWithWindow: aWindow]; |
} |
} |
} |
- (void)setCursor: (int)cursor |
{ |
if(_cursorID != cursor) { |
GLUTWindow * window = (GLUTWindow *) [self window]; |
[_nativeCursor release]; |
_nativeCursor = nil; |
_cursorID = cursor; |
[window invalidateCursorRectsForView: self]; |
[self _recursiveInvalidateCursorRectsWithWindow: window]; |
[window performSelector:@selector(resetCursorRects) withObject:nil afterDelay:0.0]; |
// need the above to work around some AppKit issue with setting cursors while inside cursor rects |
// [[self window] resetCursorRects]; |
} |
} |
///////////////////////////////////////////// |
#pragma mark - |
#pragma mark Work Events |
#pragma mark - |
/** |
* --- Temp fix for current sub-windowing scheme, full logic change for post Panther --- |
* |
* We need to clear and re-establish the surface of each (sub-)view which acts |
* as a GLUT sub-window, because sub-windows (ONLY those) which are moved from |
* one top-level window to another one fail to draw once they are re-inserted |
* into the new window and told to draw themselves. |
*/ |
- (void)_recursiveKickSurface |
{ |
NSArray * subviews = [self subviews]; |
int i, count = [subviews count]; |
[[self openGLContext] clearDrawable]; |
[[self openGLContext] setView: self]; |
for(i = 0; i < count; i++) |
[(GLUTView *)[subviews objectAtIndex: i] _recursiveKickSurface]; |
} |
static GLUTWindow *__glutSwitchWindowFullscreenMode(GLUTWindow *window, NSRect frame, BOOL mode) |
{ |
GLUTWindow * othWindow = nil; |
GLUTView * view = (GLUTView *)[window contentView]; |
NSDictionary * args = nil; |
int op = kGLUTMorphOperationFullscreen; |
if(!mode) { |
args = [NSDictionary dictionaryWithObject: [NSValue valueWithRect: frame] |
forKey: GLUTWindowFrame]; |
op = kGLUTMorphOperationRegular; |
} |
UNMAKE_CURRENT(); |
othWindow = [[GLUTWindow windowByMorphingWindow: window |
operation: op |
arguments: args] retain]; |
[view _recursiveKickSurface]; |
if([window isVisible]) |
[othWindow makeKeyAndOrderFront: nil]; |
MAKE_CURRENT_WINDOW(view); |
[window setReleasedWhenClosed: YES]; |
[window close]; |
return othWindow; |
} |
- (void)_updateTrackingRects: (NSNotification *)notification |
{ |
NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream]; |
NSRect bounds = [self bounds]; |
[self removeTrackingRect: _trackingRectTag]; |
_flags.wasMouseInside = NSMouseInRect([self convertPoint: mouseLoc fromView: nil], bounds, YES); |
_trackingRectTag = [self addTrackingRect: bounds owner: self userData: nil assumeInside: _flags.wasMouseInside]; |
} |
/** |
* Updates the current event mask so that it becomes equivalent to |
* the event mask stored in _eventMask. |
*/ |
- (void)_updateCurrentEventMask |
{ |
BOOL forceEntry = NO; |
// kPassiveMotionEvents |
if((_curEventMask & kPassiveMotionEvents) ^ (_eventMask & kPassiveMotionEvents)) { |
if((_eventMask & kPassiveMotionEvents) == kPassiveMotionEvents) { |
// turn 'em on |
[(GLUTWindow *) [self window] enableMouseMovedEvents]; |
} else { |
// turn 'em off |
[(GLUTWindow *) [self window] disableMouseMovedEvents]; |
/* Force recreation of any necessary tracking rectangle because |
the AppKit forgets about them as soon as you enable the |
generation of mouse moved events... */ |
if((_curEventMask & kEntryEvents) == kEntryEvents) { |
[self removeTrackingRect: _trackingRectTag]; |
[self setPostsFrameChangedNotifications: NO]; |
[[NSNotificationCenter defaultCenter] removeObserver: self]; |
_trackingRectTag = 0; |
forceEntry = YES; |
} |
} |
} |
// kEntryEvents |
if(forceEntry || (_curEventMask & kEntryEvents) ^ (_eventMask & kEntryEvents)) { |
if((_eventMask & kEntryEvents) == kEntryEvents) { |
/* setup our tracking rect */ |
NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream]; |
NSRect bounds = [self bounds]; |
_flags.wasMouseInside = NSMouseInRect([self convertPoint: mouseLoc fromView: nil], bounds, YES); |
_trackingRectTag = [self addTrackingRect: bounds |
owner: self |
userData: nil |
assumeInside: _flags.wasMouseInside]; |
[self setPostsFrameChangedNotifications: YES]; |
[[NSNotificationCenter defaultCenter] addObserver: self |
selector: @selector(_updateTrackingRects:) |
name: NSViewFrameDidChangeNotification |
object: self]; |
} |
} else { |
if(_trackingRectTag) { |
[self removeTrackingRect: _trackingRectTag]; |
[self setPostsFrameChangedNotifications: NO]; |
[[NSNotificationCenter defaultCenter] removeObserver: self]; |
_trackingRectTag = 0; |
} |
} |
_curEventMask = _eventMask; |
} |
- (void)handleWorkEvent: (GLUTWorkEvent *)event |
{ |
int workMask; |
BOOL isSub = _flags.isSubwindow; |
#if __GLUT_LOG_WORK_EVENTS |
__glutPrintWorkMask(event, _winid, _eventMask); // dump the events to process |
#endif |
/* Capture work mask for work that needs to be done to this |
window, then clear the window's work mask (excepting the |
dummy work bit, see below). Then, process the captured |
work mask. This allows callbacks in the processing the |
captured work mask to set the window's work mask for |
subsequent processing. */ |
workMask = event->workMask; |
assert((workMask & GLUT_DUMMY_WORK) == 0); |
/* Set the dummy work bit, clearing all other bits, to |
indicate that the window is currently on the window work |
list _and_ that the window's work mask is currently being |
processed. This convinces __glutPostWorkEvent that this |
window is on the work list still. */ |
event->workMask = GLUT_DUMMY_WORK; |
/* Optimization: most of the time, the work to do is a |
redisplay and not these other types of work. Check for |
the following cases as a group to before checking each one |
individually one by one. */ |
if(workMask & (GLUT_EVENT_MASK_WORK | GLUT_DEVICE_MASK_WORK | GLUT_CONFIGURE_WORK | |
GLUT_COLORMAP_WORK | GLUT_MAP_WORK)) { |
if(workMask & GLUT_MAP_WORK) { |
/* Show / hide window */ |
if(isSub) { |
NSMutableSet * views = [NSMutableSet set]; |
if(event->desiredMapState == kWithdrawnState) { |
/* hide */ |
[[self superview] setNeedsDisplay: YES]; |
/* Remove us from the superview. Doing this is save because |
the _glutViewList still retains us. */ |
_savedSuperview = (GLUTView *)[self superview]; |
[views unionSet: [self coveredViews]]; |
[views addObject: self]; |
[self removeFromSuperview]; |
[GLUTView evaluateVisibilityOfViews: views]; |
[self setShown: NO]; |
} else { |
/* show */ |
if(_savedSuperview) { |
GLUTList * list = &_savedSuperview->_allChildrens; |
if(_siblings.pred == &list->head) { |
/* We're the bottom most of all siblings */ |
[_savedSuperview addSubview: self positioned: NSWindowBelow relativeTo: nil]; |
} else { |
/* We're somewhere in the middle of the stack or the top-most */ |
GLUTView * refView = _siblings.pred->obj; |
[_savedSuperview addSubview: self positioned: NSWindowAbove relativeTo: refView]; |
} |
_savedSuperview = nil; |
[views unionSet: [self coveredViews]]; |
[self recursiveCollectViewsIntoSet: views]; |
[GLUTView evaluateVisibilityOfViews: views]; |
[self setShown: YES]; |
[self setNeedsDisplay: YES]; |
// ggs: fix for not initially accepting keyboard events. |
// set subview to be first responder |
[[self window] makeFirstResponder: self]; |
} |
} |
} else { |
/* Use the persistent window here because [self window] will return |
nil if we're currently miniaturized, but [self persistentWindow] |
always returns our true GLUTWindow miniaturized or not. */ |
GLUTWindow * window = (GLUTWindow *) [self window]; |
switch(event->desiredMapState) { |
case kWithdrawnState: |
[window orderOut: nil]; |
break; |
case kNormalState: |
if([window isAffectedByFullscreenWindow]) |
[window setLevel: GLUT_FULLSCREEN_LEVEL]; |
if([window isMiniaturized]) |
[window deminiaturize: nil]; |
[window makeKeyAndOrderFront: nil]; |
break; |
case kGameModeState: |
[window setLevel: GLUT_GAMEMODE_LEVEL]; |
if([window isMiniaturized]) |
[window deminiaturize: nil]; |
[window makeKeyAndOrderFront: nil]; |
break; |
case kIconicState: |
/* Give our GLUTViews a chance to draw themselves so that |
the miniaturization code is able to pick up a meaningful |
OGL graphics and not just some randomly set pixels... */ |
[window display]; |
[window miniaturize: nil]; |
break; |
} |
} |
} |
if(workMask & GLUT_CONFIGURE_WORK) { |
if(event->desiredConfMask & (CWWidth | CWHeight)) { |
/* resize window */ |
NSSize Xsize = NSMakeSize(event->desiredWidth, event->desiredHeight); |
NSMutableSet * views = [NSMutableSet set]; |
[views unionSet: [self coveredViews]]; |
if(isSub) { |
NSSize size = [self frame].size; |
if(size.width != Xsize.width || size.height != Xsize.height) { |
[[self superview] setNeedsDisplayInRect: [self frame]]; |
[self setFrameSize: Xsize]; |
[self _commonReshape]; |
[self setNeedsDisplay: YES]; |
} |
} else { |
GLUTWindow * window = (GLUTWindow *) [self window]; |
unsigned int mask; |
NSRect frame; |
if([window isFullscreen]) { |
frame = [window frame]; |
frame.origin.y = NSMaxY(frame) - Xsize.height; |
frame.size = Xsize; |
window = __glutSwitchWindowFullscreenMode(window, frame, NO); |
} else { |
mask = [window styleMask]; |
frame = [NSWindow contentRectForFrameRect: [window frame] styleMask: mask]; |
frame.origin.y = NSMaxY(frame) - Xsize.height; |
frame.size = Xsize; |
frame = [NSWindow frameRectForContentRect: frame styleMask: mask]; |
[window setFrame: frame display: YES]; |
} |
} |
[views unionSet: [self coveredViews]]; |
[views addObject: self]; |
[GLUTView evaluateVisibilityOfViews: views]; |
} |
if(event->desiredConfMask & (CWX | CWY)) { |
/* move window */ |
NSPoint Xpos = NSMakePoint(event->desiredX, event->desiredY); |
NSMutableSet * views = [NSMutableSet set]; |
[views unionSet: [self coveredViews]]; |
if(isSub) { |
[[self superview] setNeedsDisplayInRect: [self frame]]; |
[self setFrameOrigin: Xpos]; |
[self setNeedsDisplay: YES]; |
} else { |
GLUTWindow * window = (GLUTWindow *) [self window]; |
unsigned int mask; |
NSRect frame; |
if([window isFullscreen]) { |
frame = [window frame]; |
frame.origin.y = __glutScreenHeight - (Xpos.y + NSHeight(frame)); |
frame.origin.x = Xpos.x; |
window = __glutSwitchWindowFullscreenMode(window, frame, NO); |
} else { |
mask = [window styleMask]; |
frame = [NSWindow contentRectForFrameRect: [window frame] styleMask: mask]; |
frame.origin.y = __glutScreenHeight - (Xpos.y + NSHeight(frame)); |
frame.origin.x = Xpos.x; |
frame = [NSWindow frameRectForContentRect: frame styleMask: mask]; |
[window setFrameOrigin: frame.origin]; |
} |
} |
[views unionSet: [self coveredViews]]; |
[views addObject: self]; |
[GLUTView evaluateVisibilityOfViews: views]; |
} |
if(event->desiredConfMask & CWStackMode) { |
/* change window stacking order */ |
if(isSub) { |
GLUTView * superview = (GLUTView *) [self superview]; |
GLUTList * list = &superview->_allChildrens; |
NSMutableSet * views = [NSMutableSet set]; |
if(event->desiredStack == kAbove) { |
__glutRemoveNode(list, &_siblings); |
[self removeFromSuperview]; |
[superview addSubview: self positioned: NSWindowAbove relativeTo: nil]; |
__glutAddTailNode(list, &_siblings); |
[views unionSet: [self coveredViews]]; |
[self recursiveCollectViewsIntoSet: views]; |
[GLUTView evaluateVisibilityOfViews: views]; |
[self setNeedsDisplay: YES]; |
} else { |
[views unionSet: [self coveredViews]]; |
[self recursiveCollectViewsIntoSet: views]; |
__glutRemoveNode(list, &_siblings); |
[self removeFromSuperview]; |
[superview addSubview: self positioned: NSWindowBelow relativeTo: nil]; |
__glutAddHeadNode(list, &_siblings); |
[GLUTView evaluateVisibilityOfViews: views]; |
[[self superview] setNeedsDisplay: YES]; |
} |
} else { |
GLUTWindow * window = (GLUTWindow *) [self window]; |
if(event->desiredStack == kAbove) { |
if([window isAffectedByFullscreenWindow]) |
[window setLevel: GLUT_FULLSCREEN_LEVEL]; |
[window makeKeyAndOrderFront: nil]; |
} else { |
if([window isAffectedByFullscreenWindow]) |
[window setLevel: GLUT_NORMAL_LEVEL]; |
[window orderBack: nil]; |
} |
} |
} |
if(event->desiredConfMask & CWFullScreen) { |
GLUTWindow * window = (GLUTWindow *) [self window]; |
if(!isSub && ![window isFullscreen]) |
(void) __glutSwitchWindowFullscreenMode(window, NSZeroRect, YES); |
} |
/* Zero out the mask. */ |
event->desiredConfMask = 0; |
} |
if(workMask & GLUT_EVENT_MASK_WORK) { |
[self _updateCurrentEventMask]; |
} |
} |
if(workMask & (GLUT_REDISPLAY_WORK | GLUT_OVERLAY_REDISPLAY_WORK)) { |
/* Render to normal plane (and possibly overlay). */ |
/* Note: GLUT window redisplay is NOT recursive. That's why |
we do the -lockFocus, -drawRect and -unlockFocus stuff |
ourselves here (and we get a nice speed-up for free) */ |
if([self lockFocusIfCanDraw]) { |
[self drawRect: /* rect is ignored */NSZeroRect]; |
[self unlockFocus]; |
} |
} |
/* Combine workMask with window->workMask to determine what |
finish and debug work there is. */ |
workMask |= event->workMask; |
if(workMask & GLUT_DEBUG_WORK) { |
__glutSetWindow(self); |
glutReportErrors(); |
} |
/* Strip out dummy, finish, and debug work bits. */ |
event->workMask &= ~(GLUT_DUMMY_WORK | GLUT_DEBUG_WORK); |
} |
///////////////////////////////////////////// |
#pragma mark - |
#pragma mark Drawing & Misc |
#pragma mark - |
- (BOOL)isFlipped { return YES; } |
- (void)_commonReshape |
{ |
NSSize size = [self bounds].size; |
__glutSetWindow(self); |
(*_reshapeFunc)(size.width, size.height); |
/* For sake of compatibility with the X Windows implementation... */ |
_flags.isDamaged = YES; |
_flags.forceReshape = NO; |
} |
- (void)drawRect: (NSRect)aRect |
{ |
[[self window] setDocumentEdited: YES]; |
__glutSetWindow(self); |
if(_flags.forceReshape) { |
/* Guarantee that before a display callback is generated |
for a window, a reshape callback must be generated. */ |
[self _commonReshape]; |
} |
(*_displayFunc)(); |
_flags.isDamaged = NO; |
} |
- (void)resizeWithOldSuperviewSize: (NSSize)oldFrameSize |
{ |
[super resizeWithOldSuperviewSize: oldFrameSize]; |
[self _commonReshape]; |
} |
- (void)viewWillStartLiveResize |
{ |
if(!_flags.isSubwindow) { |
_viewStorage = [[NSMutableSet alloc] init]; |
[_viewStorage unionSet: [self coveredViews]]; |
[_viewStorage addObject: self]; |
} |
} |
- (void)viewDidEndLiveResize |
{ |
if(!_flags.isSubwindow) { |
[_viewStorage unionSet: [self coveredViews]]; |
[GLUTView evaluateVisibilityOfViews: _viewStorage]; |
[_viewStorage release]; |
_viewStorage = nil; |
} |
} |
static void flipAndfixUpAlphaComponents(NSBitmapImageRep *imageRep) |
{ |
unsigned char * sp = [imageRep bitmapData]; |
int bytesPerRow = [imageRep bytesPerRow]; |
int height = [imageRep pixelsHigh]; |
int width = [imageRep pixelsWide]; |
unsigned int alphaMask = (NS_LittleEndian == NSHostByteOrder()) ? 0xFF000000 : 0x000000FF; |
while (height > 1) { // top half mirrored to bottom |
unsigned int * pt = (unsigned int *) sp; |
unsigned int * pb = (unsigned int *) (sp + (height - 1) * bytesPerRow) ; |
int w = width; |
while (w-- > 0) { |
unsigned int tmp = *pt | alphaMask; |
*pt++ = *pb | alphaMask; |
*pb++ = tmp; |
} |
sp += bytesPerRow; |
height -= 2; |
} |
if (height) { // middle row |
int w = width; |
unsigned int * pt = (unsigned int *) sp; |
while (w-- > 0) |
*pt++ |= alphaMask; |
} |
} |
- (void)_recursiveCopyPixelsTo: (NSBitmapImageRep *)bitmap sourceRect: (NSRect)srcRect baseView: (NSView *)bView |
{ |
NSArray * childs = [self subviews]; |
unsigned int i, count = [childs count]; |
GLvoid * pixels = (GLvoid *) [bitmap bitmapData]; |
NSRect rect = NSIntersectionRect([self bounds], srcRect); |
NSPoint origin = [self convertPoint: rect.origin toView: bView]; |
GLfloat zero = 0.0f; |
[self lockFocus]; |
while(glGetError() != GL_NO_ERROR); |
glPushAttrib(GL_ALL_ATTRIB_BITS); |
glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT); |
glReadBuffer((_flags.treatAsSingle) ? GL_FRONT : GL_BACK); |
glDisable(GL_COLOR_TABLE); |
glDisable(GL_CONVOLUTION_1D); |
glDisable(GL_CONVOLUTION_2D); |
glDisable(GL_HISTOGRAM); |
glDisable(GL_MINMAX); |
glDisable(GL_POST_COLOR_MATRIX_COLOR_TABLE); |
glDisable(GL_POST_CONVOLUTION_COLOR_TABLE); |
glDisable(GL_SEPARABLE_2D); |
glPixelMapfv(GL_PIXEL_MAP_R_TO_R, 1, &zero); |
glPixelMapfv(GL_PIXEL_MAP_G_TO_G, 1, &zero); |
glPixelMapfv(GL_PIXEL_MAP_B_TO_B, 1, &zero); |
glPixelMapfv(GL_PIXEL_MAP_A_TO_A, 1, &zero); |
if (NS_LittleEndian == NSHostByteOrder()) |
glPixelStorei(GL_PACK_SWAP_BYTES, 1); |
else |
glPixelStorei(GL_PACK_SWAP_BYTES, 0); |
glPixelStorei(GL_PACK_LSB_FIRST, 0); |
glPixelStorei(GL_PACK_IMAGE_HEIGHT, 0); |
glPixelStoref(GL_PACK_ROW_LENGTH, NSWidth(srcRect)); |
glPixelStoref(GL_PACK_SKIP_PIXELS, origin.x); |
glPixelStoref(GL_PACK_SKIP_ROWS, NSHeight(srcRect) - (origin.y + NSHeight(rect))); |
glPixelStorei(GL_PACK_SKIP_IMAGES, 0); |
glPixelTransferi(GL_MAP_COLOR, 0); |
glPixelTransferf(GL_RED_SCALE, 1.0f); |
glPixelTransferf(GL_RED_BIAS, 0.0f); |
glPixelTransferf(GL_GREEN_SCALE, 1.0f); |
glPixelTransferf(GL_GREEN_BIAS, 0.0f); |
glPixelTransferf(GL_BLUE_SCALE, 1.0f); |
glPixelTransferf(GL_BLUE_BIAS, 0.0f); |
glPixelTransferf(GL_ALPHA_SCALE, 1.0f); |
glPixelTransferf(GL_ALPHA_BIAS, 0.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_RED_SCALE, 1.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_RED_BIAS, 0.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_GREEN_SCALE, 1.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_GREEN_BIAS, 0.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_BLUE_SCALE, 1.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_BLUE_BIAS, 0.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_ALPHA_SCALE, 1.0f); |
glPixelTransferf(GL_POST_COLOR_MATRIX_ALPHA_BIAS, 0.0f); |
glReadPixels((GLint) NSMinX(rect), (GLint) NSMinY(rect), |
(GLsizei) NSWidth(rect), (GLsizei) NSHeight(rect), |
GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, pixels); |
glPopClientAttrib(); |
glPopAttrib(); |
// Get rid of any error, in order to not mislead GLUT clients... |
while(glGetError() != GL_NO_ERROR); |
[self unlockFocus]; |
for(i = 0; i < count; i++) { |
[(GLUTView *)[childs objectAtIndex: i] _recursiveCopyPixelsTo: bitmap |
sourceRect: srcRect |
baseView: bView]; |
} |
} |
- (NSBitmapImageRep *)bitmapInsideRect: (NSRect)rect |
{ |
NSBitmapImageRep * bitmap = nil; |
if(NSIsEmptyRect(rect)) |
rect = [self bounds]; |
if((bitmap = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes: NULL |
pixelsWide: NSWidth(rect) |
pixelsHigh: NSHeight(rect) |
bitsPerSample: 8 |
samplesPerPixel: 4 |
hasAlpha: YES |
isPlanar: NO |
colorSpaceName: NSDeviceRGBColorSpace |
bytesPerRow: NSWidth(rect) * 4 |
bitsPerPixel: 32] autorelease]) == nil) { |
return nil; |
} |
[self _recursiveCopyPixelsTo: bitmap sourceRect: rect baseView: self]; |
flipAndfixUpAlphaComponents(bitmap); |
return bitmap; |
} |
- (NSImage *)imageWithTIFFInsideRect: (NSRect)rect |
{ |
NSBitmapImageRep * bitmap = nil; |
NSImage * image = nil; |
// Create an NSImage containing the actual window contents as a TIFF graphics |
if((image = [[[NSImage alloc] init] autorelease]) == nil) |
return nil; |
if((bitmap = [self bitmapInsideRect: rect]) == nil) |
return nil; |
[image addRepresentation: bitmap]; |
return image; |
} |
- (void)prepareForMiniaturization |
{ |
NSBitmapImageRep * bitmap = [self bitmapInsideRect: NSZeroRect]; |
if([self lockFocusIfCanDraw]) { |
[bitmap draw]; |
[self unlockFocus]; |
} |
} |
- (void)recursiveWillBeginMorph: (int)op |
{ |
GLUTNode * curChild = _allChildrens.head.succ; |
[self willBeginMorph: op]; |
while(curChild) { |
[(id)(curChild->obj) recursiveWillBeginMorph: op]; |
curChild = curChild->succ; |
} |
} |
- (void)recursiveDidEndMorph: (int)op |
{ |
GLUTNode * curChild = _allChildrens.head.succ; |
while(curChild) { |
[(id)(curChild->obj) recursiveDidEndMorph: op]; |
curChild = curChild->succ; |
} |
[self didEndMorph: op]; |
} |
- (void)willBeginMorph: (int)op |
{ |
if(_trackingRectTag) { |
[self removeTrackingRect: _trackingRectTag]; |
_trackingRectTag = 0; |
} |
} |
- (void)didEndMorph: (int)op |
{ |
if((_curEventMask & kEntryEvents) == kEntryEvents) { |
NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream]; |
NSRect bounds = [self bounds]; |
_flags.wasMouseInside = NSMouseInRect([self convertPoint: mouseLoc fromView: nil], bounds, YES); |
_trackingRectTag = [self addTrackingRect: bounds owner: self userData: nil assumeInside: _flags.wasMouseInside]; |
} |
} |
/* Add a subview in its HIDDEN state. A latter Map work event will then |
put the view into the view hierarchy */ |
- (void)attachSubview: (GLUTView *)aView |
{ |
__glutAddTailNode(&_allChildrens, &aView->_siblings); |
aView->_savedSuperview = self; |
} |
- (void)detachFromSuperview |
{ |
GLUTView * parent = (GLUTView *)[self superview]; |
NSMutableSet * views = [NSMutableSet set]; |
[views unionSet: [self coveredViews]]; |
[views addObject: self]; |
__glutRemoveNode(&parent->_allChildrens, &_siblings); |
[self removeFromSuperview]; |
[GLUTView evaluateVisibilityOfViews: views]; |
[self setShown: NO]; |
} |
- (GLUquadricObj *)_getQuadObj |
{ |
if (_quadObj == NULL) |
_quadObj = gluNewQuadric(); |
return _quadObj; |
} |
// A helper for quadric objects |
GLUquadricObj *__glutGetQuadObj(void) |
{ |
if (__glutViewList == NULL) // This means you never initialized GLUT - BUT you are still allowed to call glutWireShphere() |
// and such anyway. Fixes bug #2742838 |
return gluNewQuadric(); |
return [__glutCurrentView _getQuadObj]; |
} |
- (void)processJoystick: (id)sender |
{ |
int buttonMask; |
int x, y, z; |
if(_joystickFunc) { |
__glutGetJoystickInput (&buttonMask, &x, &y, &z); |
__glutSetWindow(self); |
if (buttonMask || x || y || z) |
(*_joystickFunc)(buttonMask, x, y, z); |
} |
} |
- (void)processSpaceball: (id)sender |
{ |
static int buttonMask = 0; |
static int x = 0, y = 0, z = 0, rx = 0, ry = 0, rz = 0; |
int savebuttonMask = 0; |
int savex = 0, savey = 0, savez = 0, saverx = 0, savery = 0, saverz = 0; |
if(_spaceballMotionFunc || _spaceballRotateFunc || _spaceballButtonFunc) { |
__glutGetSpaceballInput (&buttonMask, &x, &y, &z, &rx, &ry, &rz); |
__glutSetWindow(self); |
if (_spaceballMotionFunc && ((savex != x) || (savey != y) || (savez != z))) |
(*_spaceballMotionFunc)(x, y, z); |
if (_spaceballRotateFunc && ((saverx != rx) || (savery != ry) || (saverz != rz))) |
(*_spaceballRotateFunc)(rx, ry, rz); |
if (_spaceballButtonFunc && (savebuttonMask != buttonMask)) { |
short i; |
for (i = 0; i < __glutGetSpaceballNumButtons(); i++) // for every current button |
if (((1 << i) & savebuttonMask) != ((1 << i) & buttonMask)) // if masks at this position are different |
(*_spaceballButtonFunc)(i+1, ((1 << i) & buttonMask)); // 0 is down 1 is up so look at old position as indicator |
} |
// store current values |
savex = x; savey = y; savez = z; |
saverx = rx; savery = ry; saverz = rz; |
savebuttonMask = buttonMask; |
} |
} |
///////////////////////////////////////////// |
#pragma mark - |
#pragma mark Menus |
#pragma mark - |
- (void)attachMenu: (GLUTMenu *)menu toButton: (int)button |
{ |
if ((_menu[button] != menu) && (button < GLUT_MAX_MENUS)) { |
/* Give the GLUT menu object a chance to build the native menu |
now, so that the user won't notice a delay when clicking |
while the GLUTMenu is occupied building the native menu. */ |
(void) [menu nativeMenu]; |
[_menu[button] release]; |
_menu[button] = nil; |
_menu[button] = [menu retain]; |
} |
} |
- (void)detachMenuFromButton: (int)button |
{ |
if (button < GLUT_MAX_MENUS) { |
[_menu[button] release]; |
_menu[button] = nil; |
} |
} |
- (void)_popUpContextMenu: (GLUTMenu *)aMenu withEvent: (NSEvent *)theEvent |
{ |
__glutStartMenu(aMenu, self, [theEvent locationInWindow]); |
[NSMenu popUpContextMenu: [aMenu nativeMenu] withEvent: theEvent forView: self]; |
if((_curEventMask & kPassiveMotionEvents) == kPassiveMotionEvents) { |
/* Work around a bug in the AppKit whereby a window with mouse moved events |
generation turned on, stops sending any further such events after the |
user has played around with either a menubar menu or a contextual menu. |
Looks like as if the Carbon MenuManager changes the window's event mask |
so that it includes mouse moved events before it enters its internal |
modal event loop. However, it removes the mouse moved events from the |
window's event mask as soon as the modal event loop ended. All this happens |
behind the back of the AppKit so it still thinks mouse moved events are |
generated when in fact they are no longer 'cause the MM turned them off. */ |
NSWindow * window = [self window]; |
[window setAcceptsMouseMovedEvents: NO]; |
[window setAcceptsMouseMovedEvents: YES]; |
} |
} |
///////////////////////////////////////////// |
#pragma mark - |
#pragma mark Events |
#pragma mark - |
- (BOOL)acceptsFirstMouse: (NSEvent *)theEvent { return YES; } |
- (BOOL)acceptsFirstResponder { return YES; } |
- (BOOL)becomeFirstResponder |
{ |
__glutSetWindow(self); |
return YES; |
} |
/* Key up/down & special up/down */ |
- (void)keyDown: (NSEvent *)theEvent |
{ |
char utf8[16]; |
int i, len = 16; |
NSPoint loc; |
BOOL isSpecial; |
if(_flags.ignoreKeyRepeats && [theEvent isARepeat]) |
return; |
loc = [self convertPoint: [[self window] mouseLocationOutsideOfEventStream] fromView: nil]; |
_iMouseLocX = rint(loc.x); |
_iMouseLocY = rint(loc.y); |
__glutModifierMask = [theEvent modifierFlags]; |
__glutMapKeyCode(theEvent, utf8, &len, &isSpecial); |
__glutSetWindow(self); |
for(i = 0; i < len; i++) { |
if(!isSpecial && _keyDownFunc) { |
(*_keyDownFunc)((unsigned char) utf8[i], _iMouseLocX, _iMouseLocY); |
} else if(_specialFunc) { |
(*_specialFunc)(utf8[i], _iMouseLocX, _iMouseLocY); |
} |
} |
__glutModifierMask = ~0; |
} |
- (void)keyUp: (NSEvent *)theEvent |
{ |
char utf8[16]; |
int i, len = 16; |
BOOL isSpecial; |
if(_flags.ignoreKeyRepeats && [theEvent isARepeat]) |
return; |
__glutModifierMask = [theEvent modifierFlags]; |
__glutMapKeyCode(theEvent, utf8, &len, &isSpecial); |
__glutSetWindow(self); |
for(i = 0; i < len; i++) { |
if(!isSpecial && _keyUpFunc) { |
(*_keyUpFunc)((unsigned char) utf8[i], _iMouseLocX, _iMouseLocY); |
} else if(_specialUpFunc) { |
(*_specialUpFunc)(utf8[i], _iMouseLocX, _iMouseLocY); |
} |
} |
__glutModifierMask = ~0; |
} |
- (void)_commonMouseDown: (NSEvent *)theEvent |
{ |
GLUTMenu * menu = nil; // initially no menus |
int buttonID, buttonPhysID; |
__glutMapMouseButton(theEvent, &buttonID, &buttonPhysID, &__glutModifierMask); |
if (buttonID != buttonPhysID) |
_flags.wasMouseEmulated = 1 << (buttonID-1); // let mouse up know later what button was emulated |
if ((buttonID >= 0) && (buttonID < GLUT_MAX_MENUS)) { // ensure we are only accessing menus that exist |
if (!__glutGameModeWindow) // only do menus when not in gamemode |
menu = _menu[buttonID]; |
if (menu) { |
[self _popUpContextMenu: menu withEvent: theEvent]; |
} else if(_mouseFunc) { |
// Only send in events where this isn't a duplicate mouse down (emulation) |
// This way we can maintain a 1 to 1 up/down ratio |
if( !(_flags.hadMouseDown & (1 << buttonID)) ) { |
NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; |
__glutSetWindow(self); |
(*_mouseFunc)(buttonID, GLUT_DOWN, rint(location.x), rint(location.y)); |
_flags.hadMouseDown |= 1 << buttonID; |
} |
else // catch when the button is down twice via emulation |
_flags.duplicateEmulatedMouseDown = 1; |
} |
__glutModifierMask = ~0; |
} |
} |
- (void)_commonMouseUp: (NSEvent *)theEvent |
{ |
/* Only pass the mouse up event to the GLUT application if it belongs |
to a previously seen mouse down. Spurious mouse up events may come |
along if (1) a pop-up menu is assigned to a mouse button, (2) the |
user invoked the menu and (3) the user dismissed the menu by |
clicking *outside* of the pop-up menu. Such mouse up events should |
not be passed to the GLUT app. */ |
if(_mouseFunc) { |
NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; |
int buttonID, buttonPhysID; |
__glutMapMouseButton(theEvent, &buttonID, &buttonPhysID, &__glutModifierMask); |
// Check for emulation when looking at button 0 up events |
if(buttonPhysID == 0) { |
if(_flags.wasMouseEmulated) { |
// make mouse up event match emulated mouse down button |
buttonID = 1 << (_flags.wasMouseEmulated-1); |
} |
else buttonID = 0; |
} |
if(buttonID >= 0 && (_flags.hadMouseDown & (1<<buttonID))) { |
if(_flags.duplicateEmulatedMouseDown && |
((_flags.wasMouseEmulated<<1) & (1<<buttonID))) { |
// Emulation caused this button to be down twice at the same time |
_flags.duplicateEmulatedMouseDown = 0; |
// Ignore this mouse up event and wait for a second one |
} |
else { |
__glutSetWindow(self); |
(*_mouseFunc)(buttonID, GLUT_UP, rint(location.x), rint(location.y)); |
_flags.hadMouseDown &= ~(1 << buttonID); |
} |
} |
__glutModifierMask = ~0; |
if((buttonPhysID == 0) && _flags.wasMouseEmulated) |
_flags.wasMouseEmulated = 0; |
} |
} |
- (void)_commonMouseDragged: (NSEvent *)theEvent |
{ |
if([theEvent buttonNumber] <= 2 && _motionFunc) { |
NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; |
__glutSetWindow(self); |
(*_motionFunc)(rint(location.x), rint(location.y)); |
} |
} |
/* Left mouse */ |
- (void)mouseDown: (NSEvent *)theEvent |
{ |
[self _commonMouseDown: theEvent]; |
} |
- (void)mouseUp: (NSEvent *)theEvent |
{ |
[self _commonMouseUp: theEvent]; |
} |
- (void)mouseDragged: (NSEvent *)theEvent |
{ |
[self _commonMouseDragged: theEvent]; |
} |
- (void)mouseMoved: (NSEvent *)theEvent |
{ |
NSPoint location = [self convertPoint: [theEvent locationInWindow] fromView: nil]; |
if(_entryFunc) { |
/* Generate a facked enter/exit event because the AppKit forgets |
about its tracking rects as soon as you enable the generation |
of mouse moved events... */ |
BOOL isInside = NSMouseInRect(location, [self bounds], YES); |
if(isInside ^ _flags.wasMouseInside) { |
_flags.wasMouseInside = isInside; |
__glutSetWindow(self); |
(*_entryFunc)((isInside) ? GLUT_ENTERED : GLUT_LEFT); |
} |
} |
if(_passiveMotionFunc) { |
__glutSetWindow(self); |
(*_passiveMotionFunc)(rint(location.x), rint(location.y)); |
} |
} |
/* Right mouse */ |
- (void)rightMouseDown: (NSEvent *)theEvent |
{ |
[self _commonMouseDown: theEvent]; |
} |
- (void)rightMouseUp: (NSEvent *)theEvent |
{ |
[self _commonMouseUp: theEvent]; |
} |
- (void)rightMouseDragged: (NSEvent *)theEvent |
{ |
[self _commonMouseDragged: theEvent]; |
} |
/* Middle mouse */ |
- (void)otherMouseDown: (NSEvent *)theEvent |
{ |
[self _commonMouseDown: theEvent]; |
} |
- (void)otherMouseUp: (NSEvent *)theEvent |
{ |
[self _commonMouseUp: theEvent]; |
} |
- (void)otherMouseDragged: (NSEvent *)theEvent |
{ |
[self _commonMouseDragged: theEvent]; |
} |
- (void)mouseEntered: (NSEvent *)theEvent |
{ |
if(_entryFunc) { |
_flags.wasMouseInside = YES; |
__glutSetWindow(self); |
(*_entryFunc)(GLUT_ENTERED); |
} |
} |
- (void)mouseExited: (NSEvent *)theEvent |
{ |
if(_entryFunc) { |
_flags.wasMouseInside = NO; |
__glutSetWindow(self); |
(*_entryFunc)(GLUT_LEFT); |
} |
} |
- (BOOL)validateMenuItem: (NSMenuItem *)menuItem |
{ |
/* User is about to start a menu tracking session. We install a special |
timer which will execute the idle function while menu tracking is |
going on because menu tracking happens in its own modal event loop. */ |
__glutStartIdleFuncTimer(); |
if(__glutDisablePrinting) { |
if([menuItem action] == @selector(print:)) |
return NO; |
} |
return YES; |
} |
- (void)print: (id)sender |
{ |
[[self window] print: sender]; |
} |
///////////////////////////////////////////// |
#pragma mark - |
#pragma mark Visibility |
#pragma mark - |
- (BOOL)isVisible |
{ |
if(_flags.isSubwindow) |
return ([self superview] != nil); |
else |
return [[self window] isVisible]; |
} |
/** |
* Returns an ordered list of all currently visible siblings of the |
* receiver. Views are ordered from front to back. |
*/ |
- (NSArray *)_orderedSiblings |
{ |
NSMutableArray * siblings = nil; |
if(_flags.isSubwindow) { |
// We're a subwindow |
NSArray * subviews = [[self superview] subviews]; |
NSEnumerator * enumerator = [subviews reverseObjectEnumerator]; |
id obj; |
siblings = [NSMutableArray arrayWithCapacity: [subviews count]]; |
while((obj = [enumerator nextObject]) != nil) |
[siblings addObject: obj]; |
} else { |
// We're a top-level window |
NSArray * windows = [NSApp orderedWindows]; |
unsigned i, count = [windows count]; |
siblings = [NSMutableArray arrayWithCapacity: count]; |
for(i = 0; i < count; i++) { |
NSWindow * curWindow = [windows objectAtIndex: i]; |
NSView * contView = [curWindow contentView]; |
if([curWindow isVisible] && |
[curWindow isKindOfClass: [GLUTWindow class]] && |
contView) |
[siblings addObject: contView]; |
} |
} |
return siblings; |
} |
/** |
* Puts all siblings of the receiver into the given mutable set which |
* are at a lower place in the view (window) stack. Further, adds the |
* receiver's parent view to the set if it is a subwindow. |
*/ |
- (NSSet *)coveredViews |
{ |
NSMutableSet * aSet = [NSMutableSet setWithCapacity: 13]; |
NSWindow * window = [self window]; |
NSArray * siblings = [self _orderedSiblings]; // front -> back |
unsigned i, count = [siblings count]; |
NSRect myBounds = [self bounds]; |
// convert my bounds to screen space |
myBounds = [self convertRect: myBounds toView: nil]; |
myBounds.origin = [window convertBaseToScreen: myBounds.origin]; |
// add parent, if we're a subwindow |
if(_flags.isSubwindow && [self superview]) { |
[aSet addObject: [self superview]]; |
} |
if(count > 1) { |
// add all siblings which are below us |
for(i = count - ([siblings indexOfObjectIdenticalTo: self] + 1); i < count; i++) { |
NSView * curView = [siblings objectAtIndex: i]; |
NSRect curBounds = [curView bounds]; |
curBounds = [curView convertRect: curBounds toView: nil]; |
curBounds.origin = [[curView window] convertBaseToScreen: curBounds.origin]; |
if(!NSIsEmptyRect(NSIntersectionRect(myBounds, curBounds))) |
[aSet addObject: curView]; |
} |
} |
return aSet; |
} |
/** |
* Evaluates the receiver's visibility based on the current state |
* of the window or view hierarchy. |
*/ |
- (void)_evaluateVisibility |
{ |
int status = GLUT_FULLY_RETAINED; |
if([self isVisible]) { |
/* We're visible */ |
NSArray * others; |
NSRect myBounds = [self bounds]; |
double myArea, myVisibleArea; |
unsigned i, count; |
// compute my screen space bounds |
myBounds = [self convertRect: myBounds toView: nil]; |
myBounds.origin = [[self window] convertBaseToScreen: myBounds.origin]; |
// compute my area in pixels |
myArea = NSWidth(myBounds) * NSHeight(myBounds); |
myVisibleArea = myArea; |
// (1) find out what influence all my siblings above me have on my visibility |
others = [self _orderedSiblings]; |
count = [others indexOfObjectIdenticalTo: self]; |
for(i = 0; (i < count) && (myVisibleArea >= DBL_EPSILON); i++) { |
NSView * curView = [others objectAtIndex: i]; |
NSRect curBounds = [curView bounds]; |
NSRect iRect; |
curBounds = [curView convertRect: curBounds toView: nil]; |
curBounds.origin = [[curView window] convertBaseToScreen: curBounds.origin]; |
iRect = NSIntersectionRect(myBounds, curBounds); |
myVisibleArea -= NSWidth(iRect) * NSHeight(iRect); |
} |
if(myVisibleArea >= DBL_EPSILON) { |
// (2) find out what influence all my child views have on my visibility |
others = [self subviews]; |
count = [others count]; |
for(i = 0; (i < count) && (myVisibleArea >= DBL_EPSILON); i++) { |
NSView * curView = [others objectAtIndex: i]; |
NSRect curBounds = [curView bounds]; |
NSRect iRect; |
curBounds = [curView convertRect: curBounds toView: nil]; |
curBounds.origin = [[curView window] convertBaseToScreen: curBounds.origin]; |
iRect = NSIntersectionRect(myBounds, curBounds); |
myVisibleArea -= NSWidth(iRect) * NSHeight(iRect); |
} |
} |
// classify our remaining visible area |
if(myVisibleArea < DBL_EPSILON) |
status = GLUT_FULLY_COVERED; |
else if(myVisibleArea < (myArea - DBL_EPSILON)) |
status = GLUT_PARTIALLY_RETAINED; |
else |
status = GLUT_FULLY_RETAINED; |
} else { |
/* We're hidden */ |
status = GLUT_HIDDEN; |
} |
/* Remember the newly computed visibility state and put us onto the visibility |
update list, if we're not already there. */ |
if(_newVisState == GLUT_UNKNOWN_VISIBILITY) { |
_visibilityNext = NULL; |
if(__glutVisibilityUpdateList == NULL) { |
__glutVisibilityUpdateList = self; |
__glutVisibilityUpdateTail = self; |
} else { |
__glutVisibilityUpdateTail->_visibilityNext = self; |
__glutVisibilityUpdateTail = self; |
} |
} |
_newVisState = status; |
} |
- (void)_recursiveMarkHidden |
{ |
if(_visState != GLUT_HIDDEN) { // if we are not already hidden |
NSArray * childrens = [self subviews]; |
unsigned int i, count = [childrens count]; |
#if __GLUT_LOG_VISIBILITY |
__glutPrintVisibilityState(GLUT_HIDDEN, _winid); |
#endif |
_visState = GLUT_HIDDEN; // ensure it is really marked hidden |
if(_windowStatusFunc) { |
__glutSetWindow(self); |
(*_windowStatusFunc)(GLUT_HIDDEN); |
} |
/* An unmap is only reported on a single window; its |
descendents need to know they are no longer visible. */ |
for(i = 0; i < count; i++) { |
[(GLUTView *)[childrens objectAtIndex: i] _recursiveMarkHidden]; |
} |
} |
} |
- (void)_updateComputedVisibility |
{ |
if(_flags.isVisibilityUpdateAllowed) { |
int visState = _newVisState; |
_newVisState = GLUT_UNKNOWN_VISIBILITY; |
_visibilityNext = NULL; |
if(_visState != GLUT_FULLY_RETAINED && |
(visState == GLUT_PARTIALLY_RETAINED || visState == GLUT_FULLY_RETAINED)) { |
/* We just became 'more' visible or we have been partially visible |
and somehow a new partial visibility was computed */ |
_flags.isDamaged = YES; |
} |
if(visState == GLUT_HIDDEN) { |
[self _recursiveMarkHidden]; |
} else { |
if(visState != _visState) { |
#if __GLUT_LOG_VISIBILITY |
__glutPrintVisibilityState(visState, _winid); |
#endif |
_visState = visState; //ggs: always set vis state |
if(_windowStatusFunc) { |
__glutSetWindow(self); |
(*_windowStatusFunc)(visState); |
} |
} |
} |
} |
} |
- (void)enableVisibilityUpdates |
{ |
_flags.isVisibilityUpdateAllowed = YES; |
} |
/** |
* Evaluates the visibility of the given set of views. The visibility |
* state of all views is updated after all of them have been evaluated. |
*/ |
+ (void)evaluateVisibilityOfViews: (NSSet *)views |
{ |
NSEnumerator * enumerator = [views objectEnumerator]; |
GLUTView * curView; |
/* (1) compute new visibility states */ |
while((curView = (GLUTView *) [enumerator nextObject]) != nil) { |
[curView _evaluateVisibility]; |
} |
/* (2) update visibility states */ |
curView = __glutVisibilityUpdateList; |
while(curView) { |
GLUTView * tmpView = curView->_visibilityNext; |
[curView _updateComputedVisibility]; |
curView = tmpView; |
} |
__glutVisibilityUpdateList = NULL; |
__glutVisibilityUpdateTail = NULL; |
} |
- (void)recursiveCollectViewsIntoSet: (NSMutableSet *)views |
{ |
NSArray * subviews = [self subviews]; |
unsigned i, count = [subviews count]; |
[views addObject: self]; |
for(i = 0; i < count; i++) |
[(GLUTView *) [subviews objectAtIndex: i] recursiveCollectViewsIntoSet: views]; |
} |
@end |
Copyright © 2008 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2008-02-08