Mercurial > tablet-emu
comparison 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 |
comparison
equal
deleted
inserted
replaced
13:97e543f50f62 | 14:adab13145994 |
---|---|
1 // Tablet emulator, for people who don’t own one | |
2 // Copyright © 2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> | |
3 // | |
4 // This program is free software: you can redistribute it and/or modify | |
5 // it under the terms of the GNU Affero General Public License as published by | |
6 // the Free Software Foundation, either version 3 of the License, or | |
7 // (at your option) any later version. | |
8 // | |
9 // This program is distributed in the hope that it will be useful, | |
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 // GNU Affero General Public License for more details. | |
13 // | |
14 // You should have received a copy of the GNU Affero General Public License | |
15 // along with this program. If not, see <https://www.gnu.org/licenses/>. | |
16 | |
17 use std::net::UdpSocket; | |
18 use std::io::{self, ErrorKind}; | |
19 use crate::state::State; | |
20 use bitflags::bitflags; | |
21 use input_linux::Key; | |
22 | |
23 bitflags! { | |
24 /// This is the memory layout of the buttons on the 3DS. | |
25 #[derive(Default)] | |
26 struct Buttons: u32 { | |
27 const A = 0x00000001; | |
28 const B = 0x00000002; | |
29 const SELECT = 0x00000004; | |
30 const START = 0x00000008; | |
31 const RIGHT = 0x00000010; | |
32 const LEFT = 0x00000020; | |
33 const UP = 0x00000040; | |
34 const DOWN = 0x00000080; | |
35 const R = 0x00000100; | |
36 const L = 0x00000200; | |
37 const X = 0x00000400; | |
38 const Y = 0x00000800; | |
39 // Nothing | |
40 // Nothing | |
41 const ZL = 0x00004000; | |
42 const ZR = 0x00008000; | |
43 // Nothing | |
44 // Nothing | |
45 // Nothing | |
46 // Nothing | |
47 const TOUCH = 0x00100000; | |
48 // Nothing | |
49 // Nothing | |
50 // Nothing | |
51 const C_RIGHT = 0x01000000; | |
52 const C_LEFT = 0x02000000; | |
53 const C_UP = 0x04000000; | |
54 const C_DOWN = 0x08000000; | |
55 const CIRCLE_RIGHT = 0x10000000; | |
56 const CIRCLE_LEFT = 0x20000000; | |
57 const CIRCLE_UP = 0x40000000; | |
58 const CIRCLE_DOWN = 0x80000000; | |
59 } | |
60 } | |
61 | |
62 #[derive(Debug, Default)] | |
63 struct Event { | |
64 buttons: Buttons, | |
65 pad: (i16, i16), | |
66 c_pad: (i16, i16), | |
67 touch: (u16, u16), | |
68 } | |
69 | |
70 pub fn run_server(address: &str) -> io::Result<()> { | |
71 let mut state = match State::new() { | |
72 Ok(state) => state, | |
73 Err(err) => { | |
74 match err.kind() { | |
75 ErrorKind::NotFound => { | |
76 eprintln!("Couldn’t find /dev/uinput: {}", err); | |
77 eprintln!("Maybe you forgot to `modprobe uinput`?"); | |
78 } | |
79 ErrorKind::PermissionDenied => { | |
80 eprintln!("Couldn’t open /dev/uinput for writing: {}", err); | |
81 eprintln!("Maybe you aren’t allowed to create input devices?"); | |
82 } | |
83 _ => eprintln!("Couldn’t open /dev/uinput for writing: {}", err), | |
84 } | |
85 std::process::exit(1); | |
86 } | |
87 }; | |
88 | |
89 let socket = UdpSocket::bind(address)?; | |
90 println!("Listening on {:?}", socket); | |
91 println!("Here is an example client: https://hg.linkmauve.fr/remote-gamepad"); | |
92 | |
93 let mut event: Event = Default::default(); | |
94 let mut last = Some((0., 0.)); | |
95 state.set_size((320, 240)); | |
96 loop { | |
97 // TODO: Yolo-alignment. | |
98 let buf: &mut [u8; 16] = unsafe { std::mem::transmute(&mut event) }; | |
99 let (amount, source) = socket.recv_from(buf)?; | |
100 if amount != std::mem::size_of::<Event>() { | |
101 eprintln!("Invalid data length: {}", amount); | |
102 continue; | |
103 } | |
104 println!("{:?} from {:?}", event, source); | |
105 if event.buttons.contains(Buttons::A) { | |
106 state.select_tool(Key::ButtonToolPen); | |
107 } else if event.buttons.contains(Buttons::B) { | |
108 state.select_tool(Key::ButtonToolRubber); | |
109 } else if event.buttons.contains(Buttons::X) { | |
110 state.select_tool(Key::ButtonToolBrush); | |
111 } else if event.buttons.contains(Buttons::Y) { | |
112 state.select_tool(Key::ButtonToolPencil); | |
113 } else if event.buttons.contains(Buttons::SELECT) { | |
114 state.select_tool(Key::ButtonToolAirbrush); | |
115 } | |
116 let (x, y) = event.touch; | |
117 if event.buttons.contains(Buttons::TOUCH) { | |
118 if let None = last { | |
119 state.press(x as f64, y as f64)?; | |
120 last = Some((x as f64, y as f64)); | |
121 continue; | |
122 } | |
123 } else { | |
124 if let Some((x, y)) = last { | |
125 state.release(x, y)?; | |
126 last = None; | |
127 continue; | |
128 } | |
129 } | |
130 state.motion(x as f64, y as f64)?; | |
131 last = Some((x as f64, y as f64)); | |
132 } | |
133 } | |
134 | |
135 pub fn main(args: &[String]) { | |
136 let address = if args.len() > 1 { | |
137 args[1].clone() | |
138 } else { | |
139 String::from("0.0.0.0:16150") | |
140 }; | |
141 run_server(&address).unwrap(); | |
142 } |