# -*- coding: utf-8 -*- r'''验「远程鼠标控制」和「一键显示桌面」。 == 为什么要专门测 == 这两条命令能让远端**直接操控电脑的鼠标键盘**,是最"有力量"的功能。 一旦协议名写错、参数漏校验,表现会是"手表点了没反应", 而用户在教室里根本没法排查。 == 三层 == 一、协议常量在不在、值对不对 二、input.py 的坐标归一化算得对不对(纯计算,不真动鼠标) 三、服务端的 _do_mouse 对各种参数的反应(含非法输入) == 为什么不真动鼠标 == 第三层调的是服务端处理函数,它会真的调 input.move_to —— 但那只是把鼠标挪到某个像素,测试机上无所谓。 真正的"灵不灵"得在人看来验,这里保证**逻辑和参数校验**不出错。 (show_desktop 也是真按 Win+D。测试时如果窗口全被最小化了, 那就是它生效了 —— 不是 bug。) ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "windows")) FAILS: list = [] def check(name: str, ok: bool, why: str = "") -> None: print(" [%s] %s%s" % ("PASS" if ok else "FAIL", name, (" —— " + why) if why else "")) if not ok: FAILS.append(name) def main() -> int: print("=" * 58) print(" 远程鼠标 + 一键显示桌面") print("=" * 58) print() print("一、协议常量") from lanremote import protocol as P check("CMD_MOUSE 存在且值是 mouse", getattr(P, "CMD_MOUSE", "") == "mouse") check("CMD_SHOW_DESKTOP 存在且值是 show_desktop", getattr(P, "CMD_SHOW_DESKTOP", "") == "show_desktop") print() print("二、坐标归一化(SendInput 要 0..65535)") from lanremote import input as IN # 直接测归一化算式(把 _screen 固定成 1920x1080,保证可复现) IN._screen_cache["v"] = (0, 0, 1920, 1080) captured = [] real_send = IN._send def fake_send(*inputs): captured.extend(inputs) IN._send = fake_send try: IN.move_to(0, 0) mi = captured[-1].union.mi check("左上角 -> (0,0)", (mi.dx, mi.dy) == (0, 0), "拿到 (%d,%d)" % (mi.dx, mi.dy)) captured.clear() IN.move_to(1919, 1079) mi = captured[-1].union.mi check("右下角 -> (65535,65535)", (mi.dx, mi.dy) == (65535, 65535), "拿到 (%d,%d) —— 除以 (宽-1) 那条规则错了的话这里到不了 65535" % (mi.dx, mi.dy)) captured.clear() IN.move_to(960, 540) mi = captured[-1].union.mi # 960/1919*65535 = 32783.5 -> 32784;540/1079*65535 = 32800.7 -> 32801 check("正中间约等于 32768", abs(mi.dx - 32768) < 60 and abs(mi.dy - 32768) < 60, "拿到 (%d,%d)" % (mi.dx, mi.dy)) captured.clear() IN.move_to(99999, 99999) mi = captured[-1].union.mi check("超范围被夹到右下角", (mi.dx, mi.dy) == (65535, 65535), "拿到 (%d,%d)" % (mi.dx, mi.dy)) captured.clear() IN.move_to(-500, -500) mi = captured[-1].union.mi check("负坐标被夹到左上角", (mi.dx, mi.dy) == (0, 0), "拿到 (%d,%d)" % (mi.dx, mi.dy)) # 滚轮:1 格 = 120 captured.clear() IN.scroll(1) mi = captured[-1].union.mi check("滚轮 1 格 = 120", mi.mouseData == 120, "拿到 %d" % mi.mouseData) captured.clear() IN.scroll(-1) mi = captured[-1].union.mi check("滚轮 -1 格(向下)用无符号表示", mi.mouseData == (0xFFFFFFFF - 119), "拿到 %d" % mi.mouseData) # 夹取:一次滚几百格要拦住 captured.clear() IN.scroll(9999) mi = captured[-1].union.mi check("一次滚 9999 格被夹到 50", mi.mouseData == 50 * 120, "拿到 %d" % mi.mouseData) # 组合键:Win+D 应该是 按下Win,按下D,抬起D,抬起Win captured.clear() IN.show_desktop() check("Win+D 是 4 个事件(按下+抬起各两个)", len(captured) == 4, "拿到 %d 个" % len(captured)) if len(captured) == 4: vks = [i.union.ki.wVk for i in captured] ups = [bool(i.union.ki.dwFlags & IN.KEYEVENTF_KEYUP) for i in captured] check("Win+D 顺序正确(按下 LWIN,D;抬起 D,LWIN)", vks == [IN.VK_LWIN, IN.VK_D, IN.VK_D, IN.VK_LWIN] and ups == [False, False, True, True], "vks=%s ups=%s" % (vks, ups)) finally: IN._send = real_send IN._screen_cache.clear() print() print("三、服务端 _do_mouse 参数校验") import lanremote.server as S cls = None for name in dir(S): c = getattr(S, name) if isinstance(c, type) and hasattr(c, "_do_mouse"): cls = c break check("找到定义了 _do_mouse 的类", cls is not None) if cls is None: return 1 obj = cls.__new__(cls) IN._send = lambda *a: None # 测试里别真动鼠标 try: for action in ("move", "click", "down", "up", "scroll", "desktop", "moveby"): r = obj._do_mouse({"action": action, "x": 10, "y": 10, "amount": 1}) check(" action=%s 返回 ok" % action, r.get("ok") is True, "拿到 %r" % r) r = obj._do_mouse({"action": "不存在的"}) check("非法 action 被拒", r.get("ok") is False and "不认识" in str(r.get("error")), "拿到 %r" % r) r = obj._do_mouse({"action": "click", "button": "第四键"}) check("非法鼠标键被拒", r.get("ok") is False, "拿到 %r" % r) r = obj._do_mouse({"action": "scroll", "amount": "abc"}) check("滚轮量不是数字被拒", r.get("ok") is False, "拿到 %r" % r) r = obj._do_mouse({"action": "move", "x": "abc", "y": 1}) check("坐标不是数字被拒", r.get("ok") is False, "拿到 %r" % r) finally: IN._send = real_send print() print("=" * 58) if FAILS: print(" 失败 %d 项:%s" % (len(FAILS), ", ".join(FAILS))) return 1 print(" 全部通过") return 0 if __name__ == "__main__": sys.exit(main())