diff options
Diffstat (limited to 'src/newt/native/NewtMacWindow.m')
-rw-r--r-- | src/newt/native/NewtMacWindow.m | 1210 |
1 files changed, 706 insertions, 504 deletions
diff --git a/src/newt/native/NewtMacWindow.m b/src/newt/native/NewtMacWindow.m index f914467af..b4133ac7e 100644 --- a/src/newt/native/NewtMacWindow.m +++ b/src/newt/native/NewtMacWindow.m @@ -36,42 +36,140 @@ #import "KeyEvent.h" #import "MouseEvent.h" -jint GetDeltaY(NSEvent *event, jint javaMods) { - CGFloat deltaY = 0.0; +#include <CoreFoundation/CoreFoundation.h> +#include <Carbon/Carbon.h> /* For kVK_ constants, and TIS functions. */ + +#include <math.h> + +#define PRINTF(...) NSLog(@ __VA_ARGS__) + +static jfloat GetDelta(NSEvent *event, jint javaMods[]) { CGEventRef cgEvent = [event CGEvent]; + CGFloat deltaY = 0.0; + CGFloat deltaX = 0.0; + CGFloat delta = 0.0; if (CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventIsContinuous)) { // mouse pad case - deltaY = - CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1); - // fprintf(stderr, "WHEEL/PAD: %lf\n", (double)deltaY); + deltaX = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2); + deltaY = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1); + // fprintf(stderr, "WHEEL/PAD: %lf/%lf - 0x%X\n", (double)deltaX, (double)deltaY, javaMods[0]); + if( fabsf(deltaX) > fabsf(deltaY) ) { + javaMods[0] |= EVENT_SHIFT_MASK; + delta = deltaX; + } else { + delta = deltaY; + } } else { // traditional mouse wheel case + deltaX = [event deltaX]; deltaY = [event deltaY]; - // fprintf(stderr, "WHEEL/TRAD: %lf\n", (double)deltaY); - if (deltaY == 0.0 && (javaMods & EVENT_SHIFT_MASK) != 0) { + // fprintf(stderr, "WHEEL/TRACK: %lf/%lf - 0x%X\n", (double)deltaX, (double)deltaY, javaMods[0]); + if (deltaY == 0.0 && (javaMods[0] & EVENT_SHIFT_MASK) != 0) { // shift+vertical wheel scroll produces horizontal scroll // we convert it to vertical - deltaY = [event deltaX]; + delta = deltaX; + } else { + delta = deltaY; } - if (-1.0 < deltaY && deltaY < 1.0) { - deltaY *= 10.0; + if (-1.0 < delta && delta < 1.0) { + delta *= 10.0; } else { - if (deltaY < 0.0) { - deltaY = deltaY - 0.5f; + if (delta < 0.0) { + delta = delta - 0.5f; } else { - deltaY = deltaY + 0.5f; + delta = delta + 0.5f; + } + } + } + // fprintf(stderr, "WHEEL/RES: %lf - 0x%X\n", (double)delta, javaMods[0]); + return (jfloat) delta; +} + +#define kVK_Shift 0x38 +#define kVK_Option 0x3A +#define kVK_Control 0x3B +#define kVK_Command 0x37 + +static jint mods2JavaMods(NSUInteger mods) +{ + int javaMods = 0; + if (mods & NSShiftKeyMask) { + javaMods |= EVENT_SHIFT_MASK; + } + if (mods & NSControlKeyMask) { + javaMods |= EVENT_CTRL_MASK; + } + if (mods & NSCommandKeyMask) { + javaMods |= EVENT_META_MASK; + } + if (mods & NSAlternateKeyMask) { + javaMods |= EVENT_ALT_MASK; + } + return javaMods; +} + +static CFStringRef CKCH_CreateStringForKey(CGKeyCode keyCode, const UCKeyboardLayout *keyboardLayout) { + UInt32 keysDown = 0; + UniChar chars[4]; + UniCharCount realLength; + + UCKeyTranslate(keyboardLayout, keyCode, + kUCKeyActionDisplay, 0, + LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, + &keysDown, sizeof(chars) / sizeof(chars[0]), &realLength, chars); + + return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1); +} + +static CFMutableDictionaryRef CKCH_CreateCodeToCharDict(TISInputSourceRef keyboard) { + CFDataRef layoutData = (CFDataRef) TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData); + const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); + + CFMutableDictionaryRef codeToCharDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 128, NULL, NULL); + if ( NULL != codeToCharDict ) { + intptr_t i; + for (i = 0; i < 128; ++i) { + CFStringRef string = CKCH_CreateStringForKey((CGKeyCode)i, keyboardLayout); + if( NULL != string ) { + CFIndex stringLen = CFStringGetLength (string); + if ( 0 < stringLen ) { + UniChar character = CFStringGetCharacterAtIndex(string, 0); + DBG_PRINT("CKCH: MAP 0x%X -> %c\n", (int)i, character); + CFDictionaryAddValue(codeToCharDict, (const void *)i, (const void *)(intptr_t)character); + } + CFRelease(string); } } } - // fprintf(stderr, "WHEEL/res: %d\n", (int)deltaY); - return (jint) deltaY; + return codeToCharDict; +} + +static CFMutableDictionaryRef CKCH_USCodeToNNChar = NULL; + +static void CKCH_CreateDictionaries() { + TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); + CKCH_USCodeToNNChar = CKCH_CreateCodeToCharDict(currentKeyboard); + CFRelease(currentKeyboard); +} + +static UniChar CKCH_CharForKeyCode(jshort keyCode) { + UniChar rChar = 0; + + if ( NULL != CKCH_USCodeToNNChar ) { + intptr_t code = (intptr_t) keyCode; + intptr_t character = 0; + + if ( CFDictionaryGetValueIfPresent(CKCH_USCodeToNNChar, (void *)code, (const void **)&character) ) { + rChar = (UniChar) character; + DBG_PRINT("CKCH: OK 0x%X -> 0x%X\n", (int)keyCode, (int)rChar); + } + } + return rChar; } static jmethodID enqueueMouseEventID = NULL; -static jmethodID sendMouseEventID = NULL; static jmethodID enqueueKeyEventID = NULL; -static jmethodID sendKeyEventID = NULL; static jmethodID requestFocusID = NULL; static jmethodID insetsChangedID = NULL; @@ -82,24 +180,21 @@ static jmethodID focusChangedID = NULL; static jmethodID windowDestroyNotifyID = NULL; static jmethodID windowRepaintID = NULL; -// Can't use USE_SENDIO_DIRECT, ie w/o enqueueing to EDT, +// Need to enqueue all events to EDT, // since we may operate on AWT-AppKit (Main Thread) // and direct issuing 'requestFocus()' would deadlock: // AWT-AppKit // AWT-EventQueue-0 -// -// #define USE_SENDIO_DIRECT 1 @implementation NewtView - (id)initWithFrame:(NSRect)frameRect { + id res = [super initWithFrame:frameRect]; javaWindowObject = NULL; - jvmHandle = NULL; - jvmVersion = 0; destroyNotifySent = NO; - softLocked = NO; + softLockCount = 0; pthread_mutexattr_t softLockSyncAttr; pthread_mutexattr_init(&softLockSyncAttr); @@ -107,59 +202,46 @@ static jmethodID windowRepaintID = NULL; pthread_mutex_init(&softLockSync, &softLockSyncAttr); // recursive ptrTrackingTag = 0; - - /** - NSCursor crs = [NSCursor arrowCursor]; - NSImage crsImg = [crs image]; - NSPoint crsHot = [crs hotSpot]; - myCursor = [[NSCursor alloc] initWithImage: crsImg hotSpot:crsHot]; - */ myCursor = NULL; - return [super initWithFrame:frameRect]; + modsDown[0] = NO; // shift + modsDown[1] = NO; // ctrl + modsDown[2] = NO; // alt + modsDown[3] = NO; // win + mouseConfined = NO; + mouseVisible = YES; + mouseInside = NO; + cursorIsHidden = NO; + + DBG_PRINT("NewtView::create: %p (refcnt %d)\n", res, (int)[res retainCount]); + return res; } +#ifdef DBG_LIFECYCLE - (void) release { -#ifdef VERBOSE_ON - NSLog(@"NewtView::release\n"); - NSLog(@"%@",[NSThread callStackSymbols]); -#endif + DBG_PRINT("NewtView::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); [super release]; } +#endif - (void) dealloc { - if(softLocked) { + DBG_PRINT("NewtView::dealloc.0: %p (refcnt %d), ptrTrackingTag %d\n", self, (int)[self retainCount], (int)ptrTrackingTag); +#ifdef DBG_LIFECYCLE + NSLog(@"%@",[NSThread callStackSymbols]); +#endif + if( 0 < softLockCount ) { NSLog(@"NewtView::dealloc: softLock still hold @ dealloc!\n"); } + [self removeCursorRects]; + [self removeMyCursor]; + pthread_mutex_destroy(&softLockSync); -#ifdef VERBOSE_ON - NSLog(@"NewtView::dealloc\n"); - NSLog(@"%@",[NSThread callStackSymbols]); -#endif + DBG_PRINT("NewtView::dealloc.X: %p\n", self); [super dealloc]; } -- (void) setJVMHandle: (JavaVM*) vm -{ - jvmHandle = vm; -} -- (JavaVM*) getJVMHandle -{ - return jvmHandle; -} - -- (void) setJVMVersion: (int) ver -{ - jvmVersion = ver; -} - -- (int) getJVMVersion -{ - return jvmVersion; -} - - (void) setJavaWindowObject: (jobject) javaWindowObj { javaWindowObject = javaWindowObj; @@ -170,32 +252,6 @@ static jmethodID windowRepaintID = NULL; return javaWindowObject; } -- (void) rightMouseDown: (NSEvent*) theEvent -{ - NSResponder* next = [self nextResponder]; - if (next != nil) { - [next rightMouseDown: theEvent]; - } -} - -- (void) resetCursorRects -{ - [super resetCursorRects]; - - if(0 != ptrTrackingTag) { - // [self removeCursorRect: ptrRect cursor: myCursor]; - [self removeTrackingRect: ptrTrackingTag]; - } - ptrRect = [self bounds]; - // [self addCursorRect: ptrRect cursor: myCursor]; - ptrTrackingTag = [self addTrackingRect: ptrRect owner: self userData: nil assumeInside: NO]; -} - -- (NSCursor *) cursor -{ - return myCursor; -} - - (void) setDestroyNotifySent: (BOOL) v { destroyNotifySent = v; @@ -209,18 +265,27 @@ static jmethodID windowRepaintID = NULL; - (BOOL) softLock { // DBG_PRINT("*************** softLock.0: %p\n", (void*)pthread_self()); - // NSLog(@"NewtView::softLock: %@",[NSThread callStackSymbols]); - pthread_mutex_lock(&softLockSync); - softLocked = YES; + int err; + if( 0 != ( err = pthread_mutex_lock(&softLockSync) ) ) { + NSLog(@"NewtView::softLock failed: errCode %d - %@", err, [NSThread callStackSymbols]); + return NO; + } + softLockCount++; // DBG_PRINT("*************** softLock.X: %p\n", (void*)pthread_self()); - return softLocked; + return 0 < softLockCount; } -- (void) softUnlock +- (BOOL) softUnlock { // DBG_PRINT("*************** softUnlock: %p\n", (void*)pthread_self()); - softLocked = NO; - pthread_mutex_unlock(&softLockSync); + softLockCount--; + int err; + if( 0 != ( err = pthread_mutex_unlock(&softLockSync) ) ) { + softLockCount++; + NSLog(@"NewtView::softUnlock failed: Not locked by current thread - errCode %d - %@", err, [NSThread callStackSymbols]); + return NO; + } + return YES; } - (BOOL) needsDisplay @@ -256,7 +321,7 @@ static jmethodID windowRepaintID = NULL; return; } int shallBeDetached = 0; - JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, jvmVersion, &shallBeDetached); + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); if(NULL==env) { DBG_PRINT("drawRect: null JNIEnv\n"); return; @@ -268,9 +333,8 @@ static jmethodID windowRepaintID = NULL; dirtyRect.origin.x, viewFrame.size.height - dirtyRect.origin.y, dirtyRect.size.width, dirtyRect.size.height); - if (shallBeDetached) { - (*jvmHandle)->DetachCurrentThread(jvmHandle); - } + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); } - (void) viewDidHide @@ -280,7 +344,7 @@ static jmethodID windowRepaintID = NULL; return; } int shallBeDetached = 0; - JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, jvmVersion, &shallBeDetached); + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); if(NULL==env) { DBG_PRINT("viewDidHide: null JNIEnv\n"); return; @@ -288,9 +352,8 @@ static jmethodID windowRepaintID = NULL; (*env)->CallVoidMethod(env, javaWindowObject, visibleChangedID, JNI_FALSE, JNI_FALSE); - if (shallBeDetached) { - (*jvmHandle)->DetachCurrentThread(jvmHandle); - } + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); [super viewDidHide]; } @@ -302,7 +365,7 @@ static jmethodID windowRepaintID = NULL; return; } int shallBeDetached = 0; - JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, jvmVersion, &shallBeDetached); + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); if(NULL==env) { DBG_PRINT("viewDidUnhide: null JNIEnv\n"); return; @@ -310,9 +373,8 @@ static jmethodID windowRepaintID = NULL; (*env)->CallVoidMethod(env, javaWindowObject, visibleChangedID, JNI_FALSE, JNI_TRUE); - if (shallBeDetached) { - (*jvmHandle)->DetachCurrentThread(jvmHandle); - } + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); [super viewDidUnhide]; } @@ -322,232 +384,140 @@ static jmethodID windowRepaintID = NULL; return YES; } -@end - -@implementation NewtMacWindow - -+ (BOOL) initNatives: (JNIEnv*) env forClass: (jclass) clazz +- (BOOL) becomeFirstResponder { - enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZIIIIII)V"); - sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V"); - enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZIIIC)V"); - sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); - sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(ZIIZ)V"); - visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(ZZ)V"); - insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(ZIIII)V"); - positionChangedID = (*env)->GetMethodID(env, clazz, "positionChanged", "(ZII)V"); - focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(ZZ)V"); - windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "(Z)Z"); - windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(ZIIII)V"); - requestFocusID = (*env)->GetMethodID(env, clazz, "requestFocus", "(Z)V"); - if (enqueueMouseEventID && sendMouseEventID && enqueueKeyEventID && sendKeyEventID && sizeChangedID && visibleChangedID && insetsChangedID && - positionChangedID && focusChangedID && windowDestroyNotifyID && requestFocusID && windowRepaintID) - { - return YES; - } - return NO; + DBG_PRINT( "*************** View.becomeFirstResponder\n"); + return [super becomeFirstResponder]; } -- (id) initWithContentRect: (NSRect) contentRect - styleMask: (NSUInteger) windowStyle - backing: (NSBackingStoreType) bufferingType - defer: (BOOL) deferCreation - screen:(NSScreen *)screen - isFullscreenWindow:(BOOL)isfs +- (BOOL) resignFirstResponder { - id res = [super initWithContentRect: contentRect - styleMask: windowStyle - backing: bufferingType - defer: deferCreation - screen: screen]; - isFullscreenWindow = isfs; - // Why is this necessary? Without it we don't get any of the - // delegate methods like resizing and window movement. - [self setDelegate: self]; - cachedInsets[0] = 0; // l - cachedInsets[1] = 0; // r - cachedInsets[2] = 0; // t - cachedInsets[3] = 0; // b - mouseConfined = NO; - mouseVisible = YES; - mouseInside = NO; - cursorIsHidden = NO; - realized = YES; - return res; + DBG_PRINT( "*************** View.resignFirstResponder\n"); + return [super resignFirstResponder]; } -- (void) release +- (void) removeCursorRects { -#ifdef VERBOSE_ON - NSLog(@"NewtWindow::release\n"); - NSLog(@"%@",[NSThread callStackSymbols]); -#endif - [super release]; + if(0 != ptrTrackingTag) { + if(NULL != myCursor) { + [self removeCursorRect: ptrRect cursor: myCursor]; + } + [self removeTrackingRect: ptrTrackingTag]; + ptrTrackingTag = 0; + } } -- (void) dealloc +- (void) addCursorRects { -#ifdef VERBOSE_ON - NSLog(@"NewtWindow::dealloc\n"); - NSLog(@"%@",[NSThread callStackSymbols]); -#endif - [super dealloc]; + ptrRect = [self bounds]; + if(NULL != myCursor) { + [self addCursorRect: ptrRect cursor: myCursor]; + } + ptrTrackingTag = [self addTrackingRect: ptrRect owner: self userData: nil assumeInside: NO]; } -- (void) setUnrealized +- (void) removeMyCursor { - realized = NO; + if(NULL != myCursor) { + [myCursor release]; + myCursor = NULL; + } } -- (BOOL) isRealized +- (void) resetCursorRects { - return realized; + [super resetCursorRects]; + + [self removeCursorRects]; + [self addCursorRects]; } -- (void) updateInsets: (JNIEnv*) env +- (void) setPointerIcon: (NSCursor*)c { - NSView* nsview = [self contentView]; - if( ! [nsview isMemberOfClass:[NewtView class]] ) { - return; + DBG_PRINT( "setPointerIcon: %p -> %p, top %p, mouseInside %d\n", myCursor, c, [NSCursor currentCursor], (int)mouseInside); + if( c != myCursor ) { + [self removeCursorRects]; + [self removeMyCursor]; + myCursor = c; + if( NULL != myCursor ) { + [myCursor retain]; + } } - NewtView* view = (NewtView *) nsview; - jobject javaWindowObject = [view getJavaWindowObject]; - if (env==NULL || javaWindowObject == NULL) { - return; + NSWindow* nsWin = [self window]; + if( NULL != nsWin ) { + [nsWin invalidateCursorRectsForView: self]; } - - NSRect frameRect = [self frame]; - NSRect contentRect = [self contentRectForFrameRect: frameRect]; - - // note: this is a simplistic implementation which doesn't take - // into account DPI and scaling factor - CGFloat l = contentRect.origin.x - frameRect.origin.x; - cachedInsets[0] = (int)l; // l - cachedInsets[1] = (int)(frameRect.size.width - (contentRect.size.width + l)); // r - cachedInsets[2] = (jint)(frameRect.size.height - contentRect.size.height); // t - cachedInsets[3] = (jint)(contentRect.origin.y - frameRect.origin.y); // b - - DBG_PRINT( "updateInsets: [ l %d, r %d, t %d, b %d ]\n", cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3]); - - (*env)->CallVoidMethod(env, javaWindowObject, insetsChangedID, JNI_FALSE, cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3]); } -- (void) attachToParent: (NSWindow*) parent -{ - DBG_PRINT( "attachToParent.1\n"); - [parent addChildWindow: self ordered: NSWindowAbove]; - DBG_PRINT( "attachToParent.2\n"); - [self setParentWindow: parent]; - DBG_PRINT( "attachToParent.X\n"); -} - -- (void) detachFromParent: (NSWindow*) parent +- (void) mouseEntered: (NSEvent*) theEvent { - DBG_PRINT( "detachFromParent.1\n"); - [self setParentWindow: nil]; - if(NULL != parent) { - DBG_PRINT( "detachFromParent.2\n"); - [parent removeChildWindow: self]; + DBG_PRINT( "mouseEntered: confined %d, visible %d, PointerIcon %p, top %p\n", mouseConfined, mouseVisible, myCursor, [NSCursor currentCursor]); + mouseInside = YES; + [self cursorHide: !mouseVisible enter: 1]; + if(NO == mouseConfined) { + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_ENTERED]; + } + NSWindow* nsWin = [self window]; + if( NULL != nsWin ) { + [nsWin makeFirstResponder: self]; } - DBG_PRINT( "detachFromParent.X\n"); -} - -/** - * p abs screen position w/ top-left origin - * returns: abs screen position w/ bottom-left origin - */ -- (NSPoint) newtScreenWinPos2OSXScreenPos: (NSPoint) p -{ - NSView* mView = [self contentView]; - NSRect mViewFrame = [mView frame]; - int totalHeight = mViewFrame.size.height + cachedInsets[2] + cachedInsets[3]; // height + insets[top+bottom] - - NSScreen* screen = [self screen]; - NSRect screenFrame = [screen frame]; - - return NSMakePoint(screenFrame.origin.x + p.x + cachedInsets[0], - screenFrame.origin.y + screenFrame.size.height - p.y - totalHeight); -} - -/** - * p rel client window position w/ top-left origin - * returns: abs screen position w/ bottom-left origin - */ -- (NSPoint) newtClientWinPos2OSXScreenPos: (NSPoint) p -{ - NSRect winFrame = [self frame]; - - NSView* mView = [self contentView]; - NSRect mViewFrame = [mView frame]; - - return NSMakePoint(winFrame.origin.x + p.x, - winFrame.origin.y + ( mViewFrame.size.height - p.y ) ); // y-flip in view } -/** - * y-flips input / output - * p rel client window position w/ top-left origin - * returns: location in 0/0 top-left space. - */ -- (NSPoint) getLocationOnScreen: (NSPoint) p +- (void) mouseExited: (NSEvent*) theEvent { - NSScreen* screen = [self screen]; - NSRect screenRect = [screen frame]; - - NSView* view = [self contentView]; - NSRect viewFrame = [view frame]; - - NSRect r; - r.origin.x = p.x; - r.origin.y = viewFrame.size.height - p.y; // y-flip - r.size.width = 0; - r.size.height = 0; - // NSRect rS = [win convertRectToScreen: r]; // 10.7 - NSPoint oS = [self convertBaseToScreen: r.origin]; - oS.y = screenRect.origin.y + screenRect.size.height - oS.y; - return oS; + DBG_PRINT( "mouseExited: confined %d, visible %d, PointerIcon %p, top %p\n", mouseConfined, mouseVisible, myCursor, [NSCursor currentCursor]); + if(NO == mouseConfined) { + mouseInside = NO; + [self cursorHide: NO enter: -1]; + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_EXITED]; + [self resignFirstResponder]; + } else { + [self setMousePosition: lastInsideMousePosition]; + } } -- (NSPoint) screenPos2NewtClientWinPos: (NSPoint) p +- (void) setMousePosition:(NSPoint)p { - NSView* view = [self contentView]; - NSRect viewFrame = [view frame]; + NSWindow* nsWin = [self window]; + if( NULL != nsWin ) { + NSScreen* screen = [nsWin screen]; + NSRect screenRect = [screen frame]; - NSRect r; - r.origin.x = p.x; - r.origin.y = p.y; - r.size.width = 0; - r.size.height = 0; - // NSRect rS = [win convertRectFromScreen: r]; // 10.7 - NSPoint oS = [self convertScreenToBase: r.origin]; - oS.y = viewFrame.size.height - oS.y; // y-flip - return oS; + CGPoint pt = { p.x, screenRect.size.height - p.y }; // y-flip (CG is top-left origin) + CGEventRef ev = CGEventCreateMouseEvent (NULL, kCGEventMouseMoved, pt, kCGMouseButtonLeft); + CGEventPost (kCGHIDEventTap, ev); + } } -- (BOOL) isMouseInside +- (BOOL) updateMouseInside { - NSView* view = [self contentView]; - NSRect viewFrame = [view frame]; + NSRect viewFrame = [self frame]; NSPoint l1 = [NSEvent mouseLocation]; NSPoint l0 = [self screenPos2NewtClientWinPos: l1]; - return viewFrame.origin.x <= l0.x && l0.x < (viewFrame.origin.x+viewFrame.size.width) && - viewFrame.origin.y <= l0.y && l0.y < (viewFrame.origin.y+viewFrame.size.height) ; + mouseInside = viewFrame.origin.x <= l0.x && l0.x < (viewFrame.origin.x+viewFrame.size.width) && + viewFrame.origin.y <= l0.y && l0.y < (viewFrame.origin.y+viewFrame.size.height) ; + return mouseInside; } - (void) setMouseVisible:(BOOL)v hasFocus:(BOOL)focus { mouseVisible = v; - mouseInside = [self isMouseInside]; + [self updateMouseInside]; DBG_PRINT( "setMouseVisible: confined %d, visible %d (current: %d), mouseInside %d, hasFocus %d\n", mouseConfined, mouseVisible, !cursorIsHidden, mouseInside, focus); if(YES == focus && YES == mouseInside) { - [self cursorHide: !mouseVisible]; + [self cursorHide: !mouseVisible enter: 0]; } } +- (BOOL) isMouseVisible +{ + return mouseVisible; +} -- (void) cursorHide:(BOOL)v +- (void) cursorHide:(BOOL)v enter:(int)enterState { - DBG_PRINT( "cursorHide: %d -> %d\n", cursorIsHidden, v); + DBG_PRINT( "cursorHide: %d -> %d, enter %d; PointerIcon: %p, top %p\n", + cursorIsHidden, v, enterState, myCursor, [NSCursor currentCursor]); if(v) { if(!cursorIsHidden) { [NSCursor hide]; @@ -567,108 +537,105 @@ static jmethodID windowRepaintID = NULL; DBG_PRINT( "setMouseConfined: confined %d, visible %d\n", mouseConfined, mouseVisible); } -- (void) setMousePosition:(NSPoint)p +- (void) mouseMoved: (NSEvent*) theEvent { - NSScreen* screen = [self screen]; - NSRect screenRect = [screen frame]; + if( mouseInside ) { + NSCursor * currentCursor = [NSCursor currentCursor]; + BOOL setCursor = NULL != myCursor && NO == cursorIsHidden && currentCursor != myCursor; + DBG_PRINT( "mouseMoved.set: %d; mouseInside %d, CursorHidden %d, PointerIcon: %p, top %p\n", + setCursor, mouseInside, cursorIsHidden, myCursor, currentCursor); + if( setCursor ) { + // FIXME: Workaround missing NSCursor update for 'fast moving' pointer + [myCursor set]; + } + lastInsideMousePosition = [NSEvent mouseLocation]; + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; + } +} - CGPoint pt = { p.x, screenRect.size.height - p.y }; // y-flip (CG is top-left origin) - CGEventRef ev = CGEventCreateMouseEvent (NULL, kCGEventMouseMoved, pt, kCGMouseButtonLeft); - CGEventPost (kCGHIDEventTap, ev); +- (void) scrollWheel: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_WHEEL_MOVED]; } -static jint mods2JavaMods(NSUInteger mods) +- (void) mouseDown: (NSEvent*) theEvent { - int javaMods = 0; - if (mods & NSShiftKeyMask) { - javaMods |= EVENT_SHIFT_MASK; - } - if (mods & NSControlKeyMask) { - javaMods |= EVENT_CTRL_MASK; - } - if (mods & NSCommandKeyMask) { - javaMods |= EVENT_META_MASK; - } - if (mods & NSAlternateKeyMask) { - javaMods |= EVENT_ALT_MASK; - } - return javaMods; + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; } -- (void) sendKeyEvent: (NSEvent*) event eventType: (jint) evType +- (void) mouseDragged: (NSEvent*) theEvent { - NSView* nsview = [self contentView]; - if( ! [nsview isMemberOfClass:[NewtView class]] ) { - return; - } - NewtView* view = (NewtView *) nsview; - jobject javaWindowObject = [view getJavaWindowObject]; - if (javaWindowObject == NULL) { - DBG_PRINT("sendKeyEvent: null javaWindowObject\n"); - return; - } - int shallBeDetached = 0; - JavaVM *jvmHandle = [view getJVMHandle]; - JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, [view getJVMVersion], &shallBeDetached); - if(NULL==env) { - DBG_PRINT("sendKeyEvent: null JNIEnv\n"); - return; + lastInsideMousePosition = [NSEvent mouseLocation]; + // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} + +- (void) mouseUp: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; +} + +- (void) rightMouseDown: (NSEvent*) theEvent +{ + NSResponder* next = [self nextResponder]; + if (next != nil) { + [next rightMouseDown: theEvent]; } + // FIXME: ^^ OR [super rightMouseDown: theEvent] ? + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; +} - int i; - jint keyCode = (jint) [event keyCode]; - NSString* chars = [event charactersIgnoringModifiers]; - int len = [chars length]; - jint javaMods = mods2JavaMods([event modifierFlags]); +- (void) rightMouseDragged: (NSEvent*) theEvent +{ + lastInsideMousePosition = [NSEvent mouseLocation]; + // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} - for (i = 0; i < len; i++) { - // Note: the key code in the NSEvent does not map to anything we can use - jchar keyChar = (jchar) [chars characterAtIndex: i]; +- (void) rightMouseUp: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; +} - DBG_PRINT("sendKeyEvent: %d/%d char 0x%X, code 0x%X\n", i, len, (int)keyChar, (int)keyCode); +- (void) otherMouseDown: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; +} - #ifdef USE_SENDIO_DIRECT - (*env)->CallVoidMethod(env, javaWindowObject, sendKeyEventID, - evType, javaMods, keyCode, keyChar); - #else - (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID, JNI_FALSE, - evType, javaMods, keyCode, keyChar); - #endif - } +- (void) otherMouseDragged: (NSEvent*) theEvent +{ + lastInsideMousePosition = [NSEvent mouseLocation]; + // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} - if (shallBeDetached) { - (*jvmHandle)->DetachCurrentThread(jvmHandle); - } +- (void) otherMouseUp: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; } -- (void) sendMouseEvent: (NSEvent*) event eventType: (jint) evType +- (void) sendMouseEvent: (NSEvent*) event eventType: (jshort) evType { - NSView* nsview = [self contentView]; - if( ! [nsview isMemberOfClass:[NewtView class]] ) { - return; - } - NewtView* view = (NewtView *) nsview; - jobject javaWindowObject = [view getJavaWindowObject]; if (javaWindowObject == NULL) { DBG_PRINT("sendMouseEvent: null javaWindowObject\n"); return; } int shallBeDetached = 0; - JavaVM *jvmHandle = [view getJVMHandle]; - JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, [view getJVMVersion], &shallBeDetached); + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); if(NULL==env) { DBG_PRINT("sendMouseEvent: null JNIEnv\n"); return; } - jint javaMods = mods2JavaMods([event modifierFlags]); + jint javaMods[] = { 0 } ; + javaMods[0] = mods2JavaMods([event modifierFlags]); // convert to 1-based button number (or use zero if no button is involved) // TODO: detect mouse button when mouse wheel scrolled - jint javaButtonNum = 0; - jint scrollDeltaY = 0; + jshort javaButtonNum = 0; + jfloat scrollDeltaY = 0.0f; switch ([event type]) { case NSScrollWheel: { - scrollDeltaY = GetDeltaY(event, javaMods); + scrollDeltaY = GetDelta(event, javaMods); javaButtonNum = 1; break; } @@ -699,246 +666,484 @@ static jint mods2JavaMods(NSUInteger mods) NSPoint location = [self screenPos2NewtClientWinPos: [NSEvent mouseLocation]]; - #ifdef USE_SENDIO_DIRECT - (*env)->CallVoidMethod(env, javaWindowObject, sendMouseEventID, - evType, javaMods, - (jint) location.x, (jint) location.y, - javaButtonNum, scrollDeltaY); - #else (*env)->CallVoidMethod(env, javaWindowObject, enqueueMouseEventID, JNI_FALSE, - evType, javaMods, + evType, javaMods[0], (jint) location.x, (jint) location.y, javaButtonNum, scrollDeltaY); - #endif - if (shallBeDetached) { - (*jvmHandle)->DetachCurrentThread(jvmHandle); - } + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); } -- (void) focusChanged: (BOOL) gained +- (NSPoint) screenPos2NewtClientWinPos: (NSPoint) p { - DBG_PRINT( "focusChanged: gained %d\n", gained); - NSView* nsview = [self contentView]; - if( ! [nsview isMemberOfClass:[NewtView class]] ) { - return; + NSRect viewFrame = [self frame]; + + NSRect r; + r.origin.x = p.x; + r.origin.y = p.y; + r.size.width = 0; + r.size.height = 0; + // NSRect rS = [[self window] convertRectFromScreen: r]; // 10.7 + NSPoint oS = [[self window] convertScreenToBase: r.origin]; + oS.y = viewFrame.size.height - oS.y; // y-flip + return oS; +} + +- (void) handleFlagsChanged:(NSUInteger) mods +{ + [self handleFlagsChanged: NSShiftKeyMask keyIndex: 0 keyCode: kVK_Shift modifiers: mods]; + [self handleFlagsChanged: NSControlKeyMask keyIndex: 1 keyCode: kVK_Control modifiers: mods]; + [self handleFlagsChanged: NSAlternateKeyMask keyIndex: 2 keyCode: kVK_Option modifiers: mods]; + [self handleFlagsChanged: NSCommandKeyMask keyIndex: 3 keyCode: kVK_Command modifiers: mods]; +} + +- (void) handleFlagsChanged:(int) keyMask keyIndex: (int) keyIdx keyCode: (int) keyCode modifiers: (NSUInteger) mods +{ + if ( NO == modsDown[keyIdx] && 0 != ( mods & keyMask ) ) { + modsDown[keyIdx] = YES; + [self sendKeyEvent: (jshort)keyCode characters: NULL modifiers: mods|keyMask eventType: (jshort)EVENT_KEY_PRESSED]; + } else if ( YES == modsDown[keyIdx] && 0 == ( mods & keyMask ) ) { + modsDown[keyIdx] = NO; + [self sendKeyEvent: (jshort)keyCode characters: NULL modifiers: mods|keyMask eventType: (jshort)EVENT_KEY_RELEASED]; } - NewtView* view = (NewtView *) nsview; - jobject javaWindowObject = [view getJavaWindowObject]; +} + +- (void) sendKeyEvent: (NSEvent*) event eventType: (jshort) evType +{ + jshort keyCode = (jshort) [event keyCode]; + NSString* chars = [event charactersIgnoringModifiers]; + NSUInteger mods = [event modifierFlags]; + [self sendKeyEvent: keyCode characters: chars modifiers: mods eventType: evType]; +} + +- (void) sendKeyEvent: (jshort) keyCode characters: (NSString*) chars modifiers: (NSUInteger)mods eventType: (jshort) evType +{ if (javaWindowObject == NULL) { - DBG_PRINT("focusChanged: null javaWindowObject\n"); + DBG_PRINT("sendKeyEvent: null javaWindowObject\n"); return; } int shallBeDetached = 0; - JavaVM *jvmHandle = [view getJVMHandle]; - JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, [view getJVMVersion], &shallBeDetached); + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); if(NULL==env) { - DBG_PRINT("focusChanged: null JNIEnv\n"); + DBG_PRINT("sendKeyEvent: null JNIEnv\n"); return; } - (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_FALSE, (gained == YES) ? JNI_TRUE : JNI_FALSE); + int i; + int len = NULL != chars ? [chars length] : 0; + jint javaMods = mods2JavaMods(mods); + + if(len > 0) { + // printable chars + for (i = 0; i < len; i++) { + // Note: the key code in the NSEvent does not map to anything we can use + UniChar keyChar = (UniChar) [chars characterAtIndex: i]; + UniChar keySymChar = CKCH_CharForKeyCode(keyCode); + + DBG_PRINT("sendKeyEvent: %d/%d code 0x%X, char 0x%X, mods 0x%X/0x%X -> keySymChar 0x%X\n", i, len, (int)keyCode, (int)keyChar, + (int)mods, (int)javaMods, (int)keySymChar); + + (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID, JNI_FALSE, + evType, javaMods, keyCode, (jchar)keyChar, (jchar)keySymChar); + } + } else { + // non-printable chars + jchar keyChar = (jchar) 0; + + DBG_PRINT("sendKeyEvent: code 0x%X\n", (int)keyCode); - if (shallBeDetached) { - (*jvmHandle)->DetachCurrentThread(jvmHandle); + (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID, JNI_FALSE, + evType, javaMods, keyCode, keyChar, keyChar); } + + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); } -- (BOOL) becomeFirstResponder +@end + +@implementation NewtMacWindow + ++ (BOOL) initNatives: (JNIEnv*) env forClass: (jclass) clazz { - DBG_PRINT( "*************** becomeFirstResponder\n"); - return [super becomeFirstResponder]; + enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZSIIISF)V"); + enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZSISCC)V"); + sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(ZIIZ)V"); + visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(ZZ)V"); + insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(ZIIII)V"); + positionChangedID = (*env)->GetMethodID(env, clazz, "screenPositionChanged", "(ZII)V"); + focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(ZZ)V"); + windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "(Z)Z"); + windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(ZIIII)V"); + requestFocusID = (*env)->GetMethodID(env, clazz, "requestFocus", "(Z)V"); + if (enqueueMouseEventID && enqueueKeyEventID && sizeChangedID && visibleChangedID && insetsChangedID && + positionChangedID && focusChangedID && windowDestroyNotifyID && requestFocusID && windowRepaintID) + { + CKCH_CreateDictionaries(); + return YES; + } + return NO; } -- (BOOL) resignFirstResponder +- (id) initWithContentRect: (NSRect) contentRect + styleMask: (NSUInteger) windowStyle + backing: (NSBackingStoreType) bufferingType + defer: (BOOL) deferCreation + isFullscreenWindow:(BOOL)isfs { - DBG_PRINT( "*************** resignFirstResponder\n"); - return [super resignFirstResponder]; + id res = [super initWithContentRect: contentRect + styleMask: windowStyle + backing: bufferingType + defer: deferCreation]; + // OSX 10.6 + if ( [NSApp respondsToSelector:@selector(currentSystemPresentationOptions)] && + [NSApp respondsToSelector:@selector(setPresentationOptions:)] ) { + hasPresentationSwitch = YES; + defaultPresentationOptions = [NSApp currentSystemPresentationOptions]; + fullscreenPresentationOptions = + // NSApplicationPresentationDefault| + // NSApplicationPresentationAutoHideDock| + NSApplicationPresentationHideDock| + // NSApplicationPresentationAutoHideMenuBar| + NSApplicationPresentationHideMenuBar| + NSApplicationPresentationDisableAppleMenu| + // NSApplicationPresentationDisableProcessSwitching| + // NSApplicationPresentationDisableSessionTermination| + NSApplicationPresentationDisableHideApplication| + // NSApplicationPresentationDisableMenuBarTransparency| + // NSApplicationPresentationFullScreen| // OSX 10.7 + 0 ; + } else { + hasPresentationSwitch = NO; + defaultPresentationOptions = 0; + fullscreenPresentationOptions = 0; + } + + isFullscreenWindow = isfs; + // Why is this necessary? Without it we don't get any of the + // delegate methods like resizing and window movement. + [self setDelegate: self]; + cachedInsets[0] = 0; // l + cachedInsets[1] = 0; // r + cachedInsets[2] = 0; // t + cachedInsets[3] = 0; // b + realized = YES; + DBG_PRINT("NewtWindow::create: %p, realized %d, hasPresentationSwitch %d[defaultOptions 0x%X, fullscreenOptions 0x%X], (refcnt %d)\n", + res, realized, (int)hasPresentationSwitch, (int)defaultPresentationOptions, (int)fullscreenPresentationOptions, (int)[res retainCount]); + return res; } -- (BOOL) canBecomeKeyWindow +#ifdef DBG_LIFECYCLE +- (void) release { - // Even if the window is borderless, we still want it to be able - // to become the key window to receive keyboard events - return YES; + DBG_PRINT("NewtWindow::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"%@",[NSThread callStackSymbols]); + [super release]; } +#endif -- (void) becomeKeyWindow +- (void) dealloc { - DBG_PRINT( "*************** becomeKeyWindow\n"); - [super becomeKeyWindow]; + DBG_PRINT("NewtWindow::dealloc.0: %p (refcnt %d)\n", self, (int)[self retainCount]); +#ifdef DBG_LIFECYCLE + NSLog(@"%@",[NSThread callStackSymbols]); +#endif + + NewtView* mView = (NewtView *)[self contentView]; + if( NULL != mView ) { + [mView release]; + } + [super dealloc]; + DBG_PRINT("NewtWindow::dealloc.X: %p\n", self); } -- (void) resignKeyWindow +- (void) setRealized: (BOOL)v { - DBG_PRINT( "*************** resignKeyWindow: isFullscreen %d\n", (int)isFullscreenWindow); - if(!isFullscreenWindow) { - [super resignKeyWindow]; + realized = v; +} + +- (BOOL) isRealized +{ + return realized; +} + +- (void) updateInsets: (JNIEnv*) env jwin: (jobject) javaWin +{ + NSRect frameRect = [self frame]; + NSRect contentRect = [self contentRectForFrameRect: frameRect]; + + // note: this is a simplistic implementation which doesn't take + // into account DPI and scaling factor + CGFloat l = contentRect.origin.x - frameRect.origin.x; + cachedInsets[0] = (int)l; // l + cachedInsets[1] = (int)(frameRect.size.width - (contentRect.size.width + l)); // r + cachedInsets[2] = (jint)(frameRect.size.height - contentRect.size.height); // t + cachedInsets[3] = (jint)(contentRect.origin.y - frameRect.origin.y); // b + + DBG_PRINT( "updateInsets: [ l %d, r %d, t %d, b %d ]\n", cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3]); + + if( NULL != env && NULL != javaWin ) { + (*env)->CallVoidMethod(env, javaWin, insetsChangedID, JNI_FALSE, cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3]); } } -- (void) windowDidBecomeKey: (NSNotification *) notification +- (void) attachToParent: (NSWindow*) parent { - DBG_PRINT( "*************** windowDidBecomeKey\n"); - mouseInside = [self isMouseInside]; - if(YES == mouseInside) { - [self cursorHide: !mouseVisible]; + DBG_PRINT( "attachToParent.1\n"); + [parent addChildWindow: self ordered: NSWindowAbove]; + DBG_PRINT( "attachToParent.2\n"); + [self setParentWindow: parent]; + DBG_PRINT( "attachToParent.X\n"); +} + +- (void) detachFromParent: (NSWindow*) parent +{ + DBG_PRINT( "detachFromParent.1\n"); + [self setParentWindow: nil]; + if(NULL != parent) { + DBG_PRINT( "detachFromParent.2\n"); + [parent removeChildWindow: self]; } - [self focusChanged: YES]; + DBG_PRINT( "detachFromParent.X\n"); } -- (void) windowDidResignKey: (NSNotification *) notification +/** + * p abs screen position of client-area pos w/ top-left origin, using contentView's client NSSize + * returns: abs screen position w/ bottom-left origin + */ +- (NSPoint) newtAbsClientTLWinPos2AbsBLScreenPos: (NSPoint) p { - DBG_PRINT( "*************** windowDidResignKey\n"); - // Implicit mouse exit by OS X - [self focusChanged: NO]; + NSView* mView = [self contentView]; + NSRect mViewFrame = [mView frame]; + return [self newtAbsClientTLWinPos2AbsBLScreenPos: p size: mViewFrame.size]; } -- (void) keyDown: (NSEvent*) theEvent +/** + * p abs screen position of client-area pos w/ top-left origin, using given client NSSize + * returns: abs screen position w/ bottom-left origin + */ +- (NSPoint) newtAbsClientTLWinPos2AbsBLScreenPos: (NSPoint) p size: (NSSize) nsz { - [self sendKeyEvent: theEvent eventType: EVENT_KEY_PRESSED]; + int totalHeight = nsz.height + cachedInsets[3]; // height + insets.bottom + + DBG_PRINT( "newtAbsClientTLWinPos2AbsBLScreenPos: given %d/%d %dx%d, insets bottom %d -> totalHeight %d\n", + (int)p.x, (int)p.y, (int)nsz.width, (int)nsz.height, cachedInsets[3], totalHeight); + + NSScreen* screen = [self screen]; + NSRect screenFrame = [screen frame]; + + DBG_PRINT( "newtAbsClientTLWinPos2AbsBLScreenPos: screen %d/%d %dx%d\n", + (int)screenFrame.origin.x, (int)screenFrame.origin.y, (int)screenFrame.size.width, (int)screenFrame.size.height); + + NSPoint r = NSMakePoint(screenFrame.origin.x + p.x, + screenFrame.origin.y + screenFrame.size.height - p.y - totalHeight); + + DBG_PRINT( "newtAbsClientTLWinPos2AbsBLScreenPos: result %d/%d\n", (int)r.x, (int)r.y); + + return r; } -- (void) keyUp: (NSEvent*) theEvent +/** + * p rel client window position w/ top-left origin + * returns: abs screen position w/ bottom-left origin + */ +- (NSPoint) newtRelClientTLWinPos2AbsBLScreenPos: (NSPoint) p { - [self sendKeyEvent: theEvent eventType: EVENT_KEY_RELEASED]; - [self sendKeyEvent: theEvent eventType: EVENT_KEY_TYPED]; + NSRect winFrame = [self frame]; + + NSView* mView = [self contentView]; + NSRect mViewFrame = [mView frame]; + + return NSMakePoint(winFrame.origin.x + p.x, + winFrame.origin.y + ( mViewFrame.size.height - p.y ) ); // y-flip in view } -- (void) mouseEntered: (NSEvent*) theEvent +- (NSSize) newtClientSize2TLSize: (NSSize) nsz { - DBG_PRINT( "mouseEntered: confined %d, visible %d\n", mouseConfined, mouseVisible); - mouseInside = YES; - [self cursorHide: !mouseVisible]; - if(NO == mouseConfined) { - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_ENTERED]; + NSSize topSZ = { nsz.width, nsz.height + cachedInsets[2] + cachedInsets[3] }; // height + insets.top + insets.bottom + return topSZ; +} + +/** + * y-flips input / output + * p rel client window position w/ top-left origin + * returns: location in 0/0 top-left space. + */ +- (NSPoint) getLocationOnScreen: (NSPoint) p +{ + NSScreen* screen = [self screen]; + NSRect screenRect = [screen frame]; + + NSView* view = [self contentView]; + NSRect viewFrame = [view frame]; + + NSRect r; + r.origin.x = p.x; + r.origin.y = viewFrame.size.height - p.y; // y-flip + r.size.width = 0; + r.size.height = 0; + // NSRect rS = [win convertRectToScreen: r]; // 10.7 + NSPoint oS = [self convertBaseToScreen: r.origin]; + oS.y = screenRect.origin.y + screenRect.size.height - oS.y; + return oS; +} + +- (void) focusChanged: (BOOL) gained +{ + DBG_PRINT( "focusChanged: gained %d\n", gained); + NewtView* newtView = (NewtView *) [self contentView]; + if( ! [newtView isKindOfClass:[NewtView class]] ) { + return; + } + jobject javaWindowObject = [newtView getJavaWindowObject]; + if (javaWindowObject == NULL) { + DBG_PRINT("focusChanged: null javaWindowObject\n"); + return; + } + int shallBeDetached = 0; + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); + if(NULL==env) { + DBG_PRINT("focusChanged: null JNIEnv\n"); + return; } + + (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_FALSE, (gained == YES) ? JNI_TRUE : JNI_FALSE); + + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); } -- (void) mouseExited: (NSEvent*) theEvent +- (void) keyDown: (NSEvent*) theEvent { - DBG_PRINT( "mouseExited: confined %d, visible %d\n", mouseConfined, mouseVisible); - if(NO == mouseConfined) { - mouseInside = NO; - [self cursorHide: NO]; - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_EXITED]; - } else { - [self setMousePosition: lastInsideMousePosition]; + NewtView* newtView = (NewtView *) [self contentView]; + if( [newtView isKindOfClass:[NewtView class]] ) { + [newtView sendKeyEvent: theEvent eventType: (jshort)EVENT_KEY_PRESSED]; } } -- (void) mouseMoved: (NSEvent*) theEvent +- (void) keyUp: (NSEvent*) theEvent { - lastInsideMousePosition = [NSEvent mouseLocation]; - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; + NewtView* newtView = (NewtView *) [self contentView]; + if( [newtView isKindOfClass:[NewtView class]] ) { + [newtView sendKeyEvent: theEvent eventType: (jshort)EVENT_KEY_RELEASED]; + } } -- (void) scrollWheel: (NSEvent*) theEvent +- (void) flagsChanged:(NSEvent *) theEvent { - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_WHEEL_MOVED]; + NSUInteger mods = [theEvent modifierFlags]; + NewtView* newtView = (NewtView *) [self contentView]; + if( [newtView isKindOfClass:[NewtView class]] ) { + [newtView handleFlagsChanged: mods]; + } } -- (void) mouseDown: (NSEvent*) theEvent +- (BOOL) acceptsMouseMovedEvents { - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; + return YES; } -- (void) mouseDragged: (NSEvent*) theEvent +- (BOOL) acceptsFirstResponder { - lastInsideMousePosition = [NSEvent mouseLocation]; - // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; + return YES; } -- (void) mouseUp: (NSEvent*) theEvent +- (BOOL) becomeFirstResponder { - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; + DBG_PRINT( "*************** Win.becomeFirstResponder\n"); + return [super becomeFirstResponder]; } -- (void) rightMouseDown: (NSEvent*) theEvent +- (BOOL) resignFirstResponder { - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; + DBG_PRINT( "*************** Win.resignFirstResponder\n"); + return [super resignFirstResponder]; } -- (void) rightMouseDragged: (NSEvent*) theEvent +- (BOOL) canBecomeKeyWindow { - lastInsideMousePosition = [NSEvent mouseLocation]; - // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; + // Even if the window is borderless, we still want it to be able + // to become the key window to receive keyboard events + return YES; } -- (void) rightMouseUp: (NSEvent*) theEvent +- (void) becomeKeyWindow { - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; + DBG_PRINT( "*************** becomeKeyWindow\n"); + [super becomeKeyWindow]; } -- (void) otherMouseDown: (NSEvent*) theEvent +- (void) resignKeyWindow { - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; + DBG_PRINT( "*************** resignKeyWindow: isFullscreen %d\n", (int)isFullscreenWindow); + if(!isFullscreenWindow) { + [super resignKeyWindow]; + } } -- (void) otherMouseDragged: (NSEvent*) theEvent +- (void) windowDidBecomeKey: (NSNotification *) notification { - lastInsideMousePosition = [NSEvent mouseLocation]; - // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; + DBG_PRINT( "*************** windowDidBecomeKey\n"); + NewtView* newtView = (NewtView *) [self contentView]; + if( [newtView isKindOfClass:[NewtView class]] ) { + BOOL mouseInside = [newtView updateMouseInside]; + if(YES == mouseInside) { + [newtView cursorHide: ![newtView isMouseVisible] enter: 1]; + } + } + [self focusChanged: YES]; } -- (void) otherMouseUp: (NSEvent*) theEvent +- (void) windowDidResignKey: (NSNotification *) notification { - [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; + DBG_PRINT( "*************** windowDidResignKey\n"); + // Implicit mouse exit by OS X + [self focusChanged: NO]; } - (void)windowDidResize: (NSNotification*) notification { - NSView* nsview = [self contentView]; - if( ! [nsview isMemberOfClass:[NewtView class]] ) { - return; - } - NewtView* view = (NewtView *) nsview; - jobject javaWindowObject = [view getJavaWindowObject]; - if (javaWindowObject == NULL) { - DBG_PRINT("windowDidResize: null javaWindowObject\n"); - return; - } + jobject javaWindowObject = NULL; int shallBeDetached = 0; - JavaVM *jvmHandle = [view getJVMHandle]; - JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, [view getJVMVersion], &shallBeDetached); - if(NULL==env) { + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); + + if( NULL == env ) { DBG_PRINT("windowDidResize: null JNIEnv\n"); return; } + NewtView* newtView = (NewtView *) [self contentView]; + if( [newtView isKindOfClass:[NewtView class]] ) { + javaWindowObject = [newtView getJavaWindowObject]; + } + if( NULL != javaWindowObject ) { + // update insets on every window resize for lack of better hook place + [self updateInsets: env jwin:javaWindowObject]; - // update insets on every window resize for lack of better hook place - [self updateInsets: env]; - - NSRect frameRect = [self frame]; - NSRect contentRect = [self contentRectForFrameRect: frameRect]; - - (*env)->CallVoidMethod(env, javaWindowObject, sizeChangedID, JNI_FALSE, - (jint) contentRect.size.width, - (jint) contentRect.size.height, JNI_FALSE); + NSRect frameRect = [self frame]; + NSRect contentRect = [self contentRectForFrameRect: frameRect]; - if (shallBeDetached) { - (*jvmHandle)->DetachCurrentThread(jvmHandle); + (*env)->CallVoidMethod(env, javaWindowObject, sizeChangedID, JNI_FALSE, + (jint) contentRect.size.width, + (jint) contentRect.size.height, JNI_FALSE); } + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); } - (void)windowDidMove: (NSNotification*) notification { - NSView* nsview = [self contentView]; - if( ! [nsview isMemberOfClass:[NewtView class]] ) { + NewtView* newtView = (NewtView *) [self contentView]; + if( ! [newtView isKindOfClass:[NewtView class]] ) { return; } - NewtView* view = (NewtView *) nsview; - jobject javaWindowObject = [view getJavaWindowObject]; + jobject javaWindowObject = [newtView getJavaWindowObject]; if (javaWindowObject == NULL) { DBG_PRINT("windowDidMove: null javaWindowObject\n"); return; } int shallBeDetached = 0; - JavaVM *jvmHandle = [view getJVMHandle]; - JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, [view getJVMVersion], &shallBeDetached); + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); if(NULL==env) { DBG_PRINT("windowDidMove: null JNIEnv\n"); return; @@ -948,9 +1153,8 @@ static jint mods2JavaMods(NSUInteger mods) p0 = [self getLocationOnScreen: p0]; (*env)->CallVoidMethod(env, javaWindowObject, positionChangedID, JNI_FALSE, (jint) p0.x, (jint) p0.y); - if (shallBeDetached) { - (*jvmHandle)->DetachCurrentThread(jvmHandle); - } + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); } - (BOOL)windowShouldClose: (id) sender @@ -966,41 +1170,38 @@ static jint mods2JavaMods(NSUInteger mods) - (BOOL) windowClosingImpl: (BOOL) force { jboolean closed = JNI_FALSE; - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - [self cursorHide: NO]; - NSView* nsview = [self contentView]; - if( ! [nsview isMemberOfClass:[NewtView class]] ) { - return; + NewtView* newtView = (NewtView *) [self contentView]; + if( ! [newtView isKindOfClass:[NewtView class]] ) { + return NO; } - NewtView* view = (NewtView *) nsview; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + [newtView cursorHide: NO enter: -1]; - if( false == [view getDestroyNotifySent] ) { - jobject javaWindowObject = [view getJavaWindowObject]; + if( false == [newtView getDestroyNotifySent] ) { + jobject javaWindowObject = [newtView getJavaWindowObject]; DBG_PRINT( "*************** windowWillClose.0: %p\n", (void *)(intptr_t)javaWindowObject); if (javaWindowObject == NULL) { DBG_PRINT("windowWillClose: null javaWindowObject\n"); - return; + [pool release]; + return NO; } int shallBeDetached = 0; - JavaVM *jvmHandle = [view getJVMHandle]; - JNIEnv* env = NewtCommon_GetJNIEnv(jvmHandle, [view getJVMVersion], &shallBeDetached); + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); if(NULL==env) { DBG_PRINT("windowWillClose: null JNIEnv\n"); - return; + [pool release]; + return NO; } - - [view setDestroyNotifySent: true]; // earmark assumption of being closed + [newtView setDestroyNotifySent: true]; // earmark assumption of being closed closed = (*env)->CallBooleanMethod(env, javaWindowObject, windowDestroyNotifyID, force ? JNI_TRUE : JNI_FALSE); if(!force && !closed) { // not closed on java side, not force -> clear flag - [view setDestroyNotifySent: false]; + [newtView setDestroyNotifySent: false]; } - if (shallBeDetached) { - (*jvmHandle)->DetachCurrentThread(jvmHandle); - } + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); DBG_PRINT( "*************** windowWillClose.X: %p, closed %d\n", (void *)(intptr_t)javaWindowObject, (int)closed); } else { DBG_PRINT( "*************** windowWillClose (skip)\n"); @@ -1010,3 +1211,4 @@ static jint mods2JavaMods(NSUInteger mods) } @end + |