mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
internal/gamepad: remove dependency on golang.org/x/sys/unix
This commit is contained in:
parent
90e2250d17
commit
046f7e52a3
@ -18,9 +18,8 @@
|
|||||||
package gamepad
|
package gamepad
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -31,6 +30,11 @@ const (
|
|||||||
|
|
||||||
_BTN_MISC = 0x100
|
_BTN_MISC = 0x100
|
||||||
|
|
||||||
|
_EV_ABS = 0x3
|
||||||
|
_EV_CNT = 0x20
|
||||||
|
_EV_KEY = 0x1
|
||||||
|
_EV_SYN = 0x0
|
||||||
|
|
||||||
_IOC_NONE = 0
|
_IOC_NONE = 0
|
||||||
_IOC_WRITE = 1
|
_IOC_WRITE = 1
|
||||||
_IOC_READ = 2
|
_IOC_READ = 2
|
||||||
@ -86,7 +90,7 @@ type input_absinfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type input_event struct {
|
type input_event struct {
|
||||||
time unix.Timeval
|
time syscall.Timeval
|
||||||
typ uint16
|
typ uint16
|
||||||
code uint16
|
code uint16
|
||||||
value int32
|
value int32
|
||||||
@ -100,9 +104,9 @@ type input_id struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ioctl(fd int, request uint, ptr unsafe.Pointer) error {
|
func ioctl(fd int, request uint, ptr unsafe.Pointer) error {
|
||||||
r, _, e := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(request), uintptr(ptr))
|
r, _, e := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(request), uintptr(ptr))
|
||||||
if r < 0 {
|
if r < 0 {
|
||||||
return unix.Errno(e)
|
return syscall.Errno(e)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -18,19 +18,26 @@
|
|||||||
package gamepad
|
package gamepad
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
|
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func byteSliceToString(s []byte) string {
|
||||||
|
if i := bytes.IndexByte(s, 0); i != -1 {
|
||||||
|
s = s[:i]
|
||||||
|
}
|
||||||
|
return string(s)
|
||||||
|
}
|
||||||
|
|
||||||
const dirName = "/dev/input"
|
const dirName = "/dev/input"
|
||||||
|
|
||||||
var reEvent = regexp.MustCompile(`^event[0-9]+$`)
|
var reEvent = regexp.MustCompile(`^event[0-9]+$`)
|
||||||
@ -50,18 +57,18 @@ func newNativeGamepadsImpl() nativeGamepads {
|
|||||||
|
|
||||||
func (g *nativeGamepadsImpl) init(gamepads *gamepads) error {
|
func (g *nativeGamepadsImpl) init(gamepads *gamepads) error {
|
||||||
// Check the existence of the directory `dirName`.
|
// Check the existence of the directory `dirName`.
|
||||||
var stat unix.Stat_t
|
var stat syscall.Stat_t
|
||||||
if err := unix.Stat(dirName, &stat); err != nil {
|
if err := syscall.Stat(dirName, &stat); err != nil {
|
||||||
if err == unix.ENOENT {
|
if err == syscall.ENOENT {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("gamepad: Stat failed: %w", err)
|
return fmt.Errorf("gamepad: Stat failed: %w", err)
|
||||||
}
|
}
|
||||||
if stat.Mode&unix.S_IFDIR == 0 {
|
if stat.Mode&syscall.S_IFDIR == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
inotify, err := unix.InotifyInit1(unix.IN_NONBLOCK | unix.IN_CLOEXEC)
|
inotify, err := syscall.InotifyInit1(syscall.IN_NONBLOCK | syscall.IN_CLOEXEC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("gamepad: InotifyInit1 failed: %w", err)
|
return fmt.Errorf("gamepad: InotifyInit1 failed: %w", err)
|
||||||
}
|
}
|
||||||
@ -70,7 +77,7 @@ func (g *nativeGamepadsImpl) init(gamepads *gamepads) error {
|
|||||||
if g.inotify > 0 {
|
if g.inotify > 0 {
|
||||||
// Register for IN_ATTRIB to get notified when udev is done.
|
// Register for IN_ATTRIB to get notified when udev is done.
|
||||||
// This works well in practice but the true way is libudev.
|
// This works well in practice but the true way is libudev.
|
||||||
watch, err := unix.InotifyAddWatch(g.inotify, dirName, unix.IN_CREATE|unix.IN_ATTRIB|unix.IN_DELETE)
|
watch, err := syscall.InotifyAddWatch(g.inotify, dirName, syscall.IN_CREATE|syscall.IN_ATTRIB|syscall.IN_DELETE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("gamepad: InotifyAddWatch failed: %w", err)
|
return fmt.Errorf("gamepad: InotifyAddWatch failed: %w", err)
|
||||||
}
|
}
|
||||||
@ -103,53 +110,53 @@ func (*nativeGamepadsImpl) openGamepad(gamepads *gamepads, path string) (err err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := unix.Open(path, unix.O_RDONLY|unix.O_NONBLOCK, 0)
|
fd, err := syscall.Open(path, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == unix.EACCES {
|
if err == syscall.EACCES {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// This happens with the Snap sandbox.
|
// This happens with the Snap sandbox.
|
||||||
if err == unix.EPERM {
|
if err == syscall.EPERM {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// This happens just after a disconnection.
|
// This happens just after a disconnection.
|
||||||
if err == unix.ENOENT {
|
if err == syscall.ENOENT {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("gamepad: Open failed: %w", err)
|
return fmt.Errorf("gamepad: Open failed: %w", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = unix.Close(fd)
|
_ = syscall.Close(fd)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
evBits := make([]byte, (unix.EV_CNT+7)/8)
|
evBits := make([]byte, (_EV_CNT+7)/8)
|
||||||
keyBits := make([]byte, (_KEY_CNT+7)/8)
|
keyBits := make([]byte, (_KEY_CNT+7)/8)
|
||||||
absBits := make([]byte, (_ABS_CNT+7)/8)
|
absBits := make([]byte, (_ABS_CNT+7)/8)
|
||||||
var id input_id
|
var id input_id
|
||||||
if err := ioctl(fd, _EVIOCGBIT(0, uint(len(evBits))), unsafe.Pointer(&evBits[0])); err != nil {
|
if err := ioctl(fd, _EVIOCGBIT(0, uint(len(evBits))), unsafe.Pointer(&evBits[0])); err != nil {
|
||||||
return fmt.Errorf("gamepad: ioctl for evBits failed: %w", err)
|
return fmt.Errorf("gamepad: ioctl for evBits failed: %w", err)
|
||||||
}
|
}
|
||||||
if err := ioctl(fd, _EVIOCGBIT(unix.EV_KEY, uint(len(keyBits))), unsafe.Pointer(&keyBits[0])); err != nil {
|
if err := ioctl(fd, _EVIOCGBIT(_EV_KEY, uint(len(keyBits))), unsafe.Pointer(&keyBits[0])); err != nil {
|
||||||
return fmt.Errorf("gamepad: ioctl for keyBits failed: %w", err)
|
return fmt.Errorf("gamepad: ioctl for keyBits failed: %w", err)
|
||||||
}
|
}
|
||||||
if err := ioctl(fd, _EVIOCGBIT(unix.EV_ABS, uint(len(absBits))), unsafe.Pointer(&absBits[0])); err != nil {
|
if err := ioctl(fd, _EVIOCGBIT(_EV_ABS, uint(len(absBits))), unsafe.Pointer(&absBits[0])); err != nil {
|
||||||
return fmt.Errorf("gamepad: ioctl for absBits failed: %w", err)
|
return fmt.Errorf("gamepad: ioctl for absBits failed: %w", err)
|
||||||
}
|
}
|
||||||
if err := ioctl(fd, _EVIOCGID(), unsafe.Pointer(&id)); err != nil {
|
if err := ioctl(fd, _EVIOCGID(), unsafe.Pointer(&id)); err != nil {
|
||||||
return fmt.Errorf("gamepad: ioctl for an ID failed: %w", err)
|
return fmt.Errorf("gamepad: ioctl for an ID failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isBitSet(evBits, unix.EV_KEY) {
|
if !isBitSet(evBits, _EV_KEY) {
|
||||||
if err := unix.Close(fd); err != nil {
|
if err := syscall.Close(fd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !isBitSet(evBits, unix.EV_ABS) {
|
if !isBitSet(evBits, _EV_ABS) {
|
||||||
if err := unix.Close(fd); err != nil {
|
if err := syscall.Close(fd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +167,7 @@ func (*nativeGamepadsImpl) openGamepad(gamepads *gamepads, path string) (err err
|
|||||||
name := "Unknown"
|
name := "Unknown"
|
||||||
// TODO: Is it OK to ignore the error here?
|
// TODO: Is it OK to ignore the error here?
|
||||||
if err := ioctl(fd, uint(_EVIOCGNAME(uint(len(cname)))), unsafe.Pointer(&cname[0])); err == nil {
|
if err := ioctl(fd, uint(_EVIOCGNAME(uint(len(cname)))), unsafe.Pointer(&cname[0])); err == nil {
|
||||||
name = unix.ByteSliceToString(cname)
|
name = byteSliceToString(cname)
|
||||||
}
|
}
|
||||||
|
|
||||||
var sdlID string
|
var sdlID string
|
||||||
@ -236,9 +243,9 @@ func (g *nativeGamepadsImpl) update(gamepads *gamepads) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, 16384)
|
buf := make([]byte, 16384)
|
||||||
n, err := unix.Read(g.inotify, buf[:])
|
n, err := syscall.Read(g.inotify, buf[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == unix.EAGAIN {
|
if err == syscall.EAGAIN {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("gamepad: Read failed: %w", err)
|
return fmt.Errorf("gamepad: Read failed: %w", err)
|
||||||
@ -246,26 +253,26 @@ func (g *nativeGamepadsImpl) update(gamepads *gamepads) error {
|
|||||||
buf = buf[:n]
|
buf = buf[:n]
|
||||||
|
|
||||||
for len(buf) > 0 {
|
for len(buf) > 0 {
|
||||||
e := unix.InotifyEvent{
|
e := syscall.InotifyEvent{
|
||||||
Wd: int32(buf[0]) | int32(buf[1])<<8 | int32(buf[2])<<16 | int32(buf[3])<<24,
|
Wd: int32(buf[0]) | int32(buf[1])<<8 | int32(buf[2])<<16 | int32(buf[3])<<24,
|
||||||
Mask: uint32(buf[4]) | uint32(buf[5])<<8 | uint32(buf[6])<<16 | uint32(buf[7])<<24,
|
Mask: uint32(buf[4]) | uint32(buf[5])<<8 | uint32(buf[6])<<16 | uint32(buf[7])<<24,
|
||||||
Cookie: uint32(buf[8]) | uint32(buf[9])<<8 | uint32(buf[10])<<16 | uint32(buf[11])<<24,
|
Cookie: uint32(buf[8]) | uint32(buf[9])<<8 | uint32(buf[10])<<16 | uint32(buf[11])<<24,
|
||||||
Len: uint32(buf[12]) | uint32(buf[13])<<8 | uint32(buf[14])<<16 | uint32(buf[15])<<24,
|
Len: uint32(buf[12]) | uint32(buf[13])<<8 | uint32(buf[14])<<16 | uint32(buf[15])<<24,
|
||||||
}
|
}
|
||||||
name := unix.ByteSliceToString(buf[16 : 16+e.Len-1]) // len includes the null termiinate.
|
name := byteSliceToString(buf[16 : 16+e.Len-1]) // len includes the null termiinate.
|
||||||
buf = buf[16+e.Len:]
|
buf = buf[16+e.Len:]
|
||||||
if !reEvent.MatchString(name) {
|
if !reEvent.MatchString(name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
path := filepath.Join(dirName, name)
|
path := filepath.Join(dirName, name)
|
||||||
if e.Mask&(unix.IN_CREATE|unix.IN_ATTRIB) != 0 {
|
if e.Mask&(syscall.IN_CREATE|syscall.IN_ATTRIB) != 0 {
|
||||||
if err := g.openGamepad(gamepads, path); err != nil {
|
if err := g.openGamepad(gamepads, path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if e.Mask&unix.IN_DELETE != 0 {
|
if e.Mask&syscall.IN_DELETE != 0 {
|
||||||
if gp := gamepads.find(func(gamepad *Gamepad) bool {
|
if gp := gamepads.find(func(gamepad *Gamepad) bool {
|
||||||
return gamepad.native.(*nativeGamepadImpl).path == path
|
return gamepad.native.(*nativeGamepadImpl).path == path
|
||||||
}); gp != nil {
|
}); gp != nil {
|
||||||
@ -300,7 +307,7 @@ type nativeGamepadImpl struct {
|
|||||||
|
|
||||||
func (g *nativeGamepadImpl) close() {
|
func (g *nativeGamepadImpl) close() {
|
||||||
if g.fd != 0 {
|
if g.fd != 0 {
|
||||||
_ = unix.Close(g.fd)
|
_ = syscall.Close(g.fd)
|
||||||
}
|
}
|
||||||
g.fd = 0
|
g.fd = 0
|
||||||
}
|
}
|
||||||
@ -313,12 +320,12 @@ func (g *nativeGamepadImpl) update(gamepad *gamepads) error {
|
|||||||
for {
|
for {
|
||||||
buf := make([]byte, unsafe.Sizeof(input_event{}))
|
buf := make([]byte, unsafe.Sizeof(input_event{}))
|
||||||
// TODO: Should the returned byte count be cared?
|
// TODO: Should the returned byte count be cared?
|
||||||
if _, err := unix.Read(g.fd, buf); err != nil {
|
if _, err := syscall.Read(g.fd, buf); err != nil {
|
||||||
if err == unix.EAGAIN {
|
if err == syscall.EAGAIN {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Disconnected
|
// Disconnected
|
||||||
if err == unix.ENODEV {
|
if err == syscall.ENODEV {
|
||||||
g.close()
|
g.close()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -337,7 +344,7 @@ func (g *nativeGamepadImpl) update(gamepad *gamepads) error {
|
|||||||
value: int32(buf[offsetValue]) | int32(buf[offsetValue+1])<<8 | int32(buf[offsetValue+2])<<16 | int32(buf[offsetValue+3])<<24,
|
value: int32(buf[offsetValue]) | int32(buf[offsetValue+1])<<8 | int32(buf[offsetValue+2])<<16 | int32(buf[offsetValue+3])<<24,
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.typ == unix.EV_SYN {
|
if e.typ == _EV_SYN {
|
||||||
switch e.code {
|
switch e.code {
|
||||||
case _SYN_DROPPED:
|
case _SYN_DROPPED:
|
||||||
g.dropped = true
|
g.dropped = true
|
||||||
@ -353,12 +360,12 @@ func (g *nativeGamepadImpl) update(gamepad *gamepads) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch e.typ {
|
switch e.typ {
|
||||||
case unix.EV_KEY:
|
case _EV_KEY:
|
||||||
if int(e.code-_BTN_MISC) < len(g.keyMap) {
|
if int(e.code-_BTN_MISC) < len(g.keyMap) {
|
||||||
idx := g.keyMap[e.code-_BTN_MISC]
|
idx := g.keyMap[e.code-_BTN_MISC]
|
||||||
g.buttons[idx] = e.value != 0
|
g.buttons[idx] = e.value != 0
|
||||||
}
|
}
|
||||||
case unix.EV_ABS:
|
case _EV_ABS:
|
||||||
g.handleAbsEvent(int(e.code), e.value)
|
g.handleAbsEvent(int(e.code), e.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user