Mercurial > tablet-emu
diff src/server.rs @ 14:adab13145994
Add support for remote clients.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Mon, 02 Nov 2020 00:06:09 +0100 |
parents | |
children | d103f7cca0bd |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,142 @@ +// Tablet emulator, for people who don’t own one +// Copyright © 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +use std::net::UdpSocket; +use std::io::{self, ErrorKind}; +use crate::state::State; +use bitflags::bitflags; +use input_linux::Key; + +bitflags! { + /// This is the memory layout of the buttons on the 3DS. + #[derive(Default)] + struct Buttons: u32 { + const A = 0x00000001; + const B = 0x00000002; + const SELECT = 0x00000004; + const START = 0x00000008; + const RIGHT = 0x00000010; + const LEFT = 0x00000020; + const UP = 0x00000040; + const DOWN = 0x00000080; + const R = 0x00000100; + const L = 0x00000200; + const X = 0x00000400; + const Y = 0x00000800; + // Nothing + // Nothing + const ZL = 0x00004000; + const ZR = 0x00008000; + // Nothing + // Nothing + // Nothing + // Nothing + const TOUCH = 0x00100000; + // Nothing + // Nothing + // Nothing + const C_RIGHT = 0x01000000; + const C_LEFT = 0x02000000; + const C_UP = 0x04000000; + const C_DOWN = 0x08000000; + const CIRCLE_RIGHT = 0x10000000; + const CIRCLE_LEFT = 0x20000000; + const CIRCLE_UP = 0x40000000; + const CIRCLE_DOWN = 0x80000000; + } +} + +#[derive(Debug, Default)] +struct Event { + buttons: Buttons, + pad: (i16, i16), + c_pad: (i16, i16), + touch: (u16, u16), +} + +pub fn run_server(address: &str) -> io::Result<()> { + let mut state = match State::new() { + Ok(state) => state, + Err(err) => { + match err.kind() { + ErrorKind::NotFound => { + eprintln!("Couldn’t find /dev/uinput: {}", err); + eprintln!("Maybe you forgot to `modprobe uinput`?"); + } + ErrorKind::PermissionDenied => { + eprintln!("Couldn’t open /dev/uinput for writing: {}", err); + eprintln!("Maybe you aren’t allowed to create input devices?"); + } + _ => eprintln!("Couldn’t open /dev/uinput for writing: {}", err), + } + std::process::exit(1); + } + }; + + let socket = UdpSocket::bind(address)?; + println!("Listening on {:?}", socket); + println!("Here is an example client: https://hg.linkmauve.fr/remote-gamepad"); + + let mut event: Event = Default::default(); + let mut last = Some((0., 0.)); + state.set_size((320, 240)); + loop { + // TODO: Yolo-alignment. + let buf: &mut [u8; 16] = unsafe { std::mem::transmute(&mut event) }; + let (amount, source) = socket.recv_from(buf)?; + if amount != std::mem::size_of::<Event>() { + eprintln!("Invalid data length: {}", amount); + continue; + } + println!("{:?} from {:?}", event, source); + if event.buttons.contains(Buttons::A) { + state.select_tool(Key::ButtonToolPen); + } else if event.buttons.contains(Buttons::B) { + state.select_tool(Key::ButtonToolRubber); + } else if event.buttons.contains(Buttons::X) { + state.select_tool(Key::ButtonToolBrush); + } else if event.buttons.contains(Buttons::Y) { + state.select_tool(Key::ButtonToolPencil); + } else if event.buttons.contains(Buttons::SELECT) { + state.select_tool(Key::ButtonToolAirbrush); + } + let (x, y) = event.touch; + if event.buttons.contains(Buttons::TOUCH) { + if let None = last { + state.press(x as f64, y as f64)?; + last = Some((x as f64, y as f64)); + continue; + } + } else { + if let Some((x, y)) = last { + state.release(x, y)?; + last = None; + continue; + } + } + state.motion(x as f64, y as f64)?; + last = Some((x as f64, y as f64)); + } +} + +pub fn main(args: &[String]) { + let address = if args.len() > 1 { + args[1].clone() + } else { + String::from("0.0.0.0:16150") + }; + run_server(&address).unwrap(); +}