• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Lightweight cross-platform joystick input library


Commit MetaInfo

Revision2aff5840f06ea2008a09f02a351acc4ebc976eb1 (tree)
Zeit2021-06-12 07:15:08
AutorAlaskanEmily <emily@alas...>
CommiterAlaskanEmily

Log Message

Add Linux evdev backend.

Ändern Zusammenfassung

Diff

--- a/SConstruct
+++ b/SConstruct
@@ -5,6 +5,13 @@ import os
55 import sys
66 import glob
77
8+def GetLinkableLibrary(lib):
9+ if os.name == 'nt':
10+ if len(lib) == 1:
11+ return lib[0]
12+ else:
13+ return lib[1]
14+
815 AddOption('--target', dest = 'target', nargs=1, action='store', help =
916 "Override target architecture.")
1017
@@ -26,6 +33,9 @@ AddOption('--enable-dinput', dest = 'enable-dinput', nargs=1, action='store', he
2633 AddOption('--enable-bsd', dest = 'enable-bsd', nargs=1, action='store', help =
2734 "Use the BSD backend libusbhid, enabled by default on BSD systems")
2835
36+AddOption('--enable-evdev', dest = 'enable-evdev', nargs=1, action='store', help =
37+"Use the evdev backend, enabled by default on Linux and FreeBSD systems")
38+
2939 AddOption('--build-shared', dest = 'build-shared', nargs=1, action='store', help =
3040 "Build shared library, disabled by default.")
3141
@@ -57,6 +67,7 @@ def DefaultDriverOption(Driver, Default):
5767 DefaultDriverOption('xinput', os.name == 'nt')
5868 DefaultDriverOption('dinput', os.name == 'nt')
5969 DefaultDriverOption('bsd', 'bsd' in sys.platform.lower())
70+DefaultDriverOption('evdev', 'freebsd' in sys.platform.lower() or 'linux' in sys.platform.lower())
6071
6172 if os.name == 'nt':
6273 rejoy_build_shared = True
@@ -73,66 +84,77 @@ if GetOption('build-shared-modules') == 'y':
7384 else:
7485 rejoy_build_shared_modules = False
7586
87+if rejoy_build_shared:
88+ if os.name == 'nt':
89+ environment.Append(CPPDEFINES=["REJOY_INTERNAL_DLL=1"])
90+ environment.Append(CPPDEFINES=["REJOY_SHARED=1"])
91+else:
92+ environment.Append(CPPDEFINES=["REJOY_SHARED=0"])
93+
7694 rejoy_util = SConscript(dirs=["util"], exports=["CC", "environment"])
7795
7896 environment.Append(CPPPATH=os.path.join(os.getcwd(), "util"))
7997
8098 # Default source
8199 rejoy_source = ["rejoy.cpp", "rejoy_c.cpp"]
82-rejoy_libs = [rejoy_util]
100+rejoy_libs = []
83101
84102 # Dict of libraries indexed by driver backend.
85103 rejoy_lib_dict = {
86104 "dinput":["dinput8", "dxguid.lib", "user32"],
87105 "xinput":["xinput"],
88- "bsd":["usbhid"]
106+ "bsd":["usbhid"],
107+ "evdev":["dl", "pthread"]
89108 }
90109
91110 # Drivers which require the Unix source to be included.
92-rejoy_unix_drivers = [
111+rejoy_unix_drivers = (
93112 "bsd",
94113 "joy",
95- "linux"
96-]
114+ "evdev"
115+)
97116
98117 # Add up the drivers, also checking for requirement of unix.
99118 use_unix = False
100119 for driver in rejoy_drivers:
101- if driver in rejoy_lib_dict:
102- rejoy_libs += rejoy_lib_dict[driver]
103120 if driver in rejoy_unix_drivers:
104121 use_unix = True
122+ break
123+
124+if use_unix:
125+ rejoy_unix = SConscript(dirs=["unix"], exports=["environment"])
126+
127+depends_on_unix = []
128+for driver in rejoy_drivers:
129+ if driver in rejoy_lib_dict:
130+ rejoy_libs += rejoy_lib_dict[driver]
105131 # Check if there is a SConscript file to read or not.
106132 if os.path.isfile(os.path.join(driver, "SConscript")):
107- rejoy_libs.append(SConscript(dirs=[driver], exports=["CC", "environment", "rejoy_build_shared"]))
133+ lib = SConscript(dirs=[driver], exports=["CC", "environment", "rejoy_build_shared"])
134+ rejoy_libs.append(lib)
135+ if driver in rejoy_unix_drivers:
136+ environment.Depends(lib, rejoy_unix)
137+ environment.Depends(lib, rejoy_util)
108138 else:
109139 rejoy_source += glob.glob(os.path.join(driver, "*.cpp"))
110140 rejoy_source += glob.glob(os.path.join(driver, "*.c"))
111141 environment.Append(CPPDEFINES=["REJOY_DRIVER_" + driver.upper() + "=1"])
112142
143+rejoy_dep_libs = rejoy_libs + [rejoy_util]
113144 if use_unix:
114- rejoy_source += glob.glob(os.path.join("unix", "*.cpp"))
115- rejoy_source += glob.glob(os.path.join("unix", "*.c"))
145+ rejoy_dep_libs.append(rejoy_unix)
116146
117147 if rejoy_build_shared:
118- if os.name == 'nt':
119- environment.Append(CPPDEFINES=["REJOY_INTERNAL_DLL=1"])
120- environment.Append(CPPDEFINES=["REJOY_SHARED=1"])
121- rejoy = environment.SharedLibrary('rejoy', rejoy_source, LIBS=rejoy_libs)
148+ rejoy = environment.SharedLibrary('rejoy', rejoy_source, LIBS=rejoy_dep_libs)
122149 else:
123- environment.Append(CPPDEFINES=["REJOY_SHARED=0"])
124- rejoy = environment.StaticLibrary('rejoy', rejoy_source, LIBS=rejoy_libs)
125-
126-if os.name == 'nt':
127- if len(rejoy) == 1:
128- rejoy = rejoy[0]
129- else:
130- rejoy = rejoy[1]
150+ rejoy = environment.StaticLibrary('rejoy', rejoy_source, LIBS=rejoy_dep_libs)
131151
132-demo_libs = [rejoy]
133152 if not rejoy_build_shared:
134- demo_libs += rejoy_libs
135-
153+ demo_libs = [rejoy] + rejoy_libs + [rejoy_util]
154+ if use_unix:
155+ demo_libs.append(rejoy_unix)
156+else:
157+ demo_libs = [rejoy]
136158
137159 demo = environment.Program(['rejoy_demo.cpp'], LIBS=demo_libs)
138160
--- /dev/null
+++ b/evdev/Cargo.toml
@@ -0,0 +1,11 @@
1+[package]
2+name = "rejoy_evdev"
3+version = "0.0.1"
4+authors = ["AlaskanEmily"]
5+
6+[dependencies]
7+ioctl-sys = "0.7.*"
8+
9+[lib]
10+crate-type = ["staticlib", "cdylib"]
11+
--- /dev/null
+++ b/evdev/SConscript
@@ -0,0 +1,65 @@
1+# Any copyright is dedicated to the Public Domain.
2+# http://creativecommons.org/publicdomain/zero/1.0/
3+import os
4+import glob
5+
6+Import("environment rejoy_build_shared")
7+
8+# We use cxxflags.txt as a pseudo-source.
9+# This is used by both Cargo and SCons to force a rebuild if CXXFLAGS change.
10+# It might be possible to avoid this if we could build the shim within SCons,
11+# however since it requires some files generated by the Rust build script we
12+# would need to invoke the Rust build process multiple times.
13+#
14+# This also allows us to (potentially) parse the args here in Python and then
15+# give them as line-separated strings to Rust, then std::process::Command::args
16+# will properly understand them.
17+
18+env = os.environ
19+
20+# Set the CXX/CC value to be consumed by Cargo
21+env["CXX"] = str(environment["CXX"])
22+env["CC"] = str(environment["CC"])
23+
24+# Pass the location of rejoy_util in an environment variable.
25+
26+DELIMIT = '\n'
27+CXXFLAGSPATH = "cxxflags.txt"
28+
29+# Write the CXX flags, if they have changed.
30+cxxflags_file = None
31+try:
32+ cxxflags_file = open(CXXFLAGSPATH, "r")
33+ cxxflags = cxxflags.read()
34+except:
35+ cxxflags = ""
36+finally:
37+ if cxxflags_file:
38+ cxxflags_file.close()
39+
40+# Construct the new flags
41+new_cxxflags = str(environment["CXXFLAGS"]).split()
42+if rejoy_build_shared and "-fPIC" not in new_cxxflags and "-fpic" not in new_cxxflags:
43+ new_cxxflags.append("-fPIC")
44+old_cxxflags = cxxflags.split(DELIMIT)
45+
46+if new_cxxflags != old_cxxflags:
47+ cxxflags_file = open(CXXFLAGSPATH, "w")
48+ cxxflags_file.write(DELIMIT.join(new_cxxflags))
49+ cxxflags_file.close()
50+
51+rust_lib="librejoy_evdev" + environment['LIBSUFFIX']
52+src = []
53+
54+for s in ("rejoy_evdev.hpp", "rejoy_evdev.cpp", "build.rs", CXXFLAGSPATH):
55+ src.append(os.path.join(os.getcwd(), s))
56+for s in glob.glob("Cargo.*") + glob.glob("src/*.rs"):
57+ src.append(os.path.join(os.getcwd(), s))
58+
59+rejoy_evdev_rs = environment.Command(
60+ target=os.path.join(os.getcwd(), "target", "debug", rust_lib),
61+ source=src,
62+ action="cargo build",
63+ chdir=os.getcwd())
64+
65+Return("rejoy_evdev_rs")
--- /dev/null
+++ b/evdev/rejoy_evdev.cpp
@@ -0,0 +1,122 @@
1+// Copyright (c) 2021 Alaskan Emily, Transnat Games
2+//
3+// This Source Code Form is subject to the terms of the Mozilla Public
4+// License, v. 2.0. If a copy of the MPL was not distributed with this
5+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
7+#include "rejoy_evdev.hpp"
8+#include "../rejoy_private.hpp"
9+#include "../unix/rejoy_unix_core.h"
10+#include <unistd.h>
11+#include <fcntl.h>
12+
13+///////////////////////////////////////////////////////////////////////////////
14+// Manual bindings. These are irregular, one-off functions.
15+extern "C" {
16+
17+///////////////////////////////////////////////////////////////////////////////
18+// Gamepads functions
19+struct Rejoy_Evdev_Gamepad *Rejoy_Evdev_OpenGamepad(int fd);
20+struct Rejoy_Evdev_Gamepad *Rejoy_Evdev_CopyGamepad(
21+ const struct Rejoy_Evdev_Gamepad *data);
22+void Rejoy_Evdev_FinalizeGamepad(struct Rejoy_Evdev_Gamepad *data);
23+void Rejoy_Evdev_UpdateGamepad(struct Rejoy_Evdev_Gamepad *data, int fd);
24+
25+///////////////////////////////////////////////////////////////////////////////
26+// Rust Vec bindings.
27+void *Rejoy_Evdev_VecAppend(void *vec, void *val);
28+unsigned Rejoy_Evdev_VecGetLen(const void *vec);
29+Rejoy::EvdevGamepad *Rejoy_Evdev_VecGetValue(void *vec, unsigned i);
30+void Rejoy_Evdev_FinalizeVec(void *vec);
31+
32+///////////////////////////////////////////////////////////////////////////////
33+
34+} // extern "C
35+
36+///////////////////////////////////////////////////////////////////////////////
37+
38+namespace Rejoy {
39+
40+///////////////////////////////////////////////////////////////////////////////
41+
42+static inline void DeleteGamepads(void *vec) {
43+ const unsigned len = Rejoy_Evdev_VecGetLen(vec);
44+ for(unsigned i = 0; i < len; i++){
45+ delete Rejoy_Evdev_VecGetValue(vec, i);
46+ }
47+ Rejoy_Evdev_FinalizeVec(vec);
48+}
49+
50+///////////////////////////////////////////////////////////////////////////////
51+
52+EvdevGamepad::EvdevGamepad(const EvdevGamepad &other)
53+ : UnixGamepad(other)
54+ , m_data(Rejoy_Evdev_CopyGamepad(other.m_data)) {}
55+
56+///////////////////////////////////////////////////////////////////////////////
57+
58+EvdevGamepad::~EvdevGamepad() {
59+ Rejoy_Evdev_FinalizeGamepad(m_data);
60+}
61+
62+///////////////////////////////////////////////////////////////////////////////
63+
64+void EvdevGamepad::update() {
65+ Rejoy_Evdev_UpdateGamepad(m_data, fd());
66+}
67+
68+///////////////////////////////////////////////////////////////////////////////
69+
70+void EvdevDriver::enumerateGamepad(int fd){
71+ // TODO: This could be handled totally on the Rust side pretty easily.
72+ if(m_num_gamepads)
73+ return;
74+ if(struct Rejoy_Evdev_Gamepad *const data = Rejoy_Evdev_OpenGamepad(fd)){
75+ EvdevGamepad *const gamepad = new EvdevGamepad(fd, data);
76+ m_gamepad_list = Rejoy_Evdev_VecAppend(m_gamepad_list, gamepad);
77+ m_num_gamepads++;
78+ }
79+ else{
80+ // close(fd);
81+ }
82+}
83+
84+///////////////////////////////////////////////////////////////////////////////
85+
86+EvdevDriver::~EvdevDriver() {
87+ DeleteGamepads(m_gamepad_list);
88+}
89+
90+///////////////////////////////////////////////////////////////////////////////
91+
92+void EvdevDriver::update() {
93+ DeleteGamepads(m_gamepad_list);
94+ m_gamepad_list = NULL;
95+ m_num_gamepads = 0;
96+ Rejoy_Unix_IterateGlob("/dev/input/event*",
97+ O_RDWR | O_NONBLOCK,
98+ this,
99+ EvdevDriver::EnumateGamepad);
100+}
101+
102+///////////////////////////////////////////////////////////////////////////////
103+
104+Gamepad *EvdevDriver::getGamepad(unsigned i) {
105+ if(i < Rejoy_Evdev_VecGetLen(m_gamepad_list)){
106+ Gamepad *gamepad = Rejoy_Evdev_VecGetValue(m_gamepad_list, i);
107+ return gamepad;
108+ }
109+ else
110+ return NULL;
111+}
112+
113+///////////////////////////////////////////////////////////////////////////////
114+
115+REJOY_STATIC_INIT(Evdev);
116+
117+///////////////////////////////////////////////////////////////////////////////
118+
119+} // namespace Rejoy
120+
121+///////////////////////////////////////////////////////////////////////////////
122+
--- /dev/null
+++ b/evdev/rejoy_evdev.hpp
@@ -0,0 +1,81 @@
1+// Copyright (c) 2021 Alaskan Emily, Transnat Games
2+//
3+// This Source Code Form is subject to the terms of the Mozilla Public
4+// License, v. 2.0. If a copy of the MPL was not distributed with this
5+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
7+#ifndef REJOY_EVDEV_HPP
8+#define REJOY_EVDEV_HPP
9+#pragma once
10+
11+///////////////////////////////////////////////////////////////////////////////
12+
13+#include "../rejoy.hpp"
14+#include "../unix/rejoy_unix.hpp"
15+#include "rejoy_evdev_bind.h"
16+
17+///////////////////////////////////////////////////////////////////////////////
18+
19+namespace Rejoy {
20+
21+///////////////////////////////////////////////////////////////////////////////
22+// Tagged as struct so that it can be used in SLIST's
23+class EvdevGamepad : public Rejoy::UnixGamepad {
24+ struct Rejoy_Evdev_Gamepad *const m_data;
25+public:
26+ inline EvdevGamepad(int fd, struct Rejoy_Evdev_Gamepad *data)
27+ : UnixGamepad(fd,
28+ Rejoy_Evdev_GetNumAxes(data),
29+ Rejoy_Evdev_GetNumButtons(data),
30+ Rejoy_Evdev_GetNumHats(data))
31+ , m_data(data) {}
32+
33+ explicit EvdevGamepad(const EvdevGamepad &other);
34+
35+ ~EvdevGamepad();
36+
37+ virtual void update();
38+ virtual const char *name() const {
39+ return Rejoy_Evdev_GetName(m_data);
40+ }
41+ virtual short getAxis(unsigned i) const {
42+ return Rejoy_Evdev_GetAxis(m_data, i);
43+ }
44+ virtual bool getButton(unsigned i) const {
45+ return Rejoy_Evdev_GetButton(m_data, i);
46+ }
47+ virtual unsigned getHat(unsigned i) const {
48+ return Rejoy_Evdev_GetHat(m_data, i);
49+ }
50+};
51+
52+///////////////////////////////////////////////////////////////////////////////
53+
54+class EvdevDriver : public Rejoy::Driver {
55+ // A Rust slice of EvdevGamepad.
56+ void *m_gamepad_list;
57+
58+ void enumerateGamepad(int fd);
59+ static void EnumateGamepad(void *that, const char *name, int fd){
60+ (void)name;
61+ static_cast<EvdevDriver*>(that)->enumerateGamepad(fd);
62+ }
63+public:
64+ EvdevDriver()
65+ : Driver("evdev")
66+ , m_gamepad_list(NULL) {}
67+
68+ ~EvdevDriver();
69+
70+ virtual void update();
71+ virtual Gamepad *getGamepad(unsigned i);
72+};
73+
74+///////////////////////////////////////////////////////////////////////////////
75+
76+} // namespace Rejoy
77+
78+///////////////////////////////////////////////////////////////////////////////
79+
80+#endif // REJOY_EVDEV_HPP
81+
--- /dev/null
+++ b/evdev/src/bitmap.rs
@@ -0,0 +1,325 @@
1+use std::{fmt, iter};
2+
3+/// Bitmap container type with a fixed size and variable bit length elements.
4+/// TODO: This is clearly not optimal.
5+
6+pub trait Bitmap {
7+ fn get_bit(&self, i: usize) -> u8;
8+ fn set_bit(&mut self, i: usize, val: u8) -> bool;
9+ fn as_ptr(&self) -> *const u8;
10+ fn as_mut_ptr(&mut self) -> *mut u8;
11+ fn element_size(&self) -> usize;
12+ fn len(&self) -> usize;
13+}
14+
15+#[inline]
16+pub fn get_slice<B>(b: &B, i: usize) -> u32
17+where
18+ B: Bitmap
19+{
20+ let mut n: u32 = 0;
21+ let esize = b.element_size();
22+ let base = i * esize;
23+ for e in 0 .. esize {
24+ n |= (b.get_bit(base + e) as u32) << e;
25+ }
26+ n
27+}
28+
29+#[inline]
30+pub fn set_slice<B>(b: &mut B, i: usize, val: u32)
31+where
32+ B: Bitmap
33+{
34+ let esize = b.element_size();
35+ let base = i * esize;
36+ for e in 0 .. esize {
37+ b.set_bit(base + e, (val >> e) as u8 & 1);
38+ }
39+}
40+
41+#[derive(Clone, Debug, PartialEq)]
42+pub struct BitmapRef<B>(pub B, pub usize);
43+
44+impl<B> BitmapRef<B> {
45+ #[inline]
46+ pub fn new(that: B) -> Self {
47+ BitmapRef(that, 0)
48+ }
49+}
50+
51+
52+impl<'a, B> BitmapRef<&'a B>
53+where
54+ B: Bitmap
55+{
56+ #[inline]
57+ pub fn get(&self) -> u32 {
58+ get_slice(self.0, self.1)
59+ }
60+
61+ #[inline]
62+ pub fn end(&self) -> bool {
63+ self.0.len() < self.1
64+ }
65+
66+ #[inline]
67+ pub fn step(&mut self, n: usize) {
68+ self.1 = std::cmp::min(self.1 + n, self.0.len());
69+ }
70+}
71+
72+impl<'a, B> BitmapRef<&'a mut B>
73+where
74+ B: Bitmap
75+{
76+ #[inline]
77+ pub fn get(&self) -> u32 {
78+ get_slice(self.0, self.1)
79+ }
80+
81+ #[inline]
82+ pub fn end(&self) -> bool {
83+ self.0.len() < self.1
84+ }
85+
86+ #[inline]
87+ pub fn step(&mut self, n: usize) {
88+ self.1 = std::cmp::min(self.1 + n, self.0.len());
89+ }
90+}
91+
92+impl<'a, B> BitmapRef<&'a mut B>
93+where
94+ B: Bitmap
95+{
96+ pub fn set(&mut self, val: u32) {
97+ set_slice(self.0, self.1, val)
98+ }
99+}
100+
101+impl<'a, B> iter::Iterator for BitmapRef<&'a B>
102+where
103+ B: Bitmap
104+{
105+ type Item = u32;
106+ fn next(&mut self) -> Option<Self::Item> {
107+ if self.1 >= self.0.len() - 1 {
108+ None
109+ }
110+ else{
111+ let u = self.get();
112+ self.step(1);
113+ Some(u)
114+ }
115+ }
116+ #[inline]
117+ fn count(self) -> usize {
118+ self.0.len() - self.1
119+ }
120+
121+ #[inline]
122+ fn nth(&mut self, n: usize) -> Option<Self::Item> {
123+ self.step(n);
124+ self.next()
125+ }
126+}
127+
128+impl<'a, B> iter::Iterator for BitmapRef<&'a mut B>
129+where
130+ B: Bitmap
131+{
132+ type Item = u32;
133+ fn next(&mut self) -> Option<Self::Item> {
134+ if self.1 >= self.0.len() - 1 {
135+ None
136+ }
137+ else{
138+ let u = self.get();
139+ self.step(1);
140+ Some(u)
141+ }
142+ }
143+ #[inline]
144+ fn count(self) -> usize {
145+ self.0.len() - self.1
146+ }
147+
148+ #[inline]
149+ fn nth(&mut self, n: usize) -> Option<Self::Item> {
150+ self.step(n);
151+ self.next()
152+ }
153+}
154+
155+
156+#[derive(Clone, Debug, PartialEq)]
157+pub struct BitmapIterator<B, T>(BitmapRef<B>, std::marker::PhantomData<T>);
158+
159+impl<B, T> BitmapIterator<B, T> {
160+ #[inline]
161+ pub fn new(that: B) -> Self {
162+ BitmapIterator(BitmapRef::new(that), std::marker::PhantomData)
163+ }
164+}
165+
166+impl<B, T> std::convert::From<BitmapRef<B>> for BitmapIterator<B, T> {
167+ #[inline]
168+ fn from(that: BitmapRef<B>) -> Self {
169+ BitmapIterator(that, std::marker::PhantomData)
170+ }
171+}
172+
173+impl<'a, B, T> BitmapIterator<&'a B, T>
174+where
175+ B: Bitmap,
176+ T: From<u32>,
177+{
178+ #[inline]
179+ pub fn get_val(&self) -> T {
180+ self.0.get().into()
181+ }
182+}
183+
184+impl<'a, B, T> BitmapIterator<&'a mut B, T>
185+where
186+ B: Bitmap,
187+ T: From<u32>,
188+{
189+ #[inline]
190+ pub fn get_val(&self) -> T {
191+ self.0.get().into()
192+ }
193+}
194+
195+impl<'a, B, T> BitmapIterator<&'a mut B, T>
196+where
197+ B: Bitmap,
198+ T: Into<u32>,
199+{
200+ #[inline]
201+ pub fn set_val(&mut self, val: T) {
202+ self.0.set(val.into());
203+ }
204+}
205+
206+impl <'a, B, T> iter::Iterator for BitmapIterator<&'a B, T>
207+where
208+ B: Bitmap,
209+ T: From<u32>,
210+{
211+ type Item = T;
212+ #[inline]
213+ fn next(&mut self) -> Option<Self::Item> {
214+ self.0.next().map(|a| a.into())
215+ }
216+ #[inline]
217+ fn count(self) -> usize {
218+ self.0.count()
219+ }
220+}
221+
222+impl <'a, B, T> iter::Iterator for BitmapIterator<&'a mut B, T>
223+where
224+ B: Bitmap,
225+ T: From<u32>,
226+{
227+ type Item = T;
228+ #[inline]
229+ fn next(&mut self) -> Option<Self::Item> {
230+ self.0.next().map(|a| a.into())
231+ }
232+ #[inline]
233+ fn count(self) -> usize {
234+ self.0.count()
235+ }
236+}
237+
238+impl fmt::Display for dyn Bitmap {
239+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240+ let esize = self.element_size();
241+ f.write_str("{")?;
242+ for i in 0 .. self.len() {
243+ f.write_str("0b")?;
244+ for e in 0 .. esize {
245+ f.write_str(
246+ match self.get_bit(e + (i * esize)) {
247+ 0 => "0",
248+ _ => "1",
249+ })?;
250+ }
251+ f.write_str(",")?;
252+ }
253+ Ok(())
254+ }
255+}
256+
257+macro_rules! bitmap_size {
258+ ($esize:expr, $count:expr) => { ((($esize * $count) + 7) / 8) as usize }
259+}
260+
261+macro_rules! bitmap {
262+ ($name:ident [ $esize:expr ; $count:expr ] ) => {
263+
264+ #[derive(Clone, PartialEq, Debug)]
265+ #[repr(transparent)]
266+ pub struct $name([u8; bitmap_size!($esize, $count)]);
267+
268+ impl $name {
269+ #[inline]
270+ pub fn new() -> Self {
271+ $name([0; bitmap_size!($esize, $count)])
272+ }
273+ #[inline]
274+ pub fn iter<'a>(&'a self) -> crate::bitmap::BitmapRef<&'a Self> {
275+ crate::bitmap::BitmapRef(self, 0)
276+ }
277+ #[inline]
278+ pub fn iter_mut<'a>(&'a mut self) -> crate::bitmap::BitmapRef<&'a mut Self> {
279+ crate::bitmap::BitmapRef(self, 0)
280+ }
281+ #[inline]
282+ pub fn ptr_size() -> usize { bitmap_size!($esize, $count) }
283+ }
284+
285+ impl Default for $name {
286+ #[inline]
287+ fn default() -> Self { $name::new() }
288+ }
289+
290+ impl crate::bitmap::Bitmap for $name {
291+ #[inline]
292+ fn get_bit(&self, i: usize) -> u8 {
293+ (self.0[i >> 3] >> (i & 7)) & 1
294+ }
295+ fn set_bit(&mut self, i: usize, val: u8) -> bool {
296+ let mask = 1 << (i & 7);
297+ let old = (self.0[i >> 3] & mask) != 0;
298+ if val != 0 {
299+ self.0[i >> 3] |= mask;
300+ }
301+ else{
302+ self.0[i >> 3] &= !mask;
303+ }
304+ old
305+ }
306+ #[inline]
307+ fn element_size(&self) -> usize { $esize }
308+ #[inline]
309+ fn len(&self) -> usize { $count }
310+ #[inline]
311+ fn as_ptr(&self) -> *const u8 { self.0.as_ptr() }
312+ #[inline]
313+ fn as_mut_ptr(&mut self) -> *mut u8 { self.0.as_mut_ptr() }
314+ }
315+
316+ impl<'a> std::iter::IntoIterator for &'a $name {
317+ type Item = u32;
318+ type IntoIter = crate::bitmap::BitmapRef<Self>;
319+ fn into_iter(self) -> Self::IntoIter {
320+ crate::bitmap::BitmapRef(self, 0)
321+ }
322+ }
323+ }
324+}
325+
--- /dev/null
+++ b/evdev/src/gamepad.rs
@@ -0,0 +1,312 @@
1+use std::cmp;
2+use std::ffi::CString;
3+use std::fs::File;
4+use std::io::{self, Read};
5+use std::vec::Vec;
6+use ioctl::{self, AbsInfo, AbsValue};
7+use bitmap::{Bitmap, BitmapIterator};
8+use std::os::unix::io::RawFd as Fd;
9+use std::os::unix::io::{IntoRawFd, FromRawFd};
10+
11+// Generated bindings
12+include!{concat!(env!("OUT_DIR"), "/rejoy_evdev_bind.rs")}
13+
14+extern "C" {
15+// For consistency with other drivers, use the same scaling function.
16+fn Rejoy_ScaleValue(from: i32, to: i32, t: i32) -> i32;
17+}
18+
19+macro_rules! btn_ranges {
20+ ($($name:ident : $start:literal .. $end:literal,)*) => {
21+ $(
22+ const $name: std::ops::Range<u16> = std::ops::Range {
23+ start: $start,
24+ end: $end,
25+ };
26+ )*
27+ const TOTAL_BTN_COUNT: usize = 0 $( + $end - $start )*;
28+ #[inline]
29+ fn for_each_btn<F>(mut op: F)
30+ where
31+ F: FnMut(u16)
32+ {
33+ $(
34+ for i in $name {
35+ op(i);
36+ }
37+ )*
38+ }
39+ }
40+}
41+
42+// Ranges of useful BTN values.
43+btn_ranges!(
44+ BTN_JOYSTICK: 0x120 .. 0x130,
45+ BTN_GAMEPAD: 0x130 .. 0x13F,
46+ BTN_WHEEL: 0x150 .. 0x152,
47+ );
48+
49+const REJOY_C_TIMEVAL_SIZE: usize = super::REJOY_C_TIMEVAL_SIZE as usize;
50+const INPUT_EVENT_SIZE: usize = REJOY_C_TIMEVAL_SIZE + 8;
51+
52+#[derive(Debug)]
53+#[repr(C)]
54+struct InputEvent {
55+ time: [u8; REJOY_C_TIMEVAL_SIZE],
56+ typ: u16,
57+ code: u16,
58+ value: i32,
59+}
60+
61+#[derive(Clone, PartialEq, Debug)]
62+struct AbsPair(AbsValue, AbsInfo);
63+
64+// TODO: This will be used when we handle SYN_DROPPED.
65+/*
66+impl AbsPair {
67+ #[inline]
68+ fn update(&mut self, fd: Fd) {
69+ if let Ok(info) = ioctl::evdev_get_abs(fd, self.0) {
70+ self.1 = info
71+ }
72+ }
73+ #[inline]
74+ fn value<'a>(&'a self) -> &'a AbsInfo {
75+ &self.1
76+ }
77+}
78+*/
79+
80+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
81+struct Button(u8);
82+
83+const REJOY_MAX_BUTTONS: usize = super::REJOY_MAX_BUTTONS as usize;
84+const SOME_BUTTON: Button = Button(2);
85+const NONE_BUTTON: Button = Button(0);
86+
87+impl Button {
88+ #[inline]
89+ fn present(&self) -> bool {
90+ (self.0 & 2) != 0
91+ }
92+ #[inline]
93+ fn value(&self) -> bool {
94+ (self.0 & 1) != 0
95+ }
96+ #[inline]
97+ fn update(&mut self, val: bool) {
98+ self.0 = if val { self.0 | 1 } else { self.0 & 2 };
99+ }
100+}
101+
102+impl std::convert::From<u32> for Button {
103+ #[inline]
104+ fn from(that: u32) -> Self {
105+ Button(that as u8)
106+ }
107+}
108+
109+impl std::convert::From<Button> for u32 {
110+ #[inline]
111+ fn from(that: Button) -> Self {
112+ that.0 as u32
113+ }
114+}
115+
116+bitmap!{ Buttons [2; TOTAL_BTN_COUNT] }
117+
118+/*
119+#[inline]
120+fn cmp_abs_pair<T>(a: &(AbsValue, T), b: &(AbsValue, T)) -> std::cmp::Ordering {
121+ AbsValue::cmp(a, b)
122+}
123+*/
124+
125+#[derive(Clone, PartialEq, Debug)]
126+pub struct Gamepad {
127+ name: CString,
128+ axes: Vec<(AbsValue, AbsInfo)>,
129+ hats: Vec<(AbsValue, AbsInfo)>,
130+ buttons: Buttons,
131+}
132+
133+impl Gamepad {
134+ fn open(fd: Fd) -> io::Result<Self> {
135+ let name = CString::new(ioctl::evdev_get_name(fd)?)?;
136+ let mut hats = Vec::new();
137+ let mut axes = Vec::new();
138+ ioctl::AbsValue::for_each(|a|
139+ if let Ok(info) = ioctl::evdev_get_abs(fd, a) {
140+ if info.min == info.max {
141+ return; // Not an interesting axis.
142+ }
143+ if a.is_hat() {
144+ hats.push((a, info));
145+ }
146+ else if a.is_axis() {
147+ axes.push((a, info));
148+ }
149+ });
150+ if hats.is_empty() && axes.is_empty() {
151+ Err(io::Error::new(io::ErrorKind::Other, "Not a gamepad/joystick"))
152+ }
153+ else{
154+ // Test for buttons.
155+ let mut buttons = Buttons::new();
156+ let mut button_iter = buttons.iter_mut();
157+ if let Ok(button_test) = ioctl::evdev_get_btn(fd) {
158+ for_each_btn(|i| {
159+ let bit = button_test.get_bit(i as usize);
160+ let b = if bit != 0 { SOME_BUTTON } else { NONE_BUTTON };
161+ button_iter.set(b.into());
162+ button_iter.step(1);
163+ });
164+ }
165+ Ok( Gamepad{ name, axes, hats, buttons } )
166+ }
167+ }
168+ fn button_iter<'a>(&'a self) -> BitmapIterator<&'a Buttons, Button> {
169+ BitmapIterator::<&'a Buttons, Button>::new(&self.buttons)
170+ }
171+ fn button_iter_mut<'a>(&'a mut self) -> BitmapIterator<&'a mut Buttons, Button> {
172+ BitmapIterator::<&'a mut Buttons, Button>::new(&mut self.buttons)
173+ }
174+ fn get_button(&self, i: usize) -> bool {
175+ self.button_iter().filter(|b| b.present()).nth(i).map(|b| b.value()).unwrap_or(false)
176+ }
177+ #[inline]
178+ fn get_num_buttons(&self) -> usize {
179+ let n = self.button_iter().filter(|b| b.present()).count();
180+ cmp::min(n, REJOY_MAX_BUTTONS)
181+ }
182+ #[inline]
183+ fn get_axis(&self, i: usize) -> i16 {
184+ let info = self.axes[i].1;
185+ unsafe { Rejoy_ScaleValue(info.min, info.max, info.value) as i16 }
186+ }
187+ #[inline]
188+ fn get_num_axes(&self) -> usize { self.axes.len() }
189+ #[inline]
190+ fn get_hat(&self, _i: usize) -> u32 {
191+ 0
192+ }
193+ #[inline]
194+ fn get_num_hats(&self) -> usize { self.hats.len() }
195+ fn update(&mut self, fd: Fd) {
196+ assert!(std::mem::size_of::<InputEvent>() == INPUT_EVENT_SIZE);
197+ let mut f = unsafe { File::from_raw_fd(fd) };
198+ let mut buffer: [u8; INPUT_EVENT_SIZE] = [0; INPUT_EVENT_SIZE];
199+
200+ // Read as many events as possible.
201+ let mut result = f.read(&mut buffer);
202+ while let Ok(INPUT_EVENT_SIZE) = result {
203+ let event_ptr = buffer.as_ptr() as *const InputEvent;
204+ let event = unsafe{ &*event_ptr };
205+ match event.typ {
206+ ioctl::EV_KEY => {
207+ let mut button_iter = self.button_iter_mut();
208+ for_each_btn(|i| {
209+ if i == event.code {
210+ let mut b = button_iter.get_val();
211+ if !b.present() {
212+ if cfg!(debug_assertions) {
213+ eprintln!("Invalid button {} set", i);
214+ }
215+ }
216+ else {
217+ b.update(event.value != 0);
218+ }
219+ button_iter.set_val(b.into());
220+ }
221+ button_iter.next();
222+ });
223+ },
224+ ioctl::EV_MSC => {
225+ // println!("{:?}", event);
226+ },
227+ ioctl::EV_ABS => {
228+ let a = ioctl::ABS_VALUE_INT[event.code as usize];
229+ if a.is_hat() {
230+ if let Some(mut abspair) = self.hats.iter_mut().find(|p| p.0 == a) {
231+ abspair.1.value = event.value;
232+ }
233+ }
234+ else if a.is_axis() {
235+ if let Some(mut abspair) = self.axes.iter_mut().find(|p| p.0 == a) {
236+ abspair.1.value = event.value;
237+ }
238+ }
239+ else if cfg!(debug_assertions) {
240+ eprintln!("Unexpected ABS type {}", a);
241+ }
242+ },
243+ ioctl::EV_SYN => {
244+ // TODO: We should do a full re-read of the gamepad's
245+ // state when we get SYN_DROPPED.
246+ },
247+ _ => {
248+ if cfg!(debug_assertions) {
249+ eprintln!("Unkown event type {}", event.typ);
250+ }
251+ },
252+ }
253+ result = f.read(&mut buffer);
254+ }
255+ // Drop the file descriptor again.
256+ f.into_raw_fd();
257+
258+ if !cfg!(debug_assertions) {
259+ return;
260+ }
261+
262+ match result {
263+ Ok(len) =>
264+ if len != INPUT_EVENT_SIZE && len != 0 {
265+ eprintln!("Invalid read size {}", len);
266+ },
267+ Err(e) =>
268+ if e.kind() != io::ErrorKind::WouldBlock {
269+ eprintln!("{} err: {}", fd, e);
270+ },
271+ }
272+ }
273+}
274+
275+#[allow(non_camel_case_types)]
276+type Rejoy_Evdev_Gamepad = Gamepad;
277+
278+#[allow(non_snake_case)]
279+#[no_mangle]
280+pub extern "C" fn Rejoy_Evdev_OpenGamepad(fd: Fd) -> *mut Gamepad {
281+ match Gamepad::open(fd) {
282+ Ok(gamepad) => {
283+ let gamepad_box = Box::new(gamepad);
284+ Box::leak(gamepad_box) as *mut Gamepad
285+ },
286+ Err(e) => {
287+ if cfg!(debug_assertions) { eprintln!("Could not open gamepad: {}", e); }
288+ std::ptr::null_mut()
289+ },
290+ }
291+}
292+
293+#[allow(non_snake_case)]
294+#[no_mangle]
295+pub extern "C" fn Rejoy_Evdev_CopyGamepad(gamepad_ptr: *const Gamepad) -> *mut Gamepad {
296+ let gamepad = unsafe { (*gamepad_ptr).clone() };
297+ let gamepad_box = Box::new(gamepad);
298+ Box::leak(gamepad_box) as *mut Gamepad
299+}
300+
301+#[allow(non_snake_case)]
302+#[no_mangle]
303+pub unsafe extern "C" fn Rejoy_Evdev_FinalizeGamepad(gamepad_ptr: *mut Gamepad) {
304+ let _ = Box::from_raw(gamepad_ptr);
305+}
306+
307+#[allow(non_snake_case)]
308+#[no_mangle]
309+pub unsafe extern "C" fn Rejoy_Evdev_UpdateGamepad(gamepad_ptr: *mut Gamepad, fd: Fd) {
310+ (*gamepad_ptr).update(fd);
311+}
312+
--- /dev/null
+++ b/evdev/src/ioctl.rs
@@ -0,0 +1,272 @@
1+// This Source Code Form is subject to the terms of the Mozilla Public
2+// License, v. 2.0. If a copy of the MPL was not distributed with this
3+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+//! ioctl glue for using evdev.
6+
7+use ioctl_sys;
8+
9+use bitmap::Bitmap;
10+
11+use std::default::Default;
12+use std::{io, fmt};
13+use std::os::unix::io::RawFd as Fd;
14+
15+pub const KEY_MAX: u32 = 0x3FF;
16+pub const KEY_CNT: usize = KEY_MAX as usize + 1;
17+#[allow(unused)]
18+pub const SYN_REPORT: u16 = 0;
19+#[allow(unused)]
20+pub const SYN_CONFIG: u16 = 1;
21+#[allow(unused)]
22+pub const SYN_DROPPED: u16 = 3;
23+pub const EV_SYN: u16 = 0;
24+pub const EV_KEY: u16 = 1;
25+#[allow(unused)]
26+pub const EV_REL: u16 = 2;
27+pub const EV_ABS: u16 = 3;
28+pub const EV_MSC: u16 = 4;
29+#[allow(unused)]
30+pub const EV_SW: u16 = 5;
31+#[allow(unused)]
32+pub const EV_LED: u16 = 17;
33+#[allow(unused)]
34+pub const EV_FF: u16 = 31;
35+
36+
37+bitmap!{ Buttons [1; KEY_CNT] }
38+
39+#[derive(PartialEq, Eq, Clone, PartialOrd, Ord)]
40+#[allow(non_camel_case_types, unused)]
41+pub enum AbsAxisDir { x, y, other }
42+#[derive(PartialEq, Eq, Clone, PartialOrd, Ord)]
43+#[allow(non_camel_case_types, unused)]
44+pub enum AbsAxisType { hat, stick, slider, other }
45+
46+macro_rules! abs_attribute {
47+ ($func:ident $what:ident $typ:ident $($attr:ident $name:ident,)*) => (
48+ #[allow(unused)]
49+ pub fn $func(self) -> bool {
50+ match self {
51+ $(
52+ AbsValue::$name => $typ::$what == $typ::$attr,
53+ )*
54+ }
55+ }
56+ )
57+}
58+
59+macro_rules! abs_match_hat {
60+ (hat ? $t:literal : $f:tt ) => {{ $t }};
61+ (stick ? $t:literal : $f:tt ) => {{ $f }};
62+ (slider ? $t:literal : $f:tt ) => {{ $f }};
63+ (other ? $t:literal : $f:tt ) => {{ $f }};
64+}
65+
66+macro_rules! abs_match_stick {
67+ (hat ? $t:literal : $f:tt ) => {{ $f }};
68+ (stick ? $t:literal : $f:tt ) => {{ $t }};
69+ (slider ? $t:literal : $f:tt ) => {{ $f }};
70+ (other ? $t:literal : $f:tt ) => {{ $f }};
71+}
72+
73+macro_rules! abs_match_slider {
74+ (hat ? $t:literal : $f:tt ) => {{ $f }};
75+ (stick ? $t:literal : $f:tt ) => {{ $f }};
76+ (slider ? $t:literal : $f:tt ) => {{ $t }};
77+ (other ? $t:literal : $f:tt ) => {{ $f }};
78+}
79+
80+macro_rules! abs_match_other {
81+ (hat ? $t:literal : $f:tt ) => {{ $f }};
82+ (stick ? $t:literal : $f:tt ) => {{ $f }};
83+ (slider ? $t:literal : $f:tt ) => {{ $f }};
84+ (other ? $t:literal : $f:tt ) => {{ $t }};
85+}
86+
87+#[allow(unused)]
88+macro_rules! abs_match_axis {
89+ (hat ? $t:literal : $f:tt ) => {{ $f }};
90+ (stick ? $t:literal : $f:tt ) => {{ $t }};
91+ (slider ? $t:literal : $f:tt ) => {{ $t }};
92+ (other ? $t:literal : $f:tt ) => {{ $f }};
93+}
94+
95+macro_rules! abs_values {
96+
97+ ($($axis:ident $typ:ident $name:ident,)*) => {
98+ #[repr(u8)]
99+ #[allow(non_camel_case_types, unused)]
100+ #[derive(PartialEq, Eq, Clone, Copy, Debug, Ord, PartialOrd)]
101+ pub enum AbsValue {
102+ $($name,)*
103+ }
104+ pub const ABS_VALUE_MAX: usize = 0 $( + (AbsValue::$name as usize * 0) + 1)* ;
105+ #[allow(unused)]
106+ pub const ABS_NUM_HATS: usize = 0 $( + abs_match_hat!($typ ? 1 : 0) )* ;
107+ #[allow(unused)]
108+ pub const ABS_NUM_STICKS: usize = 0 $( + abs_match_stick!($typ ? 1 : 0) )* ;
109+ #[allow(unused)]
110+ pub const ABS_NUM_SLIDERS: usize = 0 $( + abs_match_slider!($typ ? 1 : 0) )* ;
111+ #[allow(unused)]
112+ pub const ABS_NUM_AXES: usize = ABS_NUM_STICKS + ABS_NUM_SLIDERS;
113+ #[allow(unused)]
114+ pub const ABS_NUM_OTHER: usize = 0 $( + abs_match_other!($typ ? 1 : 0) )* ;
115+ // This kind of sucks but here we are.
116+ pub const ABS_VALUE_INT: &'static [AbsValue; ABS_VALUE_MAX] = &[
117+ $(AbsValue::$name,)*
118+ ];
119+ impl AbsValue {
120+ abs_attribute!(is_x x AbsAxisDir $($axis $name,)*);
121+ abs_attribute!(is_y y AbsAxisDir $($axis $name,)*);
122+ abs_attribute!(is_stick stick AbsAxisType $($typ $name,)*);
123+ abs_attribute!(is_hat hat AbsAxisType $($typ $name,)*);
124+ abs_attribute!(is_slider slider AbsAxisType $($typ $name,)*);
125+
126+ #[inline]
127+ #[allow(unused)]
128+ pub fn is_axis(self) -> bool {
129+ self.is_stick() || self.is_slider()
130+ }
131+
132+ #[inline]
133+ #[allow(unused)]
134+ pub fn for_each<F>(mut op: F)
135+ where
136+ F: FnMut(AbsValue)
137+ {
138+ $(op(AbsValue::$name);)*
139+ }
140+ }
141+
142+ impl fmt::Display for AbsValue {
143+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144+ f.write_str(match *self {
145+ $(
146+ AbsValue::$name => stringify!($name),
147+ )*
148+ })
149+ }
150+ }
151+ }
152+}
153+
154+abs_values!(
155+ x stick AbsX,
156+ y stick AbsY,
157+ other stick AbsZ,
158+ x stick AbsRX,
159+ y stick AbsRY,
160+ other stick AbsRZ,
161+ other slider AbsThrottle,
162+ x slider AbsRudder,
163+ x other AbsWheel,
164+ other slider AbsGas,
165+ other slider AbsBrake,
166+ x hat AbsHat0X,
167+ y hat AbsHat1Y,
168+ x hat AbsHat1X,
169+ y hat AbsHat2Y,
170+ x hat AbsHat2X,
171+ y hat AbsHat3Y,
172+ x hat AbsHat3X,
173+ other other AbsPressure,
174+ other other AbsDistance,
175+ x other AbsTiltX,
176+ y other AbsTiltY,
177+ other other AbsToolWidth,
178+ other slider AbsVolume,
179+ other other AbsMisc,
180+);
181+
182+#[repr(C)]
183+#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
184+/// Data for EVIOCSABS/EVIOCGABS
185+pub struct AbsInfo {
186+ /// Current value. Not clamped.
187+ pub value: i32,
188+ /// Logical minimum.
189+ pub min: i32,
190+ /// Logical maximum.
191+ pub max: i32,
192+ /// "Fuzz" value for input filtering.
193+ pub fuzz: i32,
194+ /// Deadzone threshold. Not useful for joysticks.
195+ pub flat: i32,
196+ /// Resolution for the values
197+ pub res: i32,
198+}
199+
200+#[inline]
201+fn io_result_fn<T, F>(res: i32, that: T, op: F) -> io::Result<T>
202+where
203+ F: FnOnce(i32) -> bool,
204+{
205+ if op(res) {
206+ Ok(that)
207+ }
208+ else{
209+ Err(std::io::Error::last_os_error())
210+ }
211+}
212+
213+fn io_result<T>(res: i32, that: T) -> io::Result<T> {
214+ io_result_fn(res, that, |i| i == 0)
215+}
216+
217+pub fn evdev_get_abs(fd: Fd, abs: AbsValue) -> io::Result<AbsInfo> {
218+ let mut info: AbsInfo = Default::default();
219+ let info_addr: *mut AbsInfo = &mut info;
220+ let res = unsafe {
221+ ioctl_sys::ioctl(fd,
222+ ior!(b'E', 0x40 + abs as u8, std::mem::size_of::<AbsInfo>()).into(),
223+ info_addr)
224+ };
225+ io_result(res, info)
226+}
227+
228+const NAME_BUFFER_LEN: usize = 255;
229+
230+pub fn evdev_get_name(fd: Fd) -> io::Result<String> {
231+ let mut buffer: [u8; NAME_BUFFER_LEN] = [0; NAME_BUFFER_LEN];
232+ let buffer_slice = &mut buffer[0 .. NAME_BUFFER_LEN];
233+ let res = unsafe {
234+ ioctl_sys::ioctl(fd,
235+ ior!(b'E', 0x06, NAME_BUFFER_LEN).into(),
236+ buffer_slice.as_mut_ptr())
237+ };
238+ io_result_fn(res, buffer_slice, |i| i > 0).map(|n| {
239+ String::from_utf8_lossy(&n[0 .. (res as usize) - 1]).into()
240+ })
241+}
242+
243+pub fn evdev_get_btn(fd: Fd) -> io::Result<Buttons> {
244+ let mut buttons = Buttons::new();
245+ let res = unsafe {
246+ get_bits(fd, EV_KEY.into(), buttons.as_mut_ptr(), Buttons::ptr_size())
247+ };
248+ io_result_fn(res, buttons, |i| i >= 0)
249+}
250+
251+unsafe fn get_bits(fd: Fd, what: u32, bmp: *mut u8, len: usize) -> i32 {
252+ ioctl_sys::ioctl(fd,
253+ ioc!(ioctl_sys::READ, b'E', 0x20 + what, len).into(),
254+ bmp)
255+}
256+
257+// TODO: props!
258+#[allow(unused)]
259+const EVDEV_NUM_PROPS: usize = 32;
260+
261+#[allow(unused)]
262+pub fn evdev_get_props(fd: Fd) -> io::Result<[u8; EVDEV_NUM_PROPS]> {
263+ let mut buffer: [u8; EVDEV_NUM_PROPS] = [0; EVDEV_NUM_PROPS];
264+ let buffer_slice = &mut buffer[0 .. EVDEV_NUM_PROPS];
265+ let res = unsafe {
266+ ioctl_sys::ioctl(fd,
267+ ior!(b'E', 0x09, EVDEV_NUM_PROPS).into(),
268+ buffer_slice.as_mut_ptr())
269+ };
270+ io_result(res, buffer)
271+}
272+
--- /dev/null
+++ b/evdev/src/lib.rs
@@ -0,0 +1,75 @@
1+#[macro_use]
2+extern crate ioctl_sys;
3+
4+use std::ptr;
5+
6+// Generated constants from the C headers.
7+include!{concat!(env!("OUT_DIR"), "/rejoy_evdev_defs.rs")}
8+
9+#[macro_use]
10+mod bitmap;
11+
12+#[macro_use]
13+mod ioctl;
14+mod gamepad;
15+
16+// Handwritten bindings.
17+type CVec = Vec<*mut core::ffi::c_void>;
18+
19+/// C/C++ Binding to Vec::push
20+///
21+/// Specially handles a NULL input to create a new Vec.
22+#[allow(non_snake_case)]
23+#[no_mangle]
24+pub extern "C" fn Rejoy_Evdev_VecAppend(ptr: *mut core::ffi::c_void, val: *mut core::ffi::c_void) -> *mut core::ffi::c_void {
25+ (if ptr != ptr::null_mut() {
26+ let vec_ptr = ptr as *mut CVec;
27+ unsafe { (*vec_ptr).push(val); }
28+ vec_ptr
29+ }
30+ else{
31+ let vec: CVec = vec![val];
32+ let vec_box = Box::new(vec);
33+ Box::leak(vec_box) as *mut CVec
34+ }) as *mut core::ffi::c_void
35+}
36+
37+/// C/C++ Binding to Vec::len
38+///
39+/// Specially handles a NULL input to be zero-length.
40+#[allow(non_snake_case)]
41+#[no_mangle]
42+pub extern "C" fn Rejoy_Evdev_VecGetLen(ptr: *const core::ffi::c_void) -> u32 {
43+ if ptr == ptr::null_mut() {
44+ 0
45+ }
46+ else{
47+ let vec_ptr = ptr as *const CVec;
48+ unsafe { (*vec_ptr).len() as u32 }
49+ }
50+}
51+
52+/// C/C++ Binding to Vec[]
53+///
54+/// Specially handles a NULL input to be filled with NULL's.
55+#[allow(non_snake_case)]
56+#[no_mangle]
57+pub extern "C" fn Rejoy_Evdev_VecGetValue(ptr: *const core::ffi::c_void, i: u32) -> *mut core::ffi::c_void {
58+ if ptr == ptr::null_mut() {
59+ ptr::null_mut()
60+ }
61+ else{
62+ let vec_ptr = ptr as *const CVec;
63+ unsafe { (*vec_ptr)[i as usize] }
64+ }
65+}
66+
67+/// C/C++ Binding to dropping a Vec
68+#[allow(non_snake_case)]
69+#[no_mangle]
70+pub extern "C" fn Rejoy_Evdev_FinalizeVec(ptr: *mut core::ffi::c_void) {
71+ if ptr != ptr::null_mut() {
72+ let vec_ptr = ptr as *mut CVec;
73+ let _ = unsafe { Box::from_raw(vec_ptr) };
74+ }
75+}
--- a/rejoy.cpp
+++ b/rejoy.cpp
@@ -91,6 +91,10 @@ REJOY_DRIVER_INIT(XInput);
9191 REJOY_DRIVER_INIT(BSD);
9292 #endif
9393
94+#ifdef REJOY_DRIVER_EVDEV
95+REJOY_DRIVER_INIT(Evdev);
96+#endif
97+
9498 ///////////////////////////////////////////////////////////////////////////////
9599 // This is used by DriverList when statically starting up.
96100 static struct DriverList *driver_list = NULL;
@@ -110,11 +114,16 @@ void Init(const char **drivers){
110114 if(rejoy_find_driver(drivers, "xinput"))
111115 InitXInput();
112116 #endif
113-
117+
114118 #ifdef REJOY_DRIVER_BSD
115119 if(rejoy_find_driver(drivers, "bsd") || rejoy_find_driver(drivers, "uhid"))
116120 InitBSD();
117121 #endif
122+
123+#ifdef REJOY_DRIVER_EVDEV
124+ if(rejoy_find_driver(drivers, "evdev") || rejoy_find_driver(drivers, "evdev"))
125+ InitEvdev();
126+#endif
118127 }
119128
120129 ///////////////////////////////////////////////////////////////////////////////
@@ -133,6 +142,10 @@ void Shutdown() {
133142 ShutdownBSD();
134143 #endif
135144
145+#ifdef REJOY_DRIVER_EVDEV
146+ ShutdownEvdev();
147+#endif
148+
136149 driver_list = NULL;
137150
138151 }
--- a/rejoy_demo.cpp
+++ b/rejoy_demo.cpp
@@ -51,7 +51,7 @@ int main(int argc, char **argv){
5151 }
5252
5353 while(true){
54- REJOY_SLEEP(100);
54+ REJOY_SLEEP(64);
5555 for(unsigned i = 0; i < num_drivers; i++){
5656 Rejoy::Driver *&driver = drivers[i];
5757 const unsigned num_gamepads = driver->getNumGamepads();
--- /dev/null
+++ b/unix/SConscript
@@ -0,0 +1,10 @@
1+# Any copyright is dedicated to the Public Domain.
2+# http://creativecommons.org/publicdomain/zero/1.0/
3+import os
4+
5+Import("environment")
6+
7+source = ["rejoy_unix.cpp", "rejoy_unix_core.c"]
8+rejoy_unix = environment.StaticLibrary("rejoy_unix", source)
9+
10+Return("rejoy_unix")
--- a/unix/rejoy_unix_core.c
+++ b/unix/rejoy_unix_core.c
@@ -92,6 +92,28 @@ void Rejoy_Unix_IterateGlob(const char *glob_path,
9292
9393 cb(arg, path, fd);
9494 }
95+ else{
96+ printf("Could not open %s (err %i): \n", path, errno);
97+#define ERRCASE(NAME, DSCR) case NAME: puts( #NAME ": " DSCR); break
98+ switch(errno){
99+ ERRCASE(EACCES, "permission denied");
100+ ERRCASE(EINTR, "timed out");
101+ ERRCASE(EMFILE, "too many files open");
102+ ERRCASE(ENODEV, "invalid device");
103+ ERRCASE(ELOOP, "symlink loop");
104+#ifdef ENOTDIR
105+ ERRCASE(ENOTDIR, "invalid directory component");
106+#endif
107+#ifdef EOVERFLOW
108+ ERRCASE(EOVERFLOW, "file too large");
109+#endif
110+#ifdef EFBIG
111+ ERRCASE(EFBIG, "file too large");
112+#endif
113+ ERRCASE(EWOULDBLOCK, "opening would block");
114+ default: puts("unknown error.");
115+ }
116+ }
95117 }
96118 }
97119 globfree(&glob_data);
--- a/util/SConscript
+++ b/util/SConscript
@@ -14,12 +14,19 @@ else:
1414 env["PATH"] = extra_path
1515
1616 asflags=None
17+print("Building for " + str(target))
1718 if target == "x86" or target == "amd64":
1819 if os.name == "nt":
1920 if target == "amd64":
2021 asflags = "-f win64 -D REJOY_WINDOWS"
2122 elif target == "x86":
2223 asflags = "-f win32"
24+ elif "darwin" in os.name.lower():
25+ asflags = "-f macho"
26+ elif target == "x86":
27+ asflags = "-f elf32"
28+ elif target == "amd64":
29+ asflags = "-f elf64"
2330 util_env = Environment(ENV = os.environ, TOOLS=["default", "nasm"], TARGET_ARCH=target)
2431 # Try to find yasm or nasm.
2532 conf = util_env.Configure()
--- a/util/rejoy_util.amd64.s
+++ b/util/rejoy_util.amd64.s
@@ -3,7 +3,7 @@
33
44 bits 64
55
6-segment text
6+segment .text
77
88 global Rejoy_ScaleValue
99
@@ -42,5 +42,5 @@ Rejoy_ScaleValue:
4242
4343 %endif
4444
45- add eax, r10d
45+ add ax, 0x8000
4646 ret
--- a/util/rejoy_util.c
+++ b/util/rejoy_util.c
@@ -11,9 +11,11 @@
1111 REJOY_CDECL(int) Rejoy_ScaleValue(int from, int to, int t){
1212 const int item_range = to - from;
1313 int64_t scaled_data = t;
14+ if(item_range == 0)
15+ return to;
1416 scaled_data -= from;
1517 scaled_data *= REJOY_SHRT_RANGE;
1618 scaled_data /= item_range;
17- scaled_data += REJOY_SHRT_RANGE;
19+ scaled_data += SHRT_MIN;
1820 return (int)scaled_data;
1921 }
--- a/util/rejoy_util.x86.s
+++ b/util/rejoy_util.x86.s
@@ -13,21 +13,21 @@ global _Rejoy_ScaleValue
1313 ; Rejoy_ScaleValue(int from, int to, int t)
1414 Rejoy_ScaleValue:
1515 _Rejoy_ScaleValue:
16- mov eax, [esp-12]
17- sub eax, [esp-4]
16+ mov eax, [esp+12]
17+ sub eax, [esp+4]
1818
1919 mov ecx, 0x0000FFFF
2020 imul ecx
2121
2222 ; Get the item range.
23- mov ecx, [esp-8]
24- sub ecx, [esp-4]
23+ mov ecx, [esp+8]
24+ sub ecx, [esp+4]
2525 ; It's OK that this happens late in the process since it's an edge case anyway.
2626 jz div_by_zero
2727
2828 idiv ecx
29- add eax, 0x0000FFFF
29+ add ax, 0x8000
3030 ret
3131 div_by_zero:
32- xor eax, eax
32+ mov eax, [esp+4]
3333 ret