/* 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
|