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