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
CommitLineData
3a37ae12
SG
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 */
b72bedc9
SG
28package jogamp.nativewindow.macosx;
29
30import javax.media.nativewindow.NativeWindowException;
f2cfb611 31import javax.media.nativewindow.NativeWindowFactory;
7bb5885f 32import javax.media.nativewindow.util.Insets;
b72bedc9
SG
33import javax.media.nativewindow.util.Point;
34
f6e6fab2
SG
35import com.jogamp.common.util.Function;
36import com.jogamp.common.util.FunctionTask;
b0be3d76
SG
37import com.jogamp.common.util.RunnableTask;
38
b72bedc9
SG
39import jogamp.nativewindow.Debug;
40import jogamp.nativewindow.NWJNILibLoader;
92398025 41import jogamp.nativewindow.ToolkitProperties;
b72bedc9 42
92398025 43public class OSXUtil implements ToolkitProperties {
5e9c02bc 44 private static boolean isInit = false;
b72bedc9 45 private static final boolean DEBUG = Debug.debug("OSXUtil");
5e9c02bc 46
2571ed0b
SG
47 /** FIXME HiDPI: OSX unique and maximum value {@value} */
48 public static final int MAX_PIXELSCALE = 2;
49
f2cfb611
SG
50 /**
51 * Called by {@link NativeWindowFactory#initSingleton()}
92398025 52 * @see ToolkitProperties
f2cfb611 53 */
3ab518e9 54 public static synchronized void initSingleton() {
b72bedc9 55 if(!isInit) {
3ab518e9
SG
56 if(DEBUG) {
57 System.out.println("OSXUtil.initSingleton()");
58 }
b6885506
SG
59 if(!NWJNILibLoader.loadNativeWindow("macosx")) {
60 throw new NativeWindowException("NativeWindow MacOSX native library load error.");
61 }
5e9c02bc 62
b72bedc9
SG
63 if( !initIDs0() ) {
64 throw new NativeWindowException("MacOSX: Could not initialized native stub");
5e9c02bc 65 }
b72bedc9
SG
66 isInit = true;
67 }
68 }
69
f2cfb611
SG
70 /**
71 * Called by {@link NativeWindowFactory#shutdown()}
92398025 72 * @see ToolkitProperties
f2cfb611 73 */
92398025 74 public static void shutdown() { }
5e9c02bc 75
92398025
SG
76 /**
77 * Called by {@link NativeWindowFactory#initSingleton()}
78 * @see ToolkitProperties
79 */
80 public static boolean requiresToolkitLock() { return false; }
5e9c02bc 81
92398025
SG
82 /**
83 * Called by {@link NativeWindowFactory#initSingleton()}
84 * @see ToolkitProperties
85 */
7c333e3e 86 public static final boolean hasThreadingIssues() { return false; }
5e9c02bc 87
72785ac3 88 public static boolean isNSView(long object) {
28c64723 89 return 0 != object ? isNSView0(object) : false;
72785ac3 90 }
5e9c02bc 91
4dd44b98 92 public static boolean isNSWindow(long object) {
28c64723 93 return 0 != object ? isNSWindow0(object) : false;
4dd44b98 94 }
5e9c02bc 95
7bb5885f 96 /**
7bb5885f 97 * @param windowOrView
7bb5885f
SG
98 * @param src_x
99 * @param src_y
759d9067 100 * @return top-left client-area position in window units
7bb5885f 101 */
759d9067
SG
102 public static Point GetLocationOnScreen(long windowOrView, int src_x, int src_y) {
103 return (Point) GetLocationOnScreen0(windowOrView, src_x, src_y);
7bb5885f 104 }
5e9c02bc 105
7bb5885f
SG
106 public static Insets GetInsets(long windowOrView) {
107 return (Insets) GetInsets0(windowOrView);
b72bedc9 108 }
5e9c02bc 109
56d60b36
SG
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
d8fa00d3
SG
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 }
20bf031d
SG
124 public static long GetNSView(long nsWindow) {
125 return GetNSView0(nsWindow);
126 }
4dd44b98
SG
127 public static long GetNSWindow(long nsView) {
128 return GetNSWindow0(nsView);
129 }
5e9c02bc
HH
130
131 /**
896e8b02 132 * Create a CALayer suitable to act as a root CALayer.
f9a00b91
SG
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
68526d3f 137 * @see #DestroyCALayer(long)
5e9c02bc 138 * @see #AddCASublayer(long, long)
a94ff925 139 */
f9a00b91
SG
140 public static long CreateCALayer(final int width, final int height, final float contentsScale) {
141 final long l = CreateCALayer0(width, height, contentsScale);
f354fb20
SG
142 if(DEBUG) {
143 System.err.println("OSXUtil.CreateCALayer: 0x"+Long.toHexString(l)+" - "+Thread.currentThread().getName());
144 }
145 return l;
0120a81c 146 }
5e9c02bc
HH
147
148 /**
68526d3f 149 * Attach a sub CALayer to the root CALayer
a94ff925
SG
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>
896e8b02 155 * Hence it is important that related resources are not locked <i>if</i>
5e9c02bc 156 * they will be used for creation.
a94ff925 157 * </p>
f9a00b91
SG
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)
f354fb20 167 * @see #RemoveCASublayer(long, long, boolean)
a94ff925 168 */
f9a00b91
SG
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) {
0120a81c
SG
172 if(0==rootCALayer || 0==subCALayer) {
173 throw new IllegalArgumentException("rootCALayer 0x"+Long.toHexString(rootCALayer)+", subCALayer 0x"+Long.toHexString(subCALayer));
174 }
68526d3f 175 if(DEBUG) {
4b5435c6 176 System.err.println("OSXUtil.AttachCALayer: caLayerQuirks "+caLayerQuirks+", 0x"+Long.toHexString(subCALayer)+" - "+Thread.currentThread().getName());
68526d3f 177 }
f9a00b91 178 AddCASublayer0(rootCALayer, subCALayer, x, y, width, height, contentsScale, caLayerQuirks);
0120a81c 179 }
5e9c02bc
HH
180
181 /**
896e8b02 182 * Fix root and sub CALayer position to 0/0 and size
14b27853 183 * <p>
cbd8e33f 184 * If the sub CALayer implements the Objective-C NativeWindow protocol NWDedicatedSize (e.g. JOGL's MyNSOpenGLLayer),
5e9c02bc 185 * the dedicated size is passed to the layer, which propagates it appropriately.
cbd8e33f
SG
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.
14b27853 190 * </p>
5e9c02bc 191 *
14b27853
SG
192 * @param rootCALayer the root surface layer, maybe null.
193 * @param subCALayer the client surface layer, maybe null.
3b02a219 194 * @param visible TODO
f9a00b91
SG
195 * @param width the expected width in window units (points)
196 * @param height the expected height in window units (points)
4b5435c6 197 * @param caLayerQuirks TODO
14b27853 198 */
3b02a219 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) {
14b27853
SG
200 if( 0==rootCALayer && 0==subCALayer ) {
201 return;
202 }
3b02a219 203 FixCALayerLayout0(rootCALayer, subCALayer, visible, x, y, width, height, caLayerQuirks);
14b27853 204 }
5e9c02bc
HH
205
206 /**
2571ed0b
SG
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 /**
f354fb20 221 * Detach a sub CALayer from the root CALayer.
a94ff925 222 */
68526d3f 223 public static void RemoveCASublayer(final long rootCALayer, final long subCALayer) {
0120a81c
SG
224 if(0==rootCALayer || 0==subCALayer) {
225 throw new IllegalArgumentException("rootCALayer 0x"+Long.toHexString(rootCALayer)+", subCALayer 0x"+Long.toHexString(subCALayer));
226 }
68526d3f
SG
227 if(DEBUG) {
228 System.err.println("OSXUtil.DetachCALayer: 0x"+Long.toHexString(subCALayer)+" - "+Thread.currentThread().getName());
f354fb20 229 }
68526d3f 230 RemoveCASublayer0(rootCALayer, subCALayer);
0120a81c 231 }
5e9c02bc
HH
232
233 /**
f354fb20 234 * Destroy a CALayer.
f9a00b91 235 * @see #CreateCALayer(int, int, float)
5e9c02bc 236 */
68526d3f 237 public static void DestroyCALayer(final long caLayer) {
0120a81c
SG
238 if(0==caLayer) {
239 throw new IllegalArgumentException("caLayer 0x"+Long.toHexString(caLayer));
240 }
68526d3f
SG
241 if(DEBUG) {
242 System.err.println("OSXUtil.DestroyCALayer: 0x"+Long.toHexString(caLayer)+" - "+Thread.currentThread().getName());
f354fb20 243 }
68526d3f 244 DestroyCALayer0(caLayer);
5d6cbbcc 245 }
5e9c02bc 246
b0be3d76
SG
247 /**
248 * Run on OSX UI main thread.
5e9c02bc 249 * <p>
b0be3d76
SG
250 * 'waitUntilDone' is implemented on Java site via lock/wait on {@link RunnableTask} to not freeze OSX main thread.
251 * </p>
5e9c02bc 252 *
b0be3d76
SG
253 * @param waitUntilDone
254 * @param runnable
255 */
d186f6e9 256 public static void RunOnMainThread(boolean waitUntilDone, Runnable runnable) {
b0be3d76 257 if( IsMainThread0() ) {
2dd78164
SG
258 runnable.run(); // don't leave the JVM
259 } else {
f6e6fab2 260 // Utilize Java side lock/wait and simply pass the Runnable async to OSX main thread,
5e9c02bc 261 // otherwise we may freeze the OSX main thread.
f6e6fab2
SG
262 Throwable throwable = null;
263 final Object sync = new Object();
5e9c02bc 264 final RunnableTask rt = new RunnableTask( runnable, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err );
f6e6fab2
SG
265 synchronized(sync) {
266 RunOnMainThread0(rt);
267 if( waitUntilDone ) {
b0be3d76
SG
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 }
f6e6fab2 279 }
b0be3d76 280 }
2dd78164 281 }
d186f6e9 282 }
5e9c02bc 283
f354fb20 284 /**
c15d33e4
SG
285 * Run later on ..
286 * @param onMain if true, run on main-thread, otherwise on the current OSX thread.
f354fb20
SG
287 * @param runnable
288 * @param delay delay to run the runnable in milliseconds
289 */
c15d33e4
SG
290 public static void RunLater(boolean onMain, Runnable runnable, int delay) {
291 RunLater0(onMain, new RunnableTask( runnable, null, true, System.err ), delay);
f354fb20 292 }
5e9c02bc 293
f1ae8ddb 294 private static Runnable _nop = new Runnable() { @Override public void run() {}; };
5e9c02bc
HH
295
296 /** Issues a {@link #RunOnMainThread(boolean, Runnable)} w/ an <i>NOP</i> runnable, while waiting until done. */
2aeff053
SG
297 public static void WaitUntilFinish() {
298 RunOnMainThread(true, _nop);
299 }
5e9c02bc 300
f6e6fab2
SG
301 /**
302 * Run on OSX UI main thread.
5e9c02bc 303 * <p>
f6e6fab2
SG
304 * 'waitUntilDone' is implemented on Java site via lock/wait on {@link FunctionTask} to not freeze OSX main thread.
305 * </p>
5e9c02bc 306 *
f6e6fab2
SG
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,
5e9c02bc 315 // otherwise we may freeze the OSX main thread.
f6e6fab2
SG
316 Throwable throwable = null;
317 final Object sync = new Object();
5e9c02bc 318 final FunctionTask<R,A> rt = new FunctionTask<R,A>( func, waitUntilDone ? sync : null, true, waitUntilDone ? null : System.err );
f6e6fab2
SG
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 }
5e9c02bc 339
d186f6e9
SG
340 public static boolean IsMainThread() {
341 return IsMainThread0();
342 }
5e9c02bc 343
4dd44b98
SG
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 }
5e9c02bc 348
dfee8c58
SG
349 /***
350 private static boolean isAWTEDTMainThreadInit = false;
351 private static boolean isAWTEDTMainThread;
5e9c02bc 352
dfee8c58
SG
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 }
5e9c02bc 366 }
dfee8c58
SG
367 return isAWTEDTMainThread;
368 } */
5e9c02bc 369
b72bedc9 370 private static native boolean initIDs0();
72785ac3 371 private static native boolean isNSView0(long object);
4dd44b98 372 private static native boolean isNSWindow0(long object);
b72bedc9 373 private static native Object GetLocationOnScreen0(long windowOrView, int src_x, int src_y);
7bb5885f 374 private static native Object GetInsets0(long windowOrView);
56d60b36
SG
375 private static native double GetPixelScale0(int screenIndex);
376 private static native double GetPixelScale1(long windowOrView);
d8fa00d3
SG
377 private static native long CreateNSWindow0(int x, int y, int width, int height);
378 private static native void DestroyNSWindow0(long nsWindow);
20bf031d 379 private static native long GetNSView0(long nsWindow);
4dd44b98 380 private static native long GetNSWindow0(long nsView);
f9a00b91
SG
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);
3b02a219 383 private static native void FixCALayerLayout0(long rootCALayer, long subCALayer, boolean visible, int x, int y, int width, int height, int caLayerQuirks);
2571ed0b 384 private static native void SetCALayerPixelScale0(long rootCALayer, long subCALayer, float contentsScale);
0120a81c
SG
385 private static native void RemoveCASublayer0(long rootCALayer, long subCALayer);
386 private static native void DestroyCALayer0(long caLayer);
b0be3d76 387 private static native void RunOnMainThread0(Runnable runnable);
c15d33e4 388 private static native void RunLater0(boolean onMain, Runnable runnable, int delay);
d186f6e9 389 private static native boolean IsMainThread0();
4dd44b98 390 private static native int GetScreenRefreshRate0(int scrn_idx);
b72bedc9 391}
http://JogAmp.org git info: FAQ, tutorial and man pages.