Jogamp
Bug 741 HiDPI: Add ScalableSurface interface to get/set pixelScale w/ full OSX impl.
[jogl.git] / src / nativewindow / classes / jogamp / nativewindow / jawt / macosx / MacOSXJAWTWindow.java
1 /*
2  * Copyright (c) 2003 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  * You acknowledge that this software is not designed or intended for use
34  * in the design, construction, operation or maintenance of any nuclear
35  * facility.
36  *
37  * Sun gratefully acknowledges that this software was originally authored
38  * and developed by Kenneth Bradley Russell and Christopher John Kline.
39  */
40
41 package jogamp.nativewindow.jawt.macosx;
42
43 import java.awt.Component;
44 import java.nio.Buffer;
45 import java.security.AccessController;
46 import java.security.PrivilegedAction;
47
48 import javax.media.nativewindow.AbstractGraphicsConfiguration;
49 import javax.media.nativewindow.Capabilities;
50 import javax.media.nativewindow.NativeWindow;
51 import javax.media.nativewindow.NativeWindowException;
52 import javax.media.nativewindow.MutableSurface;
53 import javax.media.nativewindow.util.Point;
54
55 import com.jogamp.nativewindow.awt.JAWTWindow;
56
57 import jogamp.nativewindow.Debug;
58 import jogamp.nativewindow.awt.AWTMisc;
59 import jogamp.nativewindow.jawt.JAWT;
60 import jogamp.nativewindow.jawt.JAWTFactory;
61 import jogamp.nativewindow.jawt.JAWTUtil;
62 import jogamp.nativewindow.jawt.JAWT_DrawingSurface;
63 import jogamp.nativewindow.jawt.JAWT_DrawingSurfaceInfo;
64 import jogamp.nativewindow.jawt.macosx.JAWT_MacOSXDrawingSurfaceInfo;
65 import jogamp.nativewindow.macosx.OSXUtil;
66
67 public class MacOSXJAWTWindow extends JAWTWindow implements MutableSurface {
68   /** May lead to deadlock, due to AWT pos comparison .. don't enable for Applets! */
69   private static final boolean DEBUG_CALAYER_POS_CRITICAL;
70
71   static {
72       Debug.initSingleton();
73       DEBUG_CALAYER_POS_CRITICAL = Debug.isPropertyDefined("nativewindow.debug.JAWT.OSXCALayerPos", true /* jnlpAlias */);
74   }
75
76   public MacOSXJAWTWindow(Object comp, AbstractGraphicsConfiguration config) {
77     super(comp, config);
78     if(DEBUG) {
79         dumpInfo();
80     }
81   }
82
83   @Override
84   protected void invalidateNative() {
85       if(DEBUG) {
86           System.err.println("MacOSXJAWTWindow.invalidateNative(): osh-enabled "+isOffscreenLayerSurfaceEnabled()+
87                              ", osd-set "+offscreenSurfaceDrawableSet+
88                              ", osd "+toHexString(offscreenSurfaceDrawable)+
89                              ", osl "+toHexString(getAttachedSurfaceLayer())+
90                              ", rsl "+toHexString(rootSurfaceLayer)+
91                              ", wh "+toHexString(windowHandle)+" - "+Thread.currentThread().getName());
92       }
93       offscreenSurfaceDrawable=0;
94       offscreenSurfaceDrawableSet=false;
95       if( isOffscreenLayerSurfaceEnabled() ) {
96           if(0 != windowHandle) {
97               OSXUtil.DestroyNSWindow(windowHandle);
98           }
99           OSXUtil.RunOnMainThread(false, new Runnable() {
100               @Override
101               public void run() {
102                   if( 0 != rootSurfaceLayer ) {
103                       if( 0 != jawtSurfaceLayersHandle) {
104                           UnsetJAWTRootSurfaceLayer0(jawtSurfaceLayersHandle, rootSurfaceLayer);
105                       }
106                       OSXUtil.DestroyCALayer(rootSurfaceLayer);
107                       rootSurfaceLayer = 0;
108                   }
109                   jawtSurfaceLayersHandle = 0;
110               }
111           });
112       }
113       windowHandle=0;
114   }
115
116   @Override
117   public void setSurfaceScale(final int[] pixelScale) {
118       super.setSurfaceScale(pixelScale);
119       if( 0 != drawable ) { // locked at least once !
120           final int hadPixelScaleX = getPixelScaleX();
121           updatePixelScale();
122
123           if( hadPixelScaleX != getPixelScaleX() && 0 != getAttachedSurfaceLayer() ) {
124               OSXUtil.RunOnMainThread(false, new Runnable() {
125                   @Override
126                   public void run() {
127                       final long osl = getAttachedSurfaceLayer();
128                       if( 0 != osl ) {
129                           OSXUtil.SetCALayerPixelScale(rootSurfaceLayer, osl, getPixelScaleX());
130                       }
131                   }
132               });
133           }
134       }
135   }
136
137   @Override
138   protected void attachSurfaceLayerImpl(final long layerHandle) {
139       OSXUtil.RunOnMainThread(false, new Runnable() {
140           @Override
141           public void run() {
142               // AWT position is top-left w/ insets, where CALayer position is bottom/left from root CALayer w/o insets.
143               // Determine p0: components location on screen w/o insets.
144               // CALayer position will be determined in native code.
145               // See detailed description in {@link JAWTUtil#JAWT_OSX_CALAYER_QUIRK_LAYOUT}
146               final Point p0 = new Point();
147               final Component outterComp = getLocationOnScreenNonBlocking(p0, component);
148               final java.awt.Insets outterInsets = AWTMisc.getInsets(outterComp, true);
149               final Point p1 = (Point)p0.cloneMutable();
150               p1.translate(-outterComp.getX(), -outterComp.getY());
151               if( null != outterInsets ) {
152                   p1.translate(-outterInsets.left, -outterInsets.top);
153               }
154
155               if( DEBUG_CALAYER_POS_CRITICAL ) {
156                   final java.awt.Point pA0 = component.getLocationOnScreen();
157                   final Point pA1 = new Point(pA0.x, pA0.y);
158                   pA1.translate(-outterComp.getX(), -outterComp.getY());
159                   if( null != outterInsets ) {
160                       pA1.translate(-outterInsets.left, -outterInsets.top);
161                   }
162                   System.err.println("JAWTWindow.attachSurfaceLayerImpl: "+toHexString(layerHandle) + ", [ins "+outterInsets+"], pA "+pA0+" -> "+pA1+
163                           ", p0 "+p0+" -> "+p1+", bounds "+bounds);
164               } else if( DEBUG ) {
165                   System.err.println("JAWTWindow.attachSurfaceLayerImpl: "+toHexString(layerHandle) + ", [ins "+outterInsets+"], p0 "+p0+" -> "+p1+", bounds "+bounds);
166               }
167               // HiDPI: uniform pixel scale
168               OSXUtil.AddCASublayer(rootSurfaceLayer, layerHandle, p1.getX(), p1.getY(), getWidth(), getHeight(), getPixelScaleX(), JAWTUtil.getOSXCALayerQuirks());
169           } } );
170   }
171
172   @Override
173   protected void layoutSurfaceLayerImpl(long layerHandle, boolean visible) {
174       final int caLayerQuirks = JAWTUtil.getOSXCALayerQuirks();
175       // AWT position is top-left w/ insets, where CALayer position is bottom/left from root CALayer w/o insets.
176       // Determine p0: components location on screen w/o insets.
177       // CALayer position will be determined in native code.
178       // See detailed description in {@link JAWTUtil#JAWT_OSX_CALAYER_QUIRK_LAYOUT}
179       final Point p0 = new Point();
180       final Component outterComp = getLocationOnScreenNonBlocking(p0, component);
181       final java.awt.Insets outterInsets = AWTMisc.getInsets(outterComp, true);
182       final Point p1 = (Point)p0.cloneMutable();
183       p1.translate(-outterComp.getX(), -outterComp.getY());
184       if( null != outterInsets ) {
185           p1.translate(-outterInsets.left, -outterInsets.top);
186       }
187
188       if( DEBUG_CALAYER_POS_CRITICAL ) {
189           final java.awt.Point pA0 = component.getLocationOnScreen();
190           final Point pA1 = new Point(pA0.x, pA0.y);
191           pA1.translate(-outterComp.getX(), -outterComp.getY());
192           if( null != outterInsets ) {
193               pA1.translate(-outterInsets.left, -outterInsets.top);
194           }
195           System.err.println("JAWTWindow.layoutSurfaceLayerImpl: "+toHexString(layerHandle) + ", quirks "+caLayerQuirks+", visible "+visible+
196                   ", [ins "+outterInsets+"], pA "+pA0+" -> "+pA1+
197                   ", p0 "+p0+" -> "+p1+", bounds "+bounds);
198       } else if( DEBUG ) {
199           System.err.println("JAWTWindow.layoutSurfaceLayerImpl: "+toHexString(layerHandle) + ", quirks "+caLayerQuirks+", visible "+visible+
200                   ", [ins "+outterInsets+"], p0 "+p0+" -> "+p1+", bounds "+bounds);
201       }
202       OSXUtil.FixCALayerLayout(rootSurfaceLayer, layerHandle, visible, p1.getX(), p1.getY(), getWidth(), getHeight(), caLayerQuirks);
203   }
204
205   @Override
206   protected void detachSurfaceLayerImpl(final long layerHandle, final Runnable detachNotify) {
207       OSXUtil.RunOnMainThread(false, new Runnable() {
208           @Override
209           public void run() {
210               detachNotify.run();
211               OSXUtil.RemoveCASublayer(rootSurfaceLayer, layerHandle);
212           } } );
213   }
214
215   @Override
216   public final long getWindowHandle() {
217     return windowHandle;
218   }
219
220   @Override
221   public final long getSurfaceHandle() {
222     return offscreenSurfaceDrawableSet ? offscreenSurfaceDrawable : drawable /* super.getSurfaceHandle() */ ;
223   }
224
225   @Override
226   public void setSurfaceHandle(long surfaceHandle) {
227       if( !isOffscreenLayerSurfaceEnabled() ) {
228           throw new java.lang.UnsupportedOperationException("Not using CALAYER");
229       }
230       if(DEBUG) {
231         System.err.println("MacOSXJAWTWindow.setSurfaceHandle(): "+toHexString(surfaceHandle));
232       }
233       this.offscreenSurfaceDrawable = surfaceHandle;
234       this.offscreenSurfaceDrawableSet = true;
235   }
236
237   @Override
238   protected JAWT fetchJAWTImpl() throws NativeWindowException {
239        // use offscreen if supported and [ applet or requested ]
240       return JAWTUtil.getJAWT(getShallUseOffscreenLayer() || isApplet());
241   }
242
243   @Override
244   protected int lockSurfaceImpl() throws NativeWindowException {
245     int ret = NativeWindow.LOCK_SURFACE_NOT_READY;
246     ds = getJAWT().GetDrawingSurface(component);
247     if (ds == null) {
248       // Widget not yet realized
249       unlockSurfaceImpl();
250       return NativeWindow.LOCK_SURFACE_NOT_READY;
251     }
252     int res = ds.Lock();
253     dsLocked = ( 0 == ( res & JAWTFactory.JAWT_LOCK_ERROR ) ) ;
254     if (!dsLocked) {
255       unlockSurfaceImpl();
256       throw new NativeWindowException("Unable to lock surface");
257     }
258     // See whether the surface changed and if so destroy the old
259     // OpenGL context so it will be recreated (NOTE: removeNotify
260     // should handle this case, but it may be possible that race
261     // conditions can cause this code to be triggered -- should test
262     // more)
263     if ((res & JAWTFactory.JAWT_LOCK_SURFACE_CHANGED) != 0) {
264       ret = NativeWindow.LOCK_SURFACE_CHANGED;
265     }
266     if (firstLock) {
267       AccessController.doPrivileged(new PrivilegedAction<Object>() {
268           @Override
269           public Object run() {
270             dsi = ds.GetDrawingSurfaceInfo();
271             return null;
272           }
273         });
274     } else {
275       dsi = ds.GetDrawingSurfaceInfo();
276     }
277     if (dsi == null) {
278       unlockSurfaceImpl();
279       return NativeWindow.LOCK_SURFACE_NOT_READY;
280     }
281     updateLockedData(dsi.getBounds());
282     if (DEBUG && firstLock ) {
283       dumpInfo();
284     }
285     firstLock = false;
286     if( !isOffscreenLayerSurfaceEnabled() ) {
287         macosxdsi = (JAWT_MacOSXDrawingSurfaceInfo) dsi.platformInfo(getJAWT());
288         if (macosxdsi == null) {
289           unlockSurfaceImpl();
290           return NativeWindow.LOCK_SURFACE_NOT_READY;
291         }
292         drawable = macosxdsi.getCocoaViewRef();
293
294         if (drawable == 0) {
295           unlockSurfaceImpl();
296           return NativeWindow.LOCK_SURFACE_NOT_READY;
297         } else {
298           windowHandle = OSXUtil.GetNSWindow(drawable);
299           ret = NativeWindow.LOCK_SUCCESS;
300         }
301     } else {
302         /**
303          * Only create a fake invisible NSWindow for the drawable handle
304          * to please frameworks requiring such (eg. NEWT).
305          *
306          * The actual surface/ca-layer shall be created/attached
307          * by the upper framework (JOGL) since they require more information.
308          */
309         String errMsg = null;
310         if(0 == drawable) {
311             windowHandle = OSXUtil.CreateNSWindow(0, 0, 64, 64);
312             if(0 == windowHandle) {
313               errMsg = "Unable to create dummy NSWindow (layered case)";
314             } else {
315                 drawable = OSXUtil.GetNSView(windowHandle);
316                 if(0 == drawable) {
317                   errMsg = "Null NSView of NSWindow "+toHexString(windowHandle);
318                 }
319             }
320             if(null == errMsg) {
321                 // Fix caps reflecting offscreen! (no GL available here ..)
322                 final Capabilities caps = (Capabilities) getGraphicsConfiguration().getChosenCapabilities().cloneMutable();
323                 caps.setOnscreen(false);
324                 setChosenCapabilities(caps);
325             }
326         }
327         if(null == errMsg) {
328             jawtSurfaceLayersHandle = GetJAWTSurfaceLayersHandle0(dsi.getBuffer());
329             OSXUtil.RunOnMainThread(false, new Runnable() {
330                 @Override
331                 public void run() {
332                     String errMsg = null;
333                     if(0 == rootSurfaceLayer && 0 != jawtSurfaceLayersHandle) {
334                         rootSurfaceLayer = OSXUtil.CreateCALayer(bounds.getWidth(), bounds.getHeight(), getPixelScaleX()); // HiDPI: uniform pixel scale
335                         if(0 == rootSurfaceLayer) {
336                           errMsg = "Could not create root CALayer";
337                         } else {
338                             try {
339                                 SetJAWTRootSurfaceLayer0(jawtSurfaceLayersHandle, rootSurfaceLayer);
340                             } catch(Exception e) {
341                                 errMsg = "Could not set JAWT rootSurfaceLayerHandle "+toHexString(rootSurfaceLayer)+", cause: "+e.getMessage();
342                             }
343                         }
344                         if(null != errMsg) {
345                             if(0 != rootSurfaceLayer) {
346                               OSXUtil.DestroyCALayer(rootSurfaceLayer);
347                               rootSurfaceLayer = 0;
348                             }
349                             throw new NativeWindowException(errMsg+": "+MacOSXJAWTWindow.this);
350                         }
351                     }
352                 } } );
353         }
354         if(null != errMsg) {
355             if(0 != windowHandle) {
356               OSXUtil.DestroyNSWindow(windowHandle);
357               windowHandle = 0;
358             }
359             drawable = 0;
360             unlockSurfaceImpl();
361             throw new NativeWindowException(errMsg+": "+this);
362         }
363         ret = NativeWindow.LOCK_SUCCESS;
364     }
365
366     return ret;
367   }
368
369   @Override
370   protected void unlockSurfaceImpl() throws NativeWindowException {
371     if(null!=ds) {
372         if (null!=dsi) {
373             ds.FreeDrawingSurfaceInfo(dsi);
374         }
375         if (dsLocked) {
376             ds.Unlock();
377         }
378         getJAWT().FreeDrawingSurface(ds);
379     }
380     ds = null;
381     dsi = null;
382   }
383
384   private void dumpInfo() {
385       System.err.println("MaxOSXJAWTWindow: 0x"+Integer.toHexString(this.hashCode())+" - thread: "+Thread.currentThread().getName());
386       dumpJAWTInfo();
387   }
388
389   /**
390    * {@inheritDoc}
391    * <p>
392    * On OS X locking the surface at this point (ie after creation and for location validation)
393    * is 'tricky' since the JVM traverses through many threads and crashes at:
394    *   lockSurfaceImpl() {
395    *      ..
396    *      ds = getJAWT().GetDrawingSurface(component);
397    * due to a SIGSEGV.
398    *
399    * Hence we have some threading / sync issues with the native JAWT implementation.
400    * </p>
401    */
402   @Override
403   public Point getLocationOnScreen(Point storage) {
404       if( null == storage ) {
405           storage = new Point();
406       }
407       getLocationOnScreenNonBlocking(storage, component);
408       return storage;
409   }
410   @Override
411   protected Point getLocationOnScreenNativeImpl(final int x0, final int y0) { return null; }
412
413
414   private static native long GetJAWTSurfaceLayersHandle0(Buffer jawtDrawingSurfaceInfoBuffer);
415
416   /**
417    * Set the given root CALayer in the JAWT surface
418    */
419   private static native void SetJAWTRootSurfaceLayer0(long jawtSurfaceLayersHandle, long caLayer);
420
421   /**
422    * Unset the given root CALayer in the JAWT surface, passing the NIO DrawingSurfaceInfo buffer
423    */
424   private static native void UnsetJAWTRootSurfaceLayer0(long jawtSurfaceLayersHandle, long caLayer);
425
426   // Variables for lockSurface/unlockSurface
427   private JAWT_DrawingSurface ds;
428   private boolean dsLocked;
429   private JAWT_DrawingSurfaceInfo dsi;
430   private long jawtSurfaceLayersHandle;
431
432   private JAWT_MacOSXDrawingSurfaceInfo macosxdsi;
433
434   private volatile long rootSurfaceLayer = 0; // attached to the JAWT_SurfaceLayer
435
436   private long windowHandle = 0;
437   private long offscreenSurfaceDrawable = 0;
438   private boolean offscreenSurfaceDrawableSet = false;
439
440   // Workaround for instance of 4796548
441   private boolean firstLock = true;
442
443 }
444
http://JogAmp.org git info: FAQ, tutorial and man pages.