Skip to content

Commit

Permalink
share_keyboard_only option in tray & port scanning for connection ste…
Browse files Browse the repository at this point in the history
…p optimized
  • Loading branch information
BHznJNs committed Nov 15, 2024
1 parent 026da8b commit 2e7913a
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 54 deletions.
18 changes: 8 additions & 10 deletions input/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import threading
import time

from collections import deque
from queue import Queue
from typing import Callable
from pynput import mouse, keyboard
from scrcpy_client import key_scancode_map
Expand Down Expand Up @@ -64,7 +64,6 @@ def customized_shortcuts(k: SDL_Scancode | HIDKeymod | AKeyCode, is_redirecting:
return None

def keyboard_press_callback(k: keyboard.Key | keyboard.KeyCode, is_redirecting: bool) -> CallbackResult:
if not CONFIG.config.share_keyboard: return None
if k not in key_scancode_map: return None
generic_key = key_scancode_map[k]
shortcut_data = customized_shortcuts(generic_key, is_redirecting)
Expand All @@ -91,7 +90,6 @@ def keyboard_press_callback(k: keyboard.Key | keyboard.KeyCode, is_redirecting:
return send_data(key_event.serialize())

def keyboard_release_callback(k: keyboard.Key | keyboard.KeyCode, is_redirecting: bool) -> CallbackResult:
if not CONFIG.config.share_keyboard: return None
if k not in key_scancode_map: return None
generic_key = key_scancode_map[k]
if isinstance(generic_key, HIDKeymod):
Expand All @@ -112,7 +110,7 @@ def keyboard_release_callback(k: keyboard.Key | keyboard.KeyCode, is_redirecting
send_data(mouse_init.serialize())
last_mouse_point: tuple[int, int] | None = None
mouse_button_state = MouseButtonStateStore()
movement_queue: deque[tuple[int, int]] = deque(maxlen=4)
movement_queue: Queue[tuple[int, int]] = Queue(maxsize=4)

from input.controller import schedule_exit
def mouse_movement_sender():
Expand All @@ -130,7 +128,7 @@ def mouse_movement_sender():
if call_time_diff < interval_sec:
time.sleep(interval_sec - call_time_diff)

if len(movement_queue) == 0:
if movement_queue.empty():
no_move_counter += 1
if no_move_counter > INTERVAL_INCR_FACTOR:
interval_sec = INTERVAL_INCR
Expand All @@ -141,7 +139,7 @@ def mouse_movement_sender():

no_move_counter = 0
interval_sec = DEFAULT_INTERVAL_SEC
x, y = movement_queue.popleft()
x, y = movement_queue.get()
event = MouseMoveEvent(x, y, mouse_button_state)
res = send_data(event.serialize())
if res is not None: schedule_exit(res); break
Expand All @@ -165,7 +163,7 @@ def compute_mouse_pointer_diff(cur_x: int, cur_y: int) -> tuple[int, int] | None

def mouse_move_callback(cur_x: int, cur_y: int, is_redirecting: bool) -> CallbackResult:
nonlocal last_mouse_point
if not is_redirecting or not CONFIG.config.share_mouse:
if not is_redirecting or CONFIG.config.share_keyboard_only:
last_mouse_point = None
return None

Expand All @@ -175,11 +173,11 @@ def mouse_move_callback(cur_x: int, cur_y: int, is_redirecting: bool) -> Callbac
return None
res = compute_mouse_pointer_diff(cur_x, cur_y)
if res is None: return None
movement_queue.append(res)
movement_queue.put(res)

def mouse_click_callback(_cur_x: int, _cur_y: int, button: mouse.Button, pressed: bool, is_redirecting: bool) -> CallbackResult:
nonlocal last_mouse_point
if not is_redirecting or not CONFIG.config.share_mouse:
if not is_redirecting or CONFIG.config.share_keyboard_only:
return None

hid_button = HID_MouseButton.MOUSE_BUTTON_NONE
Expand All @@ -198,7 +196,7 @@ def mouse_click_callback(_cur_x: int, _cur_y: int, button: mouse.Button, pressed
return send_data(mouse_move_event.serialize())

def mouse_scroll_callback(_cur_x: int, _cur_y: int, _dx: int, dy: int, is_redirecting: bool) -> CallbackResult:
if not is_redirecting or not CONFIG.config.share_mouse:
if not is_redirecting or CONFIG.config.share_keyboard_only:
return None
mouse_scroll_event = MouseScrollEvent(dy)
return send_data(mouse_scroll_event.serialize())
Expand Down
16 changes: 7 additions & 9 deletions input/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,24 +96,22 @@ def toggle_redirecting_state(is_redirecting: bool):
nonlocal show_mask, hide_mask,\
start_edge_portal, pause_edge_portal
if is_redirecting:
if CONFIG.config.share_mouse:
show_mask()
start_edge_portal()
if not CONFIG.config.share_keyboard_only:
show_mask(); start_edge_portal()
LOGGER.write(LogType.Info, "Input redirecting enabled.")
else:
send_data(KeyEmptyEvent().serialize())
hide_mask()
pause_edge_portal()
hide_mask(); pause_edge_portal()
LOGGER.write(LogType.Info, "Input redirecting disabled.")

def toggle_callback(is_redirecting: bool):
if is_redirecting:
last_received = ReceivedClipboardText.read()
current_clipboard_content = Clipboard.safe_paste()
if not CONFIG.config.sync_clipboard: return
if last_received is None: return
if current_clipboard_content is None: return
if last_received == current_clipboard_content: return
if last_received is not None and\
last_received == current_clipboard_content: return
return send_data(SetClipboardEvent(current_clipboard_content).serialize())

main_errno = send_data(GetClipboardEvent().serialize()) # start server clipboard sync
Expand All @@ -134,7 +132,7 @@ def toggle_callback(is_redirecting: bool):
break

keyboard_listener = keyboard.Listener(
suppress=is_redirecting and CONFIG.config.share_keyboard,
suppress=is_redirecting,
on_press=keyboard_press_handler_factory(keyboard_press_callback),
on_release=keyboard_release_handler_factory(keyboard_release_callback),
)
Expand Down
81 changes: 64 additions & 17 deletions ui/connecting_window.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import sys
import threading
import customtkinter as ctk

from dataclasses import dataclass
from queue import Queue

from adb_controller import try_connect_device, try_pairing
from ui import ICON_ICO_PATH
from utils.config_manager import CONFIG
Expand Down Expand Up @@ -93,40 +97,83 @@ def validate_entry(text: str):
button2.pack(side=ctk.RIGHT)

def mount_connecting_view(tabview: ctk.CTkTabview) -> ctk.CTkEntry:
def connect_callback():
nonlocal addr_entry, auto_scan_port, error_label, waiting_label
error_label.configure(text=""); error_label.update()
waiting_label.configure(text=I18N(["Connecting, may take some time...", "连接中,可能需要一些时间..."])); waiting_label.update()
@dataclass
class ProcessError: msg: str
process_data_queue: Queue[str | ProcessError] = Queue()

adb_addr = addr_entry.get().strip()
valid_ip_str = is_valid_ip_str(adb_addr)
valid_ip_addr_part = is_valid_ip_addr_part(adb_addr)
if auto_scan_port.get() == 1:
def process_ip_port(addr: str, to_scan_port: bool):
valid_ip_str = is_valid_ip_str(addr)
valid_ip_addr_part = is_valid_ip_addr_part(addr)
if to_scan_port:
if not valid_ip_str and not valid_ip_addr_part:
error_label.configure(text=I18N(["Invalid connecting address!", "连接地址无效!"]))
waiting_label.configure(text="")
process_data_queue.put(
ProcessError(I18N(["Invalid connecting address!", "连接地址无效!"])))
return
# format address string into ip_part only string
ip_addr_part = addr if valid_ip_addr_part else get_ip_addr_part(addr)

ip_addr_part = adb_addr if valid_ip_addr_part else get_ip_addr_part(adb_addr)
target_port = scan_port(ip_addr_part)
if target_port is None:
error_label.configure(text=I18N(["Port scanning failed, please check the IP address.", "扫描端口失败,请检查 IP 地址是否正确。"]))
waiting_label.configure(text="")
process_data_queue.put(
ProcessError(I18N(["Port scanning failed, please check the IP address.", "扫描端口失败,请检查 IP 地址是否正确。"])))
return
adb_addr = f"{ip_addr_part}:{target_port}"
process_data_queue.put(f"{ip_addr_part}:{target_port}")
elif not valid_ip_str:
error_label.configure(text=I18N(["Invalid connecting address!", "配对地址无效!"]))
# not to_scan_port and ip-port address string is not valid
process_data_queue.put(
ProcessError(I18N(["Invalid connecting address!", "配对地址无效!"])))
else: process_data_queue.put(addr)

def process_callback():
global connecting_window
nonlocal error_label, waiting_label
if process_data_queue.empty():
# wait for ip_port data processed
connecting_window.after(func=process_callback, ms=100)
return

connect_addr = process_data_queue.get()
if type(connect_addr) == ProcessError:
error_label.configure(text=connect_addr.msg)
waiting_label.configure(text="")
enable_widgets()
connecting_window.configure(cursor="arrow")
return

ret = try_connect_device(adb_addr)
assert type(connect_addr) == str
# got valid ip_port string, connect
ret = try_connect_device(connect_addr)
if ret is None:
error_label.configure(text=I18N(["Connecting failed, please retry.", "连接失败!"]))
waiting_label.configure(text="")
return
CONFIG.config.device_ip1 = get_ip_addr_part(adb_addr)
CONFIG.config.device_ip1 = get_ip_addr_part(connect_addr)
connecting_window.destroy()

def enable_widgets():
nonlocal addr_entry, auto_scan_port, button1, button2
for wid in [addr_entry, auto_scan_port, button1, button2]:
wid.configure(state="normal")

def disable_widgets():
nonlocal addr_entry, auto_scan_port, button1, button2
for wid in [addr_entry, auto_scan_port, button1, button2]:
wid.configure(state="disabled")

def connect_callback():
global connecting_window
nonlocal addr_entry, auto_scan_port, error_label, waiting_label
error_label.configure(text=""); error_label.update()
waiting_label.configure(text=I18N(["Connecting, may take some time...", "连接中,可能需要一些时间..."])); waiting_label.update()

disable_widgets()
connecting_window.configure(cursor="watch")

adb_addr = addr_entry.get().strip()
to_scan_port = bool(auto_scan_port.get())
threading.Thread(target=process_ip_port, args=[adb_addr, to_scan_port]).start()
connecting_window.after(func=process_callback, ms=100)

def need_not_connection_callback():
connecting_window.destroy()

Expand Down
26 changes: 10 additions & 16 deletions ui/tray.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,8 @@ def send_clipboard_text():
LOGGER.write(LogType.Error, "Send data error: " + str(e))
exit_tray()

def toggle_share_mouse(_, item: MenuItem):
CONFIG.config.share_mouse = not item.checked
def toggle_share_keyboard(_, item: MenuItem):
CONFIG.config.share_keyboard = not item.checked
def toggle_share_keyboard_only(_, item: MenuItem):
CONFIG.config.share_keyboard_only = not item.checked
def toggle_sync_clipboard(_, item: MenuItem):
CONFIG.config.sync_clipboard = not item.checked

Expand All @@ -51,27 +49,23 @@ def exit_tray():
tray_menu = Menu(
MenuItem(
I18N(["Enable sharing", "开启键鼠共享"]),
main_schedule_toggle),
Menu.SEPARATOR,
action=main_schedule_toggle),
MenuItem(
I18N(["Share Mouse", "共享鼠标"]),
action=toggle_share_mouse,
checked=lambda _: CONFIG.config.share_mouse),
I18N(["Send clipboard text", "发送当前剪贴板文本"]),
action=send_clipboard_text),
Menu.SEPARATOR,
MenuItem(
I18N(["Share Keyboard", "共享键盘"]),
action=toggle_share_keyboard,
checked=lambda _: CONFIG.config.share_keyboard),
I18N(["Share keyboard only", "仅共享键盘"]),
action=toggle_share_keyboard_only,
checked=lambda _: CONFIG.config.share_keyboard_only),
MenuItem(
I18N(["Sync clipboard", "同步剪贴板"]),
action=toggle_sync_clipboard,
checked=lambda _: CONFIG.config.sync_clipboard),
MenuItem(
I18N(["Send clipboard text", "发送当前剪贴板文本"]),
send_clipboard_text),
Menu.SEPARATOR,
MenuItem(
I18N(["Exit", "退出"]),
exit_tray),
action=exit_tray),
)
tray = pystray.Icon(
"InputShare",
Expand Down
3 changes: 1 addition & 2 deletions utils/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
class ConfigFile:
device_ip1: str = ""
sync_clipboard: bool = True
share_mouse: bool = True
share_keyboard: bool = True
share_keyboard_only: bool = False

class ConfigManager:
path: str
Expand Down

0 comments on commit 2e7913a

Please sign in to comment.