From e8dd3511db74a8ae1e749e2c700d9ce2c68f84b1 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 1 Sep 2014 14:03:55 -0700 Subject: [PATCH] Add some tolerance in the tap to click code. Implement right clicking. --- libs/limelight-common.jar | Bin 416365 -> 416374 bytes src/com/limelight/Game.java | 96 ++++++++---------- .../limelight/binding/input/TouchContext.java | 93 +++++++++++++++++ 3 files changed, 138 insertions(+), 51 deletions(-) create mode 100644 src/com/limelight/binding/input/TouchContext.java diff --git a/libs/limelight-common.jar b/libs/limelight-common.jar index 3c3b9166a39f1b377460a4ffaee6e174479e049f..9e9c6f512bd5cd960819d7ceaf86ec421be1c7cc 100644 GIT binary patch delta 2939 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 delta 3141 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 nXq^mfEfc%|;h2@3pPeQ7CND!Q diff --git a/src/com/limelight/Game.java b/src/com/limelight/Game.java index addc20d8..029f0a3b 100644 --- a/src/com/limelight/Game.java +++ b/src/com/limelight/Game.java @@ -4,12 +4,14 @@ package com.limelight; import com.limelight.binding.PlatformBinding; import com.limelight.binding.input.ControllerHandler; import com.limelight.binding.input.KeyboardTranslator; +import com.limelight.binding.input.TouchContext; import com.limelight.binding.video.ConfigurableDecoderRenderer; import com.limelight.nvstream.NvConnection; import com.limelight.nvstream.NvConnectionListener; import com.limelight.nvstream.StreamConfiguration; import com.limelight.nvstream.av.video.VideoDecoderRenderer; import com.limelight.nvstream.input.KeyboardPacket; +import com.limelight.nvstream.input.MouseButtonPacket; import com.limelight.utils.Dialog; import com.limelight.utils.SpinnerDialog; @@ -42,9 +44,9 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM private int lastMouseX = Integer.MIN_VALUE; private int lastMouseY = Integer.MIN_VALUE; private int lastButtonState = 0; - private int lastTouchX = 0; - private int lastTouchY = 0; - private boolean hasMoved = false; + + // Only 2 touches are supported + private TouchContext[] touchContextMap = new TouchContext[2]; private ControllerHandler controllerHandler; private KeyboardTranslator keybTranslator; @@ -178,7 +180,7 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM String app = Game.this.getIntent().getStringExtra(EXTRA_APP); String uniqueId = Game.this.getIntent().getStringExtra(EXTRA_UNIQUEID); - // Start the connection + // Initialize the connection conn = new NvConnection(host, uniqueId, Game.this, new StreamConfiguration(app, width, height, refreshRate, bitrate * 1000, sops), PlatformBinding.getCryptoProvider(this)); @@ -194,6 +196,11 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM sh.setFixedSize(width, height); } + // Initialize touch contexts + for (int i = 0; i < touchContextMap.length; i++) { + touchContextMap[i] = new TouchContext(conn, i); + } + // The connection will be started when the surface gets created sh.addCallback(this); } @@ -368,43 +375,13 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM return true; } - public void touchDownEvent(int eventX, int eventY) - { - lastTouchX = eventX; - lastTouchY = eventY; - hasMoved = false; - } - - public void touchUpEvent(int eventX, int eventY) - { - if (!hasMoved) - { - // We haven't moved so send a click - - // Lower the mouse button - conn.sendMouseButtonDown((byte) 0x01); - - // We need to sleep a bit here because some games - // do input detection by polling - try { - Thread.sleep(100); - } catch (InterruptedException e) {} - - // Raise the mouse button - conn.sendMouseButtonUp((byte) 0x01); + private TouchContext getTouchContext(int actionIndex) + { + if (actionIndex < touchContextMap.length) { + return touchContextMap[actionIndex]; } - } - - public void touchMoveEvent(int eventX, int eventY) - { - if (eventX != lastTouchX || eventY != lastTouchY) - { - hasMoved = true; - conn.sendMouseMove((short)(eventX - lastTouchX), - (short)(eventY - lastTouchY)); - - lastTouchX = eventX; - lastTouchY = eventY; + else { + return null; } } @@ -416,19 +393,36 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN || event.getSource() == InputDevice.SOURCE_STYLUS) { - int eventX = (int)event.getX(); - int eventY = (int)event.getY(); + int actionIndex = event.getActionIndex(); + int eventX = (int)event.getX(actionIndex); + int eventY = (int)event.getY(actionIndex); + + TouchContext context = getTouchContext(actionIndex); + if (context == null) { + return super.onTouchEvent(event); + } + switch (event.getActionMasked()) { + case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_DOWN: - touchDownEvent(eventX, eventY); + context.touchDownEvent(eventX, eventY); break; + case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP: - touchUpEvent(eventX, eventY); + context.touchUpEvent(eventX, eventY); + if (actionIndex == 0 && event.getPointerCount() > 1) { + // The original secondary touch now becomes primary + context.touchDownEvent((int)event.getX(1), (int)event.getY(1)); + } break; case MotionEvent.ACTION_MOVE: - touchMoveEvent(eventX, eventY); + // ACTION_MOVE is special because it always has actionIndex == 0 + // We'll call the move handlers for all indexes manually + for (int i = 0; i < touchContextMap.length; i++) { + touchContextMap[i].touchMoveEvent(eventX, eventY); + } break; default: return super.onTouchEvent(event); @@ -441,28 +435,28 @@ public class Game extends Activity implements SurfaceHolder.Callback, OnGenericM if ((changedButtons & MotionEvent.BUTTON_PRIMARY) != 0) { if ((event.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0) { - conn.sendMouseButtonDown((byte) 0x01); + conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_LEFT); } else { - conn.sendMouseButtonUp((byte) 0x01); + conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT); } } if ((changedButtons & MotionEvent.BUTTON_SECONDARY) != 0) { if ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) { - conn.sendMouseButtonDown((byte) 0x03); + conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT); } else { - conn.sendMouseButtonUp((byte) 0x03); + conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT); } } if ((changedButtons & MotionEvent.BUTTON_TERTIARY) != 0) { if ((event.getButtonState() & MotionEvent.BUTTON_TERTIARY) != 0) { - conn.sendMouseButtonDown((byte) 0x02); + conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_MIDDLE); } else { - conn.sendMouseButtonUp((byte) 0x02); + conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_MIDDLE); } } diff --git a/src/com/limelight/binding/input/TouchContext.java b/src/com/limelight/binding/input/TouchContext.java new file mode 100644 index 00000000..ad215458 --- /dev/null +++ b/src/com/limelight/binding/input/TouchContext.java @@ -0,0 +1,93 @@ +package com.limelight.binding.input; + +import com.limelight.nvstream.NvConnection; +import com.limelight.nvstream.input.MouseButtonPacket; + +public class TouchContext { + private int lastTouchX = 0; + private int lastTouchY = 0; + private int originalTouchX = 0; + private int originalTouchY = 0; + private long originalTouchTime = 0; + + private NvConnection conn; + private int actionIndex; + + private static final int TAP_MOVEMENT_THRESHOLD = 5; + private static final int TAP_TIME_THRESHOLD = 500; + + public TouchContext(NvConnection conn, int actionIndex) + { + this.conn = conn; + this.actionIndex = actionIndex; + } + + private boolean isTap() + { + int xDelta = Math.abs(lastTouchX - originalTouchX); + int yDelta = Math.abs(lastTouchY - originalTouchY); + long timeDelta = System.currentTimeMillis() - originalTouchTime; + + return xDelta <= TAP_MOVEMENT_THRESHOLD && + yDelta <= TAP_MOVEMENT_THRESHOLD && + timeDelta <= TAP_TIME_THRESHOLD; + } + + private byte getMouseButtonIndex() + { + if (actionIndex == 1) { + return MouseButtonPacket.BUTTON_RIGHT; + } + else { + return MouseButtonPacket.BUTTON_LEFT; + } + } + + public boolean touchDownEvent(int eventX, int eventY) + { + originalTouchX = lastTouchX = eventX; + originalTouchY = lastTouchY = eventY; + originalTouchTime = System.currentTimeMillis(); + + return true; + } + + public void touchUpEvent(int eventX, int eventY) + { + if (isTap()) + { + byte buttonIndex = getMouseButtonIndex(); + + // Lower the mouse button + conn.sendMouseButtonDown(buttonIndex); + + // We need to sleep a bit here because some games + // do input detection by polling + try { + Thread.sleep(100); + } catch (InterruptedException e) {} + + // Raise the mouse button + conn.sendMouseButtonUp(buttonIndex); + } + } + + public boolean touchMoveEvent(int eventX, int eventY) + { + if (eventX != lastTouchX || eventY != lastTouchY) + { + // We only send moves for the primary touch point + if (actionIndex == 0) { + conn.sendMouseMove((short)(eventX - lastTouchX), + (short)(eventY - lastTouchY)); + } + + lastTouchX = eventX; + lastTouchY = eventY; + + return true; + } + + return false; + } +}