1 /*
2  * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved.
3  * Copyright (c) 2010 JogAmp Community. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * - Redistribution of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * - Redistribution in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any kind. ALL
21  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
22  * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
23  * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
24  * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
25  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
26  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
27  * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
28  * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
29  * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
30  * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
31  * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
32  *
33  */
34 
35 package jogamp.newt.driver.macosx;
36 
37 import com.jogamp.common.util.InterruptSource;
38 import com.jogamp.nativewindow.AbstractGraphicsConfiguration;
39 import com.jogamp.nativewindow.GraphicsConfigurationFactory;
40 import com.jogamp.nativewindow.NativeWindow;
41 import com.jogamp.nativewindow.NativeWindowException;
42 import com.jogamp.nativewindow.MutableSurface;
43 import com.jogamp.nativewindow.ScalableSurface;
44 import com.jogamp.nativewindow.VisualIDHolder;
45 import com.jogamp.nativewindow.util.Point;
46 import com.jogamp.nativewindow.util.PointImmutable;
47 
48 import jogamp.nativewindow.SurfaceScaleUtils;
49 import jogamp.nativewindow.macosx.OSXUtil;
50 import jogamp.newt.PointerIconImpl;
51 import jogamp.newt.ScreenImpl;
52 import jogamp.newt.WindowImpl;
53 import jogamp.newt.driver.DriverClearFocus;
54 import jogamp.newt.driver.DriverUpdatePosition;
55 
56 import com.jogamp.newt.event.InputEvent;
57 import com.jogamp.newt.event.KeyEvent;
58 import com.jogamp.newt.event.MonitorEvent;
59 import com.jogamp.opengl.math.FloatUtil;
60 
61 public class WindowDriver extends WindowImpl implements MutableSurface, DriverClearFocus, DriverUpdatePosition {
62 
63     static {
DisplayDriver.initSingleton()64         DisplayDriver.initSingleton();
65     }
66 
WindowDriver()67     public WindowDriver() {
68     }
69 
updatePixelScale(final boolean sendEvent, final boolean defer, final boolean deferOffThread, final float newPixelScaleRaw, final float maxPixelScaleRaw)70     private boolean updatePixelScale(final boolean sendEvent, final boolean defer, final boolean deferOffThread,
71                                      final float newPixelScaleRaw, final float maxPixelScaleRaw) {
72         final float[] newPixelScale = new float[2];
73         {
74             final float _newPixelScale = FloatUtil.isZero(newPixelScaleRaw, FloatUtil.EPSILON) ? ScalableSurface.IDENTITY_PIXELSCALE : newPixelScaleRaw;
75             newPixelScale[0]= _newPixelScale;
76             newPixelScale[1]= _newPixelScale;
77             final float _maxPixelScale = FloatUtil.isZero(maxPixelScaleRaw, FloatUtil.EPSILON) ? ScalableSurface.IDENTITY_PIXELSCALE : maxPixelScaleRaw;
78             maxPixelScale[0]= _maxPixelScale;
79             maxPixelScale[1]= _maxPixelScale;
80         }
81         // We keep minPixelScale at [1f, 1f]!
82 
83         if( SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, newPixelScale, minPixelScale, maxPixelScale, DEBUG_IMPLEMENTATION ? getClass().getName() : null) ) {
84             if( sendEvent ) {
85                 if( deferOffThread ) {
86                     superSizeChangedOffThread(defer, getWidth(), getHeight(), true);
87                 } else {
88                     super.sizeChanged(defer, getWidth(), getHeight(), true);
89                 }
90             } else {
91                 defineSize(getWidth(), getHeight());
92             }
93             return true;
94         } else {
95             return false;
96         }
97     }
98 
updatePixelScaleByDisplayID(final boolean sendEvent)99     private boolean updatePixelScaleByDisplayID(final boolean sendEvent) {
100         final float maxPixelScaleRaw = (float) OSXUtil.GetPixelScaleByDisplayID(getDisplayID());
101         if( DEBUG_IMPLEMENTATION ) {
102             System.err.println("WindowDriver.updatePixelScale.1: "+hasPixelScale[0]+", "+maxPixelScaleRaw+" (max)");
103         }
104         return updatePixelScale(sendEvent, true /* defer */, false /*offthread */, maxPixelScaleRaw, maxPixelScaleRaw);
105     }
106 
updatePixelScaleByWindowHandle(final boolean sendEvent)107     private boolean updatePixelScaleByWindowHandle(final boolean sendEvent) {
108         final long handle = getWindowHandle();
109         if( 0 != handle ) {
110             final float maxPixelScaleRaw = (float)OSXUtil.GetPixelScale(handle);
111             if( DEBUG_IMPLEMENTATION ) {
112                 System.err.println("WindowDriver.updatePixelScale.2: "+hasPixelScale[0]+", "+maxPixelScaleRaw+" (max)");
113             }
114             return updatePixelScale(sendEvent, true /* defer */, false /*offthread */, maxPixelScaleRaw, maxPixelScaleRaw);
115         } else {
116             return false;
117         }
118     }
119 
120     /** Called from native code */
updatePixelScale(final boolean defer, final float newPixelScaleRaw, final float maxPixelScaleRaw)121     protected void updatePixelScale(final boolean defer, final float newPixelScaleRaw, final float maxPixelScaleRaw) {
122         if( DEBUG_IMPLEMENTATION ) {
123             System.err.println("WindowDriver.updatePixelScale.3: "+hasPixelScale[0]+" (has) -> "+newPixelScaleRaw+" (new), "+maxPixelScaleRaw+" (max), drop "+!isNativeValid());
124         }
125         if( isNativeValid() ) {
126             updatePixelScale(true /* sendEvent*/, defer, true /*offthread */, newPixelScaleRaw, maxPixelScaleRaw);
127         }
128     }
129 
130     @Override
instantiationFinishedImpl()131     protected final void instantiationFinishedImpl() {
132         updatePixelScaleByDisplayID(false /* sendEvent*/);
133     }
134 
135     @Override
setScreen(final ScreenImpl newScreen)136     protected void setScreen(final ScreenImpl newScreen) { // never null !
137         super.setScreen(newScreen);
138         updatePixelScaleByDisplayID(false /* sendEvent*/);  // caller (reparent, ..) will send reshape event
139     }
140 
141     @Override
monitorModeChanged(final MonitorEvent me, final boolean success)142     protected void monitorModeChanged(final MonitorEvent me, final boolean success) {
143         updatePixelScaleByWindowHandle(false /* sendEvent*/); // send reshape event itself
144     }
145 
146     @Override
setSurfaceScale(final float[] pixelScale)147     public final boolean setSurfaceScale(final float[] pixelScale) {
148         super.setSurfaceScale(pixelScale);
149 
150         boolean changed = false;
151         if( isNativeValid() ) {
152             if( isOffscreenInstance ) {
153                 final NativeWindow pWin = getParent();
154                 if( pWin instanceof ScalableSurface ) {
155                     final ScalableSurface sSurf = (ScalableSurface)pWin;
156                     sSurf.setSurfaceScale(reqPixelScale);
157                     sSurf.getMaximumSurfaceScale(maxPixelScale);
158                     sSurf.getMinimumSurfaceScale(minPixelScale);
159                     final float[] pPixelScale = sSurf.getCurrentSurfaceScale(new float[2]);
160                     changed = updatePixelScale(true /* sendEvent */, true /* defer */, true /*offthread */, pPixelScale[0], maxPixelScale[0]); // HiDPI: uniformPixelScale
161                 } else {
162                     // just notify updated pixelScale if offscreen
163                     changed = updatePixelScale(true /* sendEvent */, true /* defer */, true /*offthread */, reqPixelScale[0], maxPixelScale[0]); // HiDPI: uniformPixelScale
164                 }
165             } else {
166                 // set pixelScale in native code, will issue an update updatePixelScale(..)
167                 // hence we pre-query whether pixel-scale will change, without affecting current state 'hasPixelScale'!
168                 final float[] _hasPixelScale = new float[2];
169                 System.arraycopy(hasPixelScale, 0, _hasPixelScale, 0, 2);
170                 if( SurfaceScaleUtils.setNewPixelScale(_hasPixelScale, _hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG_IMPLEMENTATION ? getClass().getName() : null) ) {
171                     OSXUtil.RunOnMainThread(true, false, new Runnable() {
172                         @Override
173                         public void run() {
174                             setPixelScale0(getWindowHandle(), surfaceHandle, _hasPixelScale[0]); // HiDPI: uniformPixelScale
175                         }
176                     } );
177                     changed = true;
178                 }
179             }
180         }
181         if( DEBUG_IMPLEMENTATION ) {
182             System.err.println("WindowDriver.setPixelScale: min["+minPixelScale[0]+", "+minPixelScale[1]+"], max["+
183                                 maxPixelScale[0]+", "+maxPixelScale[1]+"], req["+
184                                 reqPixelScale[0]+", "+reqPixelScale[1]+"] -> result["+
185                                 hasPixelScale[0]+", "+hasPixelScale[1]+"] - changed "+changed+", realized "+isNativeValid());
186         }
187         return changed;
188     }
189 
190     @Override
createNativeImpl()191     protected void createNativeImpl() {
192         final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice(), capsRequested).chooseGraphicsConfiguration(
193                 capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen(), VisualIDHolder.VID_UNDEFINED);
194         if (null == cfg) {
195             throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this);
196         }
197         setGraphicsConfiguration(cfg);
198         reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureMask(CHANGE_MASK_VISIBILITY, true));
199         if ( !isNativeValid() ) {
200             throw new NativeWindowException("Error creating window");
201         }
202     }
203 
204     @Override
closeNativeImpl()205     protected void closeNativeImpl() {
206         try {
207             if(DEBUG_IMPLEMENTATION) { System.err.println("MacWindow.CloseAction "+Thread.currentThread().getName()); }
208             final long handle = getWindowHandle();
209             visibleChanged(true, false);
210             setWindowHandle(0);
211             surfaceHandle = 0;
212             sscSurfaceHandle = 0;
213             isOffscreenInstance = false;
214             if (0 != handle) {
215                 OSXUtil.RunOnMainThread(false, true /* kickNSApp */, new Runnable() {
216                    @Override
217                    public void run() {
218                        close0( handle );
219                    } });
220             }
221         } catch (final Throwable t) {
222             if(DEBUG_IMPLEMENTATION) {
223                 final Exception e = new Exception("Warning: closeNative failed - "+Thread.currentThread().getName(), t);
224                 e.printStackTrace();
225             }
226         }
227     }
228 
229     @Override
lockSurfaceImpl()230     protected int lockSurfaceImpl() {
231         /**
232          * if( isOffscreenInstance ) {
233          *    return LOCK_SUCCESS;
234          * }
235          */
236         final long w = getWindowHandle();
237         final long v = surfaceHandle;
238         if( 0 != v && 0 != w ) {
239             return lockSurface0(w, v) ? LOCK_SUCCESS : LOCK_SURFACE_NOT_READY;
240         }
241         return LOCK_SURFACE_NOT_READY;
242     }
243 
244     @Override
unlockSurfaceImpl()245     protected void unlockSurfaceImpl() {
246         /**
247          * if( isOffscreenInstance ) {
248          *    return;
249          * }
250          */
251         final long w = getWindowHandle();
252         final long v = surfaceHandle;
253         if(0 != w && 0 != v) {
254             if( !unlockSurface0(w, v) ) {
255                 throw new NativeWindowException("Failed to unlock surface, probably not locked!");
256             }
257         }
258     }
259 
260     @Override
getSurfaceHandle()261     public final long getSurfaceHandle() {
262         return 0 != sscSurfaceHandle ? sscSurfaceHandle : surfaceHandle;
263     }
264 
265     @Override
setSurfaceHandle(final long surfaceHandle)266     public void setSurfaceHandle(final long surfaceHandle) {
267         if(DEBUG_IMPLEMENTATION) {
268             System.err.println("MacWindow.setSurfaceHandle(): 0x"+Long.toHexString(surfaceHandle));
269         }
270         sscSurfaceHandle = surfaceHandle;
271         if ( isNativeValid() ) {
272             if (0 != sscSurfaceHandle) {
273                 OSXUtil.RunOnMainThread(false, false, new Runnable() {
274                         @Override
275                         public void run() {
276                             orderOut0( 0 != getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() );
277                         } } );
278             } /** this is done by recreation!
279               else if (isVisible()){
280                 OSXUtil.RunOnMainThread(false, new Runnable() {
281                     public void run() {
282                         orderFront0( 0!=getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() );
283                     } } );
284             } */
285         }
286     }
287 
288     @Override
setTitleImpl(final String title)289     protected void setTitleImpl(final String title) {
290         OSXUtil.RunOnMainThread(false, false, new Runnable() {
291                 @Override
292                 public void run() {
293                     setTitle0(getWindowHandle(), title);
294                 } } );
295     }
296 
297     @Override
requestFocusImpl(final boolean force)298     protected void requestFocusImpl(final boolean force) {
299         final boolean _isFullscreen = isFullscreen();
300         final boolean _isOffscreenInstance = isOffscreenInstance;
301         if(DEBUG_IMPLEMENTATION) {
302             System.err.println("MacWindow: requestFocusImpl(), isOffscreenInstance "+_isOffscreenInstance+", isFullscreen "+_isFullscreen);
303         }
304         if(!_isOffscreenInstance) {
305             OSXUtil.RunOnMainThread(false, false, new Runnable() {
306                     @Override
307                     public void run() {
308                         requestFocus0(getWindowHandle(), force);
309                         if(_isFullscreen) {
310                             // 'NewtMacWindow::windowDidBecomeKey()' is not always called in fullscreen-mode!
311                             focusChanged(false, true);
312                         }
313                     } } );
314         } else {
315             focusChanged(false, true);
316         }
317     }
318 
319     @Override
clearFocus()320     public final void clearFocus() {
321         if(DEBUG_IMPLEMENTATION) {
322             System.err.println("MacWindow: clearFocus(), isOffscreenInstance "+isOffscreenInstance);
323         }
324         if(!isOffscreenInstance) {
325             OSXUtil.RunOnMainThread(false, false, new Runnable() {
326                     @Override
327                     public void run() {
328                         resignFocus0(getWindowHandle());
329                     } } );
330         } else {
331             focusChanged(false, false);
332         }
333     }
334 
useParent(final NativeWindow parent)335     private boolean useParent(final NativeWindow parent) { return null != parent && 0 != parent.getWindowHandle(); }
336 
337     @Override
updatePosition(final int x, final int y)338     public void updatePosition(final int x, final int y) {
339         final long handle = getWindowHandle();
340         if( 0 != handle && !isOffscreenInstance ) {
341             final NativeWindow parent = getParent();
342             final boolean useParent = useParent(parent);
343             final Point p0S;
344             if( useParent ) {
345                 p0S = getLocationOnScreenByParent(x, y, parent);
346             } else {
347                 p0S = (Point) getLocationOnScreen0(handle, x, y);
348             }
349             if(DEBUG_IMPLEMENTATION) {
350                 final int pX=parent.getX(), pY=parent.getY();
351                 System.err.println("MacWindow: updatePosition() parent["+useParent+" "+pX+"/"+pY+"] "+x+"/"+y+" ->  "+x+"/"+y+" rel-client-pos, "+p0S+" screen-client-pos");
352             }
353             OSXUtil.RunOnMainThread(false, false, new Runnable() {
354                     @Override
355                     public void run() {
356                         setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible());
357                     } } );
358             // no native event (fullscreen, some reparenting)
359             positionChanged(true, x, y);
360         }
361     }
362 
363     @Override
getSupportedReconfigMaskImpl()364     protected final int getSupportedReconfigMaskImpl() {
365         return minimumReconfigStateMask |
366                STATE_MASK_CHILDWIN |
367                STATE_MASK_UNDECORATED |
368                STATE_MASK_ALWAYSONTOP |
369                STATE_MASK_ALWAYSONBOTTOM |
370                STATE_MASK_STICKY |
371                STATE_MASK_RESIZABLE |
372                STATE_MASK_MAXIMIZED_VERT |
373                STATE_MASK_MAXIMIZED_HORZ |
374                STATE_MASK_POINTERVISIBLE |
375                STATE_MASK_POINTERCONFINED;
376     }
377 
378     @Override
reconfigureWindowImpl(int _x, int _y, int _width, int _height, final int flags)379     protected boolean reconfigureWindowImpl(int _x, int _y, int _width, int _height, final int flags) {
380         final boolean _isOffscreenInstance = isOffscreenInstance(this, this.getParent());
381         isOffscreenInstance = 0 != sscSurfaceHandle || _isOffscreenInstance;
382         final PointImmutable pClientLevelOnSreen;
383         if( isOffscreenInstance ) {
384             _x = 0; _y = 0;
385             pClientLevelOnSreen = new Point(0, 0);
386         } else  {
387             final NativeWindow parent = getParent();
388             if( useParent(parent) ) {
389                 pClientLevelOnSreen = getLocationOnScreenByParent(_x, _y, parent);
390             } else {
391                 if( 0 != ( ( CHANGE_MASK_MAXIMIZED_HORZ | CHANGE_MASK_MAXIMIZED_VERT ) & flags ) ) {
392                     final int[] posSize = { _x, _y, _width, _height };
393                     reconfigMaximizedManual(flags, posSize, getInsets());
394                     _x = posSize[0];
395                     _y = posSize[1];
396                     _width = posSize[2];
397                     _height = posSize[3];
398                 }
399                 pClientLevelOnSreen = new Point(_x, _y);
400             }
401         }
402         final int x=_x, y=_y;
403         final int width=_width, height=_height;
404 
405         final boolean hasFocus = hasFocus();
406 
407         if(DEBUG_IMPLEMENTATION) {
408             final AbstractGraphicsConfiguration cWinCfg = this.getGraphicsConfiguration();
409             final NativeWindow pWin = getParent();
410             final AbstractGraphicsConfiguration pWinCfg = null != pWin ? pWin.getGraphicsConfiguration() : null;
411             System.err.println("MacWindow reconfig.0: "+x+"/"+y+" -> clientPosOnScreen "+pClientLevelOnSreen+" - "+width+"x"+height+
412                                ", "+getReconfigStateMaskString(flags)+
413                                ",\n\t parent type "+(null != pWin ? pWin.getClass().getName() : null)+
414                                ",\n\t   this-chosenCaps "+(null != cWinCfg ? cWinCfg.getChosenCapabilities() : null)+
415                                ",\n\t parent-chosenCaps "+(null != pWinCfg ? pWinCfg.getChosenCapabilities() : null)+
416                                ", isOffscreenInstance(sscSurfaceHandle "+toHexString(sscSurfaceHandle)+
417                                ", ioi: "+_isOffscreenInstance+
418                                ") -> "+isOffscreenInstance);
419             // Thread.dumpStack();
420         }
421 
422         if( 0 != ( CHANGE_MASK_VISIBILITY & flags) &&
423             0 == ( STATE_MASK_VISIBLE & flags) )
424         {
425             if ( !isOffscreenInstance ) {
426                 OSXUtil.RunOnMainThread(false, false, new Runnable() {
427                         @Override
428                         public void run() {
429                             orderOut0(getWindowHandle());
430                             visibleChanged(true, false);
431                         } } );
432             } else {
433                 visibleChanged(true, false);
434             }
435         }
436         final long oldWindowHandle = getWindowHandle();
437         if( ( 0 == oldWindowHandle && 0 != ( STATE_MASK_VISIBLE & flags) ) ||
438             0 != ( CHANGE_MASK_PARENTING & flags)  ||
439             0 != ( CHANGE_MASK_DECORATION & flags) ||
440             0 != ( CHANGE_MASK_ALWAYSONTOP & flags) ||
441             0 != ( CHANGE_MASK_ALWAYSONBOTTOM & flags) ||
442             0 != ( CHANGE_MASK_RESIZABLE & flags)  ||
443             0 != ( CHANGE_MASK_FULLSCREEN & flags) ) {
444             if(isOffscreenInstance) {
445                 createWindow(true, 0 != oldWindowHandle, pClientLevelOnSreen, 64, 64, flags);
446             } else {
447                 createWindow(false, 0 != oldWindowHandle, pClientLevelOnSreen, width, height, flags);
448             }
449             // no native event (fullscreen, some reparenting)
450             updatePixelScaleByWindowHandle(false /* sendEvent */);
451             if( isOffscreenInstance) {
452                 super.sizeChanged(false, width, height, true);
453                 positionChanged(false,  x, y);
454             } else {
455                 updateSizePosInsets0(getWindowHandle(), false);
456             }
457             visibleChanged(false, 0 != ( STATE_MASK_VISIBLE & flags));
458             if( hasFocus ) {
459                 requestFocusImpl(true);
460             }
461         } else if( 0 != oldWindowHandle ) {
462             if( width>0 && height>0 ) {
463                 if( !isOffscreenInstance ) {
464                     OSXUtil.RunOnMainThread(true, false, new Runnable() {
465                             @Override
466                             public void run() {
467                                 setWindowClientTopLeftPointAndSize0(oldWindowHandle,
468                                         pClientLevelOnSreen.getX(), pClientLevelOnSreen.getY(),
469                                         width, height, 0 != ( STATE_MASK_VISIBLE & flags));
470                             } } );
471                     updateSizePosInsets0(oldWindowHandle, false);
472                 } else { // else offscreen size is realized via recreation
473                     // no native event (fullscreen, some reparenting)
474                     super.sizeChanged(false, width, height, false);
475                     positionChanged(false,  x, y);
476                 }
477             }
478             if( 0 != ( CHANGE_MASK_VISIBILITY & flags) &&
479                 0 != ( STATE_MASK_VISIBLE & flags) )
480             {
481                 if( !isOffscreenInstance ) {
482                     OSXUtil.RunOnMainThread(false, false, new Runnable() {
483                             @Override
484                             public void run() {
485                                 orderFront0(getWindowHandle());
486                                 visibleChanged(true, true);
487                             } } );
488                 } else {
489                     visibleChanged(true, true);
490                 }
491             }
492         } else {
493             throw new InternalError("Null windowHandle but no re-creation triggered, check visibility: "+getStateMaskString());
494         }
495         if(DEBUG_IMPLEMENTATION) {
496             System.err.println("MacWindow reconfig.X: "+getLocationOnScreenImpl(0, 0)+" "+getWidth()+"x"+getHeight()+", insets "+getInsets()+", "+getStateMaskString());
497         }
498         return true;
499     }
500 
501     @Override
getLocationOnScreenImpl(final int x, final int y)502     protected Point getLocationOnScreenImpl(final int x, final int y) {
503         final NativeWindow parent = getParent();
504         if( useParent(parent) ) {
505             return getLocationOnScreenByParent(x, y, parent);
506         } else {
507             final long windowHandle = getWindowHandle();
508             if( !isOffscreenInstance && 0 != windowHandle ) {
509                 return (Point) getLocationOnScreen0(windowHandle, x, y);
510             } else {
511                 return new Point(x, y);
512             }
513         }
514     }
515 
getLocationOnScreenByParent(final int x, final int y, final NativeWindow parent)516     private Point getLocationOnScreenByParent(final int x, final int y, final NativeWindow parent) {
517         return new Point(x, y).translate( parent.getLocationOnScreen(null) );
518     }
519 
520     /** Callback for native screen position change event of the client area. */
screenPositionChanged(final boolean defer, final int newX, final int newY)521     protected void screenPositionChanged(final boolean defer, final int newX, final int newY) {
522         // passed coordinates are in screen position of the client area
523         if( isNativeValid() ) {
524             final NativeWindow parent = getParent();
525             if( !useParent(parent) || isOffscreenInstance ) {
526                 if(DEBUG_IMPLEMENTATION) {
527                     System.err.println("MacWindow.positionChanged.0 (Screen Pos - TOP): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY);
528                 }
529                 positionChanged(defer, newX, newY);
530             } else {
531                 final Runnable action = new Runnable() {
532                     public void run() {
533                         // screen position -> rel child window position
534                         final Point absPos = new Point(newX, newY);
535                         final Point parentOnScreen = parent.getLocationOnScreen(null);
536                         absPos.translate( parentOnScreen.scale(-1, -1) );
537                         if(DEBUG_IMPLEMENTATION) {
538                             System.err.println("MacWindow.positionChanged.1 (Screen Pos - CHILD): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> absPos "+newX+"/"+newY+", parentOnScreen "+parentOnScreen+" -> "+absPos);
539                         }
540                         positionChanged(false, absPos.getX(), absPos.getY());
541                     } };
542                 if( defer ) {
543                     new InterruptSource.Thread(null, action).start();
544                 } else {
545                     action.run();
546                 }
547 
548             }
549         } else if(DEBUG_IMPLEMENTATION) {
550             System.err.println("MacWindow.positionChanged.2 (Screen Pos - IGN): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY);
551         }
552     }
553 
554     @Override
sizeChanged(final boolean defer, final int newWidth, final int newHeight, final boolean force)555     protected void sizeChanged(final boolean defer, final int newWidth, final int newHeight, final boolean force) {
556         if(force || getWidth() != newWidth || getHeight() != newHeight) {
557             if( isNativeValid() && !isOffscreenInstance ) {
558                 final NativeWindow parent = getParent();
559                 final boolean useParent = useParent(parent);
560                 if( useParent ) {
561                     final int x=getX(), y=getY();
562                     final Point p0S = getLocationOnScreenByParent(x, y, parent);
563                     if(DEBUG_IMPLEMENTATION) {
564                         System.err.println("MacWindow: sizeChanged() parent["+useParent+" "+x+"/"+y+"] "+getX()+"/"+getY()+" "+newWidth+"x"+newHeight+" ->  "+p0S+" screen-client-pos");
565                     }
566                     OSXUtil.RunOnMainThread(false, false, new Runnable() {
567                         @Override
568                         public void run() {
569                             setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible());
570                         } } );
571                 }
572             }
573             superSizeChangedOffThread(defer, newWidth, newHeight, force);
574         }
575     }
superSizeChangedOffThread(final boolean defer, final int newWidth, final int newHeight, final boolean force)576     private void superSizeChangedOffThread(final boolean defer, final int newWidth, final int newHeight, final boolean force) {
577         if( defer ) {
578             new InterruptSource.Thread() {
579                 public void run() {
580                     WindowDriver.super.sizeChanged(false /* defer */, newWidth, newHeight, force);
581                 } }.start();
582         } else {
583             WindowDriver.super.sizeChanged(false /* defer */, newWidth, newHeight, force);
584         }
585     }
586 
587     //
588     // Accumulated actions
589     //
590 
591     /** Triggered by implementation's WM events to update the client-area position, size and insets. */
sizeScreenPosInsetsChanged(final boolean defer, final int newX, final int newY, final int newWidth, final int newHeight, final int left, final int right, final int top, final int bottom, final boolean force)592     protected void sizeScreenPosInsetsChanged(final boolean defer,
593                                               final int newX, final int newY,
594                                               final int newWidth, final int newHeight,
595                                               final int left, final int right, final int top, final int bottom,
596                                               final boolean force) {
597         sizeChanged(defer, newWidth, newHeight, force);
598         screenPositionChanged(defer, newX, newY);
599         insetsChanged(defer, left, right, top, bottom);
600     }
601 
602     @Override
setPointerIconImpl(final PointerIconImpl pi)603     protected void setPointerIconImpl(final PointerIconImpl pi) {
604         if( !isOffscreenInstance ) {
605             final long piHandle = null != pi ? pi.validatedHandle() : 0;
606             OSXUtil.RunOnMainThread(true, false, new Runnable() { // waitUntildone due to PointerIconImpl's Lifecycle !
607                     @Override
608                     public void run() {
609                         setPointerIcon0(getWindowHandle(), piHandle);
610                     } } );
611         }
612     }
613 
614     @Override
setPointerVisibleImpl(final boolean pointerVisible)615     protected boolean setPointerVisibleImpl(final boolean pointerVisible) {
616         if( !isOffscreenInstance ) {
617             OSXUtil.RunOnMainThread(false, false, new Runnable() {
618                     @Override
619                     public void run() {
620                         setPointerVisible0(getWindowHandle(), hasFocus(), pointerVisible);
621                     } } );
622             return true;
623         }
624         return false;
625     }
626 
627     @Override
confinePointerImpl(final boolean confine)628     protected boolean confinePointerImpl(final boolean confine) {
629         if( !isOffscreenInstance ) {
630             confinePointer0(getWindowHandle(), confine);
631             return true;
632         } // else may need offscreen solution ? FIXME
633         return false;
634     }
635 
636     @Override
warpPointerImpl(final int x, final int y)637     protected void warpPointerImpl(final int x, final int y) {
638         if( !isOffscreenInstance ) {
639             warpPointer0(getWindowHandle(),
640                          SurfaceScaleUtils.scaleInv(x, getPixelScaleX()),
641                          SurfaceScaleUtils.scaleInv(y, getPixelScaleY()));
642         } // else may need offscreen solution ? FIXME
643     }
644 
645     @Override
doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers, final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale)646     protected final void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers,
647                                       final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) {
648         super.doMouseEvent(enqueue, wait, eventType, modifiers,
649                            SurfaceScaleUtils.scale(x, getPixelScaleX()),
650                            SurfaceScaleUtils.scale(y, getPixelScaleY()), button, rotationXYZ, rotationScale);
651     }
652 
653     @Override
sendKeyEvent(final short eventType, final int modifiers, final short keyCode, final short keySym, final char keyChar)654     public final void sendKeyEvent(final short eventType, final int modifiers, final short keyCode, final short keySym, final char keyChar) {
655         throw new InternalError("XXX: Adapt Java Code to Native Code Changes");
656     }
657 
658     @Override
enqueueKeyEvent(final boolean wait, final short eventType, final int modifiers, final short _keyCode, final short _keySym, final char keyChar)659     public final void enqueueKeyEvent(final boolean wait, final short eventType, final int modifiers, final short _keyCode, final short _keySym, final char keyChar) {
660         throw new InternalError("XXX: Adapt Java Code to Native Code Changes");
661     }
662 
enqueueKeyEvent(final boolean wait, final short eventType, int modifiers, final short _keyCode, final char keyChar, final char keySymChar)663     protected final void enqueueKeyEvent(final boolean wait, final short eventType, int modifiers, final short _keyCode, final char keyChar, final char keySymChar) {
664         // Note that we send the key char for the key code on this
665         // platform -- we do not get any useful key codes out of the system
666         final short keyCode = MacKeyUtil.validateKeyCode(_keyCode, keyChar);
667         final short keySym;
668         {
669             final short _keySym = KeyEvent.NULL_CHAR != keySymChar ? KeyEvent.utf16ToVKey(keySymChar) : KeyEvent.VK_UNDEFINED;
670             keySym = KeyEvent.VK_UNDEFINED != _keySym ? _keySym : keyCode;
671         }
672         /**
673         {
674             final boolean isModifierKeyCode = KeyEvent.isModifierKey(keyCode);
675             System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+
676                                ", keyCode 0x"+Integer.toHexString(_keyCode)+" -> 0x"+Integer.toHexString(keyCode)+
677                                ", keySymChar '"+keySymChar+"', 0x"+Integer.toHexString(keySymChar)+" -> 0x"+Integer.toHexString(keySym)+
678                                ", mods "+toHexString(modifiers)+
679                                ", was: pressed "+isKeyPressed(keyCode)+", isModifierKeyCode "+isModifierKeyCode+
680                                ", nativeValid "+isNativeValid()+", isOffscreen "+isOffscreenInstance);
681         } */
682 
683         // OSX delivery order is PRESSED (t0), RELEASED (t1) and TYPED (t2) -> NEWT order: PRESSED (t0) and RELEASED (t1)
684         // Auto-Repeat: OSX delivers only PRESSED, inject auto-repeat RELEASE key _before_ PRESSED
685         switch(eventType) {
686             case KeyEvent.EVENT_KEY_RELEASED:
687                 if( isKeyCodeTracked(keyCode) ) {
688                     setKeyPressed(keyCode, false);
689                 }
690                 break;
691             case KeyEvent.EVENT_KEY_PRESSED:
692                 if( isKeyCodeTracked(keyCode) ) {
693                     if( setKeyPressed(keyCode, true) ) {
694                         // key was already pressed
695                         modifiers |= InputEvent.AUTOREPEAT_MASK;
696                         super.enqueueKeyEvent(wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); // RELEASED
697                     }
698                 }
699                 break;
700         }
701         super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keySym, keyChar);
702     }
703 
getDisplayID()704     protected int getDisplayID() {
705         if( !isOffscreenInstance ) {
706             return getDisplayID0(getWindowHandle());
707         }
708         return 0;
709     }
710 
711     //----------------------------------------------------------------------
712     // Internals only
713     //
714 
createWindow(final boolean offscreenInstance, final boolean recreate, final PointImmutable pS, final int width, final int height, final int flags)715     private void createWindow(final boolean offscreenInstance, final boolean recreate,
716                               final PointImmutable pS, final int width, final int height,
717                               final int flags)
718     {
719         final long parentWinHandle = getParentWindowHandle();
720         final long preWinHandle = getWindowHandle();
721 
722         if(DEBUG_IMPLEMENTATION) {
723             System.err.println("MacWindow.createWindow on thread "+Thread.currentThread().getName()+
724                                ": offscreen "+offscreenInstance+", recreate "+recreate+
725                                ", pS "+pS+", "+width+"x"+height+", state "+getReconfigStateMaskString(flags)+
726                                ", preWinHandle "+toHexString(preWinHandle)+", parentWin "+toHexString(parentWinHandle)+
727                                ", surfaceHandle "+toHexString(surfaceHandle));
728             // Thread.dumpStack();
729         }
730 
731         try {
732             if( 0 != preWinHandle ) {
733                 setWindowHandle(0);
734                 if( 0 == surfaceHandle ) {
735                     throw new NativeWindowException("Internal Error - create w/ window, but no Newt NSView");
736                 }
737                 OSXUtil.RunOnMainThread(false, false /* kickNSApp */, new Runnable() {
738                         @Override
739                         public void run() {
740                             changeContentView0(parentWinHandle, preWinHandle, 0);
741                             close0( preWinHandle );
742                         } });
743             } else {
744                 if( 0 != surfaceHandle ) {
745                     throw new NativeWindowException("Internal Error - create w/o window, but has Newt NSView");
746                 }
747                 surfaceHandle = createView0(pS.getX(), pS.getY(), width, height);
748                 if( 0 == surfaceHandle ) {
749                     throw new NativeWindowException("Could not create native view "+Thread.currentThread().getName()+" "+this);
750                 }
751             }
752 
753             final int windowStyle;
754             {
755                 int ws = 0;
756                 if( 0 != ( STATE_MASK_UNDECORATED & flags) || offscreenInstance ) {
757                     ws = NSBorderlessWindowMask;
758                 } else {
759                     ws = NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask;
760                     if( 0 != ( STATE_MASK_RESIZABLE & flags) ) {
761                         ws |= NSResizableWindowMask;
762                     }
763                 }
764                 windowStyle = ws;
765             }
766             final long newWin = createWindow0( pS.getX(), pS.getY(), width, height,
767                                                0 != ( STATE_MASK_FULLSCREEN & flags),
768                                                windowStyle,
769                                                NSBackingStoreBuffered, surfaceHandle);
770             if ( newWin == 0 ) {
771                 throw new NativeWindowException("Could not create native window "+Thread.currentThread().getName()+" "+this);
772             }
773             setWindowHandle( newWin );
774 
775             final boolean isOpaque = getGraphicsConfiguration().getChosenCapabilities().isBackgroundOpaque() && !offscreenInstance;
776             // Blocking initialization on main-thread!
777             OSXUtil.RunOnMainThread(true, false /* kickNSApp */, new Runnable() {
778                     @Override
779                     public void run() {
780                         initWindow0( parentWinHandle, newWin, pS.getX(), pS.getY(), width, height, reqPixelScale[0] /* HiDPI uniformPixelScale */,
781                                      isOpaque,
782                                      !offscreenInstance && 0 != ( STATE_MASK_ALWAYSONTOP & flags),
783                                      !offscreenInstance && 0 != ( STATE_MASK_ALWAYSONBOTTOM & flags),
784                                      !offscreenInstance && 0 != ( STATE_MASK_VISIBLE & flags),
785                                      surfaceHandle);
786                         if( offscreenInstance ) {
787                             orderOut0(0!=parentWinHandle ? parentWinHandle : newWin);
788                         } else {
789                             setTitle0(newWin, getTitle());
790                         }
791                     } });
792         } catch (final Exception ie) {
793             ie.printStackTrace();
794         }
795     }
796 
initIDs0()797     protected static native boolean initIDs0();
createView0(int x, int y, int w, int h)798     private native long createView0(int x, int y, int w, int h);
createWindow0(int x, int y, int w, int h, boolean fullscreen, int windowStyle, int backingStoreType, long view)799     private native long createWindow0(int x, int y, int w, int h, boolean fullscreen, int windowStyle, int backingStoreType, long view);
800     /** Must be called on Main-Thread */
initWindow0(long parentWindow, long window, int x, int y, int w, int h, float reqPixelScale, boolean opaque, boolean atop, boolean abottom, boolean visible, long view)801     private native void initWindow0(long parentWindow, long window, int x, int y, int w, int h, float reqPixelScale,
802                                     boolean opaque, boolean atop, boolean abottom, boolean visible, long view);
803 
getDisplayID0(long window)804     private native int getDisplayID0(long window);
setPixelScale0(long window, long view, float reqPixelScale)805     private native void setPixelScale0(long window, long view, float reqPixelScale);
lockSurface0(long window, long view)806     private native boolean lockSurface0(long window, long view);
unlockSurface0(long window, long view)807     private native boolean unlockSurface0(long window, long view);
808     /** Must be called on Main-Thread */
requestFocus0(long window, boolean force)809     private native void requestFocus0(long window, boolean force);
810     /** Must be called on Main-Thread */
resignFocus0(long window)811     private native void resignFocus0(long window);
812     /** Must be called on Main-Thread. In case this is a child window and parent is still visible, orderBack(..) is issued instead of orderOut(). */
orderOut0(long window)813     private native void orderOut0(long window);
814     /** Must be called on Main-Thread */
orderFront0(long window)815     private native void orderFront0(long window);
816     /** Must be called on Main-Thread */
close0(long window)817     private native void close0(long window);
818     /** Must be called on Main-Thread */
setTitle0(long window, String title)819     private native void setTitle0(long window, String title);
contentView0(long window)820     private native long contentView0(long window);
821     /** Must be called on Main-Thread */
changeContentView0(long parentWindowOrView, long window, long view)822     private native void changeContentView0(long parentWindowOrView, long window, long view);
823     /** Must be called on Main-Thread */
setWindowClientTopLeftPointAndSize0(long window, int x, int y, int w, int h, boolean display)824     private native void setWindowClientTopLeftPointAndSize0(long window, int x, int y, int w, int h, boolean display);
825     /** Must be called on Main-Thread */
setWindowClientTopLeftPoint0(long window, int x, int y, boolean display)826     private native void setWindowClientTopLeftPoint0(long window, int x, int y, boolean display);
827     /** Triggers {@link #sizeScreenPosInsetsChanged(boolean, int, int, int, int, int, int, int, int, boolean)} */
updateSizePosInsets0(long window, boolean defer)828     private native void updateSizePosInsets0(long window, boolean defer);
getLocationOnScreen0(long windowHandle, int src_x, int src_y)829     private static native Object getLocationOnScreen0(long windowHandle, int src_x, int src_y);
setPointerIcon0(long windowHandle, long handle)830     private static native void setPointerIcon0(long windowHandle, long handle);
setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible)831     private static native void setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible);
confinePointer0(long windowHandle, boolean confine)832     private static native void confinePointer0(long windowHandle, boolean confine);
warpPointer0(long windowHandle, int x, int y)833     private static native void warpPointer0(long windowHandle, int x, int y);
834 
835     // Window styles
836     private static final int NSBorderlessWindowMask     = 0;
837     private static final int NSTitledWindowMask         = 1 << 0;
838     private static final int NSClosableWindowMask       = 1 << 1;
839     private static final int NSMiniaturizableWindowMask = 1 << 2;
840     private static final int NSResizableWindowMask      = 1 << 3;
841 
842     // Window backing store types
843     private static final int NSBackingStoreRetained     = 0;
844     private static final int NSBackingStoreNonretained  = 1;
845     private static final int NSBackingStoreBuffered     = 2;
846 
847     private volatile long surfaceHandle = 0;
848     private long sscSurfaceHandle = 0;
849     private boolean isOffscreenInstance = false;
850 
851 }
852