diff options
author | Sven Gothel <[email protected]> | 2019-06-23 08:03:04 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2019-06-23 08:03:04 +0200 |
commit | bba73bc096250a3c7fc036d84b1ea054d1b70b06 (patch) | |
tree | ed02575eac2a46bd49627444dcce972946ae8d2e /src/newt | |
parent | 154e91978498d8b6db9ce34a1f06b298bcf4c361 (diff) |
iOS: Initial working commit supporting iOS (ipad pro 11)
using our OpenJFK 9 x86_64 and arm64 build.
Test demo class is 'com.jogamp.opengl.demos.ios.Hello',
residing in the new demo folder 'src/demos/com/jogamp/opengl/demos/ios/Hello.java'.
This commit does not yet include a working NEWT
specialization for iOS, but it shall followup soon.
Instead this commit demonstrates JOGL operating on
native UIWindow, UIView and CAEAGLLayer as provided by
Nativewindow's IOSUtil.
Test Video https://www.youtube.com/watch?v=Z4lUQNFTGMI
+++
Notable bug: The FBO used and sharing the COLORBUFFER RENDERBUFFER
memory resources with CAEAGLLayer to be displayed in the UIView
seemingly cannot handle GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24
or GL_DEPTH_COMPONENT32 depth buffer - none at all (Device + Simulation).
Therefor the default demo GLEventListener chosen here
don't require a depth buffer ;-)
This issue can hopefully be mitigated with other means
than using a flat FBO sink similar to FBO multisampling.
Diffstat (limited to 'src/newt')
-rw-r--r-- | src/newt/classes/jogamp/newt/driver/ios/DisplayDriver.java | 91 | ||||
-rw-r--r-- | src/newt/classes/jogamp/newt/driver/ios/MacKeyUtil.java | 426 | ||||
-rw-r--r-- | src/newt/classes/jogamp/newt/driver/ios/ScreenDriver.java | 225 | ||||
-rw-r--r-- | src/newt/classes/jogamp/newt/driver/ios/WindowDriver.java | 812 | ||||
-rw-r--r-- | src/newt/native/IOSNewtUIWindow.h | 152 | ||||
-rw-r--r-- | src/newt/native/IOSNewtUIWindow.m | 783 | ||||
-rw-r--r-- | src/newt/native/IOSUIWindow.m | 1161 | ||||
-rw-r--r-- | src/newt/native/JVM_JNI8.c | 42 |
8 files changed, 3692 insertions, 0 deletions
diff --git a/src/newt/classes/jogamp/newt/driver/ios/DisplayDriver.java b/src/newt/classes/jogamp/newt/driver/ios/DisplayDriver.java new file mode 100644 index 000000000..d2bb6de02 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/ios/DisplayDriver.java @@ -0,0 +1,91 @@ +/** + * Copyright 2019 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.ios; + +import com.jogamp.nativewindow.AbstractGraphicsDevice; +import com.jogamp.nativewindow.NativeWindowException; +import com.jogamp.nativewindow.ios.IOSGraphicsDevice; +import com.jogamp.nativewindow.util.PixelFormat; + +import jogamp.newt.DisplayImpl; +import jogamp.newt.NEWTJNILibLoader; + +public class DisplayDriver extends DisplayImpl { + + static { + NEWTJNILibLoader.loadNEWT(); + + if(!initUIApplication0()) { + throw new NativeWindowException("Failed to initialize native Application hook"); + } + if(!WindowDriver.initIDs0()) { + throw new NativeWindowException("Failed to initialize jmethodIDs"); + } + if(DEBUG) { + System.err.println("MacDisplay.init App and IDs OK "+Thread.currentThread().getName()); + } + } + + public static void initSingleton() { + // just exist to ensure static init has been run + } + + public DisplayDriver() { + } + + @Override + public PixelFormat getNativePointerIconPixelFormat() { return PixelFormat.RGBA8888; } + + @Override + protected void dispatchMessagesNative() { + // nop + } + + @Override + protected void createNativeImpl() { + aDevice = new IOSGraphicsDevice(AbstractGraphicsDevice.DEFAULT_UNIT); + } + + @Override + protected void closeNativeImpl(final AbstractGraphicsDevice aDevice) { + aDevice.close(); + } + + /** + * {@inheritDoc} + * <p> + * NOTE: MUST BE DIRECT BUFFER, since NSBitmapImageRep uses buffer directly! + * </p> + */ + @Override + public final boolean getNativePointerIconForceDirectNIO() { return true; } + + private static native boolean initUIApplication0(); +} + diff --git a/src/newt/classes/jogamp/newt/driver/ios/MacKeyUtil.java b/src/newt/classes/jogamp/newt/driver/ios/MacKeyUtil.java new file mode 100644 index 000000000..e65602959 --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/ios/MacKeyUtil.java @@ -0,0 +1,426 @@ +/** + * Copyright 2019 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ +package jogamp.newt.driver.ios; + +import com.jogamp.newt.event.KeyEvent; + +public class MacKeyUtil { + + // + // KeyCodes (Layout Dependent) + // + private static final short kVK_ANSI_A = 0x00; + private static final short kVK_ANSI_S = 0x01; + private static final short kVK_ANSI_D = 0x02; + private static final short kVK_ANSI_F = 0x03; + private static final short kVK_ANSI_H = 0x04; + private static final short kVK_ANSI_G = 0x05; + private static final short kVK_ANSI_Z = 0x06; + private static final short kVK_ANSI_X = 0x07; + private static final short kVK_ANSI_C = 0x08; + private static final short kVK_ANSI_V = 0x09; + private static final short kVK_ANSI_B = 0x0B; + private static final short kVK_ANSI_Q = 0x0C; + private static final short kVK_ANSI_W = 0x0D; + private static final short kVK_ANSI_E = 0x0E; + private static final short kVK_ANSI_R = 0x0F; + private static final short kVK_ANSI_Y = 0x10; + private static final short kVK_ANSI_T = 0x11; + private static final short kVK_ANSI_1 = 0x12; + private static final short kVK_ANSI_2 = 0x13; + private static final short kVK_ANSI_3 = 0x14; + private static final short kVK_ANSI_4 = 0x15; + private static final short kVK_ANSI_6 = 0x16; + private static final short kVK_ANSI_5 = 0x17; + private static final short kVK_ANSI_Equal = 0x18; + private static final short kVK_ANSI_9 = 0x19; + private static final short kVK_ANSI_7 = 0x1A; + private static final short kVK_ANSI_Minus = 0x1B; + private static final short kVK_ANSI_8 = 0x1C; + private static final short kVK_ANSI_0 = 0x1D; + private static final short kVK_ANSI_RightBracket = 0x1E; + private static final short kVK_ANSI_O = 0x1F; + private static final short kVK_ANSI_U = 0x20; + private static final short kVK_ANSI_LeftBracket = 0x21; + private static final short kVK_ANSI_I = 0x22; + private static final short kVK_ANSI_P = 0x23; + private static final short kVK_ANSI_L = 0x25; + private static final short kVK_ANSI_J = 0x26; + private static final short kVK_ANSI_Quote = 0x27; + private static final short kVK_ANSI_K = 0x28; + private static final short kVK_ANSI_Semicolon = 0x29; + private static final short kVK_ANSI_Backslash = 0x2A; + private static final short kVK_ANSI_Comma = 0x2B; + private static final short kVK_ANSI_Slash = 0x2C; + private static final short kVK_ANSI_N = 0x2D; + private static final short kVK_ANSI_M = 0x2E; + private static final short kVK_ANSI_Period = 0x2F; + private static final short kVK_ANSI_Grave = 0x32; + private static final short kVK_ANSI_KeypadDecimal = 0x41; + private static final short kVK_ANSI_KeypadMultiply = 0x43; + private static final short kVK_ANSI_KeypadPlus = 0x45; + private static final short kVK_ANSI_KeypadClear = 0x47; + private static final short kVK_ANSI_KeypadDivide = 0x4B; + private static final short kVK_ANSI_KeypadEnter = 0x4C; + private static final short kVK_ANSI_KeypadMinus = 0x4E; + private static final short kVK_ANSI_KeypadEquals = 0x51; + private static final short kVK_ANSI_Keypad0 = 0x52; + private static final short kVK_ANSI_Keypad1 = 0x53; + private static final short kVK_ANSI_Keypad2 = 0x54; + private static final short kVK_ANSI_Keypad3 = 0x55; + private static final short kVK_ANSI_Keypad4 = 0x56; + private static final short kVK_ANSI_Keypad5 = 0x57; + private static final short kVK_ANSI_Keypad6 = 0x58; + private static final short kVK_ANSI_Keypad7 = 0x59; + private static final short kVK_ANSI_Keypad8 = 0x5B; + private static final short kVK_ANSI_Keypad9 = 0x5C; + + // + // KeyCodes (Layout Independent) + // + private static final short kVK_Return = 0x24; + private static final short kVK_Tab = 0x30; + private static final short kVK_Space = 0x31; + private static final short kVK_Delete = 0x33; + private static final short kVK_Escape = 0x35; + private static final short kVK_Command = 0x37; + private static final short kVK_Shift = 0x38; + private static final short kVK_CapsLock = 0x39; + private static final short kVK_Option = 0x3A; + private static final short kVK_Control = 0x3B; + private static final short kVK_RightShift = 0x3C; + private static final short kVK_RightOption = 0x3D; + private static final short kVK_RightControl = 0x3E; + // private static final short kVK_Function = 0x3F; + private static final short kVK_F17 = 0x40; + // private static final short kVK_VolumeUp = 0x48; + // private static final short kVK_VolumeDown = 0x49; + // private static final short kVK_Mute = 0x4A; + private static final short kVK_F18 = 0x4F; + private static final short kVK_F19 = 0x50; + private static final short kVK_F20 = 0x5A; + private static final short kVK_F5 = 0x60; + private static final short kVK_F6 = 0x61; + private static final short kVK_F7 = 0x62; + private static final short kVK_F3 = 0x63; + private static final short kVK_F8 = 0x64; + private static final short kVK_F9 = 0x65; + private static final short kVK_F11 = 0x67; + private static final short kVK_F13 = 0x69; + private static final short kVK_F16 = 0x6A; + private static final short kVK_F14 = 0x6B; + private static final short kVK_F10 = 0x6D; + private static final short kVK_F12 = 0x6F; + private static final short kVK_F15 = 0x71; + private static final short kVK_Help = 0x72; + private static final short kVK_Home = 0x73; + private static final short kVK_PageUp = 0x74; + private static final short kVK_ForwardDelete = 0x75; + private static final short kVK_F4 = 0x76; + private static final short kVK_End = 0x77; + private static final short kVK_F2 = 0x78; + private static final short kVK_PageDown = 0x79; + private static final short kVK_F1 = 0x7A; + private static final short kVK_LeftArrow = 0x7B; + private static final short kVK_RightArrow = 0x7C; + private static final short kVK_DownArrow = 0x7D; + private static final short kVK_UpArrow = 0x7E; + + // + // Key constants handled differently on Mac OS X than other platforms + // + private static final char NSUpArrowFunctionKey = 0xF700; + private static final char NSDownArrowFunctionKey = 0xF701; + private static final char NSLeftArrowFunctionKey = 0xF702; + private static final char NSRightArrowFunctionKey = 0xF703; + private static final char NSF1FunctionKey = 0xF704; + private static final char NSF2FunctionKey = 0xF705; + private static final char NSF3FunctionKey = 0xF706; + private static final char NSF4FunctionKey = 0xF707; + private static final char NSF5FunctionKey = 0xF708; + private static final char NSF6FunctionKey = 0xF709; + private static final char NSF7FunctionKey = 0xF70A; + private static final char NSF8FunctionKey = 0xF70B; + private static final char NSF9FunctionKey = 0xF70C; + private static final char NSF10FunctionKey = 0xF70D; + private static final char NSF11FunctionKey = 0xF70E; + private static final char NSF12FunctionKey = 0xF70F; + private static final char NSF13FunctionKey = 0xF710; + private static final char NSF14FunctionKey = 0xF711; + private static final char NSF15FunctionKey = 0xF712; + private static final char NSF16FunctionKey = 0xF713; + private static final char NSF17FunctionKey = 0xF714; + private static final char NSF18FunctionKey = 0xF715; + private static final char NSF19FunctionKey = 0xF716; + private static final char NSF20FunctionKey = 0xF717; + private static final char NSF21FunctionKey = 0xF718; + private static final char NSF22FunctionKey = 0xF719; + private static final char NSF23FunctionKey = 0xF71A; + private static final char NSF24FunctionKey = 0xF71B; + /** + private static final char NSF25FunctionKey = 0xF71C; + private static final char NSF26FunctionKey = 0xF71D; + private static final char NSF27FunctionKey = 0xF71E; + private static final char NSF28FunctionKey = 0xF71F; + private static final char NSF29FunctionKey = 0xF720; + private static final char NSF30FunctionKey = 0xF721; + private static final char NSF31FunctionKey = 0xF722; + private static final char NSF32FunctionKey = 0xF723; + private static final char NSF33FunctionKey = 0xF724; + private static final char NSF34FunctionKey = 0xF725; + private static final char NSF35FunctionKey = 0xF726; + */ + private static final char NSInsertFunctionKey = 0xF727; + private static final char NSDeleteFunctionKey = 0xF728; + private static final char NSHomeFunctionKey = 0xF729; + private static final char NSBeginFunctionKey = 0xF72A; + private static final char NSEndFunctionKey = 0xF72B; + private static final char NSPageUpFunctionKey = 0xF72C; + private static final char NSPageDownFunctionKey = 0xF72D; + private static final char NSPrintScreenFunctionKey = 0xF72E; + private static final char NSScrollLockFunctionKey = 0xF72F; + private static final char NSPauseFunctionKey = 0xF730; + // private static final char NSSysReqFunctionKey = 0xF731; + // private static final char NSBreakFunctionKey = 0xF732; + // private static final char NSResetFunctionKey = 0xF733; + private static final char NSStopFunctionKey = 0xF734; + /** + private static final char NSMenuFunctionKey = 0xF735; + private static final char NSUserFunctionKey = 0xF736; + private static final char NSSystemFunctionKey = 0xF737; + private static final char NSPrintFunctionKey = 0xF738; + private static final char NSClearLineFunctionKey = 0xF739; + private static final char NSClearDisplayFunctionKey = 0xF73A; + private static final char NSInsertLineFunctionKey = 0xF73B; + private static final char NSDeleteLineFunctionKey = 0xF73C; + private static final char NSInsertCharFunctionKey = 0xF73D; + private static final char NSDeleteCharFunctionKey = 0xF73E; + private static final char NSPrevFunctionKey = 0xF73F; + private static final char NSNextFunctionKey = 0xF740; + private static final char NSSelectFunctionKey = 0xF741; + private static final char NSExecuteFunctionKey = 0xF742; + private static final char NSUndoFunctionKey = 0xF743; + private static final char NSRedoFunctionKey = 0xF744; + private static final char NSFindFunctionKey = 0xF745; + private static final char NSHelpFunctionKey = 0xF746; + private static final char NSModeSwitchFunctionKey = 0xF747; + */ + + static short validateKeyCode(final short keyCode, final char keyChar) { + // OS X Virtual Keycodes + switch(keyCode) { + // + // KeyCodes (Layout Dependent) + // + case kVK_ANSI_A: return KeyEvent.VK_A; + case kVK_ANSI_S: return KeyEvent.VK_S; + case kVK_ANSI_D: return KeyEvent.VK_D; + case kVK_ANSI_F: return KeyEvent.VK_F; + case kVK_ANSI_H: return KeyEvent.VK_H; + case kVK_ANSI_G: return KeyEvent.VK_G; + case kVK_ANSI_Z: return KeyEvent.VK_Z; + case kVK_ANSI_X: return KeyEvent.VK_X; + case kVK_ANSI_C: return KeyEvent.VK_C; + case kVK_ANSI_V: return KeyEvent.VK_V; + case kVK_ANSI_B: return KeyEvent.VK_B; + case kVK_ANSI_Q: return KeyEvent.VK_Q; + case kVK_ANSI_W: return KeyEvent.VK_W; + case kVK_ANSI_E: return KeyEvent.VK_E; + case kVK_ANSI_R: return KeyEvent.VK_R; + case kVK_ANSI_Y: return KeyEvent.VK_Y; + case kVK_ANSI_T: return KeyEvent.VK_T; + case kVK_ANSI_1: return KeyEvent.VK_1; + case kVK_ANSI_2: return KeyEvent.VK_2; + case kVK_ANSI_3: return KeyEvent.VK_3; + case kVK_ANSI_4: return KeyEvent.VK_4; + case kVK_ANSI_6: return KeyEvent.VK_6; + case kVK_ANSI_5: return KeyEvent.VK_5; + case kVK_ANSI_Equal: return KeyEvent.VK_EQUALS; + case kVK_ANSI_9: return KeyEvent.VK_9; + case kVK_ANSI_7: return KeyEvent.VK_7; + case kVK_ANSI_Minus: return KeyEvent.VK_MINUS; + case kVK_ANSI_8: return KeyEvent.VK_8; + case kVK_ANSI_0: return KeyEvent.VK_0; + case kVK_ANSI_RightBracket: return KeyEvent.VK_CLOSE_BRACKET; + case kVK_ANSI_O: return KeyEvent.VK_O; + case kVK_ANSI_U: return KeyEvent.VK_U; + case kVK_ANSI_LeftBracket: return KeyEvent.VK_OPEN_BRACKET; + case kVK_ANSI_I: return KeyEvent.VK_I; + case kVK_ANSI_P: return KeyEvent.VK_P; + case kVK_ANSI_L: return KeyEvent.VK_L; + case kVK_ANSI_J: return KeyEvent.VK_J; + case kVK_ANSI_Quote: return KeyEvent.VK_QUOTE; + case kVK_ANSI_K: return KeyEvent.VK_K; + case kVK_ANSI_Semicolon: return KeyEvent.VK_SEMICOLON; + case kVK_ANSI_Backslash: return KeyEvent.VK_BACK_SLASH; + case kVK_ANSI_Comma: return KeyEvent.VK_COMMA; + case kVK_ANSI_Slash: return KeyEvent.VK_SLASH; + case kVK_ANSI_N: return KeyEvent.VK_N; + case kVK_ANSI_M: return KeyEvent.VK_M; + case kVK_ANSI_Period: return KeyEvent.VK_PERIOD; + case kVK_ANSI_Grave: return KeyEvent.VK_BACK_QUOTE; // KeyEvent.VK_DEAD_GRAVE + case kVK_ANSI_KeypadDecimal: return KeyEvent.VK_DECIMAL; + case kVK_ANSI_KeypadMultiply: return KeyEvent.VK_MULTIPLY; + case kVK_ANSI_KeypadPlus: return KeyEvent.VK_PLUS; + case kVK_ANSI_KeypadClear: return KeyEvent.VK_CLEAR; + case kVK_ANSI_KeypadDivide: return KeyEvent.VK_DIVIDE; + case kVK_ANSI_KeypadEnter: return KeyEvent.VK_ENTER; + case kVK_ANSI_KeypadMinus: return KeyEvent.VK_MINUS; + case kVK_ANSI_KeypadEquals: return KeyEvent.VK_EQUALS; + case kVK_ANSI_Keypad0: return KeyEvent.VK_0; + case kVK_ANSI_Keypad1: return KeyEvent.VK_1; + case kVK_ANSI_Keypad2: return KeyEvent.VK_2; + case kVK_ANSI_Keypad3: return KeyEvent.VK_3; + case kVK_ANSI_Keypad4: return KeyEvent.VK_4; + case kVK_ANSI_Keypad5: return KeyEvent.VK_5; + case kVK_ANSI_Keypad6: return KeyEvent.VK_6; + case kVK_ANSI_Keypad7: return KeyEvent.VK_7; + case kVK_ANSI_Keypad8: return KeyEvent.VK_8; + case kVK_ANSI_Keypad9: return KeyEvent.VK_9; + + // + // KeyCodes (Layout Independent) + // + case kVK_Return: return KeyEvent.VK_ENTER; + case kVK_Tab: return KeyEvent.VK_TAB; + case kVK_Space: return KeyEvent.VK_SPACE; + case kVK_Delete: return KeyEvent.VK_BACK_SPACE; + case kVK_Escape: return KeyEvent.VK_ESCAPE; + case kVK_Command: return KeyEvent.VK_WINDOWS; + case kVK_Shift: return KeyEvent.VK_SHIFT; + case kVK_CapsLock: return KeyEvent.VK_CAPS_LOCK; + case kVK_Option: return KeyEvent.VK_ALT; + case kVK_Control: return KeyEvent.VK_CONTROL; + case kVK_RightShift: return KeyEvent.VK_SHIFT; + case kVK_RightOption: return KeyEvent.VK_ALT_GRAPH; + case kVK_RightControl: return KeyEvent.VK_CONTROL; + // case kVK_Function: return KeyEvent.VK_F; + case kVK_F17: return KeyEvent.VK_F17; + // case kVK_VolumeUp: + // case kVK_VolumeDown: + // case kVK_Mute: + case kVK_F18: return KeyEvent.VK_F18; + case kVK_F19: return KeyEvent.VK_F19; + case kVK_F20: return KeyEvent.VK_F20; + case kVK_F5: return KeyEvent.VK_F5; + case kVK_F6: return KeyEvent.VK_F6; + case kVK_F7: return KeyEvent.VK_F7; + case kVK_F3: return KeyEvent.VK_F3; + case kVK_F8: return KeyEvent.VK_F8; + case kVK_F9: return KeyEvent.VK_F9; + case kVK_F11: return KeyEvent.VK_F11; + case kVK_F13: return KeyEvent.VK_F13; + case kVK_F16: return KeyEvent.VK_F16; + case kVK_F14: return KeyEvent.VK_F14; + case kVK_F10: return KeyEvent.VK_F10; + case kVK_F12: return KeyEvent.VK_F12; + case kVK_F15: return KeyEvent.VK_F15; + case kVK_Help: return KeyEvent.VK_HELP; + case kVK_Home: return KeyEvent.VK_HOME; + case kVK_PageUp: return KeyEvent.VK_PAGE_UP; + case kVK_ForwardDelete: return KeyEvent.VK_DELETE; + case kVK_F4: return KeyEvent.VK_F4; + case kVK_End: return KeyEvent.VK_END; + case kVK_F2: return KeyEvent.VK_F2; + case kVK_PageDown: return KeyEvent.VK_PAGE_DOWN; + case kVK_F1: return KeyEvent.VK_F1; + case kVK_LeftArrow: return KeyEvent.VK_LEFT; + case kVK_RightArrow: return KeyEvent.VK_RIGHT; + case kVK_DownArrow: return KeyEvent.VK_DOWN; + case kVK_UpArrow: return KeyEvent.VK_UP; + } + + switch (keyChar) { + case NSUpArrowFunctionKey: return KeyEvent.VK_UP; + case NSDownArrowFunctionKey: return KeyEvent.VK_DOWN; + case NSLeftArrowFunctionKey: return KeyEvent.VK_LEFT; + case NSRightArrowFunctionKey: return KeyEvent.VK_RIGHT; + case NSF1FunctionKey: return KeyEvent.VK_F1; + case NSF2FunctionKey: return KeyEvent.VK_F2; + case NSF3FunctionKey: return KeyEvent.VK_F3; + case NSF4FunctionKey: return KeyEvent.VK_F4; + case NSF5FunctionKey: return KeyEvent.VK_F5; + case NSF6FunctionKey: return KeyEvent.VK_F6; + case NSF7FunctionKey: return KeyEvent.VK_F7; + case NSF8FunctionKey: return KeyEvent.VK_F8; + case NSF9FunctionKey: return KeyEvent.VK_F9; + case NSF10FunctionKey: return KeyEvent.VK_F10; + case NSF11FunctionKey: return KeyEvent.VK_F11; + case NSF12FunctionKey: return KeyEvent.VK_F12; + case NSF13FunctionKey: return KeyEvent.VK_F13; + case NSF14FunctionKey: return KeyEvent.VK_F14; + case NSF15FunctionKey: return KeyEvent.VK_F15; + case NSF16FunctionKey: return KeyEvent.VK_F16; + case NSF17FunctionKey: return KeyEvent.VK_F17; + case NSF18FunctionKey: return KeyEvent.VK_F18; + case NSF19FunctionKey: return KeyEvent.VK_F19; + case NSF20FunctionKey: return KeyEvent.VK_F20; + case NSF21FunctionKey: return KeyEvent.VK_F21; + case NSF22FunctionKey: return KeyEvent.VK_F22; + case NSF23FunctionKey: return KeyEvent.VK_F23; + case NSF24FunctionKey: return KeyEvent.VK_F24; + case NSInsertFunctionKey: return KeyEvent.VK_INSERT; + case NSDeleteFunctionKey: return KeyEvent.VK_DELETE; + case NSHomeFunctionKey: return KeyEvent.VK_HOME; + case NSBeginFunctionKey: return KeyEvent.VK_BEGIN; + case NSEndFunctionKey: return KeyEvent.VK_END; + case NSPageUpFunctionKey: return KeyEvent.VK_PAGE_UP; + case NSPageDownFunctionKey: return KeyEvent.VK_PAGE_DOWN; + case NSPrintScreenFunctionKey: return KeyEvent.VK_PRINTSCREEN; + case NSScrollLockFunctionKey: return KeyEvent.VK_SCROLL_LOCK; + case NSPauseFunctionKey: return KeyEvent.VK_PAUSE; + // Not handled: + // NSSysReqFunctionKey + // NSBreakFunctionKey + // NSResetFunctionKey + case NSStopFunctionKey: return KeyEvent.VK_STOP; + // Not handled: + // NSMenuFunctionKey + // NSUserFunctionKey + // NSSystemFunctionKey + // NSPrintFunctionKey + // NSClearLineFunctionKey + // NSClearDisplayFunctionKey + // NSInsertLineFunctionKey + // NSDeleteLineFunctionKey + // NSInsertCharFunctionKey + // NSDeleteCharFunctionKey + // NSPrevFunctionKey + // NSNextFunctionKey + // NSSelectFunctionKey + // NSExecuteFunctionKey + // NSUndoFunctionKey + // NSRedoFunctionKey + // NSFindFunctionKey + // NSHelpFunctionKey + // NSModeSwitchFunctionKey + } + + return (short) keyChar; // let's hope for the best (compatibility of keyChar/keyCode's) + } +} diff --git a/src/newt/classes/jogamp/newt/driver/ios/ScreenDriver.java b/src/newt/classes/jogamp/newt/driver/ios/ScreenDriver.java new file mode 100644 index 000000000..47280865d --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/ios/ScreenDriver.java @@ -0,0 +1,225 @@ +/** + * Copyright 2019 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.ios; + +import com.jogamp.nativewindow.DefaultGraphicsScreen; +import com.jogamp.nativewindow.util.Rectangle; + +import jogamp.nativewindow.ios.IOSUtil; +import jogamp.newt.MonitorModeProps; +import jogamp.newt.ScreenImpl; + +import com.jogamp.common.util.ArrayHashSet; +import com.jogamp.newt.Display; +import com.jogamp.newt.MonitorDevice; +import com.jogamp.newt.MonitorMode; +import com.jogamp.opengl.math.FloatUtil; + +public class ScreenDriver extends ScreenImpl { + + static { + DisplayDriver.initSingleton(); + } + + public ScreenDriver() { + } + + @Override + protected void createNativeImpl() { + aScreen = new DefaultGraphicsScreen(getDisplay().getGraphicsDevice(), screen_idx); + } + + @Override + protected void closeNativeImpl() { } + + private MonitorMode getMonitorModeImpl(final MonitorModeProps.Cache cache, final int crt_id, final int mode_idx) { + final int[] modeProps = getMonitorMode0(crt_id, mode_idx); + final MonitorMode res; + if (null == modeProps || 0 >= modeProps.length) { + res = null; + } else { + res = MonitorModeProps.streamInMonitorMode(null, cache, modeProps, 0); + } + return res; + } + + class CrtProps { + CrtProps() { + crtIDs = getMonitorDeviceIds0(); + count = crtIDs.length; + pixelScaleArray = new float[count]; + propsOrigArray = new int[count][]; + propsFixedArray = new int[count][]; + + // + // Gather whole topology of monitors (NSScreens) + // + for(int crtIdx=0; crtIdx<count; crtIdx++) { + final int crt_id = crtIDs[crtIdx]; + final float pixelScaleRaw = (float)IOSUtil.GetPixelScaleByDisplayID(crt_id); + pixelScaleArray[crtIdx] = FloatUtil.isZero(pixelScaleRaw, FloatUtil.EPSILON) ? 1.0f : pixelScaleRaw; + propsOrigArray[crtIdx] = getMonitorProps0(crt_id); + if ( null == propsOrigArray[crtIdx] ) { + throw new InternalError("Could not gather device props "+crtIdx+"/"+count+" -> "+Display.toHexString(crt_id)); + } + // copy orig -> fixed + final int propsLen = propsOrigArray[crtIdx].length; + propsFixedArray[crtIdx] = new int[propsLen]; + System.arraycopy(propsOrigArray[crtIdx], 0, propsFixedArray[crtIdx], 0, propsLen); + } + + // + // Fix scaled viewport w/ pixelScale of each monitorProps, + // i.e. size by its own pixelScale and x/y offset by querying it's neighbors. + // + for(int crtIdx=0; crtIdx<count; crtIdx++) { + final int[] thisMonitorProps = propsFixedArray[crtIdx]; + final int x = thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+0]; + final int y = thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+1]; + final float thisPixelScale = pixelScaleArray[crtIdx]; + thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+2] *= thisPixelScale; // fix width + thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+3] *= thisPixelScale; // fix height + if( 0 != x ) { + // find matching viewport width for x-offset to apply it's pixelSize + for(int i=0; i<count; i++) { + if( i != crtIdx && x == propsOrigArray[i][MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+2] ) { + thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+0] *= pixelScaleArray[i]; + break; + } + } + } + if( 0 != y ) { + // find matching viewport height for y-offset to apply it's pixelSize + for(int i=0; i<count; i++) { + if( i != crtIdx && y == propsOrigArray[i][MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+3] ) { + thisMonitorProps[MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT+1] *= pixelScaleArray[i]; + break; + } + } + } + } + } + int getIndex(final int crt_id) { + for(int i=0; i<count; i++) { + if( crt_id == crtIDs[i] ) { + return i; + } + } + return -1; + } + final int count; + final int[] crtIDs; + final float[] pixelScaleArray; + final int[][] propsOrigArray; + final int[][] propsFixedArray; + } + + @Override + protected final void collectNativeMonitorModesAndDevicesImpl(final MonitorModeProps.Cache cache) { + final CrtProps crtProps = new CrtProps(); + + // + // Collect all monitorModes for all monitorDevices + // + for(int crtIdx=0; crtIdx<crtProps.count; crtIdx++) { + final int crt_id = crtProps.crtIDs[crtIdx]; + final ArrayHashSet<MonitorMode> supportedModes = + new ArrayHashSet<MonitorMode>(false, ArrayHashSet.DEFAULT_INITIAL_CAPACITY, ArrayHashSet.DEFAULT_LOAD_FACTOR); + int modeIdx = 0; + { + // Get all supported modes for this monitorDevice + MonitorMode mode; + while( true ) { + mode = getMonitorModeImpl(cache, crt_id, modeIdx); + if( null != mode ) { + if( mode.getSurfaceSize().getBitsPerPixel() >= 24 ) { // drop otherwise + supportedModes.getOrAdd(mode); + } + modeIdx++; // next mode on same monitor + } else { + break; // done with modes on this monitor + } + } + } + if( 0 >= modeIdx ) { + throw new InternalError("Could not gather single mode of device "+crtIdx+"/"+crtProps.count+" -> "+Display.toHexString(crt_id)); + } + final MonitorMode currentMode = getMonitorModeImpl(cache, crt_id, -1); + if ( null == currentMode ) { + throw new InternalError("Could not gather current mode of device "+crtIdx+"/"+crtProps.count+" -> "+Display.toHexString(crt_id)+", but gathered "+modeIdx+" modes"); + } + // merge monitor-props + supported modes + final float pixelScale = crtProps.pixelScaleArray[crtIdx]; + MonitorModeProps.streamInMonitorDevice(cache, this, currentMode, + new float[] { pixelScale, pixelScale }, + supportedModes, crtProps.propsFixedArray[crtIdx], 0, null); + } + } + + @Override + protected boolean updateNativeMonitorDeviceViewportImpl(final MonitorDevice monitor, final float[] pixelScale, final Rectangle viewportPU, final Rectangle viewportWU) { + final CrtProps crtProps = new CrtProps(); + final int crt_id = monitor.getId(); + if( 0 == crt_id ) { + throw new IllegalArgumentException("Invalid monitor id "+Display.toHexString(crt_id)); + } + final int crt_idx = crtProps.getIndex(crt_id); + if( 0 > crt_idx || crt_idx >= crtProps.count ) { + throw new IndexOutOfBoundsException("monitor id "+crt_idx+" not within [0.."+(crtProps.count-1)+"]"); + } + final int[] fixedMonitorProps = crtProps.propsFixedArray[crt_idx]; + int offset = MonitorModeProps.IDX_MONITOR_DEVICE_VIEWPORT; + viewportPU.set(fixedMonitorProps[offset++], fixedMonitorProps[offset++], fixedMonitorProps[offset++], fixedMonitorProps[offset++]); + viewportWU.set(fixedMonitorProps[offset++], fixedMonitorProps[offset++], fixedMonitorProps[offset++], fixedMonitorProps[offset++]); + final float _pixelScale = crtProps.pixelScaleArray[crt_idx]; + pixelScale[0] = _pixelScale; + pixelScale[1] = _pixelScale; + return true; + } + + @Override + protected MonitorMode queryCurrentMonitorModeImpl(final MonitorDevice monitor) { + return getMonitorModeImpl(null, monitor.getId(), -1); + } + + @Override + protected boolean setCurrentMonitorModeImpl(final MonitorDevice monitor, final MonitorMode mode) { + return setMonitorMode0(monitor.getId(), mode.getId(), mode.getRotation()); + } + + @Override + protected int validateScreenIndex(final int idx) { + return 0; // big-desktop w/ multiple monitor attached, only one screen available + } + + private native int[] getMonitorDeviceIds0(); + private native int[] getMonitorProps0(int crt_id); + private native int[] getMonitorMode0(int crt_id, int mode_idx); + private native boolean setMonitorMode0(int crt_id, int nativeId, int rot); +} diff --git a/src/newt/classes/jogamp/newt/driver/ios/WindowDriver.java b/src/newt/classes/jogamp/newt/driver/ios/WindowDriver.java new file mode 100644 index 000000000..2a257436a --- /dev/null +++ b/src/newt/classes/jogamp/newt/driver/ios/WindowDriver.java @@ -0,0 +1,812 @@ +/** + * Copyright 2019 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +package jogamp.newt.driver.ios; + +import com.jogamp.common.util.InterruptSource; +import com.jogamp.nativewindow.AbstractGraphicsConfiguration; +import com.jogamp.nativewindow.GraphicsConfigurationFactory; +import com.jogamp.nativewindow.NativeWindow; +import com.jogamp.nativewindow.NativeWindowException; +import com.jogamp.nativewindow.MutableSurface; +import com.jogamp.nativewindow.ScalableSurface; +import com.jogamp.nativewindow.VisualIDHolder; +import com.jogamp.nativewindow.util.Point; +import com.jogamp.nativewindow.util.PointImmutable; + +import jogamp.nativewindow.SurfaceScaleUtils; +import jogamp.nativewindow.ios.IOSUtil; +import jogamp.newt.ScreenImpl; +import jogamp.newt.WindowImpl; +import jogamp.newt.driver.DriverClearFocus; +import jogamp.newt.driver.DriverUpdatePosition; + +import com.jogamp.newt.event.InputEvent; +import com.jogamp.newt.event.KeyEvent; +import com.jogamp.newt.event.MonitorEvent; +import com.jogamp.opengl.math.FloatUtil; + +public class WindowDriver extends WindowImpl implements MutableSurface, DriverClearFocus, DriverUpdatePosition { + + static { + DisplayDriver.initSingleton(); + } + + public WindowDriver() { + } + + private boolean updatePixelScale(final boolean sendEvent, final boolean defer, final boolean deferOffThread, + final float newPixelScaleRaw, final float maxPixelScaleRaw) { + final float[] newPixelScale = new float[2]; + { + final float _newPixelScale = FloatUtil.isZero(newPixelScaleRaw, FloatUtil.EPSILON) ? ScalableSurface.IDENTITY_PIXELSCALE : newPixelScaleRaw; + newPixelScale[0]= _newPixelScale; + newPixelScale[1]= _newPixelScale; + final float _maxPixelScale = FloatUtil.isZero(maxPixelScaleRaw, FloatUtil.EPSILON) ? ScalableSurface.IDENTITY_PIXELSCALE : maxPixelScaleRaw; + maxPixelScale[0]= _maxPixelScale; + maxPixelScale[1]= _maxPixelScale; + } + // We keep minPixelScale at [1f, 1f]! + + if( SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, newPixelScale, minPixelScale, maxPixelScale, DEBUG_IMPLEMENTATION ? getClass().getName() : null) ) { + if( sendEvent ) { + if( deferOffThread ) { + superSizeChangedOffThread(defer, getWidth(), getHeight(), true); + } else { + super.sizeChanged(defer, getWidth(), getHeight(), true); + } + } else { + defineSize(getWidth(), getHeight()); + } + return true; + } else { + return false; + } + } + + private boolean updatePixelScaleByDisplayID(final boolean sendEvent) { + final float maxPixelScaleRaw = (float) IOSUtil.GetPixelScaleByDisplayID(getDisplayID()); + if( DEBUG_IMPLEMENTATION ) { + System.err.println("WindowDriver.updatePixelScale.1: "+hasPixelScale[0]+", "+maxPixelScaleRaw+" (max)"); + } + return updatePixelScale(sendEvent, true /* defer */, false /*offthread */, maxPixelScaleRaw, maxPixelScaleRaw); + } + + private boolean updatePixelScaleByWindowHandle(final boolean sendEvent) { + final long handle = getWindowHandle(); + if( 0 != handle ) { + final float maxPixelScaleRaw = (float)IOSUtil.GetPixelScale(handle); + if( DEBUG_IMPLEMENTATION ) { + System.err.println("WindowDriver.updatePixelScale.2: "+hasPixelScale[0]+", "+maxPixelScaleRaw+" (max)"); + } + return updatePixelScale(sendEvent, true /* defer */, false /*offthread */, maxPixelScaleRaw, maxPixelScaleRaw); + } else { + return false; + } + } + + /** Called from native code */ + protected void updatePixelScale(final boolean defer, final float newPixelScaleRaw, final float maxPixelScaleRaw) { + if( DEBUG_IMPLEMENTATION ) { + System.err.println("WindowDriver.updatePixelScale.3: "+hasPixelScale[0]+" (has) -> "+newPixelScaleRaw+" (new), "+maxPixelScaleRaw+" (max), drop "+!isNativeValid()); + } + if( isNativeValid() ) { + updatePixelScale(true /* sendEvent*/, defer, true /*offthread */, newPixelScaleRaw, maxPixelScaleRaw); + } + } + + @Override + protected final void instantiationFinishedImpl() { + updatePixelScaleByDisplayID(false /* sendEvent*/); + } + + @Override + protected void setScreen(final ScreenImpl newScreen) { // never null ! + super.setScreen(newScreen); + updatePixelScaleByDisplayID(false /* sendEvent*/); // caller (reparent, ..) will send reshape event + } + + @Override + protected void monitorModeChanged(final MonitorEvent me, final boolean success) { + updatePixelScaleByWindowHandle(false /* sendEvent*/); // send reshape event itself + } + + @Override + public final boolean setSurfaceScale(final float[] pixelScale) { + super.setSurfaceScale(pixelScale); + + boolean changed = false; + if( isNativeValid() ) { + if( isOffscreenInstance ) { + final NativeWindow pWin = getParent(); + if( pWin instanceof ScalableSurface ) { + final ScalableSurface sSurf = (ScalableSurface)pWin; + sSurf.setSurfaceScale(reqPixelScale); + sSurf.getMaximumSurfaceScale(maxPixelScale); + sSurf.getMinimumSurfaceScale(minPixelScale); + final float[] pPixelScale = sSurf.getCurrentSurfaceScale(new float[2]); + changed = updatePixelScale(true /* sendEvent */, true /* defer */, true /*offthread */, pPixelScale[0], maxPixelScale[0]); // HiDPI: uniformPixelScale + } else { + // just notify updated pixelScale if offscreen + changed = updatePixelScale(true /* sendEvent */, true /* defer */, true /*offthread */, reqPixelScale[0], maxPixelScale[0]); // HiDPI: uniformPixelScale + } + } else { + // set pixelScale in native code, will issue an update updatePixelScale(..) + // hence we pre-query whether pixel-scale will change, without affecting current state 'hasPixelScale'! + final float[] _hasPixelScale = new float[2]; + System.arraycopy(hasPixelScale, 0, _hasPixelScale, 0, 2); + if( SurfaceScaleUtils.setNewPixelScale(_hasPixelScale, _hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG_IMPLEMENTATION ? getClass().getName() : null) ) { + IOSUtil.RunOnMainThread(true, false, new Runnable() { + @Override + public void run() { + setPixelScale0(getWindowHandle(), surfaceHandle, _hasPixelScale[0]); // HiDPI: uniformPixelScale + } + } ); + changed = true; + } + } + } + if( DEBUG_IMPLEMENTATION ) { + System.err.println("WindowDriver.setPixelScale: min["+minPixelScale[0]+", "+minPixelScale[1]+"], max["+ + maxPixelScale[0]+", "+maxPixelScale[1]+"], req["+ + reqPixelScale[0]+", "+reqPixelScale[1]+"] -> result["+ + hasPixelScale[0]+", "+hasPixelScale[1]+"] - changed "+changed+", realized "+isNativeValid()); + } + return changed; + } + + @Override + protected void createNativeImpl() { + final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice(), capsRequested).chooseGraphicsConfiguration( + capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen(), VisualIDHolder.VID_UNDEFINED); + if (null == cfg) { + throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); + } + setGraphicsConfiguration(cfg); + reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureMask(CHANGE_MASK_VISIBILITY, true)); + if ( !isNativeValid() ) { + throw new NativeWindowException("Error creating window"); + } + } + + @Override + protected void closeNativeImpl() { + try { + if(DEBUG_IMPLEMENTATION) { System.err.println("MacWindow.CloseAction "+Thread.currentThread().getName()); } + final long handle = getWindowHandle(); + visibleChanged(true, false); + setWindowHandle(0); + surfaceHandle = 0; + sscSurfaceHandle = 0; + isOffscreenInstance = false; + resizeAnimatorPaused = false; + if (0 != handle) { + IOSUtil.RunOnMainThread(false, true /* kickNSApp */, new Runnable() { + @Override + public void run() { + close0( handle ); + } }); + } + } catch (final Throwable t) { + if(DEBUG_IMPLEMENTATION) { + final Exception e = new Exception("Warning: closeNative failed - "+Thread.currentThread().getName(), t); + e.printStackTrace(); + } + } + } + + @Override + protected int lockSurfaceImpl() { + /** + * if( isOffscreenInstance ) { + * return LOCK_SUCCESS; + * } + */ + final long w = getWindowHandle(); + final long v = surfaceHandle; + if( 0 != v && 0 != w ) { + return lockSurface0(w, v) ? LOCK_SUCCESS : LOCK_SURFACE_NOT_READY; + } + return LOCK_SURFACE_NOT_READY; + } + + @Override + protected void unlockSurfaceImpl() { + /** + * if( isOffscreenInstance ) { + * return; + * } + */ + final long w = getWindowHandle(); + final long v = surfaceHandle; + if(0 != w && 0 != v) { + if( !unlockSurface0(w, v) ) { + throw new NativeWindowException("Failed to unlock surface, probably not locked!"); + } + } + } + + @Override + public final long getSurfaceHandle() { + return 0 != sscSurfaceHandle ? sscSurfaceHandle : surfaceHandle; + } + + @Override + public void setSurfaceHandle(final long surfaceHandle) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.setSurfaceHandle(): 0x"+Long.toHexString(surfaceHandle)); + } + sscSurfaceHandle = surfaceHandle; + if ( isNativeValid() ) { + if (0 != sscSurfaceHandle) { + IOSUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + orderOut0( 0 != getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); + } } ); + } /** this is done by recreation! + else if (isVisible()){ + IOSUtil.RunOnMainThread(false, new Runnable() { + public void run() { + orderFront0( 0!=getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); + } } ); + } */ + } + } + + @Override + protected void setTitleImpl(final String title) { + IOSUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + setTitle0(getWindowHandle(), title); + } } ); + } + + @Override + protected void requestFocusImpl(final boolean force) { + final boolean _isFullscreen = isFullscreen(); + final boolean _isOffscreenInstance = isOffscreenInstance; + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow: requestFocusImpl(), isOffscreenInstance "+_isOffscreenInstance+", isFullscreen "+_isFullscreen); + } + if(!_isOffscreenInstance) { + IOSUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + requestFocus0(getWindowHandle(), force); + if(_isFullscreen) { + // 'NewtMacWindow::windowDidBecomeKey()' is not always called in fullscreen-mode! + focusChanged(false, true); + } + } } ); + } else { + focusChanged(false, true); + } + } + + @Override + public final void clearFocus() { + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow: clearFocus(), isOffscreenInstance "+isOffscreenInstance); + } + if(!isOffscreenInstance) { + IOSUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + resignFocus0(getWindowHandle()); + } } ); + } else { + focusChanged(false, false); + } + } + + private boolean useParent(final NativeWindow parent) { return null != parent && 0 != parent.getWindowHandle(); } + + @Override + public void updatePosition(final int x, final int y) { + final long handle = getWindowHandle(); + if( 0 != handle && !isOffscreenInstance ) { + final NativeWindow parent = getParent(); + final boolean useParent = useParent(parent); + final Point p0S; + if( useParent ) { + p0S = getLocationOnScreenByParent(x, y, parent); + } else { + p0S = (Point) getLocationOnScreen0(handle, x, y); + } + if(DEBUG_IMPLEMENTATION) { + final int pX=parent.getX(), pY=parent.getY(); + System.err.println("MacWindow: updatePosition() parent["+useParent+" "+pX+"/"+pY+"] "+x+"/"+y+" -> "+x+"/"+y+" rel-client-pos, "+p0S+" screen-client-pos"); + } + IOSUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible()); + } } ); + // no native event (fullscreen, some reparenting) + positionChanged(true, x, y); + } + } + + @Override + protected final int getSupportedReconfigMaskImpl() { + return minimumReconfigStateMask | + STATE_MASK_CHILDWIN | + STATE_MASK_UNDECORATED | + STATE_MASK_ALWAYSONTOP | + STATE_MASK_ALWAYSONBOTTOM | + STATE_MASK_STICKY | + STATE_MASK_RESIZABLE | + STATE_MASK_MAXIMIZED_VERT | + STATE_MASK_MAXIMIZED_HORZ | + STATE_MASK_POINTERVISIBLE | + STATE_MASK_POINTERCONFINED; + } + + @Override + protected boolean reconfigureWindowImpl(int _x, int _y, int _width, int _height, final int flags) { + final boolean _isOffscreenInstance = isOffscreenInstance(this, this.getParent()); + isOffscreenInstance = 0 != sscSurfaceHandle || _isOffscreenInstance; + final PointImmutable pClientLevelOnSreen; + if( isOffscreenInstance ) { + _x = 0; _y = 0; + pClientLevelOnSreen = new Point(0, 0); + } else { + final NativeWindow parent = getParent(); + if( useParent(parent) ) { + pClientLevelOnSreen = getLocationOnScreenByParent(_x, _y, parent); + } else { + if( 0 != ( ( CHANGE_MASK_MAXIMIZED_HORZ | CHANGE_MASK_MAXIMIZED_VERT ) & flags ) ) { + final int[] posSize = { _x, _y, _width, _height }; + reconfigMaximizedManual(flags, posSize, getInsets()); + _x = posSize[0]; + _y = posSize[1]; + _width = posSize[2]; + _height = posSize[3]; + } + pClientLevelOnSreen = new Point(_x, _y); + } + } + final int x=_x, y=_y; + final int width=_width, height=_height; + + final boolean hasFocus = hasFocus(); + + if(DEBUG_IMPLEMENTATION) { + final AbstractGraphicsConfiguration cWinCfg = this.getGraphicsConfiguration(); + final NativeWindow pWin = getParent(); + final AbstractGraphicsConfiguration pWinCfg = null != pWin ? pWin.getGraphicsConfiguration() : null; + System.err.println("MacWindow reconfig.0: "+x+"/"+y+" -> clientPosOnScreen "+pClientLevelOnSreen+" - "+width+"x"+height+ + ", "+getReconfigStateMaskString(flags)+ + ",\n\t parent type "+(null != pWin ? pWin.getClass().getName() : null)+ + ",\n\t this-chosenCaps "+(null != cWinCfg ? cWinCfg.getChosenCapabilities() : null)+ + ",\n\t parent-chosenCaps "+(null != pWinCfg ? pWinCfg.getChosenCapabilities() : null)+ + ", isOffscreenInstance(sscSurfaceHandle "+toHexString(sscSurfaceHandle)+ + ", ioi: "+_isOffscreenInstance+ + ") -> "+isOffscreenInstance); + // Thread.dumpStack(); + } + + if( 0 != ( CHANGE_MASK_VISIBILITY & flags) && + 0 == ( STATE_MASK_VISIBLE & flags) ) + { + if ( !isOffscreenInstance ) { + IOSUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + orderOut0(getWindowHandle()); + visibleChanged(true, false); + } } ); + } else { + visibleChanged(true, false); + } + } + final long oldWindowHandle = getWindowHandle(); + if( ( 0 == oldWindowHandle && 0 != ( STATE_MASK_VISIBLE & flags) ) || + 0 != ( CHANGE_MASK_PARENTING & flags) || + 0 != ( CHANGE_MASK_DECORATION & flags) || + 0 != ( CHANGE_MASK_ALWAYSONTOP & flags) || + 0 != ( CHANGE_MASK_ALWAYSONBOTTOM & flags) || + 0 != ( CHANGE_MASK_RESIZABLE & flags) || + 0 != ( CHANGE_MASK_FULLSCREEN & flags) ) { + if(isOffscreenInstance) { + createWindow(true, 0 != oldWindowHandle, pClientLevelOnSreen, 64, 64, flags); + } else { + createWindow(false, 0 != oldWindowHandle, pClientLevelOnSreen, width, height, flags); + } + // no native event (fullscreen, some reparenting) + updatePixelScaleByWindowHandle(false /* sendEvent */); + if( isOffscreenInstance) { + super.sizeChanged(false, width, height, true); + positionChanged(false, x, y); + } else { + updateSizePosInsets0(getWindowHandle(), false); + } + visibleChanged(false, 0 != ( STATE_MASK_VISIBLE & flags)); + if( hasFocus ) { + requestFocusImpl(true); + } + } else if( 0 != oldWindowHandle ) { + if( width>0 && height>0 ) { + if( !isOffscreenInstance ) { + IOSUtil.RunOnMainThread(true, false, new Runnable() { + @Override + public void run() { + setWindowClientTopLeftPointAndSize0(oldWindowHandle, + pClientLevelOnSreen.getX(), pClientLevelOnSreen.getY(), + width, height, 0 != ( STATE_MASK_VISIBLE & flags)); + } } ); + updateSizePosInsets0(oldWindowHandle, false); + } else { // else offscreen size is realized via recreation + // no native event (fullscreen, some reparenting) + super.sizeChanged(false, width, height, false); + positionChanged(false, x, y); + } + } + if( 0 != ( CHANGE_MASK_VISIBILITY & flags) && + 0 != ( STATE_MASK_VISIBLE & flags) ) + { + if( !isOffscreenInstance ) { + IOSUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + orderFront0(getWindowHandle()); + visibleChanged(true, true); + } } ); + } else { + visibleChanged(true, true); + } + } + } else { + throw new InternalError("Null windowHandle but no re-creation triggered, check visibility: "+getStateMaskString()); + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow reconfig.X: "+getLocationOnScreenImpl(0, 0)+" "+getWidth()+"x"+getHeight()+", insets "+getInsets()+", "+getStateMaskString()); + } + return true; + } + + @Override + protected Point getLocationOnScreenImpl(final int x, final int y) { + final NativeWindow parent = getParent(); + if( useParent(parent) ) { + return getLocationOnScreenByParent(x, y, parent); + } else { + final long windowHandle = getWindowHandle(); + if( !isOffscreenInstance && 0 != windowHandle ) { + return (Point) getLocationOnScreen0(windowHandle, x, y); + } else { + return new Point(x, y); + } + } + } + + private Point getLocationOnScreenByParent(final int x, final int y, final NativeWindow parent) { + return new Point(x, y).translate( parent.getLocationOnScreen(null) ); + } + + /** Callback for native screen position change event of the client area. */ + protected void screenPositionChanged(final boolean defer, final int newX, final int newY) { + // passed coordinates are in screen position of the client area + if( isNativeValid() ) { + final NativeWindow parent = getParent(); + if( !useParent(parent) || isOffscreenInstance ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.positionChanged.0 (Screen Pos - TOP): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY); + } + positionChanged(defer, newX, newY); + } else { + final Runnable action = new Runnable() { + public void run() { + // screen position -> rel child window position + final Point absPos = new Point(newX, newY); + final Point parentOnScreen = parent.getLocationOnScreen(null); + absPos.translate( parentOnScreen.scale(-1, -1) ); + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.positionChanged.1 (Screen Pos - CHILD): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> absPos "+newX+"/"+newY+", parentOnScreen "+parentOnScreen+" -> "+absPos); + } + positionChanged(false, absPos.getX(), absPos.getY()); + } }; + if( defer ) { + new InterruptSource.Thread(null, action).start(); + } else { + action.run(); + } + + } + } else if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.positionChanged.2 (Screen Pos - IGN): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY); + } + } + + @Override + protected void sizeChanged(final boolean defer, final int newWidth, final int newHeight, final boolean force) { + if(force || getWidth() != newWidth || getHeight() != newHeight) { + if( isNativeValid() && !isOffscreenInstance ) { + final NativeWindow parent = getParent(); + final boolean useParent = useParent(parent); + if( useParent ) { + final int x=getX(), y=getY(); + final Point p0S = getLocationOnScreenByParent(x, y, parent); + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow: sizeChanged() parent["+useParent+" "+x+"/"+y+"] "+getX()+"/"+getY()+" "+newWidth+"x"+newHeight+" -> "+p0S+" screen-client-pos"); + } + IOSUtil.RunOnMainThread(false, false, new Runnable() { + @Override + public void run() { + setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible()); + } } ); + } + } + superSizeChangedOffThread(defer, newWidth, newHeight, force); + } + } + private void superSizeChangedOffThread(final boolean defer, final int newWidth, final int newHeight, final boolean force) { + if( defer ) { + new InterruptSource.Thread() { + public void run() { + WindowDriver.super.sizeChanged(false /* defer */, newWidth, newHeight, force); + } }.start(); + } else { + WindowDriver.super.sizeChanged(false /* defer */, newWidth, newHeight, force); + } + } + + // + // Accumulated actions + // + + /** Triggered by implementation's WM events to update the client-area position, size and insets. */ + protected void sizeScreenPosInsetsChanged(final boolean defer, + final int newX, final int newY, + final int newWidth, final int newHeight, + final int left, final int right, final int top, final int bottom, + final boolean force, + final boolean withinLiveResize) { + final LifecycleHook lh = getLifecycleHook(); + if( withinLiveResize && !resizeAnimatorPaused && null!=lh ) { + resizeAnimatorPaused = lh.pauseRenderingAction(); + } + sizeChanged(defer, newWidth, newHeight, force); + screenPositionChanged(defer, newX, newY); + insetsChanged(defer, left, right, top, bottom); + if( !withinLiveResize && resizeAnimatorPaused ) { + resizeAnimatorPaused = false; + if( null!=lh ) { + lh.resumeRenderingAction(); + } + } + } + + @Override + protected final void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers, + final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) { + super.doMouseEvent(enqueue, wait, eventType, modifiers, + SurfaceScaleUtils.scale(x, getPixelScaleX()), + SurfaceScaleUtils.scale(y, getPixelScaleY()), button, rotationXYZ, rotationScale); + } + + @Override + public final void sendKeyEvent(final short eventType, final int modifiers, final short keyCode, final short keySym, final char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); + } + + @Override + public final void enqueueKeyEvent(final boolean wait, final short eventType, final int modifiers, final short _keyCode, final short _keySym, final char keyChar) { + throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); + } + + protected final void enqueueKeyEvent(final boolean wait, final short eventType, int modifiers, final short _keyCode, final char keyChar, final char keySymChar) { + // Note that we send the key char for the key code on this + // platform -- we do not get any useful key codes out of the system + final short keyCode = MacKeyUtil.validateKeyCode(_keyCode, keyChar); + final short keySym; + { + final short _keySym = KeyEvent.NULL_CHAR != keySymChar ? KeyEvent.utf16ToVKey(keySymChar) : KeyEvent.VK_UNDEFINED; + keySym = KeyEvent.VK_UNDEFINED != _keySym ? _keySym : keyCode; + } + /** + { + final boolean isModifierKeyCode = KeyEvent.isModifierKey(keyCode); + System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+ + ", keyCode 0x"+Integer.toHexString(_keyCode)+" -> 0x"+Integer.toHexString(keyCode)+ + ", keySymChar '"+keySymChar+"', 0x"+Integer.toHexString(keySymChar)+" -> 0x"+Integer.toHexString(keySym)+ + ", mods "+toHexString(modifiers)+ + ", was: pressed "+isKeyPressed(keyCode)+", isModifierKeyCode "+isModifierKeyCode+ + ", nativeValid "+isNativeValid()+", isOffscreen "+isOffscreenInstance); + } */ + + // OSX delivery order is PRESSED (t0), RELEASED (t1) and TYPED (t2) -> NEWT order: PRESSED (t0) and RELEASED (t1) + // Auto-Repeat: OSX delivers only PRESSED, inject auto-repeat RELEASE key _before_ PRESSED + switch(eventType) { + case KeyEvent.EVENT_KEY_RELEASED: + if( isKeyCodeTracked(keyCode) ) { + setKeyPressed(keyCode, false); + } + break; + case KeyEvent.EVENT_KEY_PRESSED: + if( isKeyCodeTracked(keyCode) ) { + if( setKeyPressed(keyCode, true) ) { + // key was already pressed + modifiers |= InputEvent.AUTOREPEAT_MASK; + super.enqueueKeyEvent(wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); // RELEASED + } + } + break; + } + super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keySym, keyChar); + } + + protected int getDisplayID() { + if( !isOffscreenInstance ) { + return getDisplayID0(getWindowHandle()); + } + return 0; + } + + //---------------------------------------------------------------------- + // Internals only + // + + private void createWindow(final boolean offscreenInstance, final boolean recreate, + final PointImmutable pS, final int width, final int height, + final int flags) + { + final long parentWinHandle = getParentWindowHandle(); + final long preWinHandle = getWindowHandle(); + + if(DEBUG_IMPLEMENTATION) { + System.err.println("MacWindow.createWindow on thread "+Thread.currentThread().getName()+ + ": offscreen "+offscreenInstance+", recreate "+recreate+ + ", pS "+pS+", "+width+"x"+height+", state "+getReconfigStateMaskString(flags)+ + ", preWinHandle "+toHexString(preWinHandle)+", parentWin "+toHexString(parentWinHandle)+ + ", surfaceHandle "+toHexString(surfaceHandle)); + // Thread.dumpStack(); + } + + try { + if( 0 != preWinHandle ) { + setWindowHandle(0); + if( 0 == surfaceHandle ) { + throw new NativeWindowException("Internal Error - create w/ window, but no Newt NSView"); + } + IOSUtil.RunOnMainThread(false, false /* kickNSApp */, new Runnable() { + @Override + public void run() { + changeContentView0(parentWinHandle, preWinHandle, 0); + close0( preWinHandle ); + } }); + } else { + if( 0 != surfaceHandle ) { + throw new NativeWindowException("Internal Error - create w/o window, but has Newt NSView"); + } + surfaceHandle = createView0(pS.getX(), pS.getY(), width, height); + if( 0 == surfaceHandle ) { + throw new NativeWindowException("Could not create native view "+Thread.currentThread().getName()+" "+this); + } + } + + final int windowStyle; + { + int ws = 0; + if( 0 != ( STATE_MASK_UNDECORATED & flags) || offscreenInstance ) { + ws = NSBorderlessWindowMask; + } else { + ws = NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask; + if( 0 != ( STATE_MASK_RESIZABLE & flags) ) { + ws |= NSResizableWindowMask; + } + } + windowStyle = ws; + } + // Blocking initialization on main-thread! + final long[] newWin = { 0 }; + IOSUtil.RunOnMainThread(true, false /* kickNSApp */, new Runnable() { + @Override + public void run() { + newWin[0] = createWindow0( pS.getX(), pS.getY(), width, height, + 0 != ( STATE_MASK_FULLSCREEN & flags), + windowStyle, + NSBackingStoreBuffered, surfaceHandle); + if ( newWin[0] != 0 ) { + final boolean isOpaque = getGraphicsConfiguration().getChosenCapabilities().isBackgroundOpaque() && !offscreenInstance; + initWindow0( parentWinHandle, newWin[0], pS.getX(), pS.getY(), width, height, reqPixelScale[0] /* HiDPI uniformPixelScale */, + isOpaque, + !offscreenInstance && 0 != ( STATE_MASK_ALWAYSONTOP & flags), + !offscreenInstance && 0 != ( STATE_MASK_ALWAYSONBOTTOM & flags), + !offscreenInstance && 0 != ( STATE_MASK_VISIBLE & flags), + surfaceHandle); + if( offscreenInstance ) { + orderOut0(0!=parentWinHandle ? parentWinHandle : newWin[0]); + } else { + setTitle0(newWin[0], getTitle()); + } + } + } }); + + if ( newWin[0] == 0 ) { + throw new NativeWindowException("Could not create native window "+Thread.currentThread().getName()+" "+this); + } + setWindowHandle( newWin[0] ); + } catch (final Exception ie) { + ie.printStackTrace(); + } + } + + protected static native boolean initIDs0(); + private native long createView0(int x, int y, int w, int h); + private native long createWindow0(int x, int y, int w, int h, boolean fullscreen, int windowStyle, int backingStoreType, long view); + /** Must be called on Main-Thread */ + private native void initWindow0(long parentWindow, long window, int x, int y, int w, int h, float reqPixelScale, + boolean opaque, boolean atop, boolean abottom, boolean visible, long view); + + private native int getDisplayID0(long window); + private native void setPixelScale0(long window, long view, float reqPixelScale); + private native boolean lockSurface0(long window, long view); + private native boolean unlockSurface0(long window, long view); + /** Must be called on Main-Thread */ + private native void requestFocus0(long window, boolean force); + /** Must be called on Main-Thread */ + private native void resignFocus0(long window); + /** Must be called on Main-Thread. In case this is a child window and parent is still visible, orderBack(..) is issued instead of orderOut(). */ + private native void orderOut0(long window); + /** Must be called on Main-Thread */ + private native void orderFront0(long window); + /** Must be called on Main-Thread */ + private native void close0(long window); + /** Must be called on Main-Thread */ + private native void setTitle0(long window, String title); + private native long contentView0(long window); + /** Must be called on Main-Thread */ + private native void changeContentView0(long parentWindowOrView, long window, long view); + /** Must be called on Main-Thread */ + private native void setWindowClientTopLeftPointAndSize0(long window, int x, int y, int w, int h, boolean display); + /** Must be called on Main-Thread */ + private native void setWindowClientTopLeftPoint0(long window, int x, int y, boolean display); + /** Triggers {@link #sizeScreenPosInsetsChanged(boolean, int, int, int, int, int, int, int, int, boolean)} */ + private native void updateSizePosInsets0(long window, boolean defer); + private static native Object getLocationOnScreen0(long windowHandle, int src_x, int src_y); + + // Window styles + private static final int NSBorderlessWindowMask = 0; + private static final int NSTitledWindowMask = 1 << 0; + private static final int NSClosableWindowMask = 1 << 1; + private static final int NSMiniaturizableWindowMask = 1 << 2; + private static final int NSResizableWindowMask = 1 << 3; + + // Window backing store types + private static final int NSBackingStoreRetained = 0; + private static final int NSBackingStoreNonretained = 1; + private static final int NSBackingStoreBuffered = 2; + + private volatile long surfaceHandle = 0; + private long sscSurfaceHandle = 0; + private boolean isOffscreenInstance = false; + private boolean resizeAnimatorPaused = false; +} diff --git a/src/newt/native/IOSNewtUIWindow.h b/src/newt/native/IOSNewtUIWindow.h new file mode 100644 index 000000000..27ca7f3c7 --- /dev/null +++ b/src/newt/native/IOSNewtUIWindow.h @@ -0,0 +1,152 @@ +/** + * Copyright 2019 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#import <UIKit/UIKit.h> +#import <QuartzCore/QuartzCore.h> +#import <pthread.h> +#import "jni.h" + +#include "NewtCommon.h" + +// #define VERBOSE_ON 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) NSLog(@ __VA_ARGS__) ; fflush(stderr) + // #define DBG_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) +#else + #define DBG_PRINT(...) +#endif + +// #define DBG_LIFECYCLE 1 + +@interface NewtUIView : UIView +{ + jobject javaWindowObject; + + volatile BOOL destroyNotifySent; + volatile int softLockCount; + pthread_mutex_t softLockSync; + + BOOL modsDown[4]; // shift, ctrl, alt/option, win/command +} + +- (id)initWithFrame:(CGRect)frameRect; + +#ifdef DBG_LIFECYCLE +- (void) release; +#endif +- (void) dealloc; + +/* Register or deregister (NULL) the java Window object, + ie, if NULL, no events are send */ +- (void) setJavaWindowObject: (jobject) javaWindowObj; +- (jobject) getJavaWindowObject; + +- (void) setDestroyNotifySent: (BOOL) v; +- (BOOL) getDestroyNotifySent; + +- (BOOL) softLock; +- (BOOL) softUnlock; + +- (void) drawRect:(CGRect)dirtyRect; +- (BOOL) acceptsFirstResponder; +- (BOOL) becomeFirstResponder; +- (BOOL) resignFirstResponder; + +- (void) sendMouseEvent: (UIEvent*) event eventType: (jshort) evType; +- (CGPoint) screenPos2NewtClientWinPos: (CGPoint) p; + +- (void) handleFlagsChanged:(NSUInteger) mods; +- (void) handleFlagsChanged:(int) keyMask keyIndex: (int) keyIdx keyCode: (int) keyCode modifiers: (NSUInteger) mods; +- (void) sendKeyEvent: (UIEvent*) event eventType: (jshort) evType; +- (void) sendKeyEvent: (jshort) keyCode characters: (NSString*) chars modifiers: (NSUInteger)mods eventType: (jshort) evType; + +@end + +@interface NewtUIWindow : UIWindow +{ + BOOL realized; + jboolean withinLiveResize; +@public + BOOL hasPresentationSwitch; + NSUInteger defaultPresentationOptions; + NSUInteger fullscreenPresentationOptions; + BOOL isFullscreenWindow; + int cachedInsets[4]; // l, r, t, b +} + ++ (BOOL) initNatives: (JNIEnv*) env forClass: (jobject) clazz; + +- (id) initWithFrame: (CGRect) contentRect + styleMask: (NSUInteger) windowStyle + backing: (NSUInteger) bufferingType + defer: (BOOL) deferCreation + isFullscreenWindow:(BOOL)isfs; +#ifdef DBG_LIFECYCLE +- (void) release; +#endif +- (void) dealloc; +- (void) setRealized: (BOOL)v; +- (BOOL) isRealized; + +- (void) setAlwaysOn: (BOOL)top bottom:(BOOL)bottom; + +- (void) updateInsets: (JNIEnv*) env jwin: (jobject) javaWin; +- (void) updateSizePosInsets: (JNIEnv*) env jwin: (jobject) javaWin defer: (jboolean)defer; +- (void) attachToParent: (UIWindow*) parent; +- (void) detachFromParent: (UIWindow*) parent; + +- (CGPoint) newtRelClientTLWinPos2AbsTLScreenPos: (CGPoint) p; +- (CGSize) newtClientSize2TLSize: (CGSize) nsz; +- (CGPoint) getLocationOnScreen: (CGPoint) p; + +- (void) focusChanged: (BOOL) gained; + +- (void) flagsChanged: (UIEvent *) theEvent; +- (BOOL) acceptsMouseMovedEvents; +- (BOOL) acceptsFirstResponder; +- (BOOL) becomeFirstResponder; +- (BOOL) resignFirstResponder; +- (BOOL) canBecomeKeyWindow; +- (void) becomeKeyWindow; +- (void) resignKeyWindow; +- (void) windowDidBecomeKey: (NSNotification *) notification; +- (void) windowDidResignKey: (NSNotification *) notification; + +- (void) windowWillStartLiveResize: (NSNotification *) notification; +- (void) windowDidEndLiveResize: (NSNotification *) notification; +- (CGSize) windowWillResize: (UIWindow *)sender toSize:(CGSize)frameSize; +- (void) windowDidResize: (NSNotification*) notification; +- (void) sendResizeEvent; + +- (void) windowDidMove: (NSNotification*) notification; +- (BOOL) windowClosingImpl: (BOOL) force; +- (BOOL) windowShouldClose: (id) sender; +- (void) windowWillClose: (NSNotification*) notification; + +@end diff --git a/src/newt/native/IOSNewtUIWindow.m b/src/newt/native/IOSNewtUIWindow.m new file mode 100644 index 000000000..6c5031efc --- /dev/null +++ b/src/newt/native/IOSNewtUIWindow.m @@ -0,0 +1,783 @@ +/** + * Copyright 2019 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#import "IOSNewtUIWindow.h" +#import "InputEvent.h" +#import "KeyEvent.h" +#import "MouseEvent.h" + +#include <CoreFoundation/CoreFoundation.h> + +#include <math.h> + +#define PRINTF(...) NSLog(@ __VA_ARGS__) + +static jmethodID enqueueMouseEventID = NULL; +static jmethodID enqueueKeyEventID = NULL; +static jmethodID requestFocusID = NULL; + +static jmethodID insetsChangedID = NULL; +static jmethodID sizeChangedID = NULL; +static jmethodID sizeScreenPosInsetsChangedID = NULL; +static jmethodID updatePixelScaleID = NULL; +static jmethodID visibleChangedID = NULL; +static jmethodID screenPositionChangedID = NULL; +static jmethodID focusChangedID = NULL; +static jmethodID windowDestroyNotifyID = NULL; +static jmethodID windowRepaintID = NULL; + +// Need to enqueue all events to EDT, +// since we may operate on AWT-AppKit (Main Thread) +// and direct issuing 'requestFocus()' would deadlock: +// AWT-AppKit +// AWT-EventQueue-0 + +@implementation NewtUIView + +- (id)initWithFrame:(CGRect)frameRect +{ + id res = [super initWithFrame:frameRect]; + javaWindowObject = NULL; + + destroyNotifySent = NO; + softLockCount = 0; + + pthread_mutexattr_t softLockSyncAttr; + pthread_mutexattr_init(&softLockSyncAttr); + pthread_mutexattr_settype(&softLockSyncAttr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&softLockSync, &softLockSyncAttr); // recursive + + modsDown[0] = NO; // shift + modsDown[1] = NO; // ctrl + modsDown[2] = NO; // alt + modsDown[3] = NO; // win + + DBG_PRINT("NewtUIView::create: %p (refcnt %d)\n", res, (int)[res retainCount]); + return res; +} + +#ifdef DBG_LIFECYCLE +- (void) release +{ + DBG_PRINT("NewtUIView::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + [super release]; +} +#endif + +- (void) dealloc +{ + DBG_PRINT("NewtUIView::dealloc.0: %p (refcnt %d), ptrTrackingTag %d\n", self, (int)[self retainCount], (int)ptrTrackingTag); +#ifdef DBG_LIFECYCLE + NSLog(@"%@",[NSThread callStackSymbols]); +#endif + if( 0 < softLockCount ) { + NSLog(@"NewtUIView::dealloc: softLock still hold @ dealloc!\n"); + } + + pthread_mutex_destroy(&softLockSync); + DBG_PRINT("NewtUIView::dealloc.X: %p\n", self); + [super dealloc]; +} + +- (void) setJavaWindowObject: (jobject) javaWindowObj +{ + javaWindowObject = javaWindowObj; +} + +- (jobject) getJavaWindowObject +{ + return javaWindowObject; +} + +- (void) setDestroyNotifySent: (BOOL) v +{ + destroyNotifySent = v; +} + +- (BOOL) getDestroyNotifySent +{ + return destroyNotifySent; +} + +- (BOOL) softLock +{ + // DBG_PRINT("*************** softLock.0: %p\n", (void*)pthread_self()); + int err; + if( 0 != ( err = pthread_mutex_lock(&softLockSync) ) ) { + NSLog(@"NewtUIView::softLock failed: errCode %d - %@", err, [NSThread callStackSymbols]); + return NO; + } + softLockCount++; + // DBG_PRINT("*************** softLock.X: %p\n", (void*)pthread_self()); + return 0 < softLockCount; +} + +- (BOOL) softUnlock +{ + // DBG_PRINT("*************** softUnlock: %p\n", (void*)pthread_self()); + softLockCount--; + int err; + if( 0 != ( err = pthread_mutex_unlock(&softLockSync) ) ) { + softLockCount++; + NSLog(@"NewtUIView::softUnlock failed: Not locked by current thread - errCode %d - %@", err, [NSThread callStackSymbols]); + return NO; + } + return YES; +} + +- (void) drawRect:(CGRect)dirtyRect +{ + DBG_PRINT("*************** dirtyRect: %p %lf/%lf %lfx%lf\n", + javaWindowObject, dirtyRect.origin.x, dirtyRect.origin.y, dirtyRect.size.width, dirtyRect.size.height); + + if(NULL==javaWindowObject) { + DBG_PRINT("drawRect: null javaWindowObject\n"); + return; + } + int shallBeDetached = 0; + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); + if(NULL==env) { + DBG_PRINT("drawRect: null JNIEnv\n"); + return; + } + + CGRect viewFrame = [self frame]; + + (*env)->CallVoidMethod(env, javaWindowObject, windowRepaintID, JNI_TRUE, // defer .. + (int)dirtyRect.origin.x, (int)viewFrame.size.height - (int)dirtyRect.origin.y, + (int)dirtyRect.size.width, (int)dirtyRect.size.height); + + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); +} + +- (BOOL) acceptsFirstResponder +{ + return YES; +} + +- (BOOL) becomeFirstResponder +{ + DBG_PRINT( "*************** View.becomeFirstResponder\n"); + return [super becomeFirstResponder]; +} + +- (BOOL) resignFirstResponder +{ + DBG_PRINT( "*************** View.resignFirstResponder\n"); + return [super resignFirstResponder]; +} + +- (void) sendMouseEvent: (UIEvent*) event eventType: (jshort) evType +{ + if (javaWindowObject == NULL) { + DBG_PRINT("sendMouseEvent: null javaWindowObject\n"); + return; + } + int shallBeDetached = 0; + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); + if(NULL==env) { + DBG_PRINT("sendMouseEvent: null JNIEnv\n"); + return; + } + jint javaMods[] = { 0 } ; + javaMods[0] = 0; // TODO mods2JavaMods([event modifierFlags]); + + // convert to 1-based button number (or use zero if no button is involved) + // TODO: detect mouse button when mouse wheel scrolled + jshort javaButtonNum = 1; + jfloat scrollDeltaY = 0.0f; + /** + switch ([event type]) { + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSLeftMouseDragged: + javaButtonNum = 1; + break; + case NSRightMouseDown: + case NSRightMouseUp: + case NSRightMouseDragged: + javaButtonNum = 3; + break; + case NSOtherMouseDown: + case NSOtherMouseUp: + case NSOtherMouseDragged: + javaButtonNum = 2; + break; + default: + javaButtonNum = 0; + break; + } */ + CGPoint location = CGPointMake(0,0); // TODO [self screenPos2NewtClientWinPos: [UIEvent mouseLocation]]; + + (*env)->CallVoidMethod(env, javaWindowObject, enqueueMouseEventID, JNI_FALSE, + evType, javaMods[0], + (jint) location.x, (jint) location.y, + javaButtonNum, scrollDeltaY); + + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); +} + +- (CGPoint) screenPos2NewtClientWinPos: (CGPoint) p +{ + CGRect viewFrame = [self frame]; + + CGRect r; + r.origin.x = p.x; + r.origin.y = p.y; + r.size.width = 0; + r.size.height = 0; + // CGRect rS = [[self window] convertRectFromScreen: r]; // 10.7 + CGPoint oS = r.origin; // TODO [[self window] convertScreenToBase: r.origin]; + oS.y = viewFrame.size.height - oS.y; // y-flip + return oS; +} + +- (void) handleFlagsChanged:(NSUInteger) mods +{ + // TODO [self handleFlagsChanged: NSShiftKeyMask keyIndex: 0 keyCode: kVK_Shift modifiers: mods]; + // TODO [self handleFlagsChanged: NSControlKeyMask keyIndex: 1 keyCode: kVK_Control modifiers: mods]; + // TODO [self handleFlagsChanged: NSAlternateKeyMask keyIndex: 2 keyCode: kVK_Option modifiers: mods]; + // TODO [self handleFlagsChanged: NSCommandKeyMask keyIndex: 3 keyCode: kVK_Command modifiers: mods]; +} + +- (void) handleFlagsChanged:(int) keyMask keyIndex: (int) keyIdx keyCode: (int) keyCode modifiers: (NSUInteger) mods +{ + if ( NO == modsDown[keyIdx] && 0 != ( mods & keyMask ) ) { + modsDown[keyIdx] = YES; + [self sendKeyEvent: (jshort)keyCode characters: NULL modifiers: mods|keyMask eventType: (jshort)EVENT_KEY_PRESSED]; + } else if ( YES == modsDown[keyIdx] && 0 == ( mods & keyMask ) ) { + modsDown[keyIdx] = NO; + [self sendKeyEvent: (jshort)keyCode characters: NULL modifiers: mods|keyMask eventType: (jshort)EVENT_KEY_RELEASED]; + } +} + +- (void) sendKeyEvent: (UIEvent*) event eventType: (jshort) evType +{ + jshort keyCode = 0; // TODO (jshort) [event keyCode]; + NSString* chars = NULL; // TODO [event charactersIgnoringModifiers]; + NSUInteger mods = 0; // TODO [event modifierFlags]; + [self sendKeyEvent: keyCode characters: chars modifiers: mods eventType: evType]; +} + +- (void) sendKeyEvent: (jshort) keyCode characters: (NSString*) chars modifiers: (NSUInteger)mods eventType: (jshort) evType +{ + if (javaWindowObject == NULL) { + DBG_PRINT("sendKeyEvent: null javaWindowObject\n"); + return; + } + int shallBeDetached = 0; + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); + if(NULL==env) { + DBG_PRINT("sendKeyEvent: null JNIEnv\n"); + return; + } + + int i; + int len = NULL != chars ? [chars length] : 0; + jint javaMods = 0; // TODO mods2JavaMods(mods); + + if(len > 0) { + // printable chars + for (i = 0; i < len; i++) { + // Note: the key code in the UIEvent does not map to anything we can use + UniChar keyChar = (UniChar) [chars characterAtIndex: i]; + UniChar keySymChar = 0; // TODO CKCH_CharForKeyCode(keyCode); + + DBG_PRINT("sendKeyEvent: %d/%d code 0x%X, char 0x%X, mods 0x%X/0x%X -> keySymChar 0x%X\n", i, len, (int)keyCode, (int)keyChar, + (int)mods, (int)javaMods, (int)keySymChar); + + (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID, JNI_FALSE, + evType, javaMods, keyCode, (jchar)keyChar, (jchar)keySymChar); + } + } else { + // non-printable chars + jchar keyChar = (jchar) 0; + + DBG_PRINT("sendKeyEvent: code 0x%X\n", (int)keyCode); + + (*env)->CallVoidMethod(env, javaWindowObject, enqueueKeyEventID, JNI_FALSE, + evType, javaMods, keyCode, keyChar, keyChar); + } + + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); +} + +@end + +@implementation NewtUIWindow + ++ (BOOL) initNatives: (JNIEnv*) env forClass: (jclass) clazz +{ + enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZSIIISF)V"); + enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZSISCC)V"); + sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(ZIIZ)V"); + updatePixelScaleID = (*env)->GetMethodID(env, clazz, "updatePixelScale", "(ZFF)V"); + visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(ZZ)V"); + insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(ZIIII)V"); + sizeScreenPosInsetsChangedID = (*env)->GetMethodID(env, clazz, "sizeScreenPosInsetsChanged", "(ZIIIIIIIIZZ)V"); + screenPositionChangedID = (*env)->GetMethodID(env, clazz, "screenPositionChanged", "(ZII)V"); + focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(ZZ)V"); + windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "(Z)Z"); + windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(ZIIII)V"); + requestFocusID = (*env)->GetMethodID(env, clazz, "requestFocus", "(Z)V"); + if (enqueueMouseEventID && enqueueKeyEventID && sizeChangedID && updatePixelScaleID && visibleChangedID && + insetsChangedID && sizeScreenPosInsetsChangedID && + screenPositionChangedID && focusChangedID && windowDestroyNotifyID && requestFocusID && windowRepaintID) + { + // TODO CKCH_CreateDictionaries(); + return YES; + } + return NO; +} + +- (id) initWithFrame: (CGRect) contentRect + styleMask: (NSUInteger) windowStyle + backing: (NSUInteger) bufferingType + defer: (BOOL) deferCreation + isFullscreenWindow:(BOOL)isfs +{ + /** + id res = [super initWithContentRect: contentRect + styleMask: windowStyle + backing: bufferingType + defer: deferCreation]; + */ + id res = [super initWithFrame: contentRect]; + // OSX 10.6 + /** TODO + if ( [NSApp respondsToSelector:@selector(currentSystemPresentationOptions)] && + [NSApp respondsToSelector:@selector(setPresentationOptions:)] ) { + hasPresentationSwitch = YES; + defaultPresentationOptions = [NSApp currentSystemPresentationOptions]; + fullscreenPresentationOptions = + // NSApplicationPresentationDefault| + // NSApplicationPresentationAutoHideDock| + NSApplicationPresentationHideDock| + // NSApplicationPresentationAutoHideMenuBar| + NSApplicationPresentationHideMenuBar| + NSApplicationPresentationDisableAppleMenu| + // NSApplicationPresentationDisableProcessSwitching| + // NSApplicationPresentationDisableSessionTermination| + NSApplicationPresentationDisableHideApplication| + // NSApplicationPresentationDisableMenuBarTransparency| + // NSApplicationPresentationFullScreen| // OSX 10.7 + 0 ; + } else { + */ + hasPresentationSwitch = NO; + defaultPresentationOptions = 0; + fullscreenPresentationOptions = 0; + // } + + isFullscreenWindow = NO; // TODO isfs; + // Why is this necessary? Without it we don't get any of the + // delegate methods like resizing and window movement. + // TODO [self setDelegate: self]; + + cachedInsets[0] = 0; // l + cachedInsets[1] = 0; // r + cachedInsets[2] = 0; // t + cachedInsets[3] = 0; // b + + realized = YES; + withinLiveResize = JNI_FALSE; + DBG_PRINT("NewtWindow::create: %p, realized %d, hasPresentationSwitch %d[defaultOptions 0x%X, fullscreenOptions 0x%X], (refcnt %d)\n", + res, realized, (int)hasPresentationSwitch, (int)defaultPresentationOptions, (int)fullscreenPresentationOptions, (int)[res retainCount]); + return res; +} + +#ifdef DBG_LIFECYCLE +- (void) release +{ + DBG_PRINT("NewtWindow::release.0: %p (refcnt %d)\n", self, (int)[self retainCount]); + // NSLog(@"%@",[NSThread callStackSymbols]); + [super release]; +} +#endif + +- (void) dealloc +{ + DBG_PRINT("NewtWindow::dealloc.0: %p (refcnt %d)\n", self, (int)[self retainCount]); +#ifdef DBG_LIFECYCLE + NSLog(@"%@",[NSThread callStackSymbols]); +#endif + + /** + NewtUIView* mView = (NewtUIView *)self; // TODO [self contentView]; + if( NULL != mView ) { + [mView release]; + } + */ + [super dealloc]; + DBG_PRINT("NewtWindow::dealloc.X: %p\n", self); +} + +- (void) setRealized: (BOOL)v +{ + realized = v; +} + +- (BOOL) isRealized +{ + return realized; +} + +- (void) setAlwaysOn: (BOOL)top bottom:(BOOL)bottom +{ + /** + if( top ) { + DBG_PRINT( "*************** setAlwaysOn -> top\n"); + [self setLevel: kCGMaximumWindowLevel]; + } else if ( bottom ) { + DBG_PRINT( "*************** setAlwaysOn -> bottom\n"); + [self setLevel: kCGDesktopIconWindowLevel]; // w/ input + } else { + DBG_PRINT( "*************** setAlwaysOn -> normal\n"); + [self setLevel:NSNormalWindowLevel]; + } */ +} + +- (void) updateInsets: (JNIEnv*) env jwin: (jobject) javaWin +{ + /** + CGRect frameRect = [self frame]; + CGRect contentRect = [self contentRectForFrameRect: frameRect]; + + // note: this is a simplistic implementation which doesn't take + // into account DPI and scaling factor + CGFloat l = contentRect.origin.x - frameRect.origin.x; + cachedInsets[0] = (int)l; // l + cachedInsets[1] = (int)(frameRect.size.width - (contentRect.size.width + l)); // r + cachedInsets[2] = (jint)(frameRect.size.height - contentRect.size.height); // t + cachedInsets[3] = (jint)(contentRect.origin.y - frameRect.origin.y); // b + */ + UIEdgeInsets uiInsets = [self safeAreaInsets]; + cachedInsets[0] = uiInsets.left; + cachedInsets[1] = uiInsets.right; + cachedInsets[2] = uiInsets.top; + cachedInsets[3] = uiInsets.bottom; + DBG_PRINT( "updateInsets: [ l %d, r %d, t %d, b %d ]\n", cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3]); + + if( NULL != env && NULL != javaWin ) { + (*env)->CallVoidMethod(env, javaWin, insetsChangedID, JNI_FALSE, cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3]); + } +} + +- (void) updateSizePosInsets: (JNIEnv*) env jwin: (jobject) javaWin defer: (jboolean)defer +{ + // update insets on every window resize for lack of better hook place + [self updateInsets: NULL jwin:NULL]; + + CGRect frameRect = [self frame]; + + UIScreen* screen = [self screen]; + CGPoint pS = [self convertPoint: frameRect.origin toCoordinateSpace: screen.fixedCoordinateSpace]; + + DBG_PRINT( "updateSize: [ w %d, h %d ], liveResize %d\n", (jint) frameRect.size.width, (jint) frameRect.size.height, (jint)withinLiveResize); + DBG_PRINT( "updatePos: [ x %d, y %d ]\n", (jint) pS.x, (jint) pS.y); + + if( NULL != env && NULL != javaWin ) { + (*env)->CallVoidMethod(env, javaWin, sizeScreenPosInsetsChangedID, defer, + (jint) pS.x, (jint) pS.y, + (jint) frameRect.size.width, (jint) frameRect.size.height, + cachedInsets[0], cachedInsets[1], cachedInsets[2], cachedInsets[3], + JNI_FALSE, // force + withinLiveResize + ); + } +} + + +- (void) attachToParent: (UIWindow*) parent +{ + /** TODO + DBG_PRINT( "attachToParent.1\n"); + [parent addChildWindow: self ordered: UIWindowAbove]; + DBG_PRINT( "attachToParent.2\n"); + [self setParentWindow: parent]; + DBG_PRINT( "attachToParent.X\n"); + */ +} + +- (void) detachFromParent: (UIWindow*) parent +{ + /** TODO + DBG_PRINT( "detachFromParent.1\n"); + [self setParentWindow: nil]; + if(NULL != parent) { + DBG_PRINT( "detachFromParent.2\n"); + [parent removeChildWindow: self]; + } + DBG_PRINT( "detachFromParent.X\n"); + */ +} + +/** + * p rel client window position w/ top-left origin + * returns: abs screen position w/ bottom-left origin + */ +- (CGPoint) newtRelClientTLWinPos2AbsTLScreenPos: (CGPoint) p +{ + return [self getLocationOnScreen: p]; +} + +- (CGSize) newtClientSize2TLSize: (CGSize) nsz +{ + CGSize topSZ = { nsz.width, nsz.height + cachedInsets[2] + cachedInsets[3] }; // height + insets.top + insets.bottom + return topSZ; +} + +/** + * p rel client window position w/ top-left origin + * returns: location in 0/0 top-left space. + */ +- (CGPoint) getLocationOnScreen: (CGPoint) p +{ + UIScreen* screen = [self screen]; + CGPoint pS = [self convertPoint: p toCoordinateSpace: screen.fixedCoordinateSpace]; + +#ifdef VERBOSE_ON + CGRect winFrame = [self frame]; + DBG_PRINT( "getLocationOnScreen: point-in[%d/%d], winFrame[%d/%d %dx%d] -> %d/%d\n", + (int)p.x, (int)p.y, + (int)winFrame.origin.x, (int)winFrame.origin.y, (int)winFrame.size.width, (int)winFrame.size.height, + (int)pS.x, (int)pS.y); +#endif + + return pS; +} + +- (void) focusChanged: (BOOL) gained +{ + DBG_PRINT( "focusChanged: gained %d\n", gained); + NewtUIView* newtView = (NewtUIView *) self; // TODO [self contentView]; + jobject javaWindowObject = [newtView getJavaWindowObject]; + if (javaWindowObject == NULL) { + DBG_PRINT("focusChanged: null javaWindowObject\n"); + return; + } + int shallBeDetached = 0; + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); + if(NULL==env) { + DBG_PRINT("focusChanged: null JNIEnv\n"); + return; + } + + (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_FALSE, (gained == YES) ? JNI_TRUE : JNI_FALSE); + + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); +} + +- (void) flagsChanged:(UIEvent *) theEvent +{ + NSUInteger mods = [theEvent modifierFlags]; + NewtUIView* newtView = (NewtUIView *) [self contentView]; + if( [newtView isKindOfClass:[NewtUIView class]] ) { + [newtView handleFlagsChanged: mods]; + } +} + +- (BOOL) acceptsMouseMovedEvents +{ + return YES; +} + +- (BOOL) acceptsFirstResponder +{ + return YES; +} + +- (BOOL) becomeFirstResponder +{ + DBG_PRINT( "*************** Win.becomeFirstResponder\n"); + return [super becomeFirstResponder]; +} + +- (BOOL) resignFirstResponder +{ + DBG_PRINT( "*************** Win.resignFirstResponder\n"); + return [super resignFirstResponder]; +} + +- (BOOL) canBecomeKeyWindow +{ + // Even if the window is borderless, we still want it to be able + // to become the key window to receive keyboard events + return YES; +} + +- (void) becomeKeyWindow +{ + DBG_PRINT( "*************** becomeKeyWindow\n"); + [super becomeKeyWindow]; +} + +- (void) resignKeyWindow +{ + DBG_PRINT( "*************** resignKeyWindow: isFullscreen %d\n", (int)isFullscreenWindow); + if(!isFullscreenWindow) { + [super resignKeyWindow]; + } +} + +- (void) windowDidResignKey: (NSNotification *) notification +{ + DBG_PRINT( "*************** windowDidResignKey\n"); + // Implicit mouse exit by OS X + [self focusChanged: NO]; +} + +- (void) windowWillStartLiveResize: (NSNotification *) notification +{ + DBG_PRINT( "*************** windowWillStartLiveResize\n"); + withinLiveResize = JNI_TRUE; +} +- (void) windowDidEndLiveResize: (NSNotification *) notification +{ + DBG_PRINT( "*************** windowDidEndLiveResize\n"); + withinLiveResize = JNI_FALSE; + [self sendResizeEvent]; +} +- (CGSize) windowWillResize: (UIWindow *)sender toSize:(CGSize)frameSize +{ + DBG_PRINT( "*************** windowWillResize %lfx%lf\n", frameSize.width, frameSize.height); + return frameSize; +} +- (void)windowDidResize: (NSNotification*) notification +{ + DBG_PRINT( "*************** windowDidResize\n"); + [self sendResizeEvent]; +} + +- (void) sendResizeEvent +{ + jobject javaWindowObject = NULL; + int shallBeDetached = 0; + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); + + if( NULL == env ) { + DBG_PRINT("windowDidResize: null JNIEnv\n"); + return; + } + NewtUIView* newtView = (NewtUIView *) [self contentView]; + if( [newtView isKindOfClass:[NewtUIView class]] ) { + javaWindowObject = [newtView getJavaWindowObject]; + } + if( NULL != javaWindowObject ) { + [self updateSizePosInsets: env jwin: javaWindowObject defer:JNI_TRUE]; + } + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); +} + +- (void)windowDidMove: (NSNotification*) notification +{ + NewtUIView* newtView = (NewtUIView *) [self contentView]; + if( ! [newtView isKindOfClass:[NewtUIView class]] ) { + return; + } + jobject javaWindowObject = [newtView getJavaWindowObject]; + if (javaWindowObject == NULL) { + DBG_PRINT("windowDidMove: null javaWindowObject\n"); + return; + } + int shallBeDetached = 0; + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); + if(NULL==env) { + DBG_PRINT("windowDidMove: null JNIEnv\n"); + return; + } + + CGPoint p0 = { 0, 0 }; + p0 = [self getLocationOnScreen: p0]; + DBG_PRINT( "windowDidMove: [ x %d, y %d ]\n", (jint) p0.x, (jint) p0.y); + (*env)->CallVoidMethod(env, javaWindowObject, screenPositionChangedID, JNI_TRUE, (jint) p0.x, (jint) p0.y); + + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); +} + +- (BOOL)windowShouldClose: (id) sender +{ + return [self windowClosingImpl: NO]; +} + +- (void)windowWillClose: (NSNotification*) notification +{ + [self windowClosingImpl: YES]; +} + +- (BOOL) windowClosingImpl: (BOOL) force +{ + jboolean closed = JNI_FALSE; + + NewtUIView* newtView = (NewtUIView *) [self contentView]; + if( ! [newtView isKindOfClass:[NewtUIView class]] ) { + return NO; + } + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + [newtView cursorHide: NO enter: -1]; + + if( false == [newtView getDestroyNotifySent] ) { + jobject javaWindowObject = [newtView getJavaWindowObject]; + DBG_PRINT( "*************** windowWillClose.0: %p\n", (void *)(intptr_t)javaWindowObject); + if (javaWindowObject == NULL) { + DBG_PRINT("windowWillClose: null javaWindowObject\n"); + [pool release]; + return NO; + } + int shallBeDetached = 0; + JNIEnv* env = NewtCommon_GetJNIEnv(1 /* asDaemon */, &shallBeDetached); + if(NULL==env) { + DBG_PRINT("windowWillClose: null JNIEnv\n"); + [pool release]; + return NO; + } + [newtView setDestroyNotifySent: true]; // earmark assumption of being closed + closed = (*env)->CallBooleanMethod(env, javaWindowObject, windowDestroyNotifyID, force ? JNI_TRUE : JNI_FALSE); + if(!force && !closed) { + // not closed on java side, not force -> clear flag + [newtView setDestroyNotifySent: false]; + } + + // detaching thread not required - daemon + // NewtCommon_ReleaseJNIEnv(shallBeDetached); + DBG_PRINT( "*************** windowWillClose.X: %p, closed %d\n", (void *)(intptr_t)javaWindowObject, (int)closed); + } else { + DBG_PRINT( "*************** windowWillClose (skip)\n"); + } + [pool release]; + return JNI_TRUE == closed ? YES : NO ; +} + +@end + diff --git a/src/newt/native/IOSUIWindow.m b/src/newt/native/IOSUIWindow.m new file mode 100644 index 000000000..780c2344f --- /dev/null +++ b/src/newt/native/IOSUIWindow.m @@ -0,0 +1,1161 @@ +/** + * Copyright 2019 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#import <inttypes.h> + +#import "jogamp_newt_driver_ios_WindowDriver.h" +#import "IOSNewtUIWindow.h" + +#import "MouseEvent.h" +#import "KeyEvent.h" +#import "ScreenMode.h" + +#import <stdio.h> + +#ifdef DBG_PERF + #include "timespec.h" +#endif + +static const char * const ClazzNamePoint = "com/jogamp/nativewindow/util/Point"; +static const char * const ClazzAnyCstrName = "<init>"; +static const char * const ClazzNamePointCstrSignature = "(II)V"; +static jclass pointClz = NULL; +static jmethodID pointCstr = NULL; + +static NSString* jstringToNSString(JNIEnv* env, jstring jstr) +{ + const jchar* jstrChars = (*env)->GetStringChars(env, jstr, NULL); + NSString* str = [[NSString alloc] initWithCharacters: jstrChars length: (*env)->GetStringLength(env, jstr)]; + (*env)->ReleaseStringChars(env, jstr, jstrChars); + return str; +} + +static void setWindowClientTopLeftPoint(NewtUIWindow* mWin, jint x, jint y, BOOL doDisplay) { + DBG_PRINT( "setWindowClientTopLeftPoint.0 - window: %p %d/%d, display %d\n", mWin, (int)x, (int)y, (int)doDisplay); + CGPoint pS = CGPointMake(x, y); + CGRect rect = [mWin frame]; + rect.origin = pS; + + [mWin setFrame: rect]; + DBG_PRINT( "setWindowClientTopLeftPoint.X: %d/%d\n", (int)pS.x, (int)pS.y); + + if( doDisplay ) { + // TODO UIView* mView = [mWin contentView]; + // TODO [mWin invalidateCursorRectsForView: mView]; + } +} + +static void setWindowClientTopLeftPointAndSize(NewtUIWindow* mWin, jint x, jint y, jint width, jint height, BOOL doDisplay) { + DBG_PRINT( "setWindowClientTopLeftPointAndSize.0 - window: %p %d/%d %dx%d, display %d\n", mWin, (int)x, (int)y, (int)width, (int)height, (int)doDisplay); + CGRect rect = CGRectMake(x, y, width, height); + DBG_PRINT( "setWindowClientTopLeftPointAndSize.1: %d/%d %dx%d\n", (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height); + + // TODO [mWin setFrame: rect display:doDisplay]; + [mWin setFrame: rect]; + DBG_PRINT( "setWindowClientTopLeftPointAndSize.X: %d/%d %dx%d\n", (int)rect.origin.x, (int)rect.origin.y, (int)rect.size.width, (int)rect.size.height); + + // -> display:YES + // if( doDisplay ) { + // UIView* mView = [mWin contentView]; + // [mWin invalidateCursorRectsForView: mView]; + // } +} + +#ifdef VERBOSE_ON +static int getRetainCount(NSObject * obj) { + return ( NULL == obj ) ? -1 : (int)([obj retainCount]) ; +} +#endif + +static void setJavaWindowObject(JNIEnv *env, jobject newJavaWindowObject, NewtUIView *view, BOOL enable) { + DBG_PRINT( "setJavaWindowObject.0: View %p\n", view); + if( !enable) { + jobject globJavaWindowObject = [view getJavaWindowObject]; + if( NULL != globJavaWindowObject ) { + DBG_PRINT( "setJavaWindowObject.1: View %p - Clear old javaWindowObject %p\n", view, globJavaWindowObject); + (*env)->DeleteGlobalRef(env, globJavaWindowObject); + [view setJavaWindowObject: NULL]; + } + } else if( NULL != newJavaWindowObject ) { + DBG_PRINT( "setJavaWindowObject.2: View %p - Set new javaWindowObject %p\n", view, newJavaWindowObject); + jobject globJavaWindowObject = (*env)->NewGlobalRef(env, newJavaWindowObject); + [view setJavaWindowObject: globJavaWindowObject]; + } + DBG_PRINT( "setJavaWindowObject.X: View %p\n", view); +} + +static void changeContentView(JNIEnv *env, jobject javaWindowObject, UIView *pview, NewtUIWindow *win, NewtUIView *newView, BOOL setJavaWindow) { + UIView* oldUIView = NULL; // TODO [win contentView]; + NewtUIView* oldNewtUIView = NULL; +#ifdef VERBOSE_ON + int dbgIdx = 1; +#endif + + if( [oldUIView isKindOfClass:[NewtUIView class]] ) { + oldNewtUIView = (NewtUIView *) oldUIView; + } + + DBG_PRINT( "changeContentView.%d win %p, view (%p,%d (%d) -> %p,%d), parent view %p\n", + dbgIdx++, win, oldUIView, getRetainCount(oldUIView), NULL!=oldNewtUIView, newView, getRetainCount(newView), pview); + + if( NULL!=oldUIView ) { +NS_DURING + // Available >= 10.5 - Makes the menubar disapear + BOOL iifs = NO; // TODO [oldUIView isInFullScreenMode]; + if( iifs ) { + // TODO [oldUIView exitFullScreenModeWithOptions: NULL]; + } +NS_HANDLER +NS_ENDHANDLER + DBG_PRINT( "changeContentView.%d win %p, view (%p,%d (%d) -> %p,%d)\n", + dbgIdx++, win, oldUIView, getRetainCount(oldUIView), NULL!=oldNewtUIView, newView, getRetainCount(newView)); + + if( NULL != oldNewtUIView ) { + [oldNewtUIView setDestroyNotifySent: false]; + setJavaWindowObject(env, NULL, oldNewtUIView, NO); + } + // TODO [oldUIView removeFromSuperviewWithoutNeedingDisplay]; + } + DBG_PRINT( "changeContentView.%d win %p, view (%p,%d -> %p,%d), isHidden %d, isHiddenOrHasHiddenAncestor: %d\n", + dbgIdx++, win, oldUIView, getRetainCount(oldUIView), newView, getRetainCount(newView), [newView isHidden], [newView isHiddenOrHasHiddenAncestor]); + + if( NULL!=newView ) { + [newView setDestroyNotifySent: false]; + if( setJavaWindow ) { + setJavaWindowObject(env, javaWindowObject, newView, YES); + } + + DBG_PRINT( "changeContentView.%d win %p, view (%p,%d -> %p,%d)\n", + dbgIdx++, win, oldUIView, getRetainCount(oldUIView), newView, getRetainCount(newView)); + + if(NULL!=pview) { + // TODO [pview addSubview: newView positioned: UIWindowAbove relativeTo: nil]; + } + } + DBG_PRINT( "changeContentView.%d win %p, view (%p,%d -> %p,%d), isHidden %d, isHiddenOrHasHiddenAncestor: %d\n", + dbgIdx++, win, oldUIView, getRetainCount(oldUIView), newView, getRetainCount(newView), [newView isHidden], [newView isHiddenOrHasHiddenAncestor]); + + // TODO [win setContentView: newView]; + + DBG_PRINT( "changeContentView.%d win %p, view (%p,%d -> %p,%d), isHidden %d, isHiddenOrHasHiddenAncestor: %d\n", + dbgIdx++, win, oldUIView, getRetainCount(oldUIView), newView, getRetainCount(newView), [newView isHidden], [newView isHiddenOrHasHiddenAncestor]); + + // make sure the insets are updated in the java object + [win updateInsets: env jwin:javaWindowObject]; + + DBG_PRINT( "changeContentView.X win %p, view (%p,%d -> %p,%d)\n", + win, oldUIView, getRetainCount(oldUIView), newView, getRetainCount(newView)); +} + +/* + * Class: jogamp_newt_driver_ios_DisplayDriver + * Method: initIDs + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_ios_DisplayDriver_initUIApplication0 + (JNIEnv *env, jclass clazz) +{ + static int initialized = 0; + + if(initialized) return JNI_TRUE; + initialized = 1; + + NewtCommon_init(env); + + // Initialize the shared NSApplication instance + [UIApplication sharedApplication]; + + // Need this when debugging, as it is necessary to attach gdb to + // the running java process -- "gdb java" doesn't work + // printf("Going to sleep for 10 seconds\n"); + // sleep(10); + + return (jboolean) JNI_TRUE; +} + +static void NewtScreen_dump() { +#ifdef VERBOSE_ON + NSArray *screens = [UIScreen screens]; + int i; + for(i=0; i<[screens count]; i++) { + UIScreen * screen = (UIScreen *) [screens objectAtIndex: i]; + CGRect screenFrame = [screen frame]; + CGRect screenVisibleFrame = [screen visibleFrame]; + CGFloat pixelScale = 1.0; // default + pixelScale = [screen scale]; // HiDPI scaling + UIWindowDepth depth = [screen depth]; // an (int) value! + DBG_PRINT( "UIScreen #%d (%p): Frame %lf/%lf %lfx%lf (vis %lf/%lf %lfx%lf), scale %lf, depth %d\n", + i, screen, + screenFrame.origin.x, screenFrame.origin.y, screenFrame.size.width, screenFrame.size.height, + screenVisibleFrame.origin.x, screenVisibleFrame.origin.y, screenVisibleFrame.size.width, screenVisibleFrame.size.height, + pixelScale, depth); + } +#endif +} + +// Duplicate each Mode by all possible rotations (4): +// For each real-mode: [mode, 0], [mode, 90], [mode, 180], [mode, 270] +#define ROTMODES_PER_REALMODE 1 + +/* + * Class: jogamp_newt_driver_ios_ScreenDriver + * Method: getMonitorDeviceIds0 + * Signature: ()I + */ +JNIEXPORT jintArray JNICALL Java_jogamp_newt_driver_ios_ScreenDriver_getMonitorDeviceIds0 + (JNIEnv *env, jobject obj) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSArray *screens = [UIScreen screens]; + int count = [screens count]; + int32_t displayIDs[count]; + int i; + for(i=0; i<count; i++) { + // UIScreen * screen = (UIScreen *) [screens objectAtIndex: i]; + displayIDs[i] = i; // TODO no unique screen name? + } + jintArray properties = (*env)->NewIntArray(env, count); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", count); + } + (*env)->SetIntArrayRegion(env, properties, 0, count, displayIDs); + [pool release]; + return properties; +} + +/* + * Class: jogamp_newt_driver_ios_ScreenDriver + * Method: getMonitorProps0 + * Signature: (I)[I + */ +JNIEXPORT jintArray JNICALL Java_jogamp_newt_driver_ios_ScreenDriver_getMonitorProps0 + (JNIEnv *env, jobject obj, jint crt_id) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + +#ifdef DBG_PERF + struct timespec t0, t1, td; + long td_ms; + timespec_now(&t0); +#endif + +#ifdef DBG_PERF + timespec_now(&t1); timespec_subtract(&td, &t1, &t0); td_ms = timespec_milliseconds(&td); + fprintf(stderr, "MacScreen_getMonitorProps0.1: %ld ms\n", td_ms); fflush(NULL); +#endif + NSArray *screens = [UIScreen screens]; + int count = [screens count]; + UIScreen * screen = (UIScreen *) [screens objectAtIndex: crt_id]; + if( NULL == screen ) { + [pool release]; + return NULL; + } + BOOL isPrimary = 0 == crt_id; +#ifdef DBG_PERF + timespec_now(&t1); timespec_subtract(&td, &t1, &t0); td_ms = timespec_milliseconds(&td); + fprintf(stderr, "MacScreen_getMonitorProps0.2: %ld ms\n", td_ms); fflush(NULL); +#endif + + UIScreenMode * screenMode = [screen currentMode]; + CGSize sizeMM = CGSizeMake(161.0, 228.0); // TODO ??? +#ifdef DBG_PERF + timespec_now(&t1); timespec_subtract(&td, &t1, &t0); td_ms = timespec_milliseconds(&td); + fprintf(stderr, "MacScreen_getMonitorProps0.3: %ld ms\n", td_ms); fflush(NULL); +#endif + + CGRect dBounds = [screen bounds]; +#ifdef VERBOSE_ON + DBG_PRINT( "getMonitorProps0: crt_id 0x%X (prim %d), top-left displayBounds[%d/%d %dx%d]\n", + (int)crt_id, isPrimary, + (int)dBounds.origin.x, (int)dBounds.origin.y, (int)dBounds.size.width, (int)dBounds.size.height); +#endif + + jsize propCount = MIN_MONITOR_DEVICE_PROPERTIES - 1 - NUM_MONITOR_MODE_PROPERTIES; + jint prop[ propCount ]; + int offset = 0; + prop[offset++] = propCount; + prop[offset++] = crt_id; + prop[offset++] = 0; // isClone + prop[offset++] = isPrimary ? 1 : 0; // isPrimary + prop[offset++] = (jint) sizeMM.width; + prop[offset++] = (jint) sizeMM.height; + prop[offset++] = (jint) dBounds.origin.x; // rotated viewport x (pixel units, will be fixed in java code) + prop[offset++] = (jint) dBounds.origin.y; // rotated viewport y (pixel units, will be fixed in java code) + prop[offset++] = (jint) dBounds.size.width; // rotated viewport width (pixel units, will be fixed in java code) + prop[offset++] = (jint) dBounds.size.height; // rotated viewport height (pixel units, will be fixed in java code) + prop[offset++] = (jint) dBounds.origin.x; // rotated viewport x (window units, will be fixed in java code) + prop[offset++] = (jint) dBounds.origin.y; // rotated viewport y (window units, will be fixed in java code) + prop[offset++] = (jint) dBounds.size.width; // rotated viewport width (window units, will be fixed in java code) + prop[offset++] = (jint) dBounds.size.height; // rotated viewport height (window units, will be fixed in java code) + + jintArray properties = (*env)->NewIntArray(env, propCount); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", propCount); + } + (*env)->SetIntArrayRegion(env, properties, 0, propCount, prop); + + [pool release]; + + return properties; +} + +/* + * Class: jogamp_newt_driver_ios_ScreenDriver + * Method: getMonitorMode0 + * Signature: (II)[I + */ +JNIEXPORT jintArray JNICALL Java_jogamp_newt_driver_ios_ScreenDriver_getMonitorMode0 + (JNIEnv *env, jobject obj, jint crt_id, jint mode_idx) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSArray<UIScreen*> *screens = [UIScreen screens]; + int count = [screens count]; + UIScreen * screen = (UIScreen *) [screens objectAtIndex: crt_id]; + if( NULL == screen ) { + [pool release]; + return NULL; + } + CGFloat pixelScale = 1.0; // default +NS_DURING + // Available >= 10.7 + pixelScale = [screen scale]; // HiDPI scaling +NS_HANDLER +NS_ENDHANDLER + + NSArray<UIScreenMode*> *availableModes = [screen availableModes]; + int numberOfAvailableModes = [availableModes count]; + CFIndex numberOfAvailableModesRots = ROTMODES_PER_REALMODE * numberOfAvailableModes; + int currentCCWRot = 0; + jint ccwRot = 0; + int nativeId = 0; + UIScreenMode * mode = NULL; + +#ifdef VERBOSE_ON + if(0 >= mode_idx) { + // only for current mode (-1) and first mode (scanning) + DBG_PRINT( "getScreenMode0: crtID 0x%X (s %p, pscale %lf), mode %d, avail: %d/%d, current rot %d ccw\n", + (uint32_t)displayID, screen, pixelScale, (int)mode_idx, (int)numberOfAvailableModes, (int)numberOfAvailableModesRots, currentCCWRot); + } +#endif + + if(numberOfAvailableModesRots<=mode_idx) { + // n/a - end of modes + DBG_PRINT( "getScreenMode0: end of modes: mode %d, avail: %d/%d\n", + (int)mode_idx, (int)numberOfAvailableModes, (int)numberOfAvailableModesRots); + [pool release]; + return NULL; + } else if(-1 < mode_idx) { + // only at initialization time, where index >= 0 + nativeId = mode_idx / ROTMODES_PER_REALMODE; + ccwRot = mode_idx % ROTMODES_PER_REALMODE * 90; + mode = (UIScreenMode*) [availableModes objectAtIndex: nativeId]; + } else { + // current mode + mode = [screen currentMode]; + ccwRot = 0; + nativeId = 0; + } + // mode = CGDisplayModeRetain(mode); // 10.6 on CGDisplayModeRef + + CGSize mSize = [mode size]; + int mWidth = (int)mSize.width; + int mHeight = (int)mSize.height; + if( -1 == mode_idx ) { + mWidth *= (int)pixelScale; // accomodate HiDPI + mHeight *= (int)pixelScale; // accomodate HiDPI + } + + // swap width and height, since OSX reflects rotated dimension, we don't + if ( 90 == currentCCWRot || 270 == currentCCWRot ) { + int tempWidth = mWidth; + mWidth = mHeight; + mHeight = tempWidth; + } + + jint prop[ NUM_MONITOR_MODE_PROPERTIES_ALL ]; + int propIndex = 0; + + int refreshRate = 60; // TODO + int fRefreshRate = ( 0 < refreshRate ) ? refreshRate : 60; // default .. (experienced on OSX 10.6.8) + prop[propIndex++] = NUM_MONITOR_MODE_PROPERTIES_ALL; + prop[propIndex++] = mWidth; + prop[propIndex++] = mHeight; + prop[propIndex++] = 32; // TODO CGDDGetModeBitsPerPixel(mode); + prop[propIndex++] = fRefreshRate * 100; // Hz*100 + prop[propIndex++] = 0; // flags + prop[propIndex++] = nativeId; + prop[propIndex++] = ccwRot; + + DBG_PRINT( "getScreenMode0: Mode %d/%d (%d): %dx%d, %d bpp, %d / %d Hz, nativeId %d, rot %d ccw\n", + (int)mode_idx, (int)numberOfAvailableModesRots, (int)numberOfAvailableModes, + (int)prop[1], (int)prop[2], (int)prop[3], + (int)prop[4], refreshRate, (int)prop[6], (int)prop[7]); + + jintArray properties = (*env)->NewIntArray(env, NUM_MONITOR_MODE_PROPERTIES_ALL); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", NUM_MONITOR_MODE_PROPERTIES_ALL); + } + (*env)->SetIntArrayRegion(env, properties, 0, NUM_MONITOR_MODE_PROPERTIES_ALL, prop); + + // CGDisplayModeRelease(mode); // 10.6 on CGDisplayModeRef + [pool release]; + + return properties; +} + +/* + * Class: jogamp_newt_driver_ios_ScreenDriver + * Method: setMonitorMode0 + * Signature: (III)Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_ios_ScreenDriver_setMonitorMode0 + (JNIEnv *env, jobject object, jint crt_id, jint nativeId, jint ccwRot) +{ + return false; +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: initIDs + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_ios_WindowDriver_initIDs0 + (JNIEnv *env, jclass clazz) +{ + static int initialized = 0; + + if(initialized) return JNI_TRUE; + initialized = 1; + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NewtScreen_dump(); + + jclass c; + c = (*env)->FindClass(env, ClazzNamePoint); + if(NULL==c) { + NewtCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_ios_WindowDriver_initIDs0: can't find %s", ClazzNamePoint); + } + pointClz = (jclass)(*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + if(NULL==pointClz) { + NewtCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_ios_WindowDriver_initIDs0: can't use %s", ClazzNamePoint); + } + pointCstr = (*env)->GetMethodID(env, pointClz, ClazzAnyCstrName, ClazzNamePointCstrSignature); + if(NULL==pointCstr) { + NewtCommon_FatalError(env, "FatalError Java_jogamp_newt_driver_ios_WindowDriver_initIDs0: can't fetch %s.%s %s", + ClazzNamePoint, ClazzAnyCstrName, ClazzNamePointCstrSignature); + } + + // Need this when debugging, as it is necessary to attach gdb to + // the running java process -- "gdb java" doesn't work + // printf("Going to sleep for 10 seconds\n"); + // sleep(10); + + BOOL res = [NewtUIWindow initNatives: env forClass: clazz]; + [pool release]; + + return (jboolean) res; +} + +/** + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: createView0 + * Signature: (IIII)J + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_ios_WindowDriver_createView0 + (JNIEnv *env, jobject jthis, jint x, jint y, jint w, jint h) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + DBG_PRINT( "createView0 - %p (this), %d/%d %dx%d (START)\n", + (void*)(intptr_t)jthis, (int)x, (int)y, (int)w, (int)h); + + CGRect rectView = CGRectMake(0, 0, w, h); + NewtUIView *myView = [[NewtUIView alloc] initWithFrame: rectView] ; + DBG_PRINT( "createView0.X - new view: %p\n", myView); + + [pool release]; + + return (jlong) (intptr_t) myView; +} + +/** + * Method creates a deferred un-initialized Window, hence no special invocation required inside method. + * + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: createWindow0 + * Signature: (IIIIZIIJ)J + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_ios_WindowDriver_createWindow0 + (JNIEnv *env, jobject jthis, jint x, jint y, jint w, jint h, + jboolean fullscreen, jint styleMask, jint bufferingType, jlong jview) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NewtUIView* myView = (NewtUIView*) (intptr_t) jview ; + + DBG_PRINT( "createWindow0 - %p (this), %d/%d %dx%d, fs %d, style %X, buffType %X, view %p (START)\n", + (void*)(intptr_t)jthis, (int)x, (int)y, (int)w, (int)h, (int)fullscreen, + (int)styleMask, (int)bufferingType, myView); + (void)myView; + + if (fullscreen) { + // TODO styleMask = NSBorderlessWindowMask; + } + CGRect rectWin = CGRectMake(x, y, w, h); + + // Allocate the window + NewtUIWindow* myWindow = [[NewtUIWindow alloc] initWithContentRect: rectWin + styleMask: (NSUInteger) styleMask + backing: 0 // TODO (NSBackingStoreType) bufferingType + defer: YES + isFullscreenWindow: fullscreen]; + // DBG_PRINT( "createWindow0.1 - %p, isVisible %d\n", myWindow, [myWindow isVisible]); + + DBG_PRINT( "createWindow0.X - %p, isVisible %d\n", myWindow, [myWindow isVisible]); + + [pool release]; + + return (jlong) ((intptr_t) myWindow); +} + +JNIEXPORT jint JNICALL Java_jogamp_newt_driver_ios_WindowDriver_getDisplayID0(JNIEnv *env, jobject jthis, jlong window) { + NewtUIWindow* myWindow = (NewtUIWindow*) ((intptr_t) window); + if( NULL == myWindow ) { + DBG_PRINT( "getDisplayID0 - NULL NEWT win - abort\n"); + return 0; + } + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + UIScreen *screen = [myWindow screen]; + int32_t displayID = 0; // TODO (int32_t)NewtScreen_getCGDirectDisplayIDByUIScreen(screen); + [pool release]; + return (jint) displayID; +} + +/** + * Method is called on Main-Thread, hence no special invocation required inside method. + * + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: initWindow0 + * Signature: (JJIIIIFZZZJ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_initWindow0 + (JNIEnv *env, jobject jthis, jlong parent, jlong window, jint x, jint y, jint w, jint h, jfloat reqPixelScale, + jboolean opaque, jboolean atop, jboolean abottom, jboolean visible, jlong jview) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NewtUIWindow* myWindow = (NewtUIWindow*) ((intptr_t) window); + NewtUIView* myView = (NewtUIView*) (intptr_t) jview ; + BOOL fullscreen = myWindow->isFullscreenWindow; + + DBG_PRINT( "initWindow0 - %p (this), %p (parent), %p (window), %d/%d %dx%d, reqPixScale %f, opaque %d, atop %d, abottom %d, fs %d, visible %d, view %p (START)\n", + (void*)(intptr_t)jthis, (void*)(intptr_t)parent, myWindow, (int)x, (int)y, (int)w, (int)h, (float)reqPixelScale, + (int) opaque, (int)atop, (int)abottom, (int)fullscreen, (int)visible, myView); + + // TODO [myWindow setReleasedWhenClosed: NO]; // We control UIWindow destruction! + // TODO [myWindow setPreservesContentDuringLiveResize: NO]; + + NSObject* nsParentObj = (NSObject*) ((intptr_t) parent); + UIWindow* parentWindow = NULL; + UIView* parentView = NULL; + if( nsParentObj != NULL && [nsParentObj isKindOfClass:[UIWindow class]] ) { + parentWindow = (UIWindow*) nsParentObj; + parentView = (UIView*)nsParentObj; + DBG_PRINT( "initWindow0 - Parent is UIWindow : %p (win) -> %p (view) \n", parentWindow, parentView); + } else if( nsParentObj != NULL && [nsParentObj isKindOfClass:[UIView class]] ) { + parentView = (UIView*) nsParentObj; + parentWindow = [parentView window]; + DBG_PRINT( "initWindow0 - Parent is UIView : %p -(view) > %p (win) \n", parentView, parentWindow); + } else { + DBG_PRINT( "initWindow0 - Parent is neither UIWindow nor UIView : %p\n", nsParentObj); + } + DBG_PRINT( "initWindow0 - is visible.1: %d\n", [myWindow isVisible]); + + // Remove animations for child windows + if(NULL != parentWindow) { + [UIView setAnimationsEnabled: NO]; + } + +#ifdef VERBOSE_ON + int dbgIdx = 1; +#endif + if(opaque) { + [myWindow setOpaque: YES]; + DBG_PRINT( "initWindow0.%d\n", dbgIdx++); + if (!fullscreen) { + // TODO [myWindow setShowsResizeIndicator: YES]; + } + DBG_PRINT( "initWindow0.%d\n", dbgIdx++); + } else { + [myWindow setOpaque: NO]; + [myWindow setBackgroundColor: [UIColor clearColor]]; + } + [myWindow setAlwaysOn: atop bottom:abottom]; + + // specify we want mouse-moved events + // TODO [myWindow setAcceptsMouseMovedEvents:YES]; + + DBG_PRINT( "initWindow0.%d - %p view %p, isVisible %d\n", + dbgIdx++, myWindow, myView, [myWindow isVisible]); + + // Set the content view + changeContentView(env, jthis, parentView, myWindow, myView, NO); + // TODO [myWindow setInitialFirstResponder: myView]; + + DBG_PRINT( "initWindow0.%d - %p view %p, isVisible %d\n", + dbgIdx++, myWindow, myView, [myWindow isVisible]); + + if(NULL!=parentWindow) { + [myWindow attachToParent: parentWindow]; + } + + DBG_PRINT( "initWindow0.%d - %p view %p, isVisible %d, visible %d\n", + dbgIdx++, myWindow, myView, [myWindow isVisible], visible); + + // Immediately re-position this window based on an upper-left coordinate system + setWindowClientTopLeftPointAndSize(myWindow, x, y, w, h, NO); + + DBG_PRINT( "initWindow0.%d - %p view %p, isVisible %d\n", + dbgIdx++, myWindow, myView, [myWindow isVisible]); + + // TODO [myWindow setAllowsConcurrentViewDrawing: YES]; + + DBG_PRINT( "initWindow0.%d - %p view %p, isVisible %d\n", + dbgIdx++, myWindow, myView, [myWindow isVisible]); + + // TODO [myView setCanDrawConcurrently: YES]; + + DBG_PRINT( "initWindow0.%d - %p view %p, isVisible %d\n", + dbgIdx++, myWindow, myView, [myWindow isVisible]); + + // visible on front + if( visible ) { + // TODO [myWindow orderFront: myWindow]; + } + + DBG_PRINT( "initWindow0.%d - %p view %p, isVisible %d\n", + dbgIdx++, myWindow, myView, [myWindow isVisible]); + + // force surface creation + // [myView lockFocus]; + // [myView unlockFocus]; + + // Set the next responder to be the window so that we can forward + // right mouse button down events + // TODO [myView setNextResponder: myWindow]; + + DBG_PRINT( "initWindow0.%d - %p (this), %p (parent): new window: %p, view %p\n", + dbgIdx++, (void*)(intptr_t)jthis, (void*)(intptr_t)parent, myWindow, myView); + + [myView setDestroyNotifySent: false]; + setJavaWindowObject(env, jthis, myView, YES); + + DBG_PRINT( "initWindow0.%d - %p (this), %p (parent): new window: %p, view %p\n", + dbgIdx++, (void*)(intptr_t)jthis, (void*)(intptr_t)parent, myWindow, myView); + +NS_DURING + if( fullscreen ) { + /** + * See Bug 914: We don't use exclusive fullscreen anymore (capturing display) + * allowing ALT-TAB to allow process/app switching! + * Shall have no penalty on modern GPU and is also recommended, see bottom box @ + * <https://developer.apple.com/library/mac/documentation/graphicsimaging/Conceptual/QuartzDisplayServicesConceptual/Articles/DisplayCapture.html> + * + UIScreen *myScreen = NewtScreen_getUIScreenByCoord(x, y); + if( NULL != myScreen ) { + if ( [myView respondsToSelector:@selector(enterFullScreenMode:withOptions:)] ) { + // Available >= 10.5 - Makes the menubar disapear + [myView enterFullScreenMode: myScreen withOptions:NULL]; + } + } + */ + if( myWindow->hasPresentationSwitch ) { + DBG_PRINT( "initWindow0.%d - %p view %p, setPresentationOptions 0x%X\n", + dbgIdx++, myWindow, myView, (int)myWindow->fullscreenPresentationOptions); + // TODO [NSApp setPresentationOptions: myWindow->fullscreenPresentationOptions]; + } + } +NS_HANDLER +NS_ENDHANDLER + + DBG_PRINT( "initWindow0.%d - %p (this), %p (parent): new window: %p, view %p\n", + dbgIdx++, (void*)(intptr_t)jthis, (void*)(intptr_t)parent, myWindow, myView); + + [pool release]; + DBG_PRINT( "initWindow0.X - %p (this), %p (parent): new window: %p, view %p\n", + (void*)(intptr_t)jthis, (void*)(intptr_t)parent, myWindow, myView); +} + +/** + * Method is called on Main-Thread, hence no special invocation required inside method. + * + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: setPixelScale0 + * Signature: (JJF)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_setPixelScale0 + (JNIEnv *env, jobject jthis, jlong window, jlong view, jfloat reqPixelScale) +{ + NewtUIWindow* myWindow = (NewtUIWindow*) ((intptr_t) window); + if( NULL == myWindow ) { + DBG_PRINT( "setPixelScale0 - NULL NEWT win - abort\n"); + return; + } + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NewtUIView* myView = (NewtUIView*) (intptr_t) view ; +#ifdef VERBOSE_ON + int dbgIdx = 1; +#endif + DBG_PRINT( "setPixelScale0 - %p (this), %p (window), view %p, reqPixScale %f (START)\n", + (void*)(intptr_t)jthis, myWindow, myView, (float)reqPixelScale); + (void)myWindow; + + DBG_PRINT( "setPixelScale0.%d - %p (this), window: %p, view %p\n", + dbgIdx++, (void*)(intptr_t)jthis, myWindow, myView); + + [pool release]; + DBG_PRINT( "setPixelScale0.X - %p (this), window: %p, view %p\n", + (void*)(intptr_t)jthis, myWindow, myView); +} + +/** + * Method is called on Main-Thread, hence no special invocation required inside method. + * + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: close0 + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_close0 + (JNIEnv *env, jobject unused, jlong window) +{ + NewtUIWindow* mWin = (NewtUIWindow*) ((intptr_t) window); + if( NULL == mWin ) { + DBG_PRINT( "windowClose.0 - NULL NEWT win - abort\n"); + return; + } + BOOL isNSWin = [mWin isKindOfClass:[UIWindow class]]; + BOOL isNewtWin = [mWin isKindOfClass:[NewtUIWindow class]]; + UIWindow *pWin = NULL; // TODO [mWin parentWindow]; + DBG_PRINT( "windowClose.0 - %p [isUIWindow %d, isNewtWin %d], parent %p\n", mWin, isNSWin, isNewtWin, pWin); + (void)isNSWin; // silence + if( !isNewtWin ) { + NewtCommon_throwNewRuntimeException(env, "Not a NewtUIWindow %p", mWin); + return; + } + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NewtUIView* mView = (NewtUIView *)mWin; // TODO [mWin contentView]; + BOOL fullscreen = mWin->isFullscreenWindow; + BOOL destroyNotifySent, isUIView, isNewtUIView; + if( NULL != mView ) { + isUIView = [mView isKindOfClass:[UIView class]]; + isNewtUIView = [mView isKindOfClass:[NewtUIView class]]; + destroyNotifySent = isNewtUIView ? [mView getDestroyNotifySent] : false; + } else { + isUIView = false; + isNewtUIView = false; + destroyNotifySent = false; + } + + DBG_PRINT( "windowClose.0 - %p, destroyNotifySent %d, view %p [isUIView %d, isNewtUIView %d], fullscreen %d, parent %p\n", + mWin, destroyNotifySent, mView, isUIView, isNewtUIView, (int)fullscreen, pWin); + + [mWin setRealized: NO]; + + if( isNewtUIView ) { + // cleanup view + [mView setDestroyNotifySent: true]; + setJavaWindowObject(env, NULL, mView, NO); + } + +NS_DURING + /** + * See Bug 914: We don't use exclusive fullscreen anymore (capturing display) + * See initWindow0(..) above .. + if(NULL!=mView) { + BOOL iifs; + if ( [mView respondsToSelector:@selector(isInFullScreenMode)] ) { + iifs = [mView isInFullScreenMode]; + } else { + iifs = NO; + } + if(iifs && [mView respondsToSelector:@selector(exitFullScreenModeWithOptions:)] ) { + [mView exitFullScreenModeWithOptions: NULL]; + } + } */ + // Note: mWin's release will also release it's mView! + DBG_PRINT( "windowClose.1a - %p view %p, fullscreen %d, hasPresSwitch %d, defaultPresentationOptions 0x%X\n", + mWin, mView, (int)fullscreen, (int)mWin->hasPresentationSwitch, (int)mWin->defaultPresentationOptions); + + if( fullscreen && mWin->hasPresentationSwitch ) { + DBG_PRINT( "windowClose.1b - %p view %p, setPresentationOptions 0x%X\n", + mWin, mView, (int)mWin->defaultPresentationOptions); + // TODO [NSApp setPresentationOptions: mWin->defaultPresentationOptions]; + } +NS_HANDLER +NS_ENDHANDLER + + if(NULL!=pWin) { + [mWin detachFromParent: pWin]; + } + // TODO [mWin orderOut: mWin]; + [mWin setHidden: YES]; // no release, close n/a, .. well: ref count counts :-( + + DBG_PRINT( "windowClose.2 - %p view %p, parent %p\n", mWin, mView, pWin); + + [mWin release]; + + DBG_PRINT( "windowClose.Xp\n"); + + [pool release]; +} + +/* + * Class: Java_jogamp_newt_driver_ios_WindowDriver + * Method: lockSurface0 + * Signature: (JJ)Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_ios_WindowDriver_lockSurface0 + (JNIEnv *env, jclass clazz, jlong window, jlong view) +{ + NewtUIWindow *mWin = (NewtUIWindow*) ((intptr_t) window); + if(NO == [mWin isRealized]) { + return JNI_FALSE; + } + NewtUIView * mView = (NewtUIView *) ((intptr_t) view); + return [mView softLock] == YES ? JNI_TRUE : JNI_FALSE; + /** deadlocks, since we render independent of focus + return [mView lockFocusIfCanDraw] == YES ? JNI_TRUE : JNI_FALSE; */ +} + +/* + * Class: Java_jogamp_newt_driver_ios_WindowDriver + * Method: unlockSurface0 + * Signature: (JJ)Z + */ +JNIEXPORT jboolean JNICALL Java_jogamp_newt_driver_ios_WindowDriver_unlockSurface0 + (JNIEnv *env, jclass clazz, jlong window, jlong view) +{ + // NewtUIWindow *mWin = (NewtUIWindow*) ((intptr_t) window); + (void) window; + NewtUIView * mView = (NewtUIView *) ((intptr_t) view); + return [mView softUnlock] == YES ? JNI_TRUE : JNI_FALSE; + /** deadlocks, since we render independent of focus + [mView unlockFocus]; */ +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: requestFocus0 + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_requestFocus0 + (JNIEnv *env, jobject window, jlong w, jboolean force) +{ + UIWindow* mWin = (UIWindow*) ((intptr_t) w); + if( NULL == mWin ) { + DBG_PRINT( "requestFocus - NULL NEWT win - abort\n"); + return; + } + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; +#ifdef VERBOSE_ON + BOOL hasFocus = [mWin isKeyWindow]; +#endif + DBG_PRINT( "requestFocus - window: %p, force %d, hasFocus %d (START)\n", mWin, force, hasFocus); + + // TODO [mWin setAcceptsMouseMovedEvents: YES]; + // TODO [mWin makeFirstResponder: nil]; + // TODO [mWin orderFrontRegardless]; + [mWin makeKeyWindow]; + + DBG_PRINT( "requestFocus - window: %p, force %d (END)\n", mWin, force); + + [pool release]; +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: resignFocus0 + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_resignFocus0 + (JNIEnv *env, jobject window, jlong w) +{ + UIWindow* mWin = (UIWindow*) ((intptr_t) w); + if( NULL == mWin ) { + DBG_PRINT( "resignFocus0 - NULL NEWT win - abort\n"); + return; + } + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + UIWindow* pWin = NULL; // TODO [mWin parentWindow]; + BOOL hasFocus = [mWin isKeyWindow]; + + DBG_PRINT( "resignFocus0 - window: %p, parent %p, hasFocus %d (START)\n", mWin, pWin, hasFocus ); + if( hasFocus ) { + if(NULL != pWin) { + // [mWin makeFirstResponder: pWin]; + [pWin makeKeyWindow]; + } else { + [pWin resignKeyWindow]; + } + } + DBG_PRINT( "resignFocus0 - window: %p (END)\n", mWin); + + [pool release]; +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: orderFront0 + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_orderFront0 + (JNIEnv *env, jobject unused, jlong window) +{ + UIWindow* mWin = (UIWindow*) ((intptr_t) window); + if( NULL == mWin ) { + DBG_PRINT( "orderFront0 - NULL NEWT win - abort\n"); + return; + } + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + UIWindow* pWin = NULL; // TODO [mWin parentWindow]; + + DBG_PRINT( "orderFront0 - window: (parent %p) %p visible %d (START)\n", pWin, mWin, [mWin isVisible]); + + if( NULL == pWin ) { + // TODO [mWin orderFrontRegardless]; + } else { + // TODO [mWin orderWindow: UIWindowAbove relativeTo: [pWin windowNumber]]; + } + + DBG_PRINT( "orderFront0 - window: (parent %p) %p (END)\n", pWin, mWin); + + [pool release]; +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: orderOut + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_orderOut0 + (JNIEnv *env, jobject unused, jlong window) +{ + UIWindow* mWin = (UIWindow*) ((intptr_t) window); + if( NULL == mWin ) { + DBG_PRINT( "orderOut0 - NULL NEWT win - abort\n"); + return; + } + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + UIWindow* pWin = NULL; // TODO [mWin parentWindow]; + + DBG_PRINT( "orderOut0 - window: (parent %p) %p visible %d (START)\n", pWin, mWin, [mWin isVisible]); + + if( NULL == pWin ) { + // TODO [mWin orderOut: mWin]; + } else { + // TODO [mWin orderWindow: UIWindowOut relativeTo: [pWin windowNumber]]; + } + + DBG_PRINT( "orderOut0 - window: (parent %p) %p (END)\n", pWin, mWin); + + [pool release]; +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: setTitle0 + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_setTitle0 + (JNIEnv *env, jobject unused, jlong window, jstring title) +{ + UIWindow* win = (UIWindow*) ((intptr_t) window); + if( NULL == win ) { + DBG_PRINT( "setTitle0 - NULL NEWT win - abort\n"); + return; + } + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + DBG_PRINT( "setTitle0 - window: %p (START)\n", win); + + NSString* str = jstringToNSString(env, title); + [str autorelease]; + // TODO [win setTitle: str]; + + DBG_PRINT( "setTitle0 - window: %p (END)\n", win); + + [pool release]; +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: contentView0 + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_jogamp_newt_driver_ios_WindowDriver_contentView0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + UIWindow* win = (UIWindow*) ((intptr_t) window); + UIView* nsView = (UIView*)win; // TODO [win contentView]; + NewtUIView* newtView = NULL; + + if( [nsView isKindOfClass:[NewtUIView class]] ) { + newtView = (NewtUIView *) nsView; + } + + DBG_PRINT( "contentView0 - window: %p, view: %p, newtView %p\n", win, nsView, newtView); + + jlong res = (jlong) ((intptr_t) nsView); + + [pool release]; + return res; +} + +/** + * Method is called on Main-Thread, hence no special invocation required inside method. + * + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: changeContentView + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_changeContentView0 + (JNIEnv *env, jobject jthis, jlong parentWindowOrView, jlong window, jlong jview) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NewtUIView* newView = (NewtUIView *) ((intptr_t) jview); + NewtUIWindow* win = (NewtUIWindow*) ((intptr_t) window); + + DBG_PRINT( "changeContentView0.0 - win %p, view (%p,%d)\n", + win, newView, getRetainCount(newView)); + + NSObject *nsParentObj = (NSObject*) ((intptr_t) parentWindowOrView); + UIView* pView = NULL; + if( NULL != nsParentObj ) { + if( [nsParentObj isKindOfClass:[UIWindow class]] ) { + UIWindow * pWin = (UIWindow*) nsParentObj; + pView = (UIView*)pWin; // TODO [pWin contentView]; + } else if( [nsParentObj isKindOfClass:[UIView class]] ) { + pView = (UIView*) nsParentObj; + } + } + + changeContentView(env, jthis, pView, win, newView, YES); + + DBG_PRINT( "changeContentView0.X\n"); + + [pool release]; +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: updateSizePosInsets0 + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_updateSizePosInsets0 + (JNIEnv *env, jobject jthis, jlong window, jboolean defer) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NewtUIWindow* mWin = (NewtUIWindow*) ((intptr_t) window); + + DBG_PRINT( "updateSizePosInsets - window: %p, defer %d (START)\n", mWin, (int)defer); + + [mWin updateSizePosInsets: env jwin:jthis defer:defer]; + + DBG_PRINT( "updateSizePosInsets - window: %p, defer %d (END)\n", mWin, (int)defer); + + [pool release]; +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: setWindowClientTopLeftPointAndSize0 + * Signature: (JIIIIZ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_setWindowClientTopLeftPointAndSize0 + (JNIEnv *env, jobject unused, jlong window, jint x, jint y, jint w, jint h, jboolean display) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NewtUIWindow* mWin = (NewtUIWindow*) ((intptr_t) window); + + DBG_PRINT( "setWindowClientTopLeftPointAndSize - window: %p (START)\n", mWin); + + setWindowClientTopLeftPointAndSize(mWin, x, y, w, h, display); + + DBG_PRINT( "setWindowClientTopLeftPointAndSize - window: %p (END)\n", mWin); + + [pool release]; +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: setWindowClientTopLeftPoint0 + * Signature: (JIIZ)V + */ +JNIEXPORT void JNICALL Java_jogamp_newt_driver_ios_WindowDriver_setWindowClientTopLeftPoint0 + (JNIEnv *env, jobject unused, jlong window, jint x, jint y, jboolean display) +{ + NewtUIWindow* mWin = (NewtUIWindow*) ((intptr_t) window); + if( NULL == mWin ) { + DBG_PRINT( "setWindowClientTopLeftPoint - NULL NEWT win - abort\n"); + return; + } + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + DBG_PRINT( "setWindowClientTopLeftPoint - window: %p (START)\n", mWin); + + setWindowClientTopLeftPoint(mWin, x, y, display); + + DBG_PRINT( "setWindowClientTopLeftPoint - window: %p (END)\n", mWin); + + [pool release]; +} + +/* + * Class: jogamp_newt_driver_ios_WindowDriver + * Method: getLocationOnScreen0 + * Signature: (JII)Lcom/jogamp/nativewindow/util/Point; + */ +JNIEXPORT jobject JNICALL Java_jogamp_newt_driver_ios_WindowDriver_getLocationOnScreen0 + (JNIEnv *env, jclass unused, jlong win, jint src_x, jint src_y) +{ + NewtUIWindow *mWin = (NewtUIWindow*) (intptr_t) win; + if( NULL == mWin ) { + DBG_PRINT( "getLocationOnScreen0 - NULL NEWT win - abort\n"); + return NULL; + } + if( ![mWin isKindOfClass:[NewtUIWindow class]] ) { + NewtCommon_throwNewRuntimeException(env, "Not a NewtUIWindow %p", mWin); + return NULL; + } + CGPoint p0 = [mWin getLocationOnScreen: CGPointMake(src_x, src_y)]; + return (*env)->NewObject(env, pointClz, pointCstr, (jint)p0.x, (jint)p0.y); +} + diff --git a/src/newt/native/JVM_JNI8.c b/src/newt/native/JVM_JNI8.c new file mode 100644 index 000000000..c023cb0f0 --- /dev/null +++ b/src/newt/native/JVM_JNI8.c @@ -0,0 +1,42 @@ +/** + * Copyright 2019 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of JogAmp Community. + */ + +#include <stdio.h> //required by android to identify NULL +#include <jni.h> + +#if defined (JNI_VERSION_1_8) + +JNIEXPORT jint JNICALL JNI_OnLoad_newt(JavaVM *vm, void *reserved) { + return JNI_VERSION_1_8; +} + +JNIEXPORT void JNICALL JNI_OnUnload_newt(JavaVM *vm, void *reserved) { +} + +#endif /* defined (JNI_VERSION_1_8) */ + |