basic functionality - can move mouse to some extent from a phone
This commit is contained in:
commit
c56d83881a
3 changed files with 240 additions and 0 deletions
104
capybara
Executable file
104
capybara
Executable file
|
@ -0,0 +1,104 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
from pynput.mouse import Button, Controller
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.response import file
|
||||||
|
|
||||||
|
class MessageParser():
|
||||||
|
def __init__(self):
|
||||||
|
self.handlers = {}
|
||||||
|
def add_handler(self, code: str, args={}):
|
||||||
|
self.handlers[code] = {
|
||||||
|
"args": args
|
||||||
|
}
|
||||||
|
def parse(self, message: str):
|
||||||
|
if len(message) < 1:
|
||||||
|
return None, "Message is empty"
|
||||||
|
message_code = message[0]
|
||||||
|
if message_code not in self.handlers:
|
||||||
|
return None, "Message code is not handled"
|
||||||
|
message_arguments = message[1:].split(";")
|
||||||
|
handler = self.handlers[message_code]
|
||||||
|
decoded_arguments = {}
|
||||||
|
if len(handler["args"]) != len(message_arguments):
|
||||||
|
return None, "Got message with invalid argument count"
|
||||||
|
for i, argument_name in enumerate(handler["args"]):
|
||||||
|
argument_type = handler["args"][argument_name]
|
||||||
|
if argument_type == "int":
|
||||||
|
try:
|
||||||
|
decoded_arguments[argument_name] = int(message_arguments[i])
|
||||||
|
except ValueError:
|
||||||
|
return None, "Error parsing int argument for message (is it really an int?)"
|
||||||
|
elif argument_type == "str":
|
||||||
|
decoded_arguments[argument_name] = message_arguments[i]
|
||||||
|
else:
|
||||||
|
raise ValueError("parse(): Message handler references an invalid argument type")
|
||||||
|
|
||||||
|
return message_code, decoded_arguments
|
||||||
|
|
||||||
|
|
||||||
|
class InputController():
|
||||||
|
def __init__(self):
|
||||||
|
self.mouse_controller = Controller()
|
||||||
|
self.button_code_lookup = [
|
||||||
|
Button.left,
|
||||||
|
Button.right
|
||||||
|
]
|
||||||
|
self.parser = MessageParser()
|
||||||
|
|
||||||
|
# Relative mouse movement
|
||||||
|
self.parser.add_handler("r", {
|
||||||
|
"x": "int",
|
||||||
|
"y": "int"
|
||||||
|
})
|
||||||
|
# Mouse button down
|
||||||
|
self.parser.add_handler("d", {
|
||||||
|
"button": "int"
|
||||||
|
})
|
||||||
|
# Mouse button up
|
||||||
|
self.parser.add_handler("u", {
|
||||||
|
"button": "int"
|
||||||
|
})
|
||||||
|
def button_code_to_object(self, button_code: int):
|
||||||
|
# HACK
|
||||||
|
obj = None
|
||||||
|
try:
|
||||||
|
obj = self.button_code_lookup[button_code]
|
||||||
|
except IndexError:
|
||||||
|
return self.button_code_lookup[0]
|
||||||
|
return obj
|
||||||
|
def process_message(self, message: str) -> bool:
|
||||||
|
code, args = self.parser.parse(message)
|
||||||
|
|
||||||
|
if code == None:
|
||||||
|
print("error while parsing message:", args)
|
||||||
|
return False
|
||||||
|
elif code == "r":
|
||||||
|
self.mouse_controller.move(args["x"], args["y"])
|
||||||
|
print(args["x"], args["y"])
|
||||||
|
elif code == "d":
|
||||||
|
self.mouse_controller.press(self.button_code_to_object(args["button"]))
|
||||||
|
elif code == "u":
|
||||||
|
self.mouse_controller.release(self.button_code_to_object(args["button"]))
|
||||||
|
else:
|
||||||
|
print("got invalid code from parser (is this a bug with the MessageParser?)")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
app = Sanic("capybara")
|
||||||
|
app.ctx.input_controller = InputController()
|
||||||
|
|
||||||
|
app.static("/", "./public/index.html")
|
||||||
|
|
||||||
|
@app.websocket("/gateway")
|
||||||
|
async def gateway(req, ws):
|
||||||
|
while True:
|
||||||
|
app.ctx.input_controller.process_message(await ws.recv())
|
||||||
|
|
||||||
|
def main():
|
||||||
|
app.run(host='0.0.0.0', port=4003, access_log=False)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
134
public/index.html
Normal file
134
public/index.html
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const loggerOfType = (components, type='log') => (...args) => {
|
||||||
|
let str = '%c';
|
||||||
|
const style = 'color: #5e81ac; font-weight: bold;';
|
||||||
|
for (const i in components) {
|
||||||
|
const v = components[i];
|
||||||
|
if (components[i+1] === undefined) {
|
||||||
|
str += `[${v}]`;
|
||||||
|
} else {
|
||||||
|
str += `[${v}] `;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (type) {
|
||||||
|
case 'log': {
|
||||||
|
console.log(str, style, ...args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'error': {
|
||||||
|
console.error(str, style, ...args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'warn': {
|
||||||
|
console.warn(str, style, ...args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'genmsg': {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function Logger(components, types=['warn', 'error', 'log']) {
|
||||||
|
const loggerObj = {};
|
||||||
|
|
||||||
|
for (const type of types) {
|
||||||
|
loggerObj[type] = loggerOfType(components, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loggerObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Connection {
|
||||||
|
constructor(url, logMessages=false) {
|
||||||
|
this.ws = null;
|
||||||
|
this.log = Logger(["Connection"], ["log"]).log;
|
||||||
|
this.messageLog = Logger(["Connection", "Message"], ["log"]).log;
|
||||||
|
this.logMessages = logMessages;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
this.ws = new WebSocket(this.url);
|
||||||
|
this.ws.onerror = (e) => this.log("Error", e);
|
||||||
|
this.ws.onopen = () => {
|
||||||
|
this.log("Open");
|
||||||
|
};
|
||||||
|
this.ws.onclose = () => {
|
||||||
|
this.log("Closed - attempting to reconnect in 4000ms");
|
||||||
|
setTimeout(() => this.connect(), 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(code, params=[]) {
|
||||||
|
let message = code;
|
||||||
|
params.forEach((param, i) => {
|
||||||
|
if (i == params.length - 1)
|
||||||
|
message += param;
|
||||||
|
else
|
||||||
|
message += param + ";";
|
||||||
|
});
|
||||||
|
if (this.logMessages) this.messageLog(message);
|
||||||
|
|
||||||
|
this.ws.send(message);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
this.ws.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let connection = new Connection(`ws://${location.host}/gateway`, true);
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
connection.connect();
|
||||||
|
const touchpad = document.querySelector(".touchpad");
|
||||||
|
const movementState = {
|
||||||
|
currentX: 0,
|
||||||
|
currentY: 0,
|
||||||
|
lastCheckedX: 0,
|
||||||
|
lastCheckedY: 0
|
||||||
|
}
|
||||||
|
touchpad.addEventListener("touchmove", (event) => {
|
||||||
|
movementState.currentX = event.touches[0].clientX;
|
||||||
|
movementState.currentY = event.touches[0].clientY;
|
||||||
|
const deltaX = movementState.currentX - movementState.lastCheckedX;
|
||||||
|
const deltaY = movementState.currentY - movementState.lastCheckedY;
|
||||||
|
movementState.lastCheckedX = movementState.currentX;
|
||||||
|
movementState.lastCheckedY = movementState.currentY;
|
||||||
|
connection.sendMessage("r", [deltaX, deltaY]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.touchpad {
|
||||||
|
width: 75vw;
|
||||||
|
height: 50vh;
|
||||||
|
background-color: blue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body onload="main();">
|
||||||
|
<div class="touchpad">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<button onClick="connection.sendMessage('r', [20, 20])">Move mouse test</button>
|
||||||
|
</body>
|
||||||
|
</html>
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pynput
|
||||||
|
sanic
|
Loading…
Reference in a new issue