Jogamp
Bug 741 HiDPI: Add ScalableSurface interface to get/set pixelScale w/ full OSX impl.
[jogl.git] / src / newt / native / NewtMacWindow.m
CommitLineData
a959c53b
KR
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
d2fc229b
SG
39#include <CoreFoundation/CoreFoundation.h>
40#include <Carbon/Carbon.h> /* For kVK_ constants, and TIS functions. */
41
da14d647
SG
42#include <math.h>
43
e7ffa68b
SG
44#define PRINTF(...) NSLog(@ __VA_ARGS__)
45
da14d647 46static jfloat GetDelta(NSEvent *event, jint javaMods[]) {
2e550f9f 47 CGEventRef cgEvent = [event CGEvent];
da14d647
SG
48 CGFloat deltaY = 0.0;
49 CGFloat deltaX = 0.0;
50 CGFloat delta = 0.0;
2e550f9f 51
52 if (CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventIsContinuous)) {
53 // mouse pad case
da14d647
SG
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 }
2e550f9f 63 } else {
64 // traditional mouse wheel case
da14d647 65 deltaX = [event deltaX];
2e550f9f 66 deltaY = [event deltaY];
da14d647
SG
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) {
2e550f9f 69 // shift+vertical wheel scroll produces horizontal scroll
70 // we convert it to vertical
da14d647
SG
71 delta = deltaX;
72 } else {
73 delta = deltaY;
2e550f9f 74 }
da14d647
SG
75 if (-1.0 < delta && delta < 1.0) {
76 delta *= 10.0;
2e550f9f 77 } else {
da14d647
SG
78 if (delta < 0.0) {
79 delta = delta - 0.5f;
2e550f9f 80 } else {
da14d647 81 delta = delta + 0.5f;
2e550f9f 82 }
83 }
84 }
da14d647
SG
85 // fprintf(stderr, "WHEEL/RES: %lf - 0x%X\n", (double)delta, javaMods[0]);
86 return (jfloat) delta;
2e550f9f 87}
88
fcc0e739
SG
89#define kVK_Shift 0x38
90#define kVK_Option 0x3A
91#define kVK_Control 0x3B
92#define kVK_Command 0x37
93
94static 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
112static 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
125static 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
148static CFMutableDictionaryRef CKCH_USCodeToNNChar = NULL;
149
150static void CKCH_CreateDictionaries() {
151 TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
152 CKCH_USCodeToNNChar = CKCH_CreateCodeToCharDict(currentKeyboard);
153 CFRelease(currentKeyboard);
154}
155
156static 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
472a9c60
SG
171static jmethodID enqueueMouseEventID = NULL;
172static jmethodID enqueueKeyEventID = NULL;
3db4e89c 173static jmethodID requestFocusID = NULL;
472a9c60 174
bac49fd6
SG
175static jmethodID insetsChangedID = NULL;
176static jmethodID sizeChangedID = NULL;
56d60b36 177static jmethodID updatePixelScaleID = NULL;
bac49fd6
SG
178static jmethodID visibleChangedID = NULL;
179static jmethodID positionChangedID = NULL;
180static jmethodID focusChangedID = NULL;
181static jmethodID windowDestroyNotifyID = NULL;
627083ad 182static jmethodID windowRepaintID = NULL;
bac49fd6 183
9b28aea5 184// Need to enqueue all events to EDT,
323f4974
SG
185// since we may operate on AWT-AppKit (Main Thread)
186// and direct issuing 'requestFocus()' would deadlock:
187// AWT-AppKit
188// AWT-EventQueue-0
3cffd63d 189
a959c53b 190@implementation NewtView
e5ab9757 191
aeffc47f
SG
192- (id)initWithFrame:(NSRect)frameRect
193{
8edaa978 194 id res = [super initWithFrame:frameRect];
aeffc47f
SG
195 javaWindowObject = NULL;
196
aeffc47f 197 destroyNotifySent = NO;
81cbcdc8 198 softLockCount = 0;
02e5b9b0
SG
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
aeffc47f
SG
204
205 ptrTrackingTag = 0;
fcc0e739
SG
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;
aeffc47f 216
8edaa978
SG
217 DBG_PRINT("NewtView::create: %p (refcnt %d)\n", res, (int)[res retainCount]);
218 return res;
aeffc47f
SG
219}
220
81cbcdc8 221#ifdef DBG_LIFECYCLE
6bcfe5e3
SG
222- (void) release
223{
8edaa978 224 DBG_PRINT("NewtView::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
6bcfe5e3
SG
225 [super release];
226}
81cbcdc8 227#endif
6bcfe5e3 228
33335e61
SG
229- (void) dealloc
230{
8edaa978 231 DBG_PRINT("NewtView::dealloc.0: %p (refcnt %d), ptrTrackingTag %d\n", self, (int)[self retainCount], (int)ptrTrackingTag);
81cbcdc8
SG
232#ifdef DBG_LIFECYCLE
233 NSLog(@"%@",[NSThread callStackSymbols]);
234#endif
235 if( 0 < softLockCount ) {
02e5b9b0 236 NSLog(@"NewtView::dealloc: softLock still hold @ dealloc!\n");
33335e61 237 }
fcc0e739
SG
238 [self removeCursorRects];
239 [self removeMyCursor];
240
33335e61 241 pthread_mutex_destroy(&softLockSync);
8edaa978 242 DBG_PRINT("NewtView::dealloc.X: %p\n", self);
33335e61
SG
243 [super dealloc];
244}
245
a959c53b
KR
246- (void) setJavaWindowObject: (jobject) javaWindowObj
247{
248 javaWindowObject = javaWindowObj;
249}
250
251- (jobject) getJavaWindowObject
252{
253 return javaWindowObject;
254}
255
33335e61
SG
256- (void) setDestroyNotifySent: (BOOL) v
257{
258 destroyNotifySent = v;
259}
260
261- (BOOL) getDestroyNotifySent
262{
263 return destroyNotifySent;
264}
265
33335e61
SG
266- (BOOL) softLock
267{
02e5b9b0 268 // DBG_PRINT("*************** softLock.0: %p\n", (void*)pthread_self());
81cbcdc8
SG
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++;
02e5b9b0 275 // DBG_PRINT("*************** softLock.X: %p\n", (void*)pthread_self());
81cbcdc8 276 return 0 < softLockCount;
33335e61
SG
277}
278
81cbcdc8 279- (BOOL) softUnlock
33335e61 280{
02e5b9b0 281 // DBG_PRINT("*************** softUnlock: %p\n", (void*)pthread_self());
81cbcdc8
SG
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;
33335e61
SG
290}
291
292- (BOOL) needsDisplay
293{
33335e61 294 return NO == destroyNotifySent && [super needsDisplay];
33335e61
SG
295}
296
297- (void) displayIfNeeded
298{
02e5b9b0
SG
299 if( YES == [self needsDisplay] ) {
300 [self softLock];
33335e61 301 [super displayIfNeeded];
02e5b9b0 302 [self softUnlock];
33335e61 303 }
33335e61
SG
304}
305
02e5b9b0 306- (void) display
fa2e6727 307{
02e5b9b0
SG
308 if( NO == destroyNotifySent ) {
309 [self softLock];
310 [super display];
311 [self softUnlock];
312 }
18bf27fa
SG
313}
314
627083ad
SG
315- (void) drawRect:(NSRect)dirtyRect
316{
02e5b9b0 317 DBG_PRINT("*************** dirtyRect: %p %lf/%lf %lfx%lf\n",
627083ad
SG
318 javaWindowObject, dirtyRect.origin.x, dirtyRect.origin.y, dirtyRect.size.width, dirtyRect.size.height);
319
bc933c6e
SG
320 if(NULL==javaWindowObject) {
321 DBG_PRINT("drawRect: null javaWindowObject\n");
322 return;
323 }
627083ad 324 int shallBeDetached = 0;
6647b4a6 325 JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
627083ad 326 if(NULL==env) {
bc933c6e 327 DBG_PRINT("drawRect: null JNIEnv\n");
627083ad
SG
328 return;
329 }
330
331 NSRect viewFrame = [self frame];
332
02e5b9b0 333 (*env)->CallVoidMethod(env, javaWindowObject, windowRepaintID, JNI_TRUE, // defer ..
56d60b36
SG
334 (int)dirtyRect.origin.x, (int)viewFrame.size.height - (int)dirtyRect.origin.y,
335 (int)dirtyRect.size.width, (int)dirtyRect.size.height);
627083ad 336
6647b4a6
SG
337 // detaching thread not required - daemon
338 // NewtCommon_ReleaseJNIEnv(shallBeDetached);
627083ad
SG
339}
340
341- (void) viewDidHide
18bf27fa 342{
bc933c6e
SG
343 if(NULL==javaWindowObject) {
344 DBG_PRINT("viewDidHide: null javaWindowObject\n");
345 return;
346 }
e5ab9757 347 int shallBeDetached = 0;
6647b4a6 348 JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
e5ab9757 349 if(NULL==env) {
d7ee8ff7 350 DBG_PRINT("viewDidHide: null JNIEnv\n");
e5ab9757
SG
351 return;
352 }
353
3cffd63d 354 (*env)->CallVoidMethod(env, javaWindowObject, visibleChangedID, JNI_FALSE, JNI_FALSE);
e5ab9757 355
6647b4a6
SG
356 // detaching thread not required - daemon
357 // NewtCommon_ReleaseJNIEnv(shallBeDetached);
e5ab9757 358
18bf27fa
SG
359 [super viewDidHide];
360}
361
627083ad 362- (void) viewDidUnhide
18bf27fa 363{
bc933c6e
SG
364 if(NULL==javaWindowObject) {
365 DBG_PRINT("viewDidUnhide: null javaWindowObject\n");
366 return;
367 }
e5ab9757 368 int shallBeDetached = 0;
6647b4a6 369 JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
e5ab9757 370 if(NULL==env) {
bc933c6e 371 DBG_PRINT("viewDidUnhide: null JNIEnv\n");
e5ab9757
SG
372 return;
373 }
374
3cffd63d 375 (*env)->CallVoidMethod(env, javaWindowObject, visibleChangedID, JNI_FALSE, JNI_TRUE);
e5ab9757 376
6647b4a6
SG
377 // detaching thread not required - daemon
378 // NewtCommon_ReleaseJNIEnv(shallBeDetached);
e5ab9757 379
18bf27fa
SG
380 [super viewDidUnhide];
381}
fa2e6727 382
627083ad 383- (BOOL) acceptsFirstResponder
cac4e2e3
SG
384{
385 return YES;
386}
387
fcc0e739
SG
388- (BOOL) becomeFirstResponder
389{
390 DBG_PRINT( "*************** View.becomeFirstResponder\n");
391 return [super becomeFirstResponder];
d2fc229b
SG
392}
393
fcc0e739
SG
394- (BOOL) resignFirstResponder
395{
396 DBG_PRINT( "*************** View.resignFirstResponder\n");
397 return [super resignFirstResponder];
398}
d2fc229b 399
fcc0e739
SG
400- (void) removeCursorRects
401{
402 if(0 != ptrTrackingTag) {
403 if(NULL != myCursor) {
404 [self removeCursorRect: ptrRect cursor: myCursor];
d2fc229b 405 }
fcc0e739
SG
406 [self removeTrackingRect: ptrTrackingTag];
407 ptrTrackingTag = 0;
d2fc229b 408 }
d2fc229b
SG
409}
410
fcc0e739
SG
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}
d2fc229b 419
fcc0e739
SG
420- (void) removeMyCursor
421{
422 if(NULL != myCursor) {
423 [myCursor release];
424 myCursor = NULL;
425 }
d2fc229b
SG
426}
427
fcc0e739
SG
428- (void) resetCursorRects
429{
430 [super resetCursorRects];
d2fc229b 431
fcc0e739
SG
432 [self removeCursorRects];
433 [self addCursorRects];
434}
d2fc229b 435
fcc0e739
SG
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];
d2fc229b
SG
445 }
446 }
fcc0e739
SG
447 NSWindow* nsWin = [self window];
448 if( NULL != nsWin ) {
449 [nsWin invalidateCursorRectsForView: self];
450 }
d2fc229b
SG
451}
452
fcc0e739 453- (void) mouseEntered: (NSEvent*) theEvent
a959c53b 454{
fcc0e739
SG
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];
a959c53b 464 }
a959c53b
KR
465}
466
fcc0e739 467- (void) mouseExited: (NSEvent*) theEvent
627083ad 468{
fcc0e739
SG
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];
0237bde0 475 } else {
fcc0e739 476 [self setMousePosition: lastInsideMousePosition];
0237bde0 477 }
fcc0e739 478}
e7ffa68b 479
759d9067
SG
480/**
481 * p abs screen position w/ bottom-left origin
482 */
fcc0e739
SG
483- (void) setMousePosition:(NSPoint)p
484{
485 NSWindow* nsWin = [self window];
486 if( NULL != nsWin ) {
487 NSScreen* screen = [nsWin screen];
fcc0e739 488
759d9067
SG
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
fcc0e739
SG
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;
6647b4a6 637 JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
fcc0e739 638 if(NULL==env) {
6647b4a6 639 DBG_PRINT("sendMouseEvent: null JNIEnv\n");
fcc0e739
SG
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
6647b4a6
SG
687 // detaching thread not required - daemon
688 // NewtCommon_ReleaseJNIEnv(shallBeDetached);
fcc0e739
SG
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;
6647b4a6 740 JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
fcc0e739 741 if(NULL==env) {
6647b4a6 742 DBG_PRINT("sendKeyEvent: null JNIEnv\n");
fcc0e739
SG
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
6647b4a6
SG
773 // detaching thread not required - daemon
774 // NewtCommon_ReleaseJNIEnv(shallBeDetached);
fcc0e739
SG
775}
776
56d60b36
SG
777- (void)viewDidChangeBackingProperties
778{
779 [super viewDidChangeBackingProperties];
780
2571ed0b
SG
781 // HiDPI scaling
782 BOOL useHiDPI = [self wantsBestResolutionOpenGLSurface];
783 CGFloat pixelScaleRaw = [[self window] backingScaleFactor];
784 CGFloat pixelScaleUse = useHiDPI ? pixelScaleRaw : 1.0;
785 DBG_PRINT("viewDidChangeBackingProperties: PixelScale: HiDPI %d, raw %f -> use %f\n", useHiDPI, (float)pixelScaleRaw, (float)pixelScaleUse);
786 [[self layer] setContentsScale: pixelScaleUse];
56d60b36
SG
787
788 if (javaWindowObject == NULL) {
789 DBG_PRINT("viewDidChangeBackingProperties: null javaWindowObject\n");
790 return;
791 }
792 int shallBeDetached = 0;
793 JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
794 if(NULL==env) {
795 DBG_PRINT("viewDidChangeBackingProperties: null JNIEnv\n");
796 return;
797 }
798
2571ed0b 799 (*env)->CallVoidMethod(env, javaWindowObject, updatePixelScaleID, JNI_TRUE, (jfloat)pixelScaleUse); // defer
56d60b36
SG
800
801 // detaching thread not required - daemon
802 // NewtCommon_ReleaseJNIEnv(shallBeDetached);
803}
804
805
fcc0e739
SG
806@end
807
808@implementation NewtMacWindow
809
810+ (BOOL) initNatives: (JNIEnv*) env forClass: (jclass) clazz
811{
812 enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZSIIISF)V");
813 enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZSISCC)V");
56d60b36
SG
814 sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(ZIIZ)V");
815 updatePixelScaleID = (*env)->GetMethodID(env, clazz, "updatePixelScale", "(ZF)V");
fcc0e739
SG
816 visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(ZZ)V");
817 insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(ZIIII)V");
818 positionChangedID = (*env)->GetMethodID(env, clazz, "screenPositionChanged", "(ZII)V");
819 focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(ZZ)V");
820 windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "(Z)Z");
821 windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(ZIIII)V");
822 requestFocusID = (*env)->GetMethodID(env, clazz, "requestFocus", "(Z)V");
56d60b36 823 if (enqueueMouseEventID && enqueueKeyEventID && sizeChangedID && updatePixelScaleID && visibleChangedID && insetsChangedID &&
fcc0e739
SG
824 positionChangedID && focusChangedID && windowDestroyNotifyID && requestFocusID && windowRepaintID)
825 {
826 CKCH_CreateDictionaries();
827 return YES;
828 }
829 return NO;
830}
831
832- (id) initWithContentRect: (NSRect) contentRect
833 styleMask: (NSUInteger) windowStyle
834 backing: (NSBackingStoreType) bufferingType
835 defer: (BOOL) deferCreation
836 isFullscreenWindow:(BOOL)isfs
837{
838 id res = [super initWithContentRect: contentRect
839 styleMask: windowStyle
840 backing: bufferingType
841 defer: deferCreation];
842 // OSX 10.6
843 if ( [NSApp respondsToSelector:@selector(currentSystemPresentationOptions)] &&
844 [NSApp respondsToSelector:@selector(setPresentationOptions:)] ) {
845 hasPresentationSwitch = YES;
846 defaultPresentationOptions = [NSApp currentSystemPresentationOptions];
847 fullscreenPresentationOptions =
848 // NSApplicationPresentationDefault|
849 // NSApplicationPresentationAutoHideDock|
850 NSApplicationPresentationHideDock|
851 // NSApplicationPresentationAutoHideMenuBar|
852 NSApplicationPresentationHideMenuBar|
853 NSApplicationPresentationDisableAppleMenu|
854 // NSApplicationPresentationDisableProcessSwitching|
855 // NSApplicationPresentationDisableSessionTermination|
856 NSApplicationPresentationDisableHideApplication|
857 // NSApplicationPresentationDisableMenuBarTransparency|
858 // NSApplicationPresentationFullScreen| // OSX 10.7
859 0 ;
860 } else {
861 hasPresentationSwitch = NO;
862 defaultPresentationOptions = 0;
863 fullscreenPresentationOptions = 0;
864 }
865
866 isFullscreenWindow = isfs;
867 // Why is this necessary? Without it we don't get any of the
868 // delegate methods like resizing and window movement.
869 [self setDelegate: self];
56d60b36 870
fcc0e739
SG
871 cachedInsets[0] = 0; // l
872 cachedInsets[1] = 0; // r
873 cachedInsets[2] = 0; // t
874 cachedInsets[3] = 0; // b
56d60b36 875
4e0eb391 876 realized = YES;
0237bde0
SG
877 DBG_PRINT("NewtWindow::create: %p, realized %d, hasPresentationSwitch %d[defaultOptions 0x%X, fullscreenOptions 0x%X], (refcnt %d)\n",
878 res, realized, (int)hasPresentationSwitch, (int)defaultPresentationOptions, (int)fullscreenPresentationOptions, (int)[res retainCount]);
627083ad
SG
879 return res;
880}
881
81cbcdc8 882#ifdef DBG_LIFECYCLE
6bcfe5e3
SG
883- (void) release
884{
8edaa978 885 DBG_PRINT("NewtWindow::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
8edaa978 886 // NSLog(@"%@",[NSThread callStackSymbols]);
6bcfe5e3
SG
887 [super release];
888}
81cbcdc8 889#endif
6bcfe5e3
SG
890
891- (void) dealloc
892{
8edaa978 893 DBG_PRINT("NewtWindow::dealloc.0: %p (refcnt %d)\n", self, (int)[self retainCount]);
81cbcdc8
SG
894#ifdef DBG_LIFECYCLE
895 NSLog(@"%@",[NSThread callStackSymbols]);
6bcfe5e3 896#endif
81cbcdc8 897
8edaa978
SG
898 NewtView* mView = (NewtView *)[self contentView];
899 if( NULL != mView ) {
900 [mView release];
901 }
6bcfe5e3 902 [super dealloc];
8edaa978 903 DBG_PRINT("NewtWindow::dealloc.X: %p\n", self);
6bcfe5e3
SG
904}
905
81cbcdc8 906- (void) setRealized: (BOOL)v
4e0eb391 907{
81cbcdc8 908 realized = v;
4e0eb391
SG
909}
910
911- (BOOL) isRealized
912{
913 return realized;
914}
915
81cbcdc8 916- (void) updateInsets: (JNIEnv*) env jwin: (jobject) javaWin
29b675c2 917{
29b675c2 918 NSRect frameRect = [self frame];
919 NSRect contentRect = [self contentRectForFrameRect: frameRect];
920
921 // note: this is a simplistic implementation which doesn't take
922 // into account DPI and scaling factor
923 CGFloat l = contentRect.origin.x - frameRect.origin.x;
7b8e2ef5
SG
924 cachedInsets[0] = (int)l; // l
925 cachedInsets[1] = (int)(frameRect.size.width - (contentRect.size.width + l)); // r
926 cachedInsets[2] = (jint)(frameRect.size.height - contentRect.size.height); // t
927 cachedInsets[3] = (jint)(contentRect.origin.y - frameRect.origin.y); // b
29b675c2 928
7b8e2ef5 929 DBG_PRINT( "updateInsets: [ l %d, r %d, t %d, b %d ]\n", cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3]);
e5ab9757 930
81cbcdc8
SG
931 if( NULL != env && NULL != javaWin ) {
932 (*env)->CallVoidMethod(env, javaWin, insetsChangedID, JNI_FALSE, cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3]);
933 }
29b675c2 934}
935
627083ad 936- (void) attachToParent: (NSWindow*) parent
a959c53b 937{
627083ad
SG
938 DBG_PRINT( "attachToParent.1\n");
939 [parent addChildWindow: self ordered: NSWindowAbove];
940 DBG_PRINT( "attachToParent.2\n");
941 [self setParentWindow: parent];
942 DBG_PRINT( "attachToParent.X\n");
943}
944
945- (void) detachFromParent: (NSWindow*) parent
946{
947 DBG_PRINT( "detachFromParent.1\n");
948 [self setParentWindow: nil];
bc933c6e
SG
949 if(NULL != parent) {
950 DBG_PRINT( "detachFromParent.2\n");
951 [parent removeChildWindow: self];
952 }
627083ad 953 DBG_PRINT( "detachFromParent.X\n");
a959c53b
KR
954}
955
aeffc47f 956/**
7d5c51b6 957 * p abs screen position of client-area pos w/ top-left origin, using contentView's client NSSize
aeffc47f
SG
958 * returns: abs screen position w/ bottom-left origin
959 */
7d5c51b6 960- (NSPoint) newtAbsClientTLWinPos2AbsBLScreenPos: (NSPoint) p
aeffc47f
SG
961{
962 NSView* mView = [self contentView];
963 NSRect mViewFrame = [mView frame];
7d5c51b6
SG
964 return [self newtAbsClientTLWinPos2AbsBLScreenPos: p size: mViewFrame.size];
965}
966
967/**
968 * p abs screen position of client-area pos w/ top-left origin, using given client NSSize
969 * returns: abs screen position w/ bottom-left origin
970 */
971- (NSPoint) newtAbsClientTLWinPos2AbsBLScreenPos: (NSPoint) p size: (NSSize) nsz
972{
973 int totalHeight = nsz.height + cachedInsets[3]; // height + insets.bottom
aeffc47f 974
759d9067 975 DBG_PRINT( "newtAbsClientTLWinPos2AbsBLScreenPos: point-in[%d/%d], size-in[%dx%d], insets bottom %d -> totalHeight %d\n",
81cbcdc8
SG
976 (int)p.x, (int)p.y, (int)nsz.width, (int)nsz.height, cachedInsets[3], totalHeight);
977
aeffc47f 978 NSScreen* screen = [self screen];
aeffc47f 979
759d9067
SG
980 CGDirectDisplayID display = NewtScreen_getCGDirectDisplayIDByNSScreen(screen);
981 CGRect frameTL = CGDisplayBounds (display); // origin top-left
982 NSRect frameBL = [screen frame]; // origin bottom-left
983 NSPoint r = NSMakePoint(p.x, frameBL.origin.y + frameBL.size.height - ( p.y - frameTL.origin.y ) - totalHeight); // y-flip from TL-screen -> BL-screen
a959c53b 984
759d9067
SG
985 DBG_PRINT( "newtAbsClientTLWinPos2AbsBLScreenPos: screen tl[%d/%d %dx%d] bl[%d/%d %dx%d -> %d/%d\n",
986 (int)frameTL.origin.x, (int)frameTL.origin.y, (int)frameTL.size.width, (int)frameTL.size.height,
987 (int)frameBL.origin.x, (int)frameBL.origin.y, (int)frameBL.size.width, (int)frameBL.size.height,
988 (int)r.x, (int)r.y);
e5ab9757 989
fcc0e739 990 return r;
a959c53b
KR
991}
992
fcc0e739
SG
993/**
994 * p rel client window position w/ top-left origin
995 * returns: abs screen position w/ bottom-left origin
996 */
997- (NSPoint) newtRelClientTLWinPos2AbsBLScreenPos: (NSPoint) p
a959c53b 998{
fcc0e739 999 NSRect winFrame = [self frame];
a959c53b 1000
fcc0e739
SG
1001 NSView* mView = [self contentView];
1002 NSRect mViewFrame = [mView frame];
759d9067
SG
1003 NSPoint r = NSMakePoint(winFrame.origin.x + p.x,
1004 winFrame.origin.y + ( mViewFrame.size.height - p.y ) ); // y-flip in view
1005
1006 DBG_PRINT( "newtRelClientTLWinPos2AbsBLScreenPos: point-in[%d/%d], winFrame[%d/%d %dx%d], viewFrame[%d/%d %dx%d] -> %d/%d\n",
1007 (int)p.x, (int)p.y,
1008 (int)winFrame.origin.x, (int)winFrame.origin.y, (int)winFrame.size.width, (int)winFrame.size.height,
1009 (int)mViewFrame.origin.x, (int)mViewFrame.origin.y, (int)mViewFrame.size.width, (int)mViewFrame.size.height,
1010 (int)r.x, (int)r.y);
a959c53b 1011
759d9067 1012 return r;
fcc0e739 1013}
aeffc47f 1014
fcc0e739
SG
1015- (NSSize) newtClientSize2TLSize: (NSSize) nsz
1016{
1017 NSSize topSZ = { nsz.width, nsz.height + cachedInsets[2] + cachedInsets[3] }; // height + insets.top + insets.bottom
1018 return topSZ;
1019}
aeffc47f 1020
fcc0e739
SG
1021/**
1022 * y-flips input / output
1023 * p rel client window position w/ top-left origin
1024 * returns: location in 0/0 top-left space.
1025 */
1026- (NSPoint) getLocationOnScreen: (NSPoint) p
1027{
fcc0e739
SG
1028 NSView* view = [self contentView];
1029 NSRect viewFrame = [view frame];
fcc0e739
SG
1030 NSRect r;
1031 r.origin.x = p.x;
1032 r.origin.y = viewFrame.size.height - p.y; // y-flip
1033 r.size.width = 0;
1034 r.size.height = 0;
759d9067
SG
1035 // NSRect rS = [self convertRectToScreen: r]; // 10.7
1036 NSPoint oS = [self convertBaseToScreen: r.origin]; // BL-screen
1037
1038 NSScreen* screen = [self screen];
1039 CGDirectDisplayID display = NewtScreen_getCGDirectDisplayIDByNSScreen(screen);
1040 CGRect frameTL = CGDisplayBounds (display); // origin top-left
1041 NSRect frameBL = [screen frame]; // origin bottom-left
1042 oS.y = frameTL.origin.y + frameTL.size.height - ( oS.y - frameBL.origin.y ); // y-flip from BL-screen -> TL-screen
1043
1044#ifdef VERBOSE_ON
1045 NSRect winFrame = [self frame];
1046 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",
1047 (int)p.x, (int)p.y,
1048 (int)winFrame.origin.x, (int)winFrame.origin.y, (int)winFrame.size.width, (int)winFrame.size.height,
1049 (int)viewFrame.origin.x, (int)viewFrame.origin.y, (int)viewFrame.size.width, (int)viewFrame.size.height,
1050 (int)frameTL.origin.x, (int)frameTL.origin.y, (int)frameTL.size.width, (int)frameTL.size.height,
1051 (int)frameBL.origin.x, (int)frameBL.origin.y, (int)frameBL.size.width, (int)frameBL.size.height,
1052 (int)oS.x, (int)oS.y);
1053#endif
1054
fcc0e739 1055 return oS;
a959c53b
KR
1056}
1057
d25c2533 1058- (void) focusChanged: (BOOL) gained
aeffc47f 1059{
d25c2533 1060 DBG_PRINT( "focusChanged: gained %d\n", gained);
fcc0e739
SG
1061 NewtView* newtView = (NewtView *) [self contentView];
1062 if( ! [newtView isKindOfClass:[NewtView class]] ) {
d25c2533
SG
1063 return;
1064 }
fcc0e739 1065 jobject javaWindowObject = [newtView getJavaWindowObject];
d25c2533
SG
1066 if (javaWindowObject == NULL) {
1067 DBG_PRINT("focusChanged: null javaWindowObject\n");
1068 return;
1069 }
1070 int shallBeDetached = 0;
6647b4a6 1071 JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
d25c2533 1072 if(NULL==env) {
6647b4a6 1073 DBG_PRINT("focusChanged: null JNIEnv\n");
d25c2533
SG
1074 return;
1075 }
1076
1077 (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_FALSE, (gained == YES) ? JNI_TRUE : JNI_FALSE);
1078
6647b4a6
SG
1079 // detaching thread not required - daemon
1080 // NewtCommon_ReleaseJNIEnv(shallBeDetached);
aeffc47f
SG
1081}
1082
fcc0e739
SG
1083- (void) keyDown: (NSEvent*) theEvent
1084{
1085 NewtView* newtView = (NewtView *) [self contentView];
1086 if( [newtView isKindOfClass:[NewtView class]] ) {
1087 [newtView sendKeyEvent: theEvent eventType: (jshort)EVENT_KEY_PRESSED];
1088 }
1089}
1090
1091- (void) keyUp: (NSEvent*) theEvent
1092{
1093 NewtView* newtView = (NewtView *) [self contentView];
1094 if( [newtView isKindOfClass:[NewtView class]] ) {
1095 [newtView sendKeyEvent: theEvent eventType: (jshort)EVENT_KEY_RELEASED];
1096 }
1097}
1098
1099- (void) flagsChanged:(NSEvent *) theEvent
1100{
1101 NSUInteger mods = [theEvent modifierFlags];
1102 NewtView* newtView = (NewtView *) [self contentView];
1103 if( [newtView isKindOfClass:[NewtView class]] ) {
1104 [newtView handleFlagsChanged: mods];
1105 }
1106}
1107
1108- (BOOL) acceptsMouseMovedEvents
1109{
1110 return YES;
1111}
1112
1113- (BOOL) acceptsFirstResponder
1114{
1115 return YES;
1116}
1117
d25c2533 1118- (BOOL) becomeFirstResponder
aeffc47f 1119{
fcc0e739 1120 DBG_PRINT( "*************** Win.becomeFirstResponder\n");
d25c2533
SG
1121 return [super becomeFirstResponder];
1122}
1123
1124- (BOOL) resignFirstResponder
1125{
fcc0e739 1126 DBG_PRINT( "*************** Win.resignFirstResponder\n");
d25c2533
SG
1127 return [super resignFirstResponder];
1128}
1129
1130- (BOOL) canBecomeKeyWindow
1131{
1132 // Even if the window is borderless, we still want it to be able
1133 // to become the key window to receive keyboard events
1134 return YES;
1135}
1136
1137- (void) becomeKeyWindow
1138{
1139 DBG_PRINT( "*************** becomeKeyWindow\n");
1140 [super becomeKeyWindow];
1141}
1142
1143- (void) resignKeyWindow
1144{
bc933c6e
SG
1145 DBG_PRINT( "*************** resignKeyWindow: isFullscreen %d\n", (int)isFullscreenWindow);
1146 if(!isFullscreenWindow) {
1147 [super resignKeyWindow];
1148 }
d25c2533
SG
1149}
1150
1151- (void) windowDidBecomeKey: (NSNotification *) notification
1152{
1153 DBG_PRINT( "*************** windowDidBecomeKey\n");
fcc0e739
SG
1154 NewtView* newtView = (NewtView *) [self contentView];
1155 if( [newtView isKindOfClass:[NewtView class]] ) {
1156 BOOL mouseInside = [newtView updateMouseInside];
1157 if(YES == mouseInside) {
1158 [newtView cursorHide: ![newtView isMouseVisible] enter: 1];
1159 }
aeffc47f 1160 }
d25c2533 1161 [self focusChanged: YES];
aeffc47f
SG
1162}
1163
d25c2533 1164- (void) windowDidResignKey: (NSNotification *) notification
aeffc47f 1165{
d25c2533
SG
1166 DBG_PRINT( "*************** windowDidResignKey\n");
1167 // Implicit mouse exit by OS X
1168 [self focusChanged: NO];
aeffc47f
SG
1169}
1170
a959c53b
KR
1171- (void)windowDidResize: (NSNotification*) notification
1172{
81cbcdc8 1173 jobject javaWindowObject = NULL;
e5ab9757 1174 int shallBeDetached = 0;
6647b4a6 1175 JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
81cbcdc8 1176
6647b4a6
SG
1177 if( NULL == env ) {
1178 DBG_PRINT("windowDidResize: null JNIEnv\n");
1179 return;
1180 }
fcc0e739
SG
1181 NewtView* newtView = (NewtView *) [self contentView];
1182 if( [newtView isKindOfClass:[NewtView class]] ) {
1183 javaWindowObject = [newtView getJavaWindowObject];
a959c53b 1184 }
6647b4a6
SG
1185 if( NULL != javaWindowObject ) {
1186 // update insets on every window resize for lack of better hook place
1187 [self updateInsets: env jwin:javaWindowObject];
a959c53b 1188
81cbcdc8
SG
1189 NSRect frameRect = [self frame];
1190 NSRect contentRect = [self contentRectForFrameRect: frameRect];
a959c53b 1191
56d60b36 1192 (*env)->CallVoidMethod(env, javaWindowObject, sizeChangedID, JNI_TRUE, // defer
81cbcdc8
SG
1193 (jint) contentRect.size.width,
1194 (jint) contentRect.size.height, JNI_FALSE);
e5ab9757 1195 }
6647b4a6
SG
1196 // detaching thread not required - daemon
1197 // NewtCommon_ReleaseJNIEnv(shallBeDetached);
a959c53b
KR
1198}
1199
1200- (void)windowDidMove: (NSNotification*) notification
1201{
fcc0e739
SG
1202 NewtView* newtView = (NewtView *) [self contentView];
1203 if( ! [newtView isKindOfClass:[NewtView class]] ) {
a959c53b
KR
1204 return;
1205 }
fcc0e739 1206 jobject javaWindowObject = [newtView getJavaWindowObject];
e5ab9757 1207 if (javaWindowObject == NULL) {
d7ee8ff7 1208 DBG_PRINT("windowDidMove: null javaWindowObject\n");
e5ab9757
SG
1209 return;
1210 }
1211 int shallBeDetached = 0;
6647b4a6 1212 JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
e5ab9757 1213 if(NULL==env) {
6647b4a6 1214 DBG_PRINT("windowDidMove: null JNIEnv\n");
a959c53b
KR
1215 return;
1216 }
1217
7b8e2ef5
SG
1218 NSPoint p0 = { 0, 0 };
1219 p0 = [self getLocationOnScreen: p0];
3cffd63d 1220 (*env)->CallVoidMethod(env, javaWindowObject, positionChangedID, JNI_FALSE, (jint) p0.x, (jint) p0.y);
e5ab9757 1221
6647b4a6
SG
1222 // detaching thread not required - daemon
1223 // NewtCommon_ReleaseJNIEnv(shallBeDetached);
a959c53b
KR
1224}
1225
4e0eb391
SG
1226- (BOOL)windowShouldClose: (id) sender
1227{
1228 return [self windowClosingImpl: NO];
1229}
1230
a959c53b
KR
1231- (void)windowWillClose: (NSNotification*) notification
1232{
4e0eb391
SG
1233 [self windowClosingImpl: YES];
1234}
1235
1236- (BOOL) windowClosingImpl: (BOOL) force
1237{
1238 jboolean closed = JNI_FALSE;
e5ab9757 1239
fcc0e739
SG
1240 NewtView* newtView = (NewtView *) [self contentView];
1241 if( ! [newtView isKindOfClass:[NewtView class]] ) {
20bf031d 1242 return NO;
a959c53b 1243 }
6647b4a6 1244 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
fcc0e739
SG
1245 [newtView cursorHide: NO enter: -1];
1246
1247 if( false == [newtView getDestroyNotifySent] ) {
1248 jobject javaWindowObject = [newtView getJavaWindowObject];
02e5b9b0 1249 DBG_PRINT( "*************** windowWillClose.0: %p\n", (void *)(intptr_t)javaWindowObject);
e5ab9757 1250 if (javaWindowObject == NULL) {
d7ee8ff7 1251 DBG_PRINT("windowWillClose: null javaWindowObject\n");
6647b4a6 1252 [pool release];
20bf031d 1253 return NO;
e5ab9757
SG
1254 }
1255 int shallBeDetached = 0;
6647b4a6 1256 JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached);
e5ab9757 1257 if(NULL==env) {
6647b4a6
SG
1258 DBG_PRINT("windowWillClose: null JNIEnv\n");
1259 [pool release];
20bf031d 1260 return NO;
e5ab9757 1261 }
fcc0e739 1262 [newtView setDestroyNotifySent: true]; // earmark assumption of being closed
4e0eb391
SG
1263 closed = (*env)->CallBooleanMethod(env, javaWindowObject, windowDestroyNotifyID, force ? JNI_TRUE : JNI_FALSE);
1264 if(!force && !closed) {
1265 // not closed on java side, not force -> clear flag
fcc0e739 1266 [newtView setDestroyNotifySent: false];
4e0eb391 1267 }
e5ab9757 1268
6647b4a6
SG
1269 // detaching thread not required - daemon
1270 // NewtCommon_ReleaseJNIEnv(shallBeDetached);
4e0eb391 1271 DBG_PRINT( "*************** windowWillClose.X: %p, closed %d\n", (void *)(intptr_t)javaWindowObject, (int)closed);
e5ab9757
SG
1272 } else {
1273 DBG_PRINT( "*************** windowWillClose (skip)\n");
1274 }
1275 [pool release];
4e0eb391 1276 return JNI_TRUE == closed ? YES : NO ;
a959c53b
KR
1277}
1278
a959c53b 1279@end
d2fc229b 1280
http://JogAmp.org git info: FAQ, tutorial and man pages.