Jogamp
695fdac88344c9900afbc2a9ed5b24424bb1ae3f
[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 int[] setSurfaceScale(final int[] result, final int[] pixelScale) {
118       super.setSurfaceScale(result, pixelScale);
119       if( 0 != getWindowHandle() ) { // 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       return result;
136   }
137
138   @Override
139   protected void attachSurfaceLayerImpl(final long layerHandle) {
140       OSXUtil.RunOnMainThread(false, new Runnable() {
141           @Override
142           public void run() {
143               // AWT position is top-left w/ insets, where CALayer position is bottom/left from root CALayer w/o insets.
144               // Determine p0: components location on screen w/o insets.
145               // CALayer position will be determined in native code.
146               // See detailed description in {@link JAWTUtil#JAWT_OSX_CALAYER_QUIRK_LAYOUT}
147               final Point p0 = new Point();
148               final Component outterComp = getLocationOnScreenNonBlocking(p0, component);
149               final java.awt.Insets outterInsets = AWTMisc.getInsets(outterComp, true);
150               final Point p1 = (Point)p0.cloneMutable();
151               p1.translate(-outterComp.getX(), -outterComp.getY());
152               if( null != outterInsets ) {
153                   p1.translate(-outterInsets.left, -outterInsets.top);
154               }
155
156               if( DEBUG_CALAYER_POS_CRITICAL ) {
157                   final java.awt.Point pA0 = component.getLocationOnScreen();
158                   final Point pA1 = new Point(pA0.x, pA0.y);
159                   pA1.translate(-outterComp.getX(), -outterComp.getY());
160                   if( null != outterInsets ) {
161                       pA1.translate(-outterInsets.left, -outterInsets.top);
162                   }
163                   System.err.println("JAWTWindow.attachSurfaceLayerImpl: "+toHexString(layerHandle) + ", [ins "+outterInsets+"], pA "+pA0+" -> "+pA1+
164                           ", p0 "+p0+" -> "+p1+", bounds "+bounds);
165               } else if( DEBUG ) {
166                   System.err.println("JAWTWindow.attachSurfaceLayerImpl: "+toHexString(layerHandle) + ", [ins "+outterInsets+"], p0 "+p0+" -> "+p1+", bounds "+bounds);
167               }
168               // HiDPI: uniform pixel scale
169               OSXUtil.AddCASublayer(rootSurfaceLayer, layerHandle, p1.getX(), p1.getY(), getWidth(), getHeight(), getPixelScaleX(), JAWTUtil.getOSXCALayerQuirks());
170           } } );
171   }
172
173   @Override
174   protected void layoutSurfaceLayerImpl(long layerHandle, boolean visible) {
175       final int caLayerQuirks = JAWTUtil.getOSXCALayerQuirks();
176       // AWT position is top-left w/ insets, where CALayer position is bottom/left from root CALayer w/o insets.
177       // Determine p0: components location on screen w/o insets.
178       // CALayer position will be determined in native code.
179       // See detailed description in {@link JAWTUtil#JAWT_OSX_CALAYER_QUIRK_LAYOUT}
180       final Point p0 = new Point();
181       final Component outterComp = getLocationOnScreenNonBlocking(p0, component);
182       final java.awt.Insets outterInsets = AWTMisc.getInsets(outterComp, true);
183       final Point p1 = (Point)p0.cloneMutable();
184       p1.translate(-outterComp.getX(), -outterComp.getY());
185       if( null != outterInsets ) {
186           p1.translate(-outterInsets.left, -outterInsets.top);
187       }
188
189       if( DEBUG_CALAYER_POS_CRITICAL ) {
190           final java.awt.Point pA0 = component.getLocationOnScreen();
191           final Point pA1 = new Point(pA0.x, pA0.y);
192           pA1.translate(-outterComp.getX(), -outterComp.getY());
193           if( null != outterInsets ) {
194               pA1.translate(-outterInsets.left, -outterInsets.top);
195           }
196           System.err.println("JAWTWindow.layoutSurfaceLayerImpl: "+toHexString(layerHandle) + ", quirks "+caLayerQuirks+", visible "+visible+
197                   ", [ins "+outterInsets+"], pA "+pA0+" -> "+pA1+
198                   ", p0 "+p0+" -> "+p1+", bounds "+bounds);
199       } else if( DEBUG ) {
200           System.err.println("JAWTWindow.layoutSurfaceLayerImpl: "+toHexString(layerHandle) + ", quirks "+caLayerQuirks+", visible "+visible+
201                   ", [ins "+outterInsets+"], p0 "+p0+" -> "+p1+", bounds "+bounds);
202       }
203       OSXUtil.FixCALayerLayout(rootSurfaceLayer, layerHandle, visible, p1.getX(), p1.getY(), getWidth(), getHeight(), caLayerQuirks);
204   }
205
206   @Override
207   protected void detachSurfaceLayerImpl(final long layerHandle, final Runnable detachNotify) {
208       OSXUtil.RunOnMainThread(false, new Runnable() {
209           @Override
210           public void run() {
211               detachNotify.run();
212               OSXUtil.RemoveCASublayer(rootSurfaceLayer, layerHandle);
213           } } );
214   }
215
216   @Override
217   public final long getWindowHandle() {
218     return windowHandle;
219   }
220
221   @Override
222   public final long getSurfaceHandle() {
223     return offscreenSurfaceDrawableSet ? offscreenSurfaceDrawable : drawable /* super.getSurfaceHandle() */ ;
224   }
225
226   @Override
227   public void setSurfaceHandle(long surfaceHandle) {
228       if( !isOffscreenLayerSurfaceEnabled() ) {
229           throw new java.lang.UnsupportedOperationException("Not using CALAYER");
230       }
231       if(DEBUG) {
232         System.err.println("MacOSXJAWTWindow.setSurfaceHandle(): "+toHexString(surfaceHandle));
233       }
234       this.offscreenSurfaceDrawable = surfaceHandle;
235       this.offscreenSurfaceDrawableSet = true;
236   }
237
238   @Override
239   protected JAWT fetchJAWTImpl() throws NativeWindowException {
240        // use offscreen if supported and [ applet or requested ]
241       return JAWTUtil.getJAWT(getShallUseOffscreenLayer() || isApplet());
242   }
243
244   @Override
245   protected int lockSurfaceImpl() throws NativeWindowException {
246     int ret = NativeWindow.LOCK_SURFACE_NOT_READY;
247     ds = getJAWT().GetDrawingSurface(component);
248     if (ds == null) {
249       // Widget not yet realized
250       unlockSurfaceImpl();
251       return NativeWindow.LOCK_SURFACE_NOT_READY;
252     }
253     int res = ds.Lock();
254     dsLocked = ( 0 == ( res & JAWTFactory.JAWT_LOCK_ERROR ) ) ;
255     if (!dsLocked) {
256       unlockSurfaceImpl();
257       throw new NativeWindowException("Unable to lock surface");
258     }
259     // See whether the surface changed and if so destroy the old
260     // OpenGL context so it will be recreated (NOTE: removeNotify
261     // should handle this case, but it may be possible that race
262     // conditions can cause this code to be triggered -- should test
263     // more)
264     if ((res & JAWTFactory.JAWT_LOCK_SURFACE_CHANGED) != 0) {
265       ret = NativeWindow.LOCK_SURFACE_CHANGED;
266     }
267     if (firstLock) {
268       AccessController.doPrivileged(new PrivilegedAction<Object>() {
269           @Override
270           public Object run() {
271             dsi = ds.GetDrawingSurfaceInfo();
272             return null;
273           }
274         });
275     } else {
276       dsi = ds.GetDrawingSurfaceInfo();
277     }
278     if (dsi == null) {
279       unlockSurfaceImpl();
280       return NativeWindow.LOCK_SURFACE_NOT_READY;
281     }
282     updateLockedData(dsi.getBounds());
283     if (DEBUG && firstLock ) {
284       dumpInfo();
285     }
286     firstLock = false;
287     if( !isOffscreenLayerSurfaceEnabled() ) {
288         macosxdsi = (JAWT_MacOSXDrawingSurfaceInfo) dsi.platformInfo(getJAWT());
289         if (macosxdsi == null) {
290           unlockSurfaceImpl();
291           return NativeWindow.LOCK_SURFACE_NOT_READY;
292         }
293         drawable = macosxdsi.getCocoaViewRef();
294
295         if (drawable == 0) {
296           unlockSurfaceImpl();
297           return NativeWindow.LOCK_SURFACE_NOT_READY;
298         } else {
299           windowHandle = OSXUtil.GetNSWindow(drawable);
300           ret = NativeWindow.LOCK_SUCCESS;
301         }
302     } else {
303         /**
304          * Only create a fake invisible NSWindow for the drawable handle
305          * to please frameworks requiring such (eg. NEWT).
306          *
307          * The actual surface/ca-layer shall be created/attached
308          * by the upper framework (JOGL) since they require more information.
309          */
310         String errMsg = null;
311         if(0 == drawable) {
312             windowHandle = OSXUtil.CreateNSWindow(0, 0, 64, 64);
313             if(0 == windowHandle) {
314               errMsg = "Unable to create dummy NSWindow (layered case)";
315             } else {
316                 drawable = OSXUtil.GetNSView(windowHandle);
317                 if(0 == drawable) {
318                   errMsg = "Null NSView of NSWindow "+toHexString(windowHandle);
319                 }
320             }
321             if(null == errMsg) {
322                 // Fix caps reflecting offscreen! (no GL available here ..)
323                 final Capabilities caps = (Capabilities) getGraphicsConfiguration().getChosenCapabilities().cloneMutable();
324                 caps.setOnscreen(false);
325                 setChosenCapabilities(caps);
326             }
327         }
328         if(null == errMsg) {
329             jawtSurfaceLayersHandle = GetJAWTSurfaceLayersHandle0(dsi.getBuffer());
330             OSXUtil.RunOnMainThread(false, new Runnable() {
331                 @Override
332                 public void run() {
333                     String errMsg = null;
334                     if(0 == rootSurfaceLayer && 0 != jawtSurfaceLayersHandle) {
335                         rootSurfaceLayer = OSXUtil.CreateCALayer(bounds.getWidth(), bounds.getHeight(), getPixelScaleX()); // HiDPI: uniform pixel scale
336                         if(0 == rootSurfaceLayer) {
337                           errMsg = "Could not create root CALayer";
338                         } else {
339                             try {
340                                 SetJAWTRootSurfaceLayer0(jawtSurfaceLayersHandle, rootSurfaceLayer);
341                             } catch(Exception e) {
342                                 errMsg = "Could not set JAWT rootSurfaceLayerHandle "+toHexString(rootSurfaceLayer)+", cause: "+e.getMessage();
343                             }
344                         }
345                         if(null != errMsg) {
346                             if(0 != rootSurfaceLayer) {
347                               OSXUtil.DestroyCALayer(rootSurfaceLayer);
348                               rootSurfaceLayer = 0;
349                             }
350                             throw new NativeWindowException(errMsg+": "+MacOSXJAWTWindow.this);
351                         }
352                     }
353                 } } );
354         }
355         if(null != errMsg) {
356             if(0 != windowHandle) {
357               OSXUtil.DestroyNSWindow(windowHandle);
358               windowHandle = 0;
359             }
360             drawable = 0;
361             unlockSurfaceImpl();
362             throw new NativeWindowException(errMsg+": "+this);
363         }
364         ret = NativeWindow.LOCK_SUCCESS;
365     }
366
367     return ret;
368   }
369
370   @Override
371   protected void unlockSurfaceImpl() throws NativeWindowException {
372     if(null!=ds) {
373         if (null!=dsi) {
374             ds.FreeDrawingSurfaceInfo(dsi);
375         }
376         if (dsLocked) {
377             ds.Unlock();
378         }
379         getJAWT().FreeDrawingSurface(ds);
380     }
381     ds = null;
382     dsi = null;
383   }
384
385   private void dumpInfo() {
386       System.err.println("MaxOSXJAWTWindow: 0x"+Integer.toHexString(this.hashCode())+" - thread: "+Thread.currentThread().getName());
387       dumpJAWTInfo();
388   }
389
390   /**
391    * {@inheritDoc}
392    * <p>
393    * On OS X locking the surface at this point (ie after creation and for location validation)
394    * is 'tricky' since the JVM traverses through many threads and crashes at:
395    *   lockSurfaceImpl() {
396    *      ..
397    *      ds = getJAWT().GetDrawingSurface(component);
398    * due to a SIGSEGV.
399    *
400    * Hence we have some threading / sync issues with the native JAWT implementation.
401    * </p>
402    */
403   @Override
404   public Point getLocationOnScreen(Point storage) {
405       if( null == storage ) {
406           storage = new Point();
407       }
408       getLocationOnScreenNonBlocking(storage, component);
409       return storage;
410   }
411   @Override
412   protected Point getLocationOnScreenNativeImpl(final int x0, final int y0) { return null; }
413
414
415   private static native long GetJAWTSurfaceLayersHandle0(Buffer jawtDrawingSurfaceInfoBuffer);
416
417   /**
418    * Set the given root CALayer in the JAWT surface
419    */
420   private static native void SetJAWTRootSurfaceLayer0(long jawtSurfaceLayersHandle, long caLayer);
421
422   /**
423    * Unset the given root CALayer in the JAWT surface, passing the NIO DrawingSurfaceInfo buffer
424    */
425   private static native void UnsetJAWTRootSurfaceLayer0(long jawtSurfaceLayersHandle, long caLayer);
426
427   // Variables for lockSurface/unlockSurface
428   private JAWT_DrawingSurface ds;
429   private boolean dsLocked;
430   private JAWT_DrawingSurfaceInfo dsi;
431   private long jawtSurfaceLayersHandle;
432
433   private JAWT_MacOSXDrawingSurfaceInfo macosxdsi;
434
435   private volatile long rootSurfaceLayer = 0; // attached to the JAWT_SurfaceLayer
436
437   private long windowHandle = 0;
438   private long offscreenSurfaceDrawable = 0;
439   private boolean offscreenSurfaceDrawableSet = false;
440
441   // Workaround for instance of 4796548
442   private boolean firstLock = true;
443
444 }
445
http://JogAmp.org git info: FAQ, tutorial and man pages.