Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/src/main/java/com/limelight/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -2078,6 +2078,10 @@ public boolean handleKeyDown(KeyEvent event) {
return false;
}

if (event.getKeyCode() == KeyEvent.KEYCODE_HOME) {
disconnect();
}
Comment on lines +2081 to +2083

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change adds a new behavior (disconnecting on HOME) that isn’t described in the PR title/description about trackpad three-finger tap. If this is intentional, it should be in a separate PR or at least documented/guarded (e.g., behind a setting) since HOME/Guide keys on some controllers/remotes may be pressed unintentionally and cause unexpected disconnects.

Copilot uses AI. Check for mistakes.

// We'll send it as a raw key event if we have a key mapping, otherwise we'll send it
// as UTF-8 text (if it's a printable character).
short translated = keyboardTranslator.translate(event.getKeyCode(), event.getScanCode(), deviceId);
Expand Down Expand Up @@ -3158,12 +3162,14 @@ private boolean handleTouchInput(MotionEvent event, TouchContext[] inputContextM
// some devices report inverted axis when trackpad pointer is captured
// but not when they're simulated as swipes on the screen
if (invertAxis) {
aTouchContextMap.setActualPointerCount(actualPointerCount);
aTouchContextMap.touchMoveEvent(
historicalY,
historicalX,
event.getHistoricalEventTime(i)
);
} else {
aTouchContextMap.setActualPointerCount(actualPointerCount);
aTouchContextMap.touchMoveEvent(
historicalX,
historicalY,
Expand Down Expand Up @@ -3222,7 +3228,9 @@ private boolean handleTouchInput(MotionEvent event, TouchContext[] inputContextM
case MotionEvent.ACTION_DOWN:
for (TouchContext touchContext : inputContextMap) {
touchContext.setPointerCount(pointerCount);
touchContext.setActualPointerCount(actualPointerCount);
}
context.setActualPointerCount(actualPointerCount);
context.touchDownEvent(eventX, eventY, event.getEventTime(), true);
break;
case MotionEvent.ACTION_POINTER_UP:
Expand Down Expand Up @@ -3251,11 +3259,13 @@ private boolean handleTouchInput(MotionEvent event, TouchContext[] inputContextM
context.cancelTouch();
}
else {
context.setActualPointerCount(actualPointerCount);
context.touchUpEvent(eventX, eventY, event.getEventTime());
}

for (TouchContext touchContext : inputContextMap) {
touchContext.setPointerCount(pointerCount - 1);
touchContext.setActualPointerCount(actualPointerCount);
}
if (actionIndex == 0 && pointerCount > 1 && !context.isCancelled()) {
// The original secondary touch now becomes primary
Expand All @@ -3266,6 +3276,7 @@ private boolean handleTouchInput(MotionEvent event, TouchContext[] inputContextM
pointer1X = (int)normalizedCoords[0];
pointer1Y = (int)normalizedCoords[1];
}
context.setActualPointerCount(actualPointerCount);
context.touchDownEvent(
pointer1X,
pointer1Y,
Expand All @@ -3276,6 +3287,7 @@ private boolean handleTouchInput(MotionEvent event, TouchContext[] inputContextM
for (TouchContext aTouchContext : inputContextMap) {
aTouchContext.cancelTouch();
aTouchContext.setPointerCount(0);
aTouchContext.setActualPointerCount(0);
}
break;
default:
Expand Down Expand Up @@ -3341,6 +3353,7 @@ private void cancelStaleTouchState(MotionEvent event, View view) {
for (TouchContext aTouchContext : touchContextMap) {
aTouchContext.cancelTouch();
aTouchContext.setPointerCount(0);
aTouchContext.setActualPointerCount(0);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ public class KeyboardAccessibilityService extends AccessibilityService {
private final static List BLACKLIST_KEYS = Arrays.asList(
KeyEvent.KEYCODE_VOLUME_UP,
KeyEvent.KEYCODE_VOLUME_DOWN,
KeyEvent.KEYCODE_POWER
KeyEvent.KEYCODE_POWER,
KeyEvent.KEYCODE_BRIGHTNESS_DOWN,
KeyEvent.KEYCODE_BRIGHTNESS_UP
);

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import android.view.KeyEvent;

import com.limelight.LimeLog;
import com.limelight.binding.input.touch.Debug;
import com.limelight.nvstream.input.KeyboardPacket;
import com.limelight.preferences.PreferenceConfiguration;
import com.limelight.utils.KeyMapper;
Expand Down Expand Up @@ -52,7 +53,8 @@ public class KeyboardTranslator implements InputManager.InputDeviceListener {
public static final int VK_SEMICOLON = 59;
public static final int VK_SLASH = 47;
public static final int VK_SPACE = 32;
public static final int VK_PRINTSCREEN = 154;
// public static final int VK_PRINTSCREEN = 154; why???
public static final int VK_PRINTSCREEN = 44;
public static final int VK_TAB = 9;
public static final int VK_LEFT = 37;
public static final int VK_RIGHT = 39;
Expand Down Expand Up @@ -182,8 +184,8 @@ public boolean hasNormalizedMapping(int keycode, int deviceId) {
*/
public short translate(int keycode, int scancode, int deviceId) {
int translated;

// If a device ID was provided, look up the keyboard mapping
Debug.format("keycode {0}", keycode);
// If a device ID was provided, look up the keylllboard mapping
Comment on lines +187 to +188

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

translate() is typically a hot path; calling Debug.format() (MessageFormat + allocation) on every key translation can add avoidable overhead. Consider gating this behind a debug flag (e.g., BuildConfig.DEBUG and/or a runtime setting) or removing it from production code paths.

Copilot uses AI. Check for mistakes.

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected spelling of 'keylllboard' to 'keyboard'.

Suggested change
// If a device ID was provided, look up the keylllboard mapping
// If a device ID was provided, look up the keyboard mapping

Copilot uses AI. Check for mistakes.
// Force qwerty will break user's keyboard layout settings
if (prefConfig.forceQwerty && deviceId >= 0) {
KeyboardMapping mapping = keyboardMappings.get(deviceId);
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/java/com/limelight/binding/input/touch/Debug.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.limelight.binding.input.touch;

import java.text.MessageFormat;

public class Debug {
public static int maxCount = 0;
public static String actions = "";

public static void format(String pattern, Object... arguments) {
actions = MessageFormat.format(pattern, arguments);
Comment on lines +6 to +10

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.maxCount and Debug.actions are mutable static globals accessed from multiple subsystems (input, decoder/overlay). As written, updates are not thread-safe and can be torn/lost across threads. Use thread-safe primitives (e.g., AtomicInteger / AtomicReference<String> or synchronization) and consider bounding/accumulating actions in a controlled way (the current format() overwrites the entire string).

Suggested change
public static int maxCount = 0;
public static String actions = "";
public static void format(String pattern, Object... arguments) {
actions = MessageFormat.format(pattern, arguments);
public static volatile int maxCount = 0;
public static volatile String actions = "";
private static final Object LOCK = new Object();
public static void format(String pattern, Object... arguments) {
synchronized (LOCK) {
actions = MessageFormat.format(pattern, arguments);
}

Copilot uses AI. Check for mistakes.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
public interface TouchContext {
int getActionIndex();
void setPointerCount(int pointerCount);
default void setActualPointerCount(int pointerCount){}
boolean touchDownEvent(int eventX, int eventY, long eventTime, boolean isNewFinger);
boolean touchMoveEvent(int eventX, int eventY, long eventTime);
void touchUpEvent(int eventX, int eventY, long eventTime);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class TrackpadContext implements TouchContext {
private double distanceMoved;
private int pointerCount;
private boolean clickedMiddle = false;
private int maxCount = 0;
private int actualPointerCount = 0;
Comment on lines +25 to +26

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maxCount and actualPointerCount are ambiguous in meaning, especially alongside the existing maxPointerCountInGesture. Consider renaming to something explicit (e.g., maxActualPointerCountInGesture and actualPointerCountIncludingLiftingFinger) and/or consolidating with maxPointerCountInGesture to reduce confusion and future bugs.

Copilot uses AI. Check for mistakes.
private int maxPointerCountInGesture;
private boolean isClickPending;
private boolean isDblClickPending;
Expand Down Expand Up @@ -168,11 +170,11 @@ private boolean isTap(long eventTime) {
}

private byte getMouseButtonIndex() {
if (pointerCount == 2) {
return MouseButtonPacket.BUTTON_RIGHT;
} else if (pointerCount == 3) {
if (maxCount == 3) {
clickedMiddle = true;
return MouseButtonPacket.BUTTON_MIDDLE;
} else if (pointerCount == 2) {
return MouseButtonPacket.BUTTON_RIGHT;
} else {
return MouseButtonPacket.BUTTON_LEFT;
}
Expand All @@ -198,26 +200,30 @@ public boolean touchDownEvent(int eventX, int eventY, long eventTime, boolean is
isScrollTransitioning = false;
maxPointerCountInGesture = pointerCount;
originalTouchTime = eventTime;
maxCount = pointerCount;
cancelled = confirmedMove = confirmedScroll = false;
distanceMoved = 0;
velocityX = 0;
velocityY = 0;
lastMoveTime = eventTime;
if (pointerCount == 3){
clickedMiddle = true;
}
if (isClickPending) {
isClickPending = false;
isDblClickPending = true;
confirmedDrag = true;
}
} else {
if (pointerCount == 2 && !confirmedMove) {
if ((actualPointerCount == 3) && !confirmedMove) {
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_MIDDLE);
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_MIDDLE);
isClickPending = false;
isDblClickPending = false;
confirmedDrag = false;
clickedMiddle = true;
// Second finger released, should trigger right click immediately
} else if (pointerCount == 1 && !confirmedMove && !clickedMiddle) {
} else if (pointerCount == 1 && actualPointerCount == 2 && !confirmedMove && !clickedMiddle && maxCount == 2) {
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
isClickPending = false;
Expand Down Expand Up @@ -385,7 +391,7 @@ public boolean touchMoveEvent(int eventX, int eventY, long eventTime) {
if (sendDeltaX != 0 || sendDeltaY != 0) {
conn.sendMouseMove(sendDeltaX, sendDeltaY);
}
} else if (pointerCount == 2) {
} else if (pointerCount == 2 && maxCount == 2 && actualPointerCount == 2) {
checkForConfirmedScroll();
if (confirmedScroll) {
if (absDeltaX > absDeltaY) {
Expand Down Expand Up @@ -430,6 +436,13 @@ public boolean isCancelled() {
return cancelled;
}

@Override
public void setActualPointerCount(int pointerCount){
// Debug.format("Set pointerCount {0}", pointerCount);
actualPointerCount = pointerCount;
maxCount = Math.max(maxCount, pointerCount);
}

@Override
public void setPointerCount(int pointerCount) {
if (this.pointerCount == 2 && pointerCount == 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.limelight.BuildConfig;
import com.limelight.LimeLog;
import com.limelight.R;
import com.limelight.binding.input.touch.Debug;
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
import com.limelight.nvstream.jni.MoonBridge;
import com.limelight.preferences.PreferenceConfiguration;
Expand Down Expand Up @@ -1791,6 +1792,10 @@ public int submitDecodeUnit(byte[] decodeUnitData, int decodeUnitLength, int dec
float decodeTimeMs = (float)lastTwo.decoderTimeMs / lastTwo.totalFramesReceived;
long rttInfo = MoonBridge.getEstimatedRttInfo();
StringBuilder sb = new StringBuilder();
sb.append("max count: ").append(Debug.maxCount).append("\n");
sb.append(Debug.actions);
Debug.maxCount = 0;
Debug.actions = "";
Comment on lines +1795 to +1798

Copilot AI Mar 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.maxCount is printed and reset here, but in the changes shown there is no code that ever updates Debug.maxCount, so the overlay will likely always show 0. Either (a) wire the real trackpad max pointer count into this field, or (b) remove maxCount from the overlay to avoid misleading output.

Suggested change
sb.append("max count: ").append(Debug.maxCount).append("\n");
sb.append(Debug.actions);
Debug.maxCount = 0;
Debug.actions = "";
sb.append(Debug.actions);
Debug.actions = "";

Copilot uses AI. Check for mistakes.
if(prefs.enablePerfOverlayLite){
if(TrafficStatsHelper.getPackageRxBytes(Process.myUid()) != TrafficStats.UNSUPPORTED){
long netData=TrafficStatsHelper.getPackageRxBytes(Process.myUid())+TrafficStatsHelper.getPackageTxBytes(Process.myUid());
Expand Down
Loading