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.
macx_event.m
/* Copyright (c) Dietmar Planitzer, 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 "macx_glut.h" |
#import "GLUTApplication.h" |
#import "GLUTView.h" |
#import "GLUTWindow.h" |
GLUTidleCB __glutIdleFunc = NULL; |
GLUTidleFCB __fglutIdleFunc = NULL; /* fortran callback */ |
GLUTTimer * __glutMostRecentTimer = NULL; |
static GLUTTimer * __glutCachedTimers = NULL; |
static int __glutNumCachedTimers = 0; |
static GLUTWorkEvent * __glutWindowWorkList = NULL; |
static GLUTWorkEvent * __glutCachedWorkEvents = NULL; |
static int __glutNumCachedWorkEvents = 0; |
unsigned int __glutMouseFirstModifiers = kMouseFirstModifiers; /* initial values */ |
static int __glutMouseFirstButton = GLUT_RIGHT_BUTTON; |
unsigned int __glutMouseSecondModifiers = kMouseSecondModifiers; |
static int __glutMouseSecondButton = GLUT_MIDDLE_BUTTON; |
static unsigned int __glutMouseMiddleModifiers = 0; |
BOOL __glutEmulateMouseButtons = kEmulateMouseButtons; |
/* CENTRY */ |
void APIENTRY glutIdleFunc(void (*func)(void)) |
{ |
__glutIdleFunc = func; |
} |
/* ENDCENTRY */ |
static void __glutTimerCallBack(CFRunLoopTimerRef timer, GLUTTimer* context) |
{ |
GLUTtimerCB func = context->func; |
int value = context->value; |
__glutProcessWorkEvents(); |
void __glutFreeTimer(GLUTTimer *); |
__glutFreeTimer(context); |
if(func) |
func(value); |
if(__glutMostRecentTimer == context) { |
/* Set __glutMostRecentTimer to NULL, if we're the most recent timer. |
If another timer has been created in the meantime, __glutMostRecentTimer |
would now point to that timer and consequently we would not change it. */ |
__glutMostRecentTimer = NULL; |
} |
__glutProcessWorkEvents(); // ensure we do not miss glut work events |
} |
void __glutFreeTimer(GLUTTimer *timer) |
{ |
CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer->timer, kCFRunLoopCommonModes); |
if(__glutNumCachedTimers < GLUT_MAX_CACHED_TIMERS) { |
timer->next = __glutCachedTimers; |
__glutCachedTimers = timer; |
__glutNumCachedTimers++; |
} else { |
CFRelease(timer->timer); |
free(timer); |
} |
} |
static GLUTTimer *__glutAllocateTimer(unsigned int msecs) |
{ |
GLUTTimer * timer; |
CFAbsoluteTime fireTime; |
// Compute first/next fire time |
if (msecs) // ensure timers of zero delay fire |
fireTime = CFAbsoluteTimeGetCurrent() + ((CFAbsoluteTime) msecs) / 1000.0; |
else |
fireTime = CFAbsoluteTimeGetCurrent() + 0.00001; |
if(__glutNumCachedTimers > 0) { |
/* Recycle one of the cached timers */ |
timer = __glutCachedTimers; |
__glutCachedTimers = timer->next; |
__glutNumCachedTimers--; |
if(__glutNumCachedTimers == 0) |
__glutCachedTimers = NULL; |
CFRunLoopTimerSetNextFireDate(timer->timer, fireTime); |
} else { |
/* Our cache is empty - allocate a new timer */ |
timer = (GLUTTimer *) calloc(1, sizeof(GLUTTimer)); |
if(!timer) { |
__glutFatalError("out of memory"); |
} |
timer->context.info = timer; |
timer->timer = CFRunLoopTimerCreate(NULL, |
fireTime, |
1.0, // interval |
0, // flags |
0, // order |
(CFRunLoopTimerCallBack) __glutTimerCallBack, |
(CFRunLoopTimerContext *) timer); |
if(!timer->timer) { |
free(timer); |
__glutFatalError("out of memory"); |
} |
} |
return timer; |
} |
/* CENTRY */ |
void APIENTRY glutTimerFunc(unsigned int msecs, void (*func)(int value), int value) |
{ |
GLUTTimer * timer = __glutAllocateTimer(msecs); |
timer->func = func; |
timer->value = value; |
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer->timer, kCFRunLoopCommonModes); |
__glutMostRecentTimer = timer; |
} |
/* ENDCENTRY */ |
static inline GLUTWorkEvent *__glutFindEventForWindow(int win) |
{ |
register GLUTWorkEvent * evp = __glutWindowWorkList; |
while(evp) { |
if(evp->windowNum == win) |
break; |
evp = evp->prevWorkEvent; |
} |
return evp; |
} |
static GLUTWorkEvent *__glutAllocateWorkEvent(void) |
{ |
register GLUTWorkEvent * evp; |
if(__glutNumCachedWorkEvents > 0) { |
/* Recycle one of the cached work events */ |
evp = __glutCachedWorkEvents; |
__glutCachedWorkEvents = evp->prevWorkEvent; |
__glutNumCachedWorkEvents--; |
if(__glutNumCachedWorkEvents == 0) |
__glutCachedWorkEvents = NULL; |
} else { |
/* Our cache is empty - allocate a new work event */ |
evp = (GLUTWorkEvent *) malloc(sizeof(GLUTWorkEvent)); |
} |
if(!evp) { |
__glutFatalError("out of memory"); |
} |
return evp; |
} |
static void __glutFreeWorkEvent(GLUTWorkEvent *evt) |
{ |
if(__glutNumCachedWorkEvents < GLUT_MAX_CACHED_WORK_EVENTS) { |
/* We abuse the prevWorkEvent ptr as a nextWorkEvent ptr... */ |
evt->prevWorkEvent = __glutCachedWorkEvents; |
__glutCachedWorkEvents = evt; |
__glutNumCachedWorkEvents++; |
} else { |
free(evt); |
} |
} |
void __glutPostWorkEvent(GLUTWorkEvent *event) |
{ |
register GLUTWorkEvent * evp; |
/* Find out if there is already another event destinated |
for the same window as the event we're supposed to |
send. If so merge both together. */ |
evp = __glutFindEventForWindow(event->windowNum); |
if(evp) { |
/* Another event for an already known window - merge |
new event into already existing one */ |
evp->workMask |= event->workMask; |
if(event->workMask & (GLUT_MAP_WORK | GLUT_CONFIGURE_WORK)) { |
if(event->workMask & GLUT_MAP_WORK) |
evp->desiredMapState = event->desiredMapState; |
if(event->workMask & GLUT_CONFIGURE_WORK) { |
evp->desiredConfMask |= event->desiredConfMask; |
if(event->desiredConfMask & (CWX | CWY)) { |
evp->desiredX = event->desiredX; |
evp->desiredY = event->desiredY; |
} |
if(event->desiredConfMask & (CWWidth | CWHeight)) { |
evp->desiredWidth = event->desiredWidth; |
evp->desiredHeight = event->desiredHeight; |
} |
if(event->desiredConfMask & CWStackMode) { |
evp->desiredStack = event->desiredStack; |
} |
} |
} |
} else { |
/* An event for an unknown window - allocate a new |
event record and enqueue it */ |
evp = __glutAllocateWorkEvent(); |
*evp = *event; |
evp->prevWorkEvent = __glutWindowWorkList; |
__glutWindowWorkList = evp; |
} |
#if __GLUT_LOG_WORK_EVENTS |
printf("__glutPostWorkEvent() {\n"); |
__glutPrintWorkMask(evp, event->windowNum, 0); |
printf("}\n"); |
#endif |
} |
void __glutPurgeWorkEvents(int win) |
{ |
GLUTWorkEvent ** pEntry = &__glutWindowWorkList; |
GLUTWorkEvent * entry = __glutWindowWorkList; |
/* Traverse singly-linked work list and look for the window. */ |
while(entry) { |
if(entry->windowNum == win) { |
/* Found it; delete it. */ |
*pEntry = entry->prevWorkEvent; |
__glutFreeWorkEvent(entry); |
return; |
} |
pEntry = &entry->prevWorkEvent; |
entry = *pEntry; |
} |
} |
void __glutPostRedisplay(GLUTView * view, int layerMask) |
{ |
int shown = [view isShown]; |
int visState = [view visibilityState]; |
/* Post a redisplay if the window is visible (or the |
visibility of the window is unknown, ie. window->visState |
== -1) _and_ the layer is known to be shown. */ |
if(visState != GLUT_HIDDEN && visState != GLUT_FULLY_COVERED && shown) { |
GLUTWorkEvent event; |
INIT_WORK_EVENT(&event); |
event.workMask = layerMask; |
event.windowNum = [view windowID]; |
__glutPostWorkEvent(&event); |
} |
} |
/* CENTRY */ |
void APIENTRY glutPostRedisplay(void) |
{ |
GLUTAPI_DECLARATIONS_FAST |
GLUTAPI_BEGIN_FAST |
__glutPostRedisplay(__glutCurrentView, GLUT_REDISPLAY_WORK); |
GLUTAPI_END_FAST |
} |
/* The advantage of this routine is that it saves the cost of a |
glutSetWindow call (entailing an expensive OpenGL context switch), |
particularly useful when multiple windows need redisplays posted at |
the same times. See also glutPostWindowOverlayRedisplay. */ |
void APIENTRY glutPostWindowRedisplay(int win) |
{ |
GLUTView * view = __glutGetWindowByNum(win); |
GLUTAPI_DECLARATIONS_FAST |
GLUTAPI_BEGIN_FAST |
if(view) |
__glutPostRedisplay(view, GLUT_REDISPLAY_WORK); |
else |
__glutWarning("glutPostWindowRedisplay attempted on bogus window."); |
GLUTAPI_END_FAST |
} |
/* ENDCENTRY */ |
static GLUTWorkEvent **beforeEnd; |
static GLUTWorkEvent *processWindowWorkList(GLUTWorkEvent *event) |
{ |
GLUTView * view = nil; |
GLUTWorkEvent * tmpEvt; |
if(event->prevWorkEvent) { |
event->prevWorkEvent = processWindowWorkList(event->prevWorkEvent); |
} else { |
beforeEnd = &event->prevWorkEvent; |
} |
view = __glutGetWindowByNum(event->windowNum); |
if(!view) { |
/* GLUT window died in the meantime - just ignore event */ |
tmpEvt = event->prevWorkEvent; |
__glutFreeWorkEvent(event); |
return tmpEvt; |
} |
[view handleWorkEvent: event]; |
if (event->workMask) { |
/* Leave on work list. */ |
return event; |
} else { |
/* Remove current window from work list. */ |
tmpEvt = event->prevWorkEvent; |
__glutFreeWorkEvent(event); |
return tmpEvt; |
} |
} |
void __glutProcessWorkEvents(void) |
{ |
if(__glutWindowWorkList) { |
GLUTWorkEvent * workRemainder, *work; |
work = __glutWindowWorkList; |
__glutWindowWorkList = NULL; |
if(work) { |
workRemainder = processWindowWorkList(work); |
if(workRemainder) { |
*beforeEnd = __glutWindowWorkList; |
__glutWindowWorkList = workRemainder; |
} |
} |
} |
} |
BOOL __glutHasWorkEvents(void) |
{ |
return (__glutWindowWorkList != NULL); |
} |
static int __glutCountWindows(void) |
{ |
NSArray * windows = [NSApp windows]; |
unsigned int i, count = [windows count]; |
int num = 0; |
for(i = 0; i < count; i++) { |
id win = [windows objectAtIndex: i]; |
if([win isKindOfClass: [GLUTWindow class]]) |
num++; |
} |
return num; |
} |
/* CENTRY */ |
void APIENTRY glutCheckLoop(void) |
{ |
GLUTAPI_DECLARATIONS |
GLUTAPI_BEGIN |
if(NSApp == nil) { |
__glutFatalUsage("main loop entered without proper initialization."); // will exit |
} |
[NSApp runOnce]; |
GLUTAPI_END |
} |
void APIENTRY glutMainLoop(void) |
{ |
GLUTAPI_DECLARATIONS |
GLUTAPI_BEGIN |
if(NSApp == nil) { |
__glutFatalUsage("main loop entered without proper initialization."); // will exit |
} |
if(__glutCountWindows() == 0) |
__glutFatalUsage("main loop entered with no windows created."); // will exit |
[NSApp run]; |
GLUTAPI_END |
} |
/* ENDCENTRY */ |
void __glutMapKeyCode(NSEvent *theEvent, char *utf8, int *utf8Length, BOOL *isSpecial) |
{ |
NSString * utf16 = [theEvent characters]; |
const char * ptr; |
int i; |
if(*utf8Length < 1) |
return; |
*isSpecial = NO; |
if([utf16 length] == 1) { |
/* A single UTF16 character. Do one of the following: |
a) Is it in the range [0, 127] ? -> simply return it as a single UTF8 character. |
b) Is it a special character ? -> simply return it as a special character. |
c) Else -> convert the UTF16 string to a UTF8 string and return it. */ |
unichar ukey = [utf16 characterAtIndex: 0]; |
int savedLength = *utf8Length; |
*utf8Length = 1; |
if(ukey <= 127) { |
utf8[0] = (ukey & 0xFF); |
return; |
} |
*isSpecial = YES; |
switch(ukey) { |
case NSF1FunctionKey: |
utf8[0] = GLUT_KEY_F1; |
return; |
case NSF2FunctionKey: |
utf8[0] = GLUT_KEY_F2; |
return; |
case NSF3FunctionKey: |
utf8[0] = GLUT_KEY_F3; |
return; |
case NSF4FunctionKey: |
utf8[0] = GLUT_KEY_F4; |
return; |
case NSF5FunctionKey: |
utf8[0] = GLUT_KEY_F5; |
return; |
case NSF6FunctionKey: |
utf8[0] = GLUT_KEY_F6; |
return; |
case NSF7FunctionKey: |
utf8[0] = GLUT_KEY_F7; |
return; |
case NSF8FunctionKey: |
utf8[0] = GLUT_KEY_F8; |
return; |
case NSF9FunctionKey: |
utf8[0] = GLUT_KEY_F9; |
return; |
case NSF10FunctionKey: |
utf8[0] = GLUT_KEY_F10; |
return; |
case NSF11FunctionKey: |
utf8[0] = GLUT_KEY_F11; |
return; |
case NSF12FunctionKey: |
utf8[0] = GLUT_KEY_F12; |
return; |
case NSUpArrowFunctionKey: |
utf8[0] = GLUT_KEY_UP; |
return; |
case NSDownArrowFunctionKey: |
utf8[0] = GLUT_KEY_DOWN; |
return; |
case NSLeftArrowFunctionKey: |
utf8[0] = GLUT_KEY_LEFT; |
return; |
case NSRightArrowFunctionKey: |
utf8[0] = GLUT_KEY_RIGHT; |
return; |
case NSPageUpFunctionKey: |
utf8[0] = GLUT_KEY_PAGE_UP; |
return; |
case NSPageDownFunctionKey: |
utf8[0] = GLUT_KEY_PAGE_DOWN; |
return; |
case NSHomeFunctionKey: |
utf8[0] = GLUT_KEY_HOME; |
return; |
case NSEndFunctionKey: |
utf8[0] = GLUT_KEY_END; |
return; |
case NSInsertFunctionKey: |
case NSInsertCharFunctionKey: |
utf8[0] = GLUT_KEY_INSERT; |
return; |
case NSDeleteFunctionKey: |
case NSDeleteCharFunctionKey: |
/* This is actually Backspace */ |
*isSpecial = NO; |
utf8[0] = 0x08; |
return; |
} |
*utf8Length = savedLength; |
} |
*isSpecial = NO; |
/* Special handling for non-US keyboards: Some valid ASCII characters |
are generated with the ALT modifier. I.e. on German keyboards, |
the '[' character is generated by entering Alt-5. However, |
generically, we want to ignore the effect which the ALT key |
has on the characters.*/ |
utf16 = [theEvent charactersIgnoringModifiers]; |
/* Still here ? Then either 'utf16' contains more than one UTF16 character |
and thus is most likely a composite character sequence or 'utf16' |
contains a single 'real' Unicode character. */ |
ptr = [utf16 UTF8String]; |
i = 0; |
while(i < *utf8Length && ptr[i] != '\0') { |
utf8[i] = ptr[i]; |
i++; |
} |
*utf8Length = i; |
} |
/** |
* Analyzes the given mouse up/down event and returns in 'buttonID' what mouse |
* button the given event corresponds to. 'modifiers' is set to the modifier |
* flags which should be passed on to the GLUT application. |
*/ |
void __glutMapMouseButton(NSEvent *theEvent, int *buttonID, int *buttonPhysID, unsigned int *modifiers) |
{ |
unsigned int mflags = [theEvent modifierFlags]; |
if ((__glutEmulateMouseButtons) && ([theEvent buttonNumber] == 0)) { |
*buttonPhysID = 0; // main mouse button (left) is physically pressed |
if((mflags & __glutMouseFirstModifiers) == __glutMouseFirstModifiers) { // right |
*buttonID = __glutMouseFirstButton; |
*modifiers = mflags & ~__glutMouseFirstModifiers; |
} else if((mflags & __glutMouseSecondModifiers) == __glutMouseSecondModifiers) { // middle |
*buttonID = __glutMouseSecondButton; |
*modifiers = mflags & ~__glutMouseSecondModifiers; |
} else { |
*buttonID = GLUT_LEFT_BUTTON; |
*modifiers = mflags; |
} |
} else { // handle non-emulated button 0 and all other buttons |
switch([theEvent buttonNumber]) { |
case 0: |
*buttonID = GLUT_LEFT_BUTTON; |
break; |
case 1: |
*buttonID = GLUT_RIGHT_BUTTON; |
break; |
case 2: |
*buttonID = GLUT_MIDDLE_BUTTON; |
break; |
default: |
*buttonID = -1; |
break; |
} |
*buttonPhysID = *buttonID; |
*modifiers = mflags; |
} |
} |
/** |
* Sorts the given middle & right modifiers so that __glutMapMouseButton() will |
* always prefer the mouse button that has the most modifier flags set. |
*/ |
void __glutSetMouseModifiers(unsigned int middleFlags, unsigned int rightFlags) |
{ |
int middleRank = 0, rightRank = 0; |
/* Compute ranks for middle & right flags */ |
if((middleFlags & NSAlternateKeyMask) == NSAlternateKeyMask) |
middleRank++; |
if((middleFlags & NSControlKeyMask) == NSControlKeyMask) |
middleRank++; |
if((middleFlags & NSShiftKeyMask) == NSShiftKeyMask) |
middleRank++; |
if((middleFlags & NSCommandKeyMask) == NSCommandKeyMask) |
middleRank++; |
if((rightFlags & NSAlternateKeyMask) == NSAlternateKeyMask) |
rightRank++; |
if((rightFlags & NSControlKeyMask) == NSControlKeyMask) |
rightRank++; |
if((rightFlags & NSShiftKeyMask) == NSShiftKeyMask) |
rightRank++; |
if((rightFlags & NSCommandKeyMask) == NSCommandKeyMask) |
rightRank++; |
if(middleRank == rightRank) { |
/* Always prefer right mouse clicks over middle ones, if both have equal ranks */ |
__glutMouseFirstModifiers = rightFlags; |
__glutMouseFirstButton = GLUT_RIGHT_BUTTON; |
__glutMouseSecondModifiers = middleFlags; |
__glutMouseSecondButton = GLUT_MIDDLE_BUTTON; |
} else if(middleRank > rightRank) { |
/* Middle mouse button requires more modifiers, __glutMapMouseButton() should check it first */ |
__glutMouseFirstModifiers = middleFlags; |
__glutMouseFirstButton = GLUT_MIDDLE_BUTTON; |
__glutMouseSecondModifiers = rightFlags; |
__glutMouseSecondButton = GLUT_RIGHT_BUTTON; |
} else { |
/* Right mouse button requires more modifiers, __glutMapMouseButton() should check it first */ |
__glutMouseFirstModifiers = rightFlags; |
__glutMouseFirstButton = GLUT_RIGHT_BUTTON; |
__glutMouseSecondModifiers = middleFlags; |
__glutMouseSecondButton = GLUT_MIDDLE_BUTTON; |
} |
/* Remember middle flags explicitly for two button mices */ |
__glutMouseMiddleModifiers = middleFlags; |
} |
Copyright © 2008 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2008-02-08