Jogamp
Bug 741 HiDPI: Add ScalableSurface interface to get/set pixelScale w/ full OSX impl.
[jogl.git] / src / nativewindow / classes / jogamp / nativewindow / macosx / OSXUtil.java
1 /**
2  * Copyright 2011 JogAmp Community. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  *    1. Redistributions of source code must retain the above copyright notice, this list of
8  *       conditions and the following disclaimer.
9  *
10  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
11  *       of conditions and the following disclaimer in the documentation and/or other materials
12  *       provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  * or implied, of JogAmp Community.
27  */
28 package jogamp.nativewindow.macosx;
29
30 import javax.media.nativewindow.NativeWindowException;
31 import javax.media.nativewindow.NativeWindowFactory;
32 import javax.media.nativewindow.util.Insets;
33 import javax.media.nativewindow.util.Point;
34
35 import com.jogamp.common.util.Function;
36 import com.jogamp.common.util.FunctionTask;
37 import com.jogamp.common.util.RunnableTask;
38
39 import jogamp.nativewindow.Debug;
40 import jogamp.nativewindow.NWJNILibLoader;
41 import jogamp.nativewindow.ToolkitProperties;
42
43 public class OSXUtil implements ToolkitProperties {
44     private static boolean isInit = false;
45     private static final boolean DEBUG = Debug.debug("OSXUtil");
46
47     /** FIXME HiDPI: OSX unique and maximum value {@value} */
48     public static final int MAX_PIXELSCALE = 2;
49
50     /**
51      * Called by {@link NativeWindowFactory#initSingleton()}
52      * @see ToolkitProperties
53      */
54     public static synchronized void initSingleton() {
55       if(!isInit) {
56           if(DEBUG) {
57               System.out.println("OSXUtil.initSingleton()");
58           }
59           if(!NWJNILibLoader.loadNativeWindow("macosx")) {
60               throw new NativeWindowException("NativeWindow MacOSX native library load error.");
61           }
62
63           if( !initIDs0() ) {
64               throw new NativeWindowException("MacOSX: Could not initialized native stub");
65           }
66           isInit = true;
67       }
68     }
69
70     /**
71      * Called by {@link NativeWindowFactory#shutdown()}
72      * @see ToolkitProperties
73      */
74     public static void shutdown() { }
75
76     /**
77      * Called by {@link NativeWindowFactory#initSingleton()}
78      * @see ToolkitProperties
79      */
80     public static boolean requiresToolkitLock() { return false; }
81
82     /**
83      * Called by {@link NativeWindowFactory#initSingleton()}
84      * @see ToolkitProperties
85      */
86     public static final boolean hasThreadingIssues() { return false; }
87
88     public static boolean isNSView(long object) {
89         return 0 != object ? isNSView0(object) : false;
90     }
91
92     public static boolean isNSWindow(long object) {
93         return 0 != object ? isNSWindow0(object) : false;
94     }
95
96     /**
97      * @param windowOrView
98      * @param src_x
99      * @param src_y
100      * @return top-left client-area position in window units
101      */
102     public static Point GetLocationOnScreen(long windowOrView, int src_x, int src_y) {
103       return (Point) GetLocationOnScreen0(windowOrView, src_x, src_y);
104     }
105
106     public static Insets GetInsets(long windowOrView) {
107       return (Insets) GetInsets0(windowOrView);
108     }
109
110     public static double GetPixelScale(int screenIndex) {
111       return GetPixelScale0(screenIndex);
112     }
113
114     public static double GetPixelScale(long windowOrView) {
115       return GetPixelScale1(windowOrView);
116     }
117
118     public static long CreateNSWindow(int x, int y, int width, int height) {
119       return CreateNSWindow0(x, y, width, height);
120     }
121     public static void DestroyNSWindow(long nsWindow) {
122         DestroyNSWindow0(nsWindow);
123     }
124     public static long GetNSView(long nsWindow) {
125       return GetNSView0(nsWindow);
126     }
127     public static long GetNSWindow(long nsView) {
128       return GetNSWindow0(nsView);
129     }
130
131     /**
132      * Create a CALayer suitable to act as a root CALayer.
133      * @param width width of the CALayer in window units (points)
134      * @param height height of the CALayer in window units (points)
135      * @param contentsScale scale for HiDPI support: pixel-dim = window-dim x scale
136      * @return the new CALayer object
137      * @see #DestroyCALayer(long)
138      * @see #AddCASublayer(long, long)
139      */
140     public static long CreateCALayer(final int width, final int height, final float contentsScale) {
141       final long l = CreateCALayer0(width, height, contentsScale);
142       if(DEBUG) {
143           System.err.println("OSXUtil.CreateCALayer: 0x"+Long.toHexString(l)+" - "+Thread.currentThread().getName());
144       }
145       return l;
146     }
147
148     /**
149      * Attach a sub CALayer to the root CALayer
150      * <p>
151      * Method will trigger a <code>display</code>
152      * call to the CALayer hierarchy to enforce resource creation if required, e.g. an NSOpenGLContext.
153      * </p>
154      * <p>
155      * Hence it is important that related resources are not locked <i>if</i>
156      * they will be used for creation.
157      * </p>
158      * @param rootCALayer
159      * @param subCALayer
160      * @param x x-coord of the sub-CALayer in window units (points)
161      * @param y y-coord of the sub-CALayer in window units (points)
162      * @param width width of the sub-CALayer in window units (points)
163      * @param height height of the sub-CALayer in window units (points)
164      * @param contentsScale scale for HiDPI support: pixel-dim = window-dim x scale
165      * @param caLayerQuirks
166      * @see #CreateCALayer(int, int, float)
167      * @see #RemoveCASublayer(long, long, boolean)
168      */
169     public static void AddCASublayer(final long rootCALayer, final long subCALayer,
170                                      final int x, final int y, final int width, final int height,
171                                      final float contentsScale, final int caLayerQuirks) {
172         if(0==rootCALayer || 0==subCALayer) {
173             throw new IllegalArgumentException("rootCALayer 0x"+Long.toHexString(rootCALayer)+", subCALayer 0x"+Long.toHexString(subCALayer));
174         }
175         if(DEBUG) {
176             System.err.println("OSXUtil.AttachCALayer: caLayerQuirks "+caLayerQuirks+", 0x"+Long.toHexString(subCALayer)+" - "+Thread.currentThread().getName());
177         }
178         AddCASublayer0(rootCALayer, subCALayer, x, y, width, height, contentsScale, caLayerQuirks);
179     }
180
181     /**
182      * Fix root and sub CALayer position to 0/0 and size
183      * <p>
184      * If the sub CALayer implements the Objective-C NativeWindow protocol NWDedicatedSize (e.g. JOGL's MyNSOpenGLLayer),
185      * the dedicated size is passed to the layer, which propagates it appropriately.
186      * </p>
187      * <p>
188      * On OSX/Java7 our root CALayer's frame position and size gets corrupted by its NSView,
189      * hence we have created the NWDedicatedSize protocol.
190      * </p>
191      *
192      * @param rootCALayer the root surface layer, maybe null.
193      * @param subCALayer the client surface layer, maybe null.
194      * @param visible TODO
195      * @param width the expected width in window units (points)
196      * @param height the expected height in window units (points)
197      * @param caLayerQuirks TODO
198      */
199     public static void FixCALayerLayout(final long rootCALayer, final long subCALayer, final boolean visible, final int x, final int y, final int width, final int height, final int caLayerQuirks) {
200         if( 0==rootCALayer && 0==subCALayer ) {
201             return;
202         }
203         FixCALayerLayout0(rootCALayer, subCALayer, visible, x, y, width, height, caLayerQuirks);
204     }
205
206     /**
207      * Set root and sub CALayer pixelScale / contentScale for HiDPI
208      *
209      * @param rootCALayer the root surface layer, maybe null.
210      * @param subCALayer the client surface layer, maybe null.
211      * @param contentsScale scale for HiDPI support: pixel-dim = window-dim x scale
212      */
213     public static void SetCALayerPixelScale(final long rootCALayer, final long subCALayer, final float contentsScale) {
214         if( 0==rootCALayer && 0==subCALayer ) {
215             return;
216         }
217         SetCALayerPixelScale0(rootCALayer, subCALayer, contentsScale);
218     }
219
220     /**
221      * Detach a sub CALayer from the root CALayer.
222      */
223     public static void RemoveCASublayer(final long rootCALayer, final long subCALayer) {
224         if(0==rootCALayer || 0==subCALayer) {
225             throw new IllegalArgumentException("rootCALayer 0x"+Long.toHexString(rootCALayer)+", subCALayer 0x"+Long.toHexString(subCALayer));
226         }
227         if(DEBUG) {
228             System.err.println("OSXUtil.DetachCALayer: 0x"+Long.toHexString(subCALayer)+" - "+Thread.currentThread().getName());
229         }
230         RemoveCASublayer0(rootCALayer, subCALayer);
231     }
232
233     /**
234      * Destroy a CALayer.
235      * @see #CreateCALayer(int, int, float)
236      */
237     public static void DestroyCALayer(final long caLayer) {
238         if(0==caLayer) {
239             throw new IllegalArgumentException("caLayer 0x"+Long.toHexString(caLayer));
240         }
241         if(DEBUG) {
242             System.err.println("OSXUtil.DestroyCALayer: 0x"+Long.toHexString(caLayer)+" - "+Thread.currentThread().getName());
243         }
244         DestroyCALayer0(caLayer);
245     }
246
247     /**
248      * Run on OSX UI main thread.
249      * <p>
250      * 'waitUntilDone' is implemented on Java site via lock/wait on {@link RunnableTask} to not freeze OSX main thread.
251      * </p>
252      *
253      * @param waitUntilDone
254      * @param runnable
255      */
256     public static void RunOnMainThread(boolean waitUntilDone, Runnable runnable) {
257         if( IsMainThread0() ) {
258             runnable.run(); // don't leave the JVM
259         } else {
260             // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread,
261             // otherwise we may freeze the OSX main thread.
262             Throwable throwable = null;
263             final Object sync = new Object();
264             final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err );
265             synchronized(sync) {
266                 RunOnMainThread0(rt);
267                 if( waitUntilDone ) {
268                     try {
269                         sync.wait();
270                     } catch (InterruptedException ie) {
271                         throwable = ie;
272                     }
273                     if(null==throwable) {
274                         throwable = rt.getThrowable();
275                     }
276                     if(null!=throwable) {
277                         throw new RuntimeException(throwable);
278                     }
279                 }
280             }
281         }
282     }
283
284     /**
285      * Run later on ..
286      * @param onMain if true, run on main-thread, otherwise on the current OSX thread.
287      * @param runnable
288      * @param delay delay to run the runnable in milliseconds
289      */
290     public static void RunLater(boolean onMain, Runnable runnable, int delay) {
291         RunLater0(onMain, new RunnableTask( runnable, null, true, System.err ), delay);
292     }
293
294     private static Runnable _nop = new Runnable() { @Override public void run() {}; };
295
296     /** Issues a {@link #RunOnMainThread(boolean, Runnable)} w/ an <i>NOP</i> runnable, while waiting until done. */
297     public static void WaitUntilFinish() {
298         RunOnMainThread(true, _nop);
299     }
300
301     /**
302      * Run on OSX UI main thread.
303      * <p>
304      * 'waitUntilDone' is implemented on Java site via lock/wait on {@link FunctionTask} to not freeze OSX main thread.
305      * </p>
306      *
307      * @param waitUntilDone
308      * @param func
309      */
310     public static <R,A> R RunOnMainThread(boolean waitUntilDone, Function<R,A> func, A... args) {
311         if( IsMainThread0() ) {
312             return func.eval(args); // don't leave the JVM
313         } else {
314             // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread,
315             // otherwise we may freeze the OSX main thread.
316             Throwable throwable = null;
317             final Object sync = new Object();
318             final FunctionTask<R,A> rt = new FunctionTask<R,A>( func, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err );
319             synchronized(sync) {
320                 rt.setArgs(args);
321                 RunOnMainThread0(rt);
322                 if( waitUntilDone ) {
323                     try {
324                         sync.wait();
325                     } catch (InterruptedException ie) {
326                         throwable = ie;
327                     }
328                     if(null==throwable) {
329                         throwable = rt.getThrowable();
330                     }
331                     if(null!=throwable) {
332                         throw new RuntimeException(throwable);
333                     }
334                 }
335             }
336             return rt.getResult();
337         }
338     }
339
340     public static boolean IsMainThread() {
341         return IsMainThread0();
342     }
343
344     /** Returns the screen refresh rate in Hz. If unavailable, returns 60Hz. */
345     public static int GetScreenRefreshRate(int scrn_idx) {
346         return GetScreenRefreshRate0(scrn_idx);
347     }
348
349     /***
350     private static boolean  isAWTEDTMainThreadInit = false;
351     private static boolean  isAWTEDTMainThread;
352
353     public synchronized static boolean isAWTEDTMainThread() {
354         if(!isAWTEDTMainThreadInit) {
355             isAWTEDTMainThreadInit = true;
356             if(Platform.AWT_AVAILABLE) {
357                 AWTEDTExecutor.singleton.invoke(true, new Runnable() {
358                    public void run() {
359                        isAWTEDTMainThread = IsMainThread();
360                        System.err.println("XXX: "+Thread.currentThread().getName()+" - isAWTEDTMainThread "+isAWTEDTMainThread);
361                    }
362                 });
363             } else {
364                 isAWTEDTMainThread = false;
365             }
366         }
367         return isAWTEDTMainThread;
368     } */
369
370     private static native boolean initIDs0();
371     private static native boolean isNSView0(long object);
372     private static native boolean isNSWindow0(long object);
373     private static native Object GetLocationOnScreen0(long windowOrView, int src_x, int src_y);
374     private static native Object GetInsets0(long windowOrView);
375     private static native double GetPixelScale0(int screenIndex);
376     private static native double GetPixelScale1(long windowOrView);
377     private static native long CreateNSWindow0(int x, int y, int width, int height);
378     private static native void DestroyNSWindow0(long nsWindow);
379     private static native long GetNSView0(long nsWindow);
380     private static native long GetNSWindow0(long nsView);
381     private static native long CreateCALayer0(int width, int height, float contentsScale);
382     private static native void AddCASublayer0(long rootCALayer, long subCALayer, int x, int y, int width, int height, float contentsScale, int caLayerQuirks);
383     private static native void FixCALayerLayout0(long rootCALayer, long subCALayer, boolean visible, int x, int y, int width, int height, int caLayerQuirks);
384     private static native void SetCALayerPixelScale0(long rootCALayer, long subCALayer, float contentsScale);
385     private static native void RemoveCASublayer0(long rootCALayer, long subCALayer);
386     private static native void DestroyCALayer0(long caLayer);
387     private static native void RunOnMainThread0(Runnable runnable);
388     private static native void RunLater0(boolean onMain, Runnable runnable, int delay);
389     private static native boolean IsMainThread0();
390     private static native int GetScreenRefreshRate0(int scrn_idx);
391 }
http://JogAmp.org git info: FAQ, tutorial and man pages.