吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7985|回复: 55
收起左侧

[原创工具] 我为52pj搞原创 【空鼠】 手机陀螺仪控制电脑端鼠标(有源码)

  [复制链接]
Pwaerm 发表于 2023-1-28 10:25
本帖最后由 Pwaerm 于 2023-1-28 10:36 编辑

折腾了一早上,利用安卓手机的陀螺仪控制鼠标,理论上(理论上)可以隔空玩弄,像激光笔一样指哪里打哪里。
不过精准性还有待待待提高。

使用方法:
1  电脑端打开,mouseFly.exe,在右下角显示出一个黑猫图标即表示运行成功(测试环境WIN11 Python3.10)。 双击黑猫可隐藏黑猫,但程序并未退出(因为我不知道如何结束 asyncio.get_event_loop()
2  安卓安卓安卓  手机安装   P娃儿猫mouseFly_v9.0.2.apk  ,必须开启悬浮窗权限,启动以后没有界面,但会显示一个悬浮的黑猫(自动吸附在屏幕边缘),然后根据提示输入电脑端的IP地址加端口(默认端口2309),连接上就开始晃动手机吧,电脑上的鼠标会飞起来的。
    (1) 手机沿X轴方向(俯仰)转动,控制鼠标上下。
     (2) 手机沿z轴方向(拧螺丝)转动,控制鼠标左右。(为什么不是Y轴,因为我的手机不准,其它手机就不知道了,代码中有注释,可以改)。
      (3)点击黑猫图标单击鼠标左键,长按黑猫退出程序(尤其是发现电脑端鼠标失控时)。
image.png image.png


image.png




老规则,上源码:

下载地址:https://pan.baidu.com/s/1W9RbrEIu0J3402DccRHOoQ?pwd=4qm1
提取码:4qm1

    电脑端 win11 pypthon3.10.9  
[Asm] 纯文本查看 复制代码
import asyncio
import websockets
import pyautogui
import json
import os
from infi.systray import SysTrayIcon
sw, sh = pyautogui.size()
pyautogui.FAILSAFE = False


async def deal(websocket, path):
    async for message in websocket:
        try:
            o = json.loads(message)
            if (o.get("type") is not None):
                if (o["type"] == "move"):
                    pyautogui.moveTo(o["x"]/10000*sw, o["y"]/10000*sh)
                    # pyautogui.moveTo(0.5*sw, o["y"]/3000*sh)
                if (o["type"] == "mouseDown"):
                    print("server:", message)
                    pyautogui.click()
        except Exception as e:
            print(str(e))


def onExit(*self, **args):
    print("exit")


systray = SysTrayIcon(os.path.dirname(__file__) + "/Pwaerm.ico", "P娃儿猫鼠标助手", (), on_quit=onExit)
systray.start()
try:
    startServer = websockets.serve(deal, "", 2309)
    asyncio.get_event_loop().run_until_complete(startServer)
    asyncio.get_event_loop().run_forever()
except Exception as e:
    print(str(e))
    systray.shutdown()


安卓端,基于auto.jsPro 8.3.16制作:

[Asm] 纯文本查看 复制代码
//-----------------------悬浮窗------------------------------------
function showAdvancedBtn() {
    if (advancedWin) {
        advancedWin.box.attr("alpha", "0.9");
        advancedWin.setTouchable(true);
        return;
    }
    //<fab id="advanced" src="@drawable/ic_create_black_48dp" w="auto" h="auto" margin="20" tint="#000000" alpha="0.5" />
    advancedWin = floaty.rawWindow(
        <frame id="box" alpha="0.9">
            <img id="advanced" circle="true" src="http://www.flash023.cn/Pwaerm/icon/Pwaerm.png" w="50" h="50" margin="20" alpha="0.8" />
        </frame>
    );
    setTimeout(function () {
        advancedWin.setPosition(device.width * 0.8, device.height * 0.7);
        advancedWin.setTouchable(true);
    }, 100);
    function getDistance(_d, _d2) {
        var _xd = Math.abs(_d.x - _d2.x);
        var _yd = Math.abs(_d.y - _d2.y);
        return Math.sqrt(_xd * _xd + _yd * _yd);
    }
    function moveTo(_obj, _point, _t) {
        var __t = _t * 1000 / 30;//每秒10帧
        var _T = setInterval(function () {
            var _x = _obj.getX() + (_point.x - _obj.getX()) * 0.3;
            var _y = _obj.getY() + (_point.y - _obj.getY()) * 0.3;
            _obj.setPosition(_x, _y);
            if (getDistance({ x: _x, y: _y }, _point) < 90) {
                _obj.setPosition(_point.x, _point.y);
                clearInterval(_T);
            }
        }, __t);
    }
    //记录按键被按下时的触摸坐标
    var downPoint;
    //记录按键被按下时的悬浮窗位置
    var windowPoint;
    //记录按键被按下的时间以便判断长按等动作
    var downTime;
    var downInterval;
    var offsetX = 80;
    var isLongTouch = false;
    advancedWin.advanced.setOnTouchListener(function (view, event) {
        switch (event.getAction()) {
            case event.ACTION_DOWN:
                downPoint = { x: event.getRawX(), y: event.getRawY() };
                windowPoint = { x: advancedWin.getX(), y: advancedWin.getY() };
                downTime = new Date().getTime();
                isLongTouch = false;
                downInterval = setInterval(() => {
                    var _d = getDistance({ x: event.getRawX(), y: event.getRawY() }, downPoint);
                    //console.log(_d < 100);
                    if (new Date().getTime() - downTime > 1600 && _d < 100) {
                        isLongTouch = true;
                        console.log("长按");
                        clearInterval(downInterval);
                        stopOtherEngines(true);
                    } else {
                        if (_d > 100) {
                            clearInterval(downInterval);
                        }
                    }
                }, 100);
                return true;
            case event.ACTION_MOVE:
                if (isLongTouch) {
                    return true;
                }
                //移动手指时调整悬浮窗位置
                advancedWin.setPosition(windowPoint.x + (event.getRawX() - downPoint.x), windowPoint.y + (event.getRawY() - downPoint.y));
                return true;
            case event.ACTION_UP:
                if (downInterval != undefined) {
                    clearInterval(downInterval);
                }
                if (isLongTouch) {
                    return true;
                }
                //手指弹起时如果偏移很小则判断为点击
                if (getDistance({ x: event.getRawX(), y: event.getRawY() }, downPoint) < 100) {
                    advancedWin.setPosition(windowPoint.x, windowPoint.y);
                    if (new Date().getTime() - downTime < 1000) {
                        console.log("短按");
                        events.broadcast.emit("MOUSE_DOWN", "");
                    }
                    if (new Date().getTime() - downTime > 3000) {
                        console.log("长按后弹起");
                    }
                } else {
                    var _x = event.getRawX() > device.width * 0.5 ? device.width - advancedWin.width * 0.5 - offsetX : offsetX - advancedWin.width * 0.5;
                    var _y = windowPoint.y + (event.getRawY() - downPoint.y);
                    if (event.getRawY() < device.height * 0.1) {
                        _y = device.height * 0.1;
                    }
                    if (event.getRawY() > device.height * 0.8) {
                        _y = device.height * 0.8;
                    }
                    _y -= advancedWin.height * 0.5;
                    moveTo(advancedWin, { x: _x, y: _y }, 0.5);
                }
                return true;
        }
        return true;
    });
}
function stopOtherEngines(_exit) {
    if (workThread && workThread.isAlive()) {
        workThread.interrupt();
    }
    workThread = null;
    if (_exit) {
        engines.myEngine().forceStop();
    }

}
function startWorkEngines() {
    stopOtherEngines();
    workThread = threads.start(workHandler);
}
function showUrlInput(_title) {
    var _url = rawInput(_title, storage.get("url"));
    if (_url) {
        if (_url && _url.length >= 4) {
            storage.put("url", _url);
            showContentDialog("地址已经保存,尝试连接中...");
            showAdvancedBtn();
        }
    };
}
if (typeof storage == "undefined") {
    storage = storages.create("mouseFly");
}
function showContentDialog(_title) {
    hideConnectDialog();
    connectDialog = dialogs.build({
        title: _title,
        progress: {
            max: -1
        },
        cancelable: false
    }).show();
    if (!_title) {
        toastLog("呼叫超时,请检查...");
    }
    setTimeout(hideConnectDialog, 5000);
}
function hideConnectDialog() {
    if (connectDialog) {
        connectDialog.dismiss();
        connectDialog = null;
    }
}
function init() {
    if (!storage.get("url")) {
        showUrlInput("请输入电脑端的IP地址和端口号:");
    } else {
        showContentDialog("正在呼叫电脑端...");
        showAdvancedBtn();
    }
    events.broadcast.on("SHOW_URL_INPUT", showUrlInput);
    events.broadcast.on("HIDE_CONNECT_DIALOG", hideConnectDialog)
    startWorkEngines();
    setInterval(() => { }, 1000);
}
var advancedWin;
var workThread;
var connectDialog;
//-------------------------主要功能区-------------------------
function workHandler() {
    var azimuth;
    var data;
    sensors.register("gyroscope").on("change", (event, _x, _y, _z) => {
        //console.log(Math.floor(_y * 1000));
    })
    sensors.register("orientation").on("change", (event, _x, _y, _z) => {
        /*
        event SensorEvent 传感器事件,用于获取传感器数据变化时的所有信息
        _x {number} 方位角,从地磁指北方向线起,依顺时针方向到y轴之间的水平夹角,单位角度,范围0~359
        _y {number} 绕x轴旋转的角度,当设备水平放置时该值为0,当设备顶部翘起时该值为正数,当设备尾部翘起时该值为负数,单位角度,范围-180~180
        _z {number} 绕z轴顺时针旋转的角度,单位角度,范围-90~90
        */
        //我的手机指南针方向不准,所以改为Z轴方向
        _x = -_z;
        if (azimuth == undefined) {
            azimuth = Math.floor(_x);
        }
        var _v = 20;
        _x -= azimuth;
        if (_x < -_v) {
            _x = -_v;
        }
        if (_x > _v) {
            _x = _v;
        }
        _x = Math.floor((_v + _x) / (_v * 2) * 10000);
        //console.log(_x)
        //限制垂直方向的角度为+-20,精度为10000
        if (_y < -_v) {
            _y = -_v;
        }
        if (_y > _v) {
            _y = _v;
        }
        _y = Math.floor((_v + _y) / (_v * 2) * 10000);
        data = { type: "move", x: _x, y: _y };
        if (ws) {
            ws.send(JSON.stringify(data));
        }
    });
    events.broadcast.on("MOUSE_DOWN", () => {
        if (ws) {
            console.log("---------------短按------------------");
            data = { type: "mouseDown" };
            ws.send(JSON.stringify(data));
        }
    })
    function socketInit() {
        if (!storage.get("url")) {
            log(storage.get("url"));
            events.broadcast.emit("SHOW_URL_INPUT", "");
            return;
        }
        ws = web.newWebSocket("ws://" + storage.get("url"), {
            eventThread: 'this'
        });
        ws.on("open", (res, ws) => {
            events.broadcast.emit("HIDE_CONNECT_DIALOG", "");
            toastLog("WebSocket连接成功!");
        })
        ws.on("failure", (err, res, _ws) => {
            //log("WebSocket连接失败");
            //console.error(err);
            toastLog("socket连接失败,请确认电脑端软件已经开启且地址输入正确。");
            events.broadcast.emit("HIDE_CONNECT_DIALOG", "");
            events.broadcast.emit("SHOW_URL_INPUT", "socket连接失败,请确认电脑端软件已经开启且地址输入正确。");
            ws.removeAllListeners();
            ws = null;
            setTimeout(socketInit, 5000);
        })
        ws.on("closed", (code, reason, _ws) => {
            ws.removeAllListeners();
            ws = null;
        });
    }
    var ws;
    socketInit();
    setInterval(() => { }, 3000);
}
init();




免费评分

参与人数 12吾爱币 +16 热心值 +12 收起 理由
36587285 + 1 谢谢@Thanks!
Black小衰 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Gzsod + 1 + 1 热心回复!
baoziking + 1 + 1 谢谢@Thanks!
wshq + 1 + 1 谢谢@Thanks!
HWinZnieJ + 1 + 1 谢谢@Thanks!
ll165 + 1 我很赞同!
yanchangshan + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
风之暇想 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
笙若 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
shamemist + 1 + 1 用心讨论,共获提升!
牛逼Plus + 1 + 1 加油,听起来就是个不错的想法

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

tatami 发表于 2023-1-31 00:30
之前电脑上玩塞尔达传说的时候,遇到需要通过陀螺仪控制球滚动的神庙,手柄没有陀螺仪,在网上找了个工具,不需要手机安装,只要电脑打开软件,手机浏览器输入对应地址就能控制
 楼主| Pwaerm 发表于 2023-1-28 13:41
sundeheng 发表于 2023-1-28 13:38
我已经被迫重启了,我pc右下角有那个图标,手机和pC也在一个局域网,填的ip也对。但就是连不上{:301_973: ...

要填ip:端口

如  192.168.0.9:2309

如果确认没有填写错误,仍然连接不上。我也不懂
才学python
Phantom可 发表于 2023-1-28 10:34
小懒虫丶 发表于 2023-1-28 10:39
支持原创,学习一下
约定的童话 发表于 2023-1-28 10:41
使用电脑摄像头捕捉手势进行隔空操作更实用一点,楼主可以研究下
 楼主| Pwaerm 发表于 2023-1-28 10:48
约定的童话 发表于 2023-1-28 10:41
使用电脑摄像头捕捉手势进行隔空操作更实用一点,楼主可以研究下

已经有很成熟的软件了,很多白板就是利用投影机上的摄像头来捕捉手势的。
okij12589 发表于 2023-1-28 11:32
通过WebSocket连接,简直是神操作啊,从来没想过这玩意儿还能这么用?
我还以为只能做聊天呢
koisiqi 发表于 2023-1-28 11:37
人才啊脑洞大开
tyy2020 发表于 2023-1-28 11:51
没装百度云,有其他网盘吗
头像被屏蔽
东方天涯 发表于 2023-1-28 12:02
提示: 作者被禁止或删除 内容自动屏蔽
 楼主| Pwaerm 发表于 2023-1-28 12:04
tyy2020 发表于 2023-1-28 11:51
没装百度云,有其他网盘吗

抱歉,没有呢。因为我百度云充值了,所以平常使用依赖上了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-24 20:36

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表