From 0f78c4a9a5e8f2078d1b8fc00d71162e18dddb90 Mon Sep 17 00:00:00 2001 From: Darkyenus Date: Sun, 22 Nov 2015 20:24:45 +0100 Subject: [PATCH] Fix iOS touch mapping --- .../backends/iosrobovm/IOSApplication.java | 88 +++++++++---------- .../gdx/backends/iosrobovm/IOSGraphics.java | 44 ++++------ .../gdx/backends/iosrobovm/IOSInput.java | 61 +++++++------ 3 files changed, 96 insertions(+), 97 deletions(-) diff --git a/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSApplication.java b/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSApplication.java index a61770ca6a3..3606c406605 100644 --- a/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSApplication.java +++ b/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSApplication.java @@ -18,7 +18,7 @@ import java.io.File; -import org.robovm.apple.coregraphics.CGSize; +import org.robovm.apple.coregraphics.CGRect; import org.robovm.apple.foundation.Foundation; import org.robovm.apple.foundation.NSMutableDictionary; import org.robovm.apple.foundation.NSObject; @@ -102,6 +102,8 @@ public void willTerminate (UIApplication application) { /** The display scale factor (1.0f for normal; 2.0f to use retina coordinates/dimensions). */ float displayScaleFactor; + private CGRect lastScreenBounds = null; + Array runnables = new Array(); Array executedRunnables = new Array(); Array lifecycleListeners = new Array(); @@ -152,7 +154,7 @@ final boolean didFinishLaunching (UIApplication uiApp, UIApplicationLaunchOption // setup libgdx this.input = new IOSInput(this); - this.graphics = new IOSGraphics(getBounds(null), scale, this, config, input, gl20); + this.graphics = new IOSGraphics(scale, this, config, input, gl20); this.files = new IOSFiles(); this.audio = new IOSAudio(config); this.net = new IOSNet(this); @@ -190,57 +192,55 @@ public UIWindow getUIWindow () { return uiWindow; } - /** Returns our real display dimension based on screen orientation. + /** GL View spans whole screen, that is, even under the status bar. iOS can also rotate the screen, which is not handled + * consistently over iOS versions. This method returns, in pixels, rectangle in which libGDX draws. * - * @param viewController The view controller. - * @return Or real display dimension. */ - CGSize getBounds (UIViewController viewController) { - // or screen size (always portrait) - CGSize bounds = UIScreen.getMainScreen().getApplicationFrame().getSize(); - - // determine orientation and resulting width + height - UIInterfaceOrientation orientation; - if (viewController != null) { - orientation = viewController.getInterfaceOrientation(); - } else if (config.orientationLandscape == config.orientationPortrait) { - /* - * if the app has orientation in any side then we can only check status bar orientation - */ - orientation = uiApp.getStatusBarOrientation(); - } else if (config.orientationLandscape) {// is landscape true and portrait false - orientation = UIInterfaceOrientation.LandscapeRight; - } else {// is portrait true and landscape false - orientation = UIInterfaceOrientation.Portrait; - } - int width; - int height; - switch (orientation) { + * @return dimensions of space we draw to, adjusted for device orientation */ + protected CGRect getBounds () { + final CGRect screenBounds = UIScreen.getMainScreen().getBounds(); + final CGRect statusBarFrame = uiApp.getStatusBarFrame(); + final UIInterfaceOrientation statusBarOrientation = uiApp.getStatusBarOrientation(); + + double statusBarHeight = Math.min(statusBarFrame.getWidth(), statusBarFrame.getHeight()); + + double screenWidth = screenBounds.getWidth(); + double screenHeight = screenBounds.getHeight(); + + // Make sure that the orientation is consistent with ratios. Should be, but may not be on older iOS versions + switch (statusBarOrientation) { case LandscapeLeft: case LandscapeRight: - height = (int)bounds.getWidth(); - width = (int)bounds.getHeight(); - if (width < height) { - width = (int)bounds.getWidth(); - height = (int)bounds.getHeight(); + if (screenHeight > screenWidth) { + debug("IOSApplication", "Switching reported width and height (w=" + screenWidth + " h=" + screenHeight + ")"); + double tmp = screenHeight; + // noinspection SuspiciousNameCombination + screenHeight = screenWidth; + screenWidth = tmp; } - break; - default: - // assume portrait - width = (int)bounds.getWidth(); - height = (int)bounds.getHeight(); } - Gdx.app.debug("IOSApplication", "Unscaled View: " + orientation.toString() + " " + width + "x" + height); - // update width/height depending on display scaling selected - width *= displayScaleFactor; - height *= displayScaleFactor; + screenWidth *= displayScaleFactor; + screenHeight *= displayScaleFactor; - // log screen dimensions - Gdx.app.debug("IOSApplication", "View: " + orientation.toString() + " " + width + "x" + height); + if (statusBarHeight != 0.0) { + debug("IOSApplication", "Status bar is visible (height = " + statusBarHeight + ")"); + statusBarHeight *= displayScaleFactor; + screenHeight -= statusBarHeight; + } else { + debug("IOSApplication", "Status bar is not visible"); + } + + debug("IOSApplication", "Total computed bounds are w=" + screenWidth + " h=" + screenHeight); + + return lastScreenBounds = new CGRect(0.0, statusBarHeight, screenWidth, screenHeight); + } - // return resulting view size (based on orientation) - return new CGSize(width, height); + protected CGRect getCachedBounds () { + if (lastScreenBounds == null) + return getBounds(); + else + return lastScreenBounds; } final void didBecomeActive (UIApplication uiApp) { diff --git a/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSGraphics.java b/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSGraphics.java index a26dae76bdf..2770aa5a767 100644 --- a/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSGraphics.java +++ b/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSGraphics.java @@ -16,9 +16,7 @@ package com.badlogic.gdx.backends.iosrobovm; -import org.robovm.apple.coregraphics.CGPoint; import org.robovm.apple.coregraphics.CGRect; -import org.robovm.apple.coregraphics.CGSize; import org.robovm.apple.foundation.NSObject; import org.robovm.apple.glkit.GLKView; import org.robovm.apple.glkit.GLKViewController; @@ -76,21 +74,6 @@ public void viewDidAppear (boolean animated) { if (app.viewControllerListener != null) app.viewControllerListener.viewDidAppear(animated); } - @Override - public void didRotate (UIInterfaceOrientation orientation) { - super.didRotate(orientation); - // get the view size and update graphics - // FIXME: supporting BOTH (landscape+portrait at same time) is - // currently not working correctly (needs fix) - // FIXME screen orientation needs to be stored for - // Input#getNativeOrientation - CGSize bounds = app.getBounds(this); - graphics.width = (int)bounds.getWidth(); - graphics.height = (int)bounds.getHeight(); - graphics.makeCurrent(); - app.listener.resize(graphics.width, graphics.height); - } - @Override public UIInterfaceOrientationMask getSupportedInterfaceOrientations () { long mask = 0; @@ -120,6 +103,17 @@ public boolean shouldAutorotateToInterfaceOrientation (UIInterfaceOrientation or } } + @Override + public void viewDidLayoutSubviews () { + super.viewDidLayoutSubviews(); + // get the view size and update graphics + CGRect bounds = app.getBounds(); + graphics.width = (int)bounds.getWidth(); + graphics.height = (int)bounds.getHeight(); + graphics.makeCurrent(); + app.listener.resize(graphics.width, graphics.height); + } + @Callback @BindSelector("shouldAutorotateToInterfaceOrientation:") private static boolean shouldAutorotateToInterfaceOrientation (IOSUIViewController self, Selector sel, @@ -164,36 +158,36 @@ public IOSUIView (CGRect frame, EAGLContext context) { GLKView view; IOSUIViewController viewController; - public IOSGraphics (CGSize bounds, float scale, IOSApplication app, IOSApplicationConfiguration config, IOSInput input, - GL20 gl20) { + public IOSGraphics (float scale, IOSApplication app, IOSApplicationConfiguration config, IOSInput input, GL20 gl20) { this.config = config; + + final CGRect bounds = app.getBounds(); // setup view and OpenGL width = (int)bounds.getWidth(); height = (int)bounds.getHeight(); - app.debug(tag, bounds.getWidth() + "x" + bounds.getHeight() + ", " + scale); this.gl20 = gl20; context = new EAGLContext(EAGLRenderingAPI.OpenGLES2); - view = new GLKView(new CGRect(new CGPoint(0, 0), bounds), context) { + view = new GLKView(new CGRect(0, 0, bounds.getWidth(), bounds.getHeight()), context) { @Method(selector = "touchesBegan:withEvent:") public void touchesBegan (@Pointer long touches, UIEvent event) { - IOSGraphics.this.input.touchDown(touches, event); + IOSGraphics.this.input.onTouch(touches); } @Method(selector = "touchesCancelled:withEvent:") public void touchesCancelled (@Pointer long touches, UIEvent event) { - IOSGraphics.this.input.touchUp(touches, event); + IOSGraphics.this.input.onTouch(touches); } @Method(selector = "touchesEnded:withEvent:") public void touchesEnded (@Pointer long touches, UIEvent event) { - IOSGraphics.this.input.touchUp(touches, event); + IOSGraphics.this.input.onTouch(touches); } @Method(selector = "touchesMoved:withEvent:") public void touchesMoved (@Pointer long touches, UIEvent event) { - IOSGraphics.this.input.touchMoved(touches, event); + IOSGraphics.this.input.onTouch(touches); } @Override diff --git a/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSInput.java b/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSInput.java index 67441c7f100..c9fbce6ae6d 100644 --- a/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSInput.java +++ b/backends/gdx-backend-robovm/src/com/badlogic/gdx/backends/iosrobovm/IOSInput.java @@ -26,10 +26,7 @@ import org.robovm.apple.uikit.UIAlertViewDelegate; import org.robovm.apple.uikit.UIAlertViewDelegateAdapter; import org.robovm.apple.uikit.UIAlertViewStyle; -import org.robovm.apple.uikit.UIApplication; import org.robovm.apple.uikit.UIDevice; -import org.robovm.apple.uikit.UIEvent; -import org.robovm.apple.uikit.UIInterfaceOrientation; import org.robovm.apple.uikit.UIKeyboardType; import org.robovm.apple.uikit.UIReturnKeyType; import org.robovm.apple.uikit.UITextAutocapitalizationType; @@ -53,7 +50,6 @@ import com.badlogic.gdx.backends.iosrobovm.custom.UIAccelerometer; import com.badlogic.gdx.backends.iosrobovm.custom.UIAccelerometerDelegate; import com.badlogic.gdx.backends.iosrobovm.custom.UIAccelerometerDelegateAdapter; -import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.Pool; @@ -562,19 +558,29 @@ public boolean isPeripheralAvailable (Peripheral peripheral) { @Override public int getRotation () { - UIInterfaceOrientation orientation = app.graphics.viewController != null ? app.graphics.viewController - .getInterfaceOrientation() : UIApplication.getSharedApplication().getStatusBarOrientation(); // we measure orientation counter clockwise, just like on Android - if (orientation == UIInterfaceOrientation.Portrait) return 0; - if (orientation == UIInterfaceOrientation.LandscapeLeft) return 270; - if (orientation == UIInterfaceOrientation.PortraitUpsideDown) return 180; - if (orientation == UIInterfaceOrientation.LandscapeRight) return 90; - return 0; + switch (app.uiApp.getStatusBarOrientation()) { + case LandscapeLeft: + return 270; + case PortraitUpsideDown: + return 180; + case LandscapeRight: + return 90; + case Portrait: + default: + return 0; + } } @Override public Orientation getNativeOrientation () { - return Orientation.Portrait; + switch (app.uiApp.getStatusBarOrientation()) { + case LandscapeLeft: + case LandscapeRight: + return Orientation.Landscape; + default: + return Orientation.Portrait; + } } @Override @@ -590,18 +596,8 @@ public boolean isCursorCatched () { public void setCursorPosition (int x, int y) { } - public void touchDown (long touches, UIEvent event) { - toTouchEvents(touches, event); - Gdx.graphics.requestRendering(); - } - - public void touchUp (long touches, UIEvent event) { - toTouchEvents(touches, event); - Gdx.graphics.requestRendering(); - } - - public void touchMoved (long touches, UIEvent event) { - toTouchEvents(touches, event); + protected void onTouch (long touches) { + toTouchEvents(touches); Gdx.graphics.requestRendering(); } @@ -658,18 +654,27 @@ private static class NSArrayExtensions extends NSExtensions { public static native @MachineSizedUInt long count (@Pointer long thiz); } - private void toTouchEvents (long touches, UIEvent uiEvent) { + private void toTouchEvents (long touches) { long array = NSSetExtensions.allObjects(touches); int length = (int)NSArrayExtensions.count(array); for (int i = 0; i < length; i++) { long touchHandle = NSArrayExtensions.objectAtIndex$(array, i); UITouch touch = UI_TOUCH_WRAPPER.wrap(touchHandle); - CGPoint loc = touch.getLocationInView(touch.getView()); + final int locX, locY; + // Get and map the location to our drawing space + { + CGPoint loc = touch.getLocationInView(touch.getWindow()); + final CGRect bounds = app.getCachedBounds(); + locX = (int)(loc.getX() * app.displayScaleFactor - bounds.getMinX()); + locY = (int)(loc.getY() * app.displayScaleFactor - bounds.getMinY()); + // app.debug("IOSInput","pos= "+loc+" bounds= "+bounds+" x= "+locX+" locY= "+locY); + } + synchronized (touchEvents) { UITouchPhase phase = touch.getPhase(); TouchEvent event = touchEventPool.obtain(); - event.x = (int)(loc.getX() * app.displayScaleFactor); - event.y = (int)(loc.getY() * app.displayScaleFactor); + event.x = locX; + event.y = locY; event.phase = phase; event.timestamp = (long)(touch.getTimestamp() * 1000000000); touchEvents.add(event);