Mercurial > tablet-emu
view 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 source
// 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(); }