9292import android .widget .TextView ;
9393import android .widget .Toast ;
9494
95- import androidx .annotation .RequiresApi ;
96-
9795import java .io .ByteArrayInputStream ;
9896import java .lang .reflect .InvocationTargetException ;
9997import java .lang .reflect .Method ;
@@ -366,6 +364,20 @@ private void stopAndUnbindUsbDriverService() {
366364 private boolean detectScrolling = false ;
367365 private boolean detectMouseMiddle = false ;
368366 private boolean detectMouseMiddleDown = false ;
367+ private static final String CURSOR_PREFS = "CursorPrefs" ;
368+ private static final String KEY_CURSOR_RESTORE = "cursor_switch_state" ;
369+
370+ private void saveCursorState (int state ) {
371+ getSharedPreferences (CURSOR_PREFS , MODE_PRIVATE )
372+ .edit ()
373+ .putInt (KEY_CURSOR_RESTORE , state )
374+ .commit (); // 使用 commit 确保即使进程随后被杀也能保存
375+ }
376+
377+ private int getCursorState () {
378+ return getSharedPreferences (CURSOR_PREFS , MODE_PRIVATE )
379+ .getInt (KEY_CURSOR_RESTORE , 0 );
380+ }
369381
370382 @ Override
371383 protected void onCreate (Bundle savedInstanceState ) {
@@ -1106,6 +1118,9 @@ public void onStreamViewReady(StreamView streamView) {
11061118 protected void onResume () {
11071119 super .onResume ();
11081120
1121+ // 回到游戏时,重置标志位,允许下次暂停时再次恢复
1122+ isCursorRestored = false ;
1123+
11091124 // 当 Activity 回到前台时,通知服务开始拦截键盘事件。
11101125 KeyboardAccessibilityService .setIntercepting (true );
11111126
@@ -1805,8 +1820,28 @@ protected void onDestroy() {
18051820 }
18061821 }
18071822
1823+ private boolean isCursorRestored = false ;
1824+
1825+ /**
1826+ * 封装恢复逻辑:确保只执行一次
1827+ */
1828+ public void restoreCursorIfNecessary () {
1829+ if (!isCursorRestored ) {
1830+ if (prefConfig .cursorAutoShow && cursorVisible && conn != null && connected ) {
1831+ switchRemoteCursorShow ();
1832+ saveCursorState (1 ); // 标记为已恢复/待处理
1833+ }
1834+ isCursorRestored = true ; // 锁定,防止重复执行
1835+ }
1836+ }
1837+
18081838 @ Override
18091839 protected void onPause () {
1840+ // 本地光标下,结束串流自动恢复光标显示
1841+ // 只有在非销毁(按 Home 键或切应用)的情况下尝试自动恢复
1842+ // 如果是通过按钮退出的,标志位会提前变为 true,这里就不会再跑一次
1843+ restoreCursorIfNecessary ();
1844+
18101845 // 当 Activity 进入后台时,必须停止拦截,否则会影响手机的正常使用!
18111846 KeyboardAccessibilityService .setIntercepting (false );
18121847
@@ -2003,6 +2038,40 @@ private void setInputGrabState(boolean grab) {
20032038
20042039 private final Runnable toggleGrab = () -> setInputGrabState (!grabbedInput );
20052040
2041+ private void switchRemoteCursorShow () {
2042+ final short [] keys = {
2043+ KeyboardTranslator .VK_LCONTROL ,
2044+ KeyboardTranslator .VK_MENU ,
2045+ KeyboardTranslator .VK_LSHIFT ,
2046+ KeyboardTranslator .VK_N
2047+ };
2048+
2049+ final byte [] modifier = {0 };
2050+
2051+ for (short key : keys ) {
2052+ conn .sendKeyboardInput (key , KeyboardPacket .KEY_DOWN , modifier [0 ], (byte ) 0 );
2053+ modifier [0 ] |= getModBit (key );
2054+ }
2055+
2056+ handler .postDelayed (() -> {
2057+ for (int i = keys .length - 1 ; i >= 0 ; i --) {
2058+ short key = keys [i ];
2059+ modifier [0 ] &= ~getModBit (key );
2060+ conn .sendKeyboardInput (key , KeyboardPacket .KEY_UP , modifier [0 ], (byte ) 0 );
2061+ }
2062+ }, 25L );
2063+ }
2064+
2065+ private byte getModBit (short key ) {
2066+ switch (key ) {
2067+ case KeyboardTranslator .VK_LSHIFT : return KeyboardPacket .MODIFIER_SHIFT ;
2068+ case KeyboardTranslator .VK_LCONTROL : return KeyboardPacket .MODIFIER_CTRL ;
2069+ case KeyboardTranslator .VK_LWIN : return KeyboardPacket .MODIFIER_META ;
2070+ case KeyboardTranslator .VK_MENU : return KeyboardPacket .MODIFIER_ALT ;
2071+ default : return 0 ;
2072+ }
2073+ }
2074+
20062075 // Returns true if the key stroke was consumed
20072076 private boolean handleSpecialKeys (int androidKeyCode , boolean down ) {
20082077 int modifierMask = 0 ;
@@ -2061,10 +2130,17 @@ private boolean handleSpecialKeys(int androidKeyCode, boolean down) {
20612130 grabbedInput = true ;
20622131 }
20632132 cursorVisible = !cursorVisible ;
2064- if (cursorVisible ) {
2065- inputCaptureProvider .showCursor ();
2066- } else {
2067- inputCaptureProvider .hideCursor ();
2133+ // 切换本地光标时自动切换远程鼠标可见性
2134+ if (prefConfig .cursorAutoShow && conn != null && connected ) {
2135+ switchRemoteCursorShow ();
2136+ }
2137+ if (inputCaptureProvider .isCapturingEnabled ()){
2138+ Toast .makeText (this , "切换为本地鼠标模式" , Toast .LENGTH_SHORT ).show ();
2139+ inputCaptureProvider .disableCapture ();
2140+ }
2141+ else {
2142+ Toast .makeText (this , "切换为远程鼠标模式" , Toast .LENGTH_SHORT ).show ();
2143+ inputCaptureProvider .enableCapture ();
20682144 }
20692145 break ;
20702146
@@ -2582,11 +2658,17 @@ public void toggleKeyboard() {
25822658 /**
25832659 * 启用或禁用安卓本地鼠标指针
25842660 */
2585- public void enableNativeMousePointer (boolean enable ) {
2661+ public void enableNativeMousePointer (boolean enable , boolean autoSwitch ) {
25862662 LimeLog .info ("Setting native mouse pointer: " + enable );
25872663
25882664 prefConfig .enableNativeMousePointer = enable ;
25892665
2666+ if (autoSwitch && cursorVisible != enable ) {
2667+ if (prefConfig .cursorAutoShow && conn != null && connected ) {
2668+ switchRemoteCursorShow ();
2669+ }
2670+ }
2671+
25902672 if (enable ) {
25912673 // 启用本地鼠标指针:释放鼠标捕获但保持键盘捕获
25922674 inputCaptureProvider .disableCapture ();
@@ -3149,7 +3231,7 @@ else if (eventSource == InputDevice.SOURCE_MOUSE_RELATIVE &&
31493231 int deviceSources = event .getDevice () != null ? event .getDevice ().getSources () : 0 ;
31503232
31513233 // 本地鼠标指针模式的特殊处理
3152- if (prefConfig . enableNativeMousePointer && (eventSource & InputDevice .SOURCE_CLASS_POINTER ) != 0 ) {
3234+ if (cursorVisible && (eventSource & InputDevice .SOURCE_CLASS_POINTER ) != 0 ) {
31533235 // 检查是否为真正的鼠标设备(而不是触摸屏)
31543236 boolean isActualMouse = (eventSource == InputDevice .SOURCE_MOUSE ) ||
31553237 (eventSource == InputDevice .SOURCE_MOUSE_RELATIVE ) ||
@@ -3913,9 +3995,25 @@ public void connectionStarted() {
39133995 Handler h = new Handler ();
39143996 h .postDelayed (() -> {
39153997 // 根据配置决定是否启用原生鼠标指针
3998+ int state = getCursorState ();
39163999 if (prefConfig .enableNativeMousePointer ) {
3917- enableNativeMousePointer (true );
4000+ // 使用本地鼠标时,连接开始自动关闭远程光标显示
4001+ if (state == 1 ) {
4002+ // 状态为 1,说明上次退出或暂停时重新打开了远程光标显示,现在需要关闭
4003+ if (prefConfig .cursorAutoShow ) {
4004+ switchRemoteCursorShow ();
4005+ saveCursorState (0 ); // 恢复后重置状态为 0
4006+ }
4007+ }
4008+ enableNativeMousePointer (true , false );
39184009 } else {
4010+ if (state == 0 ) {
4011+ // 状态为 0,说明上次意外退出,已经是关闭状态,现在因为设置改变需要开启
4012+ if (prefConfig .cursorAutoShow ) {
4013+ switchRemoteCursorShow ();
4014+ }
4015+ saveCursorState (1 );
4016+ }
39194017 setInputGrabState (true );
39204018 }
39214019 }, 500 );
@@ -4346,7 +4444,7 @@ private void initializeLocalCursorRenderers(int width, int height) {
43464444 // 3. 必须没开启原生鼠标 (防止冲突)
43474445 boolean shouldShow = prefConfig .enableLocalCursorRendering
43484446 && prefConfig .touchscreenTrackpad
4349- && !prefConfig . enableNativeMousePointer ;
4447+ && !cursorVisible ;
43504448
43514449 relativeContext .setEnableLocalCursorRendering (shouldShow );
43524450 }
@@ -4367,7 +4465,7 @@ private void destroyLocalCursorRenderers() {
43674465 }
43684466
43694467 public void refreshLocalCursorState (boolean enabled ) {
4370- boolean shouldRender = enabled && !prefConfig . enableNativeMousePointer ;
4468+ boolean shouldRender = enabled && !cursorVisible ;
43714469
43724470 for (TouchContext context : relativeTouchContextMap ) {
43734471 if (context instanceof RelativeTouchContext ) {
@@ -4546,7 +4644,7 @@ private void startCursorService(String hostIp) {
45464644 if (targetBitmap != null ) {
45474645 final android .graphics .Bitmap finalBmp = targetBitmap ;
45484646 runOnUiThread (() -> {
4549- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N && prefConfig . enableNativeMousePointer ) {
4647+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N && cursorVisible ) {
45504648 // 方案B:当启用了原生指针且API版本符合时,使用 PointerIcon
45514649 PointerIcon pointerIcon = PointerIcon .create (finalBmp , hotX , hotY );
45524650 streamView .setPointerIcon (pointerIcon );
@@ -4575,7 +4673,7 @@ private void startCursorService(String hostIp) {
45754673 lastReceiveTime = System .currentTimeMillis (); // 重置计时,避免疯狂触发
45764674
45774675 runOnUiThread (() -> {
4578- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N && prefConfig . enableNativeMousePointer ) {
4676+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N && cursorVisible ) {
45794677 // 恢复为默认箭头
45804678 streamView .setPointerIcon (PointerIcon .getSystemIcon (Game .this , PointerIcon .TYPE_ARROW ));
45814679 } else {
0 commit comments