Jogamp
c4800bd26c45e5ab63ed82d04701be0ea1ba5a07
[jogl.git] / src / newt / native / NewtMacWindow.m
1 /*
2  * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved.
3  * 
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  * 
8  * - Redistribution of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * 
11  * - Redistribution in binary form must reproduce the above copyright
12  *   notice, this list of conditions and the following disclaimer in the
13  *   documentation and/or other materials provided with the distribution.
14  * 
15  * Neither the name of Sun Microsystems, Inc. or the names of
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  * 
19  * This software is provided "AS IS," without a warranty of any kind. ALL
20  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
23  * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
24  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
25  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
26  * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
27  * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
28  * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
29  * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
30  * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  * 
32  */
33
34 #import "NewtMacWindow.h"
35 #import "InputEvent.h"
36 #import "KeyEvent.h"
37 #import "MouseEvent.h"
38
39 #include <CoreFoundation/CoreFoundation.h>
40 #include <Carbon/Carbon.h> /* For kVK_ constants, and TIS functions. */
41
42 #include <math.h>
43
44 #define PRINTF(...) NSLog(@ __VA_ARGS__)
45
46 static jfloat GetDelta(NSEvent *event, jint javaMods[]) {
47     CGEventRef cgEvent = [event CGEvent];
48     CGFloat deltaY = 0.0;
49     CGFloat deltaX = 0.0;
50     CGFloat delta = 0.0;
51
52     if (CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventIsContinuous)) {
53         // mouse pad case
54         deltaX = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis2);
55         deltaY = CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1);
56         // fprintf(stderr, "WHEEL/PAD: %lf/%lf - 0x%X\n", (double)deltaX, (double)deltaY, javaMods[0]);
57         if( fabsf(deltaX) > fabsf(deltaY) ) {
58             javaMods[0] |= EVENT_SHIFT_MASK;
59             delta = deltaX;
60         } else {
61             delta = deltaY;
62         }
63     } else {
64         // traditional mouse wheel case
65         deltaX = [event deltaX];
66         deltaY = [event deltaY];
67         // fprintf(stderr, "WHEEL/TRACK: %lf/%lf - 0x%X\n", (double)deltaX, (double)deltaY, javaMods[0]);
68         if (deltaY == 0.0 && (javaMods[0] & EVENT_SHIFT_MASK) != 0) {
69             // shift+vertical wheel scroll produces horizontal scroll
70             // we convert it to vertical
71             delta = deltaX;
72         } else {
73             delta = deltaY;
74         }
75         if (-1.0 < delta && delta < 1.0) {
76             delta *= 10.0;
77         } else {
78             if (delta < 0.0) {
79                 delta = delta - 0.5f;
80             } else {
81                 delta = delta + 0.5f;
82             }
83         }
84     }
85     // fprintf(stderr, "WHEEL/RES: %lf - 0x%X\n", (double)delta, javaMods[0]);
86     return (jfloat) delta;
87 }
88
89 #define kVK_Shift     0x38
90 #define kVK_Option    0x3A
91 #define kVK_Control   0x3B
92 #define kVK_Command   0x37
93
94 static jint mods2JavaMods(NSUInteger mods)
95 {
96     int javaMods = 0;
97     if (mods & NSShiftKeyMask) {
98         javaMods |= EVENT_SHIFT_MASK;
99     }
100     if (mods & NSControlKeyMask) {
101         javaMods |= EVENT_CTRL_MASK;
102     }
103     if (mods & NSCommandKeyMask) {
104         javaMods |= EVENT_META_MASK;
105     }
106     if (mods & NSAlternateKeyMask) {
107         javaMods |= EVENT_ALT_MASK;
108     }
109     return javaMods;
110 }
111
112 static CFStringRef CKCH_CreateStringForKey(CGKeyCode keyCode, const UCKeyboardLayout *keyboardLayout) {
113     UInt32 keysDown = 0;
114     UniChar chars[4];
115     UniCharCount realLength;
116
117     UCKeyTranslate(keyboardLayout, keyCode,
118                    kUCKeyActionDisplay, 0,
119                    LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit,
120                    &keysDown, sizeof(chars) / sizeof(chars[0]), &realLength, chars);
121
122     return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);
123 }
124
125 static CFMutableDictionaryRef CKCH_CreateCodeToCharDict(TISInputSourceRef keyboard) {
126     CFDataRef layoutData = (CFDataRef) TISGetInputSourceProperty(keyboard, kTISPropertyUnicodeKeyLayoutData);
127     const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
128
129     CFMutableDictionaryRef codeToCharDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 128, NULL, NULL);
130     if ( NULL != codeToCharDict ) {
131         intptr_t i;
132         for (i = 0; i < 128; ++i) {
133             CFStringRef string = CKCH_CreateStringForKey((CGKeyCode)i, keyboardLayout);
134             if( NULL != string ) {
135                 CFIndex stringLen = CFStringGetLength (string);
136                 if ( 0 < stringLen ) {
137                     UniChar character = CFStringGetCharacterAtIndex(string, 0);
138                     DBG_PRINT("CKCH: MAP 0x%X -> %c\n", (int)i, character);
139                     CFDictionaryAddValue(codeToCharDict, (const void *)i, (const void *)(intptr_t)character);
140                 }
141                 CFRelease(string);
142             }
143         }
144     }
145     return codeToCharDict;
146 }
147
148 static CFMutableDictionaryRef CKCH_USCodeToNNChar = NULL;
149
150 static void CKCH_CreateDictionaries() {
151     TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
152     CKCH_USCodeToNNChar = CKCH_CreateCodeToCharDict(currentKeyboard);
153     CFRelease(currentKeyboard);
154 }
155
156 static UniChar CKCH_CharForKeyCode(jshort keyCode) {
157     UniChar rChar = 0;
158
159     if ( NULL != CKCH_USCodeToNNChar ) {
160         intptr_t code = (intptr_t) keyCode;
161         intptr_t character = 0;
162
163         if ( CFDictionaryGetValueIfPresent(CKCH_USCodeToNNChar, (void *)code, (const void **)&character) ) {
164             rChar = (UniChar) character;
165             DBG_PRINT("CKCH: OK 0x%X -> 0x%X\n", (int)keyCode, (int)rChar);
166         }
167     }
168     return rChar;
169 }
170
171 static jmethodID enqueueMouseEventID = NULL;
172 static jmethodID enqueueKeyEventID = NULL;
173 static jmethodID requestFocusID = NULL;
174
175 static jmethodID insetsChangedID   = NULL;
176 static jmethodID sizeChangedID     = NULL;
177 static jmethodID updatePixelScaleID = NULL;
178 static jmethodID visibleChangedID = NULL;
179 static jmethodID positionChangedID = NULL;
180 static jmethodID focusChangedID    = NULL;
181 static jmethodID windowDestroyNotifyID = NULL;
182 static jmethodID windowRepaintID = NULL;
183
184 // Need to enqueue all events to EDT,
185 // since we may operate on AWT-AppKit (Main Thread)
186 // and direct issuing 'requestFocus()' would deadlock:
187 //     AWT-AppKit
188 //     AWT-EventQueue-0
189
190 @implementation NewtView
191
192 - (id)initWithFrame:(NSRect)frameRect
193 {
194     id res = [super initWithFrame:frameRect];
195     javaWindowObject = NULL;
196
197     destroyNotifySent = NO;
198     softLockCount = 0;
199
200     pthread_mutexattr_t softLockSyncAttr;
201     pthread_mutexattr_init(&softLockSyncAttr);
202     pthread_mutexattr_settype(&softLockSyncAttr, PTHREAD_MUTEX_RECURSIVE);
203     pthread_mutex_init(&softLockSync, &softLockSyncAttr); // recursive
204
205     ptrTrackingTag = 0;
206     myCursor = NULL;
207
208     modsDown[0] = NO; // shift
209     modsDown[1] = NO; // ctrl
210     modsDown[2] = NO; // alt
211     modsDown[3] = NO; // win
212     mouseConfined = NO;
213     mouseVisible = YES;
214     mouseInside = NO;
215     cursorIsHidden = NO;
216
217     DBG_PRINT("NewtView::create: %p (refcnt %d)\n", res, (int)[res retainCount]);
218     return res;
219 }
220
221 #ifdef DBG_LIFECYCLE
222 - (void) release
223 {
224     DBG_PRINT("NewtView::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
225     [super release];
226 }
227 #endif
228
229 - (void) dealloc
230 {
231     DBG_PRINT("NewtView::dealloc.0: %p (refcnt %d), ptrTrackingTag %d\n", self, (int)[self retainCount], (int)ptrTrackingTag);
232 #ifdef DBG_LIFECYCLE
233     NSLog(@"%@",[NSThread callStackSymbols]);
234 #endif
235     if( 0 < softLockCount ) {
236         NSLog(@"NewtView::dealloc: softLock still hold @ dealloc!\n");
237     }
238     [self removeCursorRects];
239     [self removeMyCursor];
240
241     pthread_mutex_destroy(&softLockSync);
242     DBG_PRINT("NewtView::dealloc.X: %p\n", self);
243     [super dealloc];
244 }
245
246 - (void) setJavaWindowObject: (jobject) javaWindowObj
247 {
248     javaWindowObject = javaWindowObj;
249 }
250
251 - (jobject) getJavaWindowObject
252 {
253     return javaWindowObject;
254 }
255
256 - (void) setDestroyNotifySent: (BOOL) v
257 {
258     destroyNotifySent = v;
259 }
260
261 - (BOOL) getDestroyNotifySent
262 {
263     return destroyNotifySent;
264 }
265
266 - (BOOL) softLock
267 {
268     // DBG_PRINT("*************** softLock.0: %p\n", (void*)pthread_self());
269     int err;
270     if( 0 != ( err = pthread_mutex_lock(&softLockSync) ) ) {
271         NSLog(@"NewtView::softLock failed: errCode %d - %@", err, [NSThread callStackSymbols]);
272         return NO;
273     }
274     softLockCount++;
275     // DBG_PRINT("*************** softLock.X: %p\n", (void*)pthread_self());
276     return 0 < softLockCount;
277 }
278
279 - (BOOL) softUnlock
280 {
281     // DBG_PRINT("*************** softUnlock: %p\n", (void*)pthread_self());
282     softLockCount--;
283     int err;
284     if( 0 != ( err = pthread_mutex_unlock(&softLockSync) ) ) {
285         softLockCount++;
286         NSLog(@"NewtView::softUnlock failed: Not locked by current thread - errCode %d -  %@", err, [NSThread callStackSymbols]);
287         return NO;
288     }
289     return YES;
290 }
291
292 - (BOOL) needsDisplay
293 {
294     return NO == destroyNotifySent && [super needsDisplay];
295 }
296
297 - (void) displayIfNeeded
298 {
299     if( YES == [self needsDisplay] ) {
300         [self softLock];
301         [super displayIfNeeded];
302         [self softUnlock];
303     }
304 }
305
306 - (void) display
307 {
308     if( NO == destroyNotifySent ) {
309         [self softLock];
310         [super display];
311         [self softUnlock];
312     }
313 }
314
315 - (void) drawRect:(NSRect)dirtyRect
316 {
317     DBG_PRINT("*************** dirtyRect: %p %lf/%lf %lfx%lf\n", 
318         javaWindowObject, dirtyRect.origin.x, dirtyRect.origin.y, dirtyRect.size.width, dirtyRect.size.height);
319
320     if(NULL==javaWindowObject) {
321         DBG_PRINT("drawRect: null javaWindowObject\n");
322         return;
323     }
324     int shallBeDetached = 0;
325     JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
326     if(NULL==env) {
327         DBG_PRINT("drawRect: null JNIEnv\n");
328         return;
329     }
330
331     NSRect viewFrame = [self frame];
332
333     (*env)->CallVoidMethod(env, javaWindowObject, windowRepaintID, JNI_TRUE, // defer ..
334         (int)dirtyRect.origin.x, (int)viewFrame.size.height - (int)dirtyRect.origin.y, 
335         (int)dirtyRect.size.width, (int)dirtyRect.size.height);
336
337     // detaching thread not required - daemon
338     // NewtCommon_ReleaseJNIEnv(shallBeDetached);
339 }
340
341 - (void) viewDidHide
342 {
343     if(NULL==javaWindowObject) {
344         DBG_PRINT("viewDidHide: null javaWindowObject\n");
345         return;
346     }
347     int shallBeDetached = 0;
348     JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
349     if(NULL==env) {
350         DBG_PRINT("viewDidHide: null JNIEnv\n");
351         return;
352     }
353
354     (*env)->CallVoidMethod(env, javaWindowObject, visibleChangedID, JNI_FALSE, JNI_FALSE);
355
356     // detaching thread not required - daemon
357     // NewtCommon_ReleaseJNIEnv(shallBeDetached);
358
359     [super viewDidHide];
360 }
361
362 - (void) viewDidUnhide
363 {
364     if(NULL==javaWindowObject) {
365         DBG_PRINT("viewDidUnhide: null javaWindowObject\n");
366         return;
367     }
368     int shallBeDetached = 0;
369     JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
370     if(NULL==env) {
371         DBG_PRINT("viewDidUnhide: null JNIEnv\n");
372         return;
373     }
374
375     (*env)->CallVoidMethod(env, javaWindowObject, visibleChangedID, JNI_FALSE, JNI_TRUE);
376
377     // detaching thread not required - daemon
378     // NewtCommon_ReleaseJNIEnv(shallBeDetached);
379
380     [super viewDidUnhide];
381 }
382
383 - (BOOL) acceptsFirstResponder 
384 {
385     return YES;
386 }
387
388 - (BOOL) becomeFirstResponder
389 {
390     DBG_PRINT( "*************** View.becomeFirstResponder\n");
391     return [super becomeFirstResponder];
392 }
393
394 - (BOOL) resignFirstResponder
395 {
396     DBG_PRINT( "*************** View.resignFirstResponder\n");
397     return [super resignFirstResponder];
398 }
399
400 - (void) removeCursorRects
401 {
402     if(0 != ptrTrackingTag) {
403         if(NULL != myCursor) {
404             [self removeCursorRect: ptrRect cursor: myCursor];
405         }
406         [self removeTrackingRect: ptrTrackingTag];
407         ptrTrackingTag = 0;
408     }
409 }
410
411 - (void) addCursorRects
412 {
413     ptrRect = [self bounds]; 
414     if(NULL != myCursor) {
415         [self addCursorRect: ptrRect cursor: myCursor];
416     }
417     ptrTrackingTag = [self addTrackingRect: ptrRect owner: self userData: nil assumeInside: NO];
418 }
419
420 - (void) removeMyCursor
421 {
422     if(NULL != myCursor) {
423         [myCursor release];
424         myCursor = NULL;
425     }
426 }
427
428 - (void) resetCursorRects
429 {
430     [super resetCursorRects];
431
432     [self removeCursorRects];
433     [self addCursorRects];
434 }
435
436 - (void) setPointerIcon: (NSCursor*)c
437 {
438     DBG_PRINT( "setPointerIcon: %p -> %p, top %p, mouseInside %d\n", myCursor, c, [NSCursor currentCursor], (int)mouseInside);
439     if( c != myCursor ) {
440         [self removeCursorRects];
441         [self removeMyCursor];
442         myCursor = c;
443         if( NULL != myCursor ) {
444             [myCursor retain];
445         }
446     }
447     NSWindow* nsWin = [self window];
448     if( NULL != nsWin ) {
449         [nsWin invalidateCursorRectsForView: self];
450     }
451 }
452
453 - (void) mouseEntered: (NSEvent*) theEvent
454 {
455     DBG_PRINT( "mouseEntered: confined %d, visible %d, PointerIcon %p, top %p\n", mouseConfined, mouseVisible, myCursor, [NSCursor currentCursor]);
456     mouseInside = YES;
457     [self cursorHide: !mouseVisible enter: 1];
458     if(NO == mouseConfined) {
459         [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_ENTERED];
460     }
461     NSWindow* nsWin = [self window];
462     if( NULL != nsWin ) {
463         [nsWin makeFirstResponder: self];
464     }
465 }
466
467 - (void) mouseExited: (NSEvent*) theEvent
468 {
469     DBG_PRINT( "mouseExited: confined %d, visible %d, PointerIcon %p, top %p\n", mouseConfined, mouseVisible, myCursor, [NSCursor currentCursor]);
470     if(NO == mouseConfined) {
471         mouseInside = NO;
472         [self cursorHide: NO enter: -1];
473         [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_EXITED];
474         [self resignFirstResponder];
475     } else {
476         [self setMousePosition: lastInsideMousePosition];
477     }
478 }
479
480 /**
481  * p abs screen position w/ bottom-left origin
482  */
483 - (void) setMousePosition:(NSPoint)p
484 {
485     NSWindow* nsWin = [self window];
486     if( NULL != nsWin ) {
487         NSScreen* screen = [nsWin screen];
488
489         CGDirectDisplayID display = NewtScreen_getCGDirectDisplayIDByNSScreen(screen);
490         CGRect frameTL = CGDisplayBounds (display); // origin top-left
491         NSRect frameBL = [screen frame]; // origin bottom-left
492         CGPoint pt = { p.x, frameTL.origin.y + frameTL.size.height - ( p.y - frameBL.origin.y ) }; // y-flip from BL-screen -> TL-screen
493
494         DBG_PRINT( "setMousePosition: point-in[%d/%d], screen tl[%d/%d %dx%d] bl[%d/%d %dx%d] -> %d/%d\n",
495             (int)p.x, (int)p.y,
496             (int)frameTL.origin.x, (int)frameTL.origin.y, (int)frameTL.size.width, (int)frameTL.size.height,
497             (int)frameBL.origin.x, (int)frameBL.origin.y, (int)frameBL.size.width, (int)frameBL.size.height,
498             (int)pt.x, (int)pt.y);
499
500         CGEventRef ev = CGEventCreateMouseEvent (NULL, kCGEventMouseMoved, pt, kCGMouseButtonLeft);
501         CGEventPost (kCGHIDEventTap, ev);
502     }
503 }
504
505 - (BOOL) updateMouseInside
506 {
507     NSRect viewFrame = [self frame];
508     NSPoint l1 = [NSEvent mouseLocation];
509     NSPoint l0 = [self screenPos2NewtClientWinPos: l1];
510     mouseInside = viewFrame.origin.x <= l0.x && l0.x < (viewFrame.origin.x+viewFrame.size.width) &&
511                   viewFrame.origin.y <= l0.y && l0.y < (viewFrame.origin.y+viewFrame.size.height) ;
512     return mouseInside;
513 }
514
515 - (void) setMouseVisible:(BOOL)v hasFocus:(BOOL)focus
516 {
517     mouseVisible = v;
518     [self updateMouseInside];
519     DBG_PRINT( "setMouseVisible: confined %d, visible %d (current: %d), mouseInside %d, hasFocus %d\n", 
520         mouseConfined, mouseVisible, !cursorIsHidden, mouseInside, focus);
521     if(YES == focus && YES == mouseInside) {
522         [self cursorHide: !mouseVisible enter: 0];
523     }
524 }
525 - (BOOL) isMouseVisible
526 {
527     return mouseVisible;
528 }
529
530 - (void) cursorHide:(BOOL)v enter:(int)enterState
531 {
532     DBG_PRINT( "cursorHide: %d -> %d, enter %d; PointerIcon: %p, top %p\n", 
533         cursorIsHidden, v, enterState, myCursor, [NSCursor currentCursor]);
534     if(v) {
535         if(!cursorIsHidden) {
536             [NSCursor hide];
537             cursorIsHidden = YES;
538         }
539     } else {
540         if(cursorIsHidden) {
541             [NSCursor unhide];
542             cursorIsHidden = NO;
543         }
544     }
545 }
546
547 - (void) setMouseConfined:(BOOL)v
548 {
549     mouseConfined = v;
550     DBG_PRINT( "setMouseConfined: confined %d, visible %d\n", mouseConfined, mouseVisible);
551 }
552
553 - (void) mouseMoved: (NSEvent*) theEvent
554 {
555     if( mouseInside ) {
556         NSCursor * currentCursor = [NSCursor currentCursor];
557         BOOL setCursor = NULL != myCursor && NO == cursorIsHidden && currentCursor != myCursor;
558         DBG_PRINT( "mouseMoved.set: %d; mouseInside %d, CursorHidden %d, PointerIcon: %p, top %p\n", 
559             setCursor, mouseInside, cursorIsHidden, myCursor, currentCursor);
560         if( setCursor ) {
561             // FIXME: Workaround missing NSCursor update for 'fast moving' pointer 
562             [myCursor set];
563         }
564         lastInsideMousePosition = [NSEvent mouseLocation];
565         [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
566     }
567 }
568
569 - (void) scrollWheel: (NSEvent*) theEvent
570 {
571     [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_WHEEL_MOVED];
572 }
573
574 - (void) mouseDown: (NSEvent*) theEvent
575 {
576     [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED];
577 }
578
579 - (void) mouseDragged: (NSEvent*) theEvent
580 {
581     lastInsideMousePosition = [NSEvent mouseLocation];
582     // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java
583     [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
584 }
585
586 - (void) mouseUp: (NSEvent*) theEvent
587 {
588     [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED];
589 }
590
591 - (void) rightMouseDown: (NSEvent*) theEvent
592 {
593     NSResponder* next = [self nextResponder];
594     if (next != nil) {
595         [next rightMouseDown: theEvent];
596     }
597     // FIXME: ^^ OR [super rightMouseDown: theEvent] ?
598     [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED];
599 }
600
601 - (void) rightMouseDragged: (NSEvent*) theEvent
602 {
603     lastInsideMousePosition = [NSEvent mouseLocation];
604     // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java
605     [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
606 }
607
608 - (void) rightMouseUp: (NSEvent*) theEvent
609 {
610     [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED];
611 }
612
613 - (void) otherMouseDown: (NSEvent*) theEvent
614 {
615     [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED];
616 }
617
618 - (void) otherMouseDragged: (NSEvent*) theEvent
619 {
620     lastInsideMousePosition = [NSEvent mouseLocation];
621     // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java
622     [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED];
623 }
624
625 - (void) otherMouseUp: (NSEvent*) theEvent
626 {
627     [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED];
628 }
629
630 - (void) sendMouseEvent: (NSEvent*) event eventType: (jshort) evType
631 {
632     if (javaWindowObject == NULL) {
633         DBG_PRINT("sendMouseEvent: null javaWindowObject\n");
634         return;
635     }
636     int shallBeDetached = 0;
637     JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
638     if(NULL==env) {
639         DBG_PRINT("sendMouseEvent: null JNIEnv\n");
640         return;
641     }
642     jint javaMods[] = { 0 } ;
643     javaMods[0] = mods2JavaMods([event modifierFlags]);
644
645     // convert to 1-based button number (or use zero if no button is involved)
646     // TODO: detect mouse button when mouse wheel scrolled  
647     jshort javaButtonNum = 0;
648     jfloat scrollDeltaY = 0.0f;
649     switch ([event type]) {
650     case NSScrollWheel: {
651         scrollDeltaY = GetDelta(event, javaMods);
652         javaButtonNum = 1;
653         break;
654     }
655     case NSLeftMouseDown:
656     case NSLeftMouseUp:
657     case NSLeftMouseDragged:
658         javaButtonNum = 1;
659         break;
660     case NSRightMouseDown:
661     case NSRightMouseUp:
662     case NSRightMouseDragged:
663         javaButtonNum = 3;
664         break;
665     case NSOtherMouseDown:
666     case NSOtherMouseUp:
667     case NSOtherMouseDragged:
668         javaButtonNum = 2;
669         break;
670     }
671
672     if (evType == EVENT_MOUSE_WHEEL_MOVED && scrollDeltaY == 0) {
673         // ignore 0 increment wheel scroll events
674         return;
675     }
676     if (evType == EVENT_MOUSE_PRESSED) {
677         (*env)->CallVoidMethod(env, javaWindowObject, requestFocusID, JNI_FALSE);
678     }
679
680     NSPoint location = [self screenPos2NewtClientWinPos: [NSEvent mouseLocation]];
681
682     (*env)->CallVoidMethod(env, javaWindowObject, enqueueMouseEventID, JNI_FALSE,
683                            evType, javaMods[0],
684                            (jint) location.x, (jint) location.y,
685                            javaButtonNum, scrollDeltaY);
686
687     // detaching thread not required - daemon
688     // NewtCommon_ReleaseJNIEnv(shallBeDetached);
689 }
690
691 - (NSPoint) screenPos2NewtClientWinPos: (NSPoint) p
692 {
693     NSRect viewFrame = [self frame];
694
695     NSRect r;
696     r.origin.x = p.x;
697     r.origin.y = p.y;
698     r.size.width = 0;
699     r.size.height = 0;
700     // NSRect rS = [[self window] convertRectFromScreen: r]; // 10.7
701     NSPoint oS = [[self window] convertScreenToBase: r.origin];
702     oS.y = viewFrame.size.height - oS.y; // y-flip
703     return oS;
704 }
705
706 - (void) handleFlagsChanged:(NSUInteger) mods
707 {
708     [self handleFlagsChanged: NSShiftKeyMask keyIndex: 0 keyCode: kVK_Shift modifiers: mods];
709     [self handleFlagsChanged: NSControlKeyMask keyIndex: 1 keyCode: kVK_Control modifiers: mods];
710     [self handleFlagsChanged: NSAlternateKeyMask keyIndex: 2 keyCode: kVK_Option modifiers: mods];
711     [self handleFlagsChanged: NSCommandKeyMask keyIndex: 3 keyCode: kVK_Command modifiers: mods];
712 }
713
714 - (void) handleFlagsChanged:(int) keyMask keyIndex: (int) keyIdx keyCode: (int) keyCode modifiers: (NSUInteger) mods
715 {
716     if ( NO == modsDown[keyIdx] && 0 != ( mods & keyMask ) )  {
717         modsDown[keyIdx] = YES;
718         [self sendKeyEvent: (jshort)keyCode characters: NULL modifiers: mods|keyMask eventType: (jshort)EVENT_KEY_PRESSED];
719     } else if ( YES == modsDown[keyIdx] && 0 == ( mods & keyMask ) )  {
720         modsDown[keyIdx] = NO;
721         [self sendKeyEvent: (jshort)keyCode characters: NULL modifiers: mods|keyMask eventType: (jshort)EVENT_KEY_RELEASED];
722     }
723 }
724
725 - (void) sendKeyEvent: (NSEvent*) event eventType: (jshort) evType
726 {
727     jshort keyCode = (jshort) [event keyCode];
728     NSString* chars = [event charactersIgnoringModifiers];
729     NSUInteger mods = [event modifierFlags];
730     [self sendKeyEvent: keyCode characters: chars modifiers: mods eventType: evType];
731 }
732
733 - (void) sendKeyEvent: (jshort) keyCode characters: (NSString*) chars modifiers: (NSUInteger)mods eventType: (jshort) evType
734 {
735     if (javaWindowObject == NULL) {
736         DBG_PRINT("sendKeyEvent: null javaWindowObject\n");
737         return;
738     }
739     int shallBeDetached = 0;
740     JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
741     if(NULL==env) {
742         DBG_PRINT("sendKeyEvent: null JNIEnv\n");
743         return;
744     }
745
746     int i;
747     int len = NULL != chars ? [chars length] : 0;
748     jint javaMods = mods2JavaMods(mods);
749
750     if(len > 0) {
751         // printable chars
752         for (i = 0; i < len; i++) {
753             // Note: the key code in the NSEvent does not map to anything we can use
754             UniChar keyChar = (UniChar) [chars characterAtIndex: i];
755             UniChar keySymChar = CKCH_CharForKeyCode(keyCode);
756
757             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, 
758                       (int)mods, (int)javaMods, (int)keySymChar);
759
760             (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID, JNI_FALSE,
761                                    evType, javaMods, keyCode, (jchar)keyChar, (jchar)keySymChar);
762         }
763     } else {
764         // non-printable chars
765         jchar keyChar = (jchar) 0;
766
767         DBG_PRINT("sendKeyEvent: code 0x%X\n", (int)keyCode);
768
769         (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID, JNI_FALSE,
770                                evType, javaMods, keyCode, keyChar, keyChar);
771     }
772
773     // detaching thread not required - daemon
774     // NewtCommon_ReleaseJNIEnv(shallBeDetached);
775 }
776
777 - (void)viewDidChangeBackingProperties
778 {
779     [super viewDidChangeBackingProperties];
780
781     CGFloat pixelScale = [[self window] backingScaleFactor];
782     [[self layer] setContentsScale: pixelScale];
783
784     if (javaWindowObject == NULL) {
785         DBG_PRINT("viewDidChangeBackingProperties: null javaWindowObject\n");
786         return;
787     }
788     int shallBeDetached = 0;
789     JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
790     if(NULL==env) {
791         DBG_PRINT("viewDidChangeBackingProperties: null JNIEnv\n");
792         return;
793     }
794
795     (*env)->CallVoidMethod(env, javaWindowObject, updatePixelScaleID, JNI_TRUE, (jfloat)pixelScale); // defer 
796
797     // detaching thread not required - daemon
798     // NewtCommon_ReleaseJNIEnv(shallBeDetached);
799 }
800
801
802 @end
803
804 @implementation NewtMacWindow
805
806 + (BOOL) initNatives: (JNIEnv*) env forClass: (jclass) clazz
807 {
808     enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZSIIISF)V");
809     enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZSISCC)V");
810     sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(ZIIZ)V");
811     updatePixelScaleID = (*env)->GetMethodID(env, clazz, "updatePixelScale", "(ZF)V");
812     visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(ZZ)V");
813     insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(ZIIII)V");
814     positionChangedID = (*env)->GetMethodID(env, clazz, "screenPositionChanged", "(ZII)V");
815     focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(ZZ)V");
816     windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "(Z)Z");
817     windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(ZIIII)V");
818     requestFocusID = (*env)->GetMethodID(env, clazz, "requestFocus", "(Z)V");
819     if (enqueueMouseEventID && enqueueKeyEventID && sizeChangedID && updatePixelScaleID && visibleChangedID && insetsChangedID &&
820         positionChangedID && focusChangedID && windowDestroyNotifyID && requestFocusID && windowRepaintID)
821     {
822         CKCH_CreateDictionaries();
823         return YES;
824     }
825     return NO;
826 }
827
828 - (id) initWithContentRect: (NSRect) contentRect
829        styleMask: (NSUInteger) windowStyle
830        backing: (NSBackingStoreType) bufferingType
831        defer: (BOOL) deferCreation
832        isFullscreenWindow:(BOOL)isfs
833 {
834     id res = [super initWithContentRect: contentRect
835                     styleMask: windowStyle
836                     backing: bufferingType
837                     defer: deferCreation];
838     // OSX 10.6
839     if ( [NSApp respondsToSelector:@selector(currentSystemPresentationOptions)] &&
840          [NSApp respondsToSelector:@selector(setPresentationOptions:)] ) {
841         hasPresentationSwitch = YES;
842         defaultPresentationOptions = [NSApp currentSystemPresentationOptions];
843         fullscreenPresentationOptions = 
844                 // NSApplicationPresentationDefault|
845                 // NSApplicationPresentationAutoHideDock|
846                 NSApplicationPresentationHideDock|
847                 // NSApplicationPresentationAutoHideMenuBar|
848                 NSApplicationPresentationHideMenuBar|
849                 NSApplicationPresentationDisableAppleMenu|
850                 // NSApplicationPresentationDisableProcessSwitching|
851                 // NSApplicationPresentationDisableSessionTermination|
852                 NSApplicationPresentationDisableHideApplication|
853                 // NSApplicationPresentationDisableMenuBarTransparency|
854                 // NSApplicationPresentationFullScreen| // OSX 10.7
855                 0 ;
856     } else {
857         hasPresentationSwitch = NO;
858         defaultPresentationOptions = 0;
859         fullscreenPresentationOptions = 0; 
860     }
861
862     isFullscreenWindow = isfs;
863     // Why is this necessary? Without it we don't get any of the
864     // delegate methods like resizing and window movement.
865     [self setDelegate: self];
866
867     cachedInsets[0] = 0; // l
868     cachedInsets[1] = 0; // r
869     cachedInsets[2] = 0; // t
870     cachedInsets[3] = 0; // b
871
872     realized = YES;
873     DBG_PRINT("NewtWindow::create: %p, realized %d, hasPresentationSwitch %d[defaultOptions 0x%X, fullscreenOptions 0x%X], (refcnt %d)\n", 
874         res, realized, (int)hasPresentationSwitch, (int)defaultPresentationOptions, (int)fullscreenPresentationOptions, (int)[res retainCount]);
875     return res;
876 }
877
878 #ifdef DBG_LIFECYCLE
879 - (void) release
880 {
881     DBG_PRINT("NewtWindow::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
882     // NSLog(@"%@",[NSThread callStackSymbols]);
883     [super release];
884 }
885 #endif
886
887 - (void) dealloc
888 {
889     DBG_PRINT("NewtWindow::dealloc.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
890 #ifdef DBG_LIFECYCLE
891     NSLog(@"%@",[NSThread callStackSymbols]);
892 #endif
893
894     NewtView* mView = (NewtView *)[self contentView];
895     if( NULL != mView ) {
896         [mView release];
897     }
898     [super dealloc];
899     DBG_PRINT("NewtWindow::dealloc.X: %p\n", self);
900 }
901
902 - (void) setRealized: (BOOL)v
903 {
904     realized = v;
905 }
906
907 - (BOOL) isRealized
908 {
909     return realized;
910 }
911
912 - (void) updateInsets: (JNIEnv*) env jwin: (jobject) javaWin
913 {
914     NSRect frameRect = [self frame];
915     NSRect contentRect = [self contentRectForFrameRect: frameRect];
916
917     // note: this is a simplistic implementation which doesn't take
918     // into account DPI and scaling factor
919     CGFloat l = contentRect.origin.x - frameRect.origin.x;
920     cachedInsets[0] = (int)l;                                                     // l
921     cachedInsets[1] = (int)(frameRect.size.width - (contentRect.size.width + l)); // r
922     cachedInsets[2] = (jint)(frameRect.size.height - contentRect.size.height);    // t
923     cachedInsets[3] = (jint)(contentRect.origin.y - frameRect.origin.y);          // b
924
925     DBG_PRINT( "updateInsets: [ l %d, r %d, t %d, b %d ]\n", cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3]);
926
927     if( NULL != env && NULL != javaWin ) {
928         (*env)->CallVoidMethod(env, javaWin, insetsChangedID, JNI_FALSE, cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3]);
929     }
930 }
931
932 - (void) attachToParent: (NSWindow*) parent
933 {
934     DBG_PRINT( "attachToParent.1\n");
935     [parent addChildWindow: self ordered: NSWindowAbove];
936     DBG_PRINT( "attachToParent.2\n");
937     [self setParentWindow: parent];
938     DBG_PRINT( "attachToParent.X\n");
939 }
940
941 - (void) detachFromParent: (NSWindow*) parent
942 {
943     DBG_PRINT( "detachFromParent.1\n");
944     [self setParentWindow: nil];
945     if(NULL != parent) {
946         DBG_PRINT( "detachFromParent.2\n");
947         [parent removeChildWindow: self];
948     }
949     DBG_PRINT( "detachFromParent.X\n");
950 }
951
952 /**
953  * p abs screen position of client-area pos w/ top-left origin, using contentView's client NSSize
954  * returns: abs screen position w/ bottom-left origin
955  */
956 - (NSPoint) newtAbsClientTLWinPos2AbsBLScreenPos: (NSPoint) p
957 {
958     NSView* mView = [self contentView];
959     NSRect mViewFrame = [mView frame]; 
960     return [self newtAbsClientTLWinPos2AbsBLScreenPos: p size: mViewFrame.size];
961 }
962
963 /**
964  * p abs screen position of client-area pos w/ top-left origin, using given client NSSize
965  * returns: abs screen position w/ bottom-left origin
966  */
967 - (NSPoint) newtAbsClientTLWinPos2AbsBLScreenPos: (NSPoint) p size: (NSSize) nsz
968 {
969     int totalHeight = nsz.height + cachedInsets[3]; // height + insets.bottom
970
971     DBG_PRINT( "newtAbsClientTLWinPos2AbsBLScreenPos: point-in[%d/%d], size-in[%dx%d], insets bottom %d -> totalHeight %d\n", 
972         (int)p.x, (int)p.y, (int)nsz.width, (int)nsz.height, cachedInsets[3], totalHeight);
973
974     NSScreen* screen = [self screen];
975
976     CGDirectDisplayID display = NewtScreen_getCGDirectDisplayIDByNSScreen(screen);
977     CGRect frameTL = CGDisplayBounds (display); // origin top-left
978     NSRect frameBL = [screen frame]; // origin bottom-left
979     NSPoint r = NSMakePoint(p.x, frameBL.origin.y + frameBL.size.height - ( p.y - frameTL.origin.y ) - totalHeight); // y-flip from TL-screen -> BL-screen
980
981     DBG_PRINT( "newtAbsClientTLWinPos2AbsBLScreenPos: screen tl[%d/%d %dx%d] bl[%d/%d %dx%d ->  %d/%d\n",
982         (int)frameTL.origin.x, (int)frameTL.origin.y, (int)frameTL.size.width, (int)frameTL.size.height,
983         (int)frameBL.origin.x, (int)frameBL.origin.y, (int)frameBL.size.width, (int)frameBL.size.height,
984         (int)r.x, (int)r.y);
985
986     return r;
987 }
988
989 /**
990  * p rel client window position w/ top-left origin
991  * returns: abs screen position w/ bottom-left origin
992  */
993 - (NSPoint) newtRelClientTLWinPos2AbsBLScreenPos: (NSPoint) p
994 {
995     NSRect winFrame = [self frame];
996
997     NSView* mView = [self contentView];
998     NSRect mViewFrame = [mView frame]; 
999     NSPoint r = NSMakePoint(winFrame.origin.x + p.x,
1000                             winFrame.origin.y + ( mViewFrame.size.height - p.y ) ); // y-flip in view
1001
1002     DBG_PRINT( "newtRelClientTLWinPos2AbsBLScreenPos: point-in[%d/%d], winFrame[%d/%d %dx%d], viewFrame[%d/%d %dx%d] -> %d/%d\n",
1003         (int)p.x, (int)p.y,
1004         (int)winFrame.origin.x, (int)winFrame.origin.y, (int)winFrame.size.width, (int)winFrame.size.height,
1005         (int)mViewFrame.origin.x, (int)mViewFrame.origin.y, (int)mViewFrame.size.width, (int)mViewFrame.size.height,
1006         (int)r.x, (int)r.y);
1007
1008     return r;
1009 }
1010
1011 - (NSSize) newtClientSize2TLSize: (NSSize) nsz
1012 {
1013     NSSize topSZ = { nsz.width, nsz.height + cachedInsets[2] + cachedInsets[3] }; // height + insets.top + insets.bottom
1014     return topSZ;
1015 }
1016
1017 /**
1018  * y-flips input / output
1019  * p rel client window position w/ top-left origin
1020  * returns: location in 0/0 top-left space.
1021  */
1022 - (NSPoint) getLocationOnScreen: (NSPoint) p
1023 {
1024     NSView* view = [self contentView];
1025     NSRect viewFrame = [view frame];
1026     NSRect r;
1027     r.origin.x = p.x;
1028     r.origin.y = viewFrame.size.height - p.y; // y-flip
1029     r.size.width = 0;
1030     r.size.height = 0;
1031     // NSRect rS = [self convertRectToScreen: r]; // 10.7
1032     NSPoint oS = [self convertBaseToScreen: r.origin]; // BL-screen
1033
1034     NSScreen* screen = [self screen];
1035     CGDirectDisplayID display = NewtScreen_getCGDirectDisplayIDByNSScreen(screen);
1036     CGRect frameTL = CGDisplayBounds (display); // origin top-left
1037     NSRect frameBL = [screen frame]; // origin bottom-left
1038     oS.y = frameTL.origin.y + frameTL.size.height - ( oS.y - frameBL.origin.y ); // y-flip from BL-screen -> TL-screen
1039
1040 #ifdef VERBOSE_ON
1041     NSRect winFrame = [self frame];
1042     DBG_PRINT( "getLocationOnScreen: point-in[%d/%d], winFrame[%d/%d %dx%d], viewFrame[%d/%d %dx%d], screen tl[%d/%d %dx%d] bl[%d/%d %dx%d] -> %d/%d\n",
1043         (int)p.x, (int)p.y,
1044         (int)winFrame.origin.x, (int)winFrame.origin.y, (int)winFrame.size.width, (int)winFrame.size.height,
1045         (int)viewFrame.origin.x, (int)viewFrame.origin.y, (int)viewFrame.size.width, (int)viewFrame.size.height,
1046         (int)frameTL.origin.x, (int)frameTL.origin.y, (int)frameTL.size.width, (int)frameTL.size.height,
1047         (int)frameBL.origin.x, (int)frameBL.origin.y, (int)frameBL.size.width, (int)frameBL.size.height,
1048         (int)oS.x, (int)oS.y);
1049 #endif
1050
1051     return oS;
1052 }
1053
1054 - (void) focusChanged: (BOOL) gained
1055 {
1056     DBG_PRINT( "focusChanged: gained %d\n", gained);
1057     NewtView* newtView = (NewtView *) [self contentView];
1058     if( ! [newtView isKindOfClass:[NewtView class]] ) {
1059         return;
1060     }
1061     jobject javaWindowObject = [newtView getJavaWindowObject];
1062     if (javaWindowObject == NULL) {
1063         DBG_PRINT("focusChanged: null javaWindowObject\n");
1064         return;
1065     }
1066     int shallBeDetached = 0;
1067     JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
1068     if(NULL==env) {
1069         DBG_PRINT("focusChanged: null JNIEnv\n");
1070         return;
1071     }
1072
1073     (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_FALSE, (gained == YES) ? JNI_TRUE : JNI_FALSE);
1074
1075     // detaching thread not required - daemon
1076     // NewtCommon_ReleaseJNIEnv(shallBeDetached);
1077 }
1078
1079 - (void) keyDown: (NSEvent*) theEvent
1080 {
1081     NewtView* newtView = (NewtView *) [self contentView];
1082     if( [newtView isKindOfClass:[NewtView class]] ) {
1083         [newtView sendKeyEvent: theEvent eventType: (jshort)EVENT_KEY_PRESSED];
1084     }
1085 }
1086
1087 - (void) keyUp: (NSEvent*) theEvent
1088 {
1089     NewtView* newtView = (NewtView *) [self contentView];
1090     if( [newtView isKindOfClass:[NewtView class]] ) {
1091         [newtView sendKeyEvent: theEvent eventType: (jshort)EVENT_KEY_RELEASED];
1092     }
1093 }
1094
1095 - (void) flagsChanged:(NSEvent *) theEvent
1096 {
1097     NSUInteger mods = [theEvent modifierFlags];
1098     NewtView* newtView = (NewtView *) [self contentView];
1099     if( [newtView isKindOfClass:[NewtView class]] ) {
1100         [newtView handleFlagsChanged: mods];
1101     }
1102 }
1103
1104 - (BOOL) acceptsMouseMovedEvents
1105 {
1106     return YES;
1107 }
1108
1109 - (BOOL) acceptsFirstResponder 
1110 {
1111     return YES;
1112 }
1113
1114 - (BOOL) becomeFirstResponder
1115 {
1116     DBG_PRINT( "*************** Win.becomeFirstResponder\n");
1117     return [super becomeFirstResponder];
1118 }
1119
1120 - (BOOL) resignFirstResponder
1121 {
1122     DBG_PRINT( "*************** Win.resignFirstResponder\n");
1123     return [super resignFirstResponder];
1124 }
1125
1126 - (BOOL) canBecomeKeyWindow
1127 {
1128     // Even if the window is borderless, we still want it to be able
1129     // to become the key window to receive keyboard events
1130     return YES;
1131 }
1132
1133 - (void) becomeKeyWindow
1134 {
1135     DBG_PRINT( "*************** becomeKeyWindow\n");
1136     [super becomeKeyWindow];
1137 }
1138
1139 - (void) resignKeyWindow
1140 {
1141     DBG_PRINT( "*************** resignKeyWindow: isFullscreen %d\n", (int)isFullscreenWindow);
1142     if(!isFullscreenWindow) {
1143         [super resignKeyWindow];
1144     }
1145 }
1146
1147 - (void) windowDidBecomeKey: (NSNotification *) notification
1148 {
1149     DBG_PRINT( "*************** windowDidBecomeKey\n");
1150     NewtView* newtView = (NewtView *) [self contentView];
1151     if( [newtView isKindOfClass:[NewtView class]] ) {
1152         BOOL mouseInside = [newtView updateMouseInside];
1153         if(YES == mouseInside) {
1154             [newtView cursorHide: ![newtView isMouseVisible] enter: 1];
1155         }
1156     }
1157     [self focusChanged: YES];
1158 }
1159
1160 - (void) windowDidResignKey: (NSNotification *) notification
1161 {
1162     DBG_PRINT( "*************** windowDidResignKey\n");
1163     // Implicit mouse exit by OS X
1164     [self focusChanged: NO];
1165 }
1166
1167 - (void)windowDidResize: (NSNotification*) notification
1168 {
1169     jobject javaWindowObject = NULL;
1170     int shallBeDetached = 0;
1171     JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
1172
1173     if( NULL == env ) {
1174         DBG_PRINT("windowDidResize: null JNIEnv\n");
1175         return;
1176     }
1177     NewtView* newtView = (NewtView *) [self contentView];
1178     if( [newtView isKindOfClass:[NewtView class]] ) {
1179         javaWindowObject = [newtView getJavaWindowObject];
1180     }
1181     if( NULL != javaWindowObject ) {
1182         // update insets on every window resize for lack of better hook place
1183         [self updateInsets: env jwin:javaWindowObject];
1184
1185         NSRect frameRect = [self frame];
1186         NSRect contentRect = [self contentRectForFrameRect: frameRect];
1187
1188         (*env)->CallVoidMethod(env, javaWindowObject, sizeChangedID, JNI_TRUE, // defer 
1189                                (jint) contentRect.size.width,
1190                                (jint) contentRect.size.height, JNI_FALSE);
1191     }
1192     // detaching thread not required - daemon
1193     // NewtCommon_ReleaseJNIEnv(shallBeDetached);
1194 }
1195
1196 - (void)windowDidMove: (NSNotification*) notification
1197 {
1198     NewtView* newtView = (NewtView *) [self contentView];
1199     if( ! [newtView isKindOfClass:[NewtView class]] ) {
1200         return;
1201     }
1202     jobject javaWindowObject = [newtView getJavaWindowObject];
1203     if (javaWindowObject == NULL) {
1204         DBG_PRINT("windowDidMove: null javaWindowObject\n");
1205         return;
1206     }
1207     int shallBeDetached = 0;
1208     JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
1209     if(NULL==env) {
1210         DBG_PRINT("windowDidMove: null JNIEnv\n");
1211         return;
1212     }
1213
1214     NSPoint p0 = { 0, 0 };
1215     p0 = [self getLocationOnScreen: p0];
1216     (*env)->CallVoidMethod(env, javaWindowObject, positionChangedID, JNI_FALSE, (jint) p0.x, (jint) p0.y);
1217
1218     // detaching thread not required - daemon
1219     // NewtCommon_ReleaseJNIEnv(shallBeDetached);
1220 }
1221
1222 - (BOOL)windowShouldClose: (id) sender
1223 {
1224     return [self windowClosingImpl: NO];
1225 }
1226
1227 - (void)windowWillClose: (NSNotification*) notification
1228 {
1229     [self windowClosingImpl: YES];
1230 }
1231
1232 - (BOOL) windowClosingImpl: (BOOL) force
1233 {
1234     jboolean closed = JNI_FALSE;
1235
1236     NewtView* newtView = (NewtView *) [self contentView];
1237     if( ! [newtView isKindOfClass:[NewtView class]] ) {
1238         return NO;
1239     }
1240     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1241     [newtView cursorHide: NO enter: -1];
1242
1243     if( false == [newtView getDestroyNotifySent] ) {
1244         jobject javaWindowObject = [newtView getJavaWindowObject];
1245         DBG_PRINT( "*************** windowWillClose.0: %p\n", (void *)(intptr_t)javaWindowObject);
1246         if (javaWindowObject == NULL) {
1247             DBG_PRINT("windowWillClose: null javaWindowObject\n");
1248             [pool release];
1249             return NO;
1250         }
1251         int shallBeDetached = 0;
1252         JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
1253         if(NULL==env) {
1254             DBG_PRINT("windowWillClose: null JNIEnv\n");
1255             [pool release];
1256             return NO;
1257         }
1258         [newtView setDestroyNotifySent: true]; // earmark assumption of being closed
1259         closed = (*env)->CallBooleanMethod(env, javaWindowObject, windowDestroyNotifyID, force ? JNI_TRUE : JNI_FALSE);
1260         if(!force && !closed) {
1261             // not closed on java side, not force -> clear flag
1262             [newtView setDestroyNotifySent: false];
1263         }
1264
1265         // detaching thread not required - daemon
1266         // NewtCommon_ReleaseJNIEnv(shallBeDetached);
1267         DBG_PRINT( "*************** windowWillClose.X: %p, closed %d\n", (void *)(intptr_t)javaWindowObject, (int)closed);
1268     } else {
1269         DBG_PRINT( "*************** windowWillClose (skip)\n");
1270     }
1271     [pool release];
1272     return JNI_TRUE == closed ? YES : NO ;
1273 }
1274
1275 @end
1276
http://JogAmp.org git info: FAQ, tutorial and man pages.