mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-02 09:52:27 -04:00
input: split out LinuxInputDeviceManager from InputDeviceManager
This commit is contained in:
parent
f189b817c6
commit
3da32b7b7c
@ -18,7 +18,7 @@
|
|||||||
#include "gamepadButton.h"
|
#include "gamepadButton.h"
|
||||||
#include "keyboardButton.h"
|
#include "keyboardButton.h"
|
||||||
#include "mouseButton.h"
|
#include "mouseButton.h"
|
||||||
#include "inputDeviceManager.h"
|
#include "linuxInputDeviceManager.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
@ -79,7 +79,8 @@ TypeHandle EvdevInputDevice::_type_handle;
|
|||||||
* Creates a new device representing the evdev device with the given index.
|
* Creates a new device representing the evdev device with the given index.
|
||||||
*/
|
*/
|
||||||
EvdevInputDevice::
|
EvdevInputDevice::
|
||||||
EvdevInputDevice(int index) :
|
EvdevInputDevice(LinuxInputDeviceManager *manager, int index) :
|
||||||
|
_manager(manager),
|
||||||
_index(index),
|
_index(index),
|
||||||
_fd(-1),
|
_fd(-1),
|
||||||
_can_write(false),
|
_can_write(false),
|
||||||
@ -203,8 +204,9 @@ do_poll() {
|
|||||||
// If we got events, we are obviously connected. Mark us so.
|
// If we got events, we are obviously connected. Mark us so.
|
||||||
if (!_is_connected) {
|
if (!_is_connected) {
|
||||||
_is_connected = true;
|
_is_connected = true;
|
||||||
InputDeviceManager *mgr = InputDeviceManager::get_global_ptr();
|
if (_manager != nullptr) {
|
||||||
mgr->add_device(this);
|
_manager->add_device(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#ifdef PHAVE_LINUX_INPUT_H
|
#ifdef PHAVE_LINUX_INPUT_H
|
||||||
|
|
||||||
|
class LinuxInputDeviceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a type of device that uses the Linux /dev/input/event# API to read
|
* This is a type of device that uses the Linux /dev/input/event# API to read
|
||||||
* data from a raw mouse or other input device. Unlike the joystick API, the
|
* data from a raw mouse or other input device. Unlike the joystick API, the
|
||||||
@ -25,7 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
class EXPCL_PANDA_DEVICE EvdevInputDevice : public InputDevice {
|
class EXPCL_PANDA_DEVICE EvdevInputDevice : public InputDevice {
|
||||||
public:
|
public:
|
||||||
EvdevInputDevice(int index);
|
EvdevInputDevice(LinuxInputDeviceManager *manager, int index);
|
||||||
virtual ~EvdevInputDevice();
|
virtual ~EvdevInputDevice();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -36,8 +38,11 @@ private:
|
|||||||
bool process_events();
|
bool process_events();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _index;
|
LinuxInputDeviceManager *_manager;
|
||||||
|
|
||||||
int _fd;
|
int _fd;
|
||||||
|
int _index;
|
||||||
|
|
||||||
bool _can_write;
|
bool _can_write;
|
||||||
int _ff_id;
|
int _ff_id;
|
||||||
bool _ff_playing;
|
bool _ff_playing;
|
||||||
|
@ -13,17 +13,10 @@
|
|||||||
|
|
||||||
#include "inputDeviceManager.h"
|
#include "inputDeviceManager.h"
|
||||||
#include "ioKitInputDevice.h"
|
#include "ioKitInputDevice.h"
|
||||||
#include "linuxJoystickDevice.h"
|
#include "linuxInputDeviceManager.h"
|
||||||
#include "winInputDeviceManager.h"
|
#include "winInputDeviceManager.h"
|
||||||
#include "throw_event.h"
|
#include "throw_event.h"
|
||||||
|
|
||||||
#ifdef PHAVE_LINUX_INPUT_H
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/inotify.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
InputDeviceManager *InputDeviceManager::_global_ptr = NULL;
|
InputDeviceManager *InputDeviceManager::_global_ptr = NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -31,51 +24,7 @@ InputDeviceManager *InputDeviceManager::_global_ptr = NULL;
|
|||||||
* connected and setting up any platform-dependent structures necessary for
|
* connected and setting up any platform-dependent structures necessary for
|
||||||
* listening for future device connect events.
|
* listening for future device connect events.
|
||||||
*/
|
*/
|
||||||
#ifdef PHAVE_LINUX_INPUT_H
|
#if defined(__APPLE__)
|
||||||
InputDeviceManager::
|
|
||||||
InputDeviceManager() : _inotify_fd(-1) {
|
|
||||||
// Use inotify to watch /dev/input for hotplugging of devices.
|
|
||||||
_inotify_fd = inotify_init1(O_NONBLOCK);
|
|
||||||
|
|
||||||
if (_inotify_fd < 0) {
|
|
||||||
device_cat.error()
|
|
||||||
<< "Error initializing inotify: " << strerror(errno) << "\n";
|
|
||||||
|
|
||||||
} else if (inotify_add_watch(_inotify_fd, "/dev/input", IN_CREATE | IN_ATTRIB | IN_DELETE) < 0) {
|
|
||||||
device_cat.error()
|
|
||||||
<< "Error adding inotify watch on /dev/input: " << strerror(errno) << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan /dev/input for a list of input devices.
|
|
||||||
DIR *dir = opendir("/dev/input");
|
|
||||||
if (dir) {
|
|
||||||
vector_int indices;
|
|
||||||
dirent *entry = readdir(dir);
|
|
||||||
while (entry != NULL) {
|
|
||||||
int index = -1;
|
|
||||||
if (entry->d_type == DT_CHR && sscanf(entry->d_name, "event%d", &index) == 1) {
|
|
||||||
indices.push_back(index);
|
|
||||||
}
|
|
||||||
entry = readdir(dir);
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
|
|
||||||
// We'll want to sort the devices by index, since the order may be
|
|
||||||
// meaningful (eg. for the Xbox wireless receiver).
|
|
||||||
std::sort(indices.begin(), indices.end());
|
|
||||||
_evdev_devices.resize(indices.back() + 1, NULL);
|
|
||||||
|
|
||||||
vector_int::const_iterator it;
|
|
||||||
for (it = indices.begin(); it != indices.end(); ++it) {
|
|
||||||
consider_add_evdev_device(*it);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
device_cat.error()
|
|
||||||
<< "Error opening directory /dev/input: " << strerror(errno) << "\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
InputDeviceManager::
|
InputDeviceManager::
|
||||||
InputDeviceManager() {
|
InputDeviceManager() {
|
||||||
_hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
_hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
|
||||||
@ -128,12 +77,7 @@ InputDeviceManager() : _lock("InputDeviceManager") {
|
|||||||
*/
|
*/
|
||||||
InputDeviceManager::
|
InputDeviceManager::
|
||||||
~InputDeviceManager() {
|
~InputDeviceManager() {
|
||||||
#ifdef PHAVE_LINUX_INPUT_H
|
#if defined(__APPLE__)
|
||||||
if (_inotify_fd >= 0) {
|
|
||||||
close(_inotify_fd);
|
|
||||||
_inotify_fd = -1;
|
|
||||||
}
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
IOHIDManagerUnscheduleFromRunLoop(_hid_manager, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
IOHIDManagerUnscheduleFromRunLoop(_hid_manager, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
IOHIDManagerClose(_hid_manager, kIOHIDOptionsTypeNone);
|
IOHIDManagerClose(_hid_manager, kIOHIDOptionsTypeNone);
|
||||||
CFRelease(_hid_manager);
|
CFRelease(_hid_manager);
|
||||||
@ -147,129 +91,13 @@ void InputDeviceManager::
|
|||||||
make_global_ptr() {
|
make_global_ptr() {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
_global_ptr = new WinInputDeviceManager;
|
_global_ptr = new WinInputDeviceManager;
|
||||||
|
#elif defined(PHAVE_LINUX_INPUT_H)
|
||||||
|
_global_ptr = new LinuxInputDeviceManager;
|
||||||
#else
|
#else
|
||||||
_global_ptr = new InputDeviceManager;
|
_global_ptr = new InputDeviceManager;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PHAVE_LINUX_INPUT_H
|
|
||||||
/**
|
|
||||||
* Checks whether the event device with the given index is accessible, and if
|
|
||||||
* so, adds it. Returns the device if it was newly connected.
|
|
||||||
*
|
|
||||||
* This is the preferred interface for input devices on Linux.
|
|
||||||
*/
|
|
||||||
InputDevice *InputDeviceManager::
|
|
||||||
consider_add_evdev_device(int ev_index) {
|
|
||||||
if (ev_index < _evdev_devices.size()) {
|
|
||||||
if (_evdev_devices[ev_index] != NULL) {
|
|
||||||
// We already have this device. FIXME: probe it and add it to the
|
|
||||||
// list of connected devices?
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Make room to store this index.
|
|
||||||
_evdev_devices.resize(ev_index + 1, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we can directly read the event device.
|
|
||||||
char path[64];
|
|
||||||
sprintf(path, "/dev/input/event%d", ev_index);
|
|
||||||
|
|
||||||
if (access(path, R_OK) == 0) {
|
|
||||||
PT(InputDevice) device = new EvdevInputDevice(ev_index);
|
|
||||||
if (device_cat.is_debug()) {
|
|
||||||
device_cat.debug()
|
|
||||||
<< "Discovered evdev input device " << *device << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
_evdev_devices[ev_index] = device;
|
|
||||||
|
|
||||||
if (device->is_connected()) {
|
|
||||||
_connected_devices.add_device(MOVE(device));
|
|
||||||
} else {
|
|
||||||
// Wait for activity on the device before it is considered connected.
|
|
||||||
_inactive_devices.add_device(MOVE(device));
|
|
||||||
}
|
|
||||||
return _evdev_devices[ev_index];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nope. The permissions haven't been configured to allow it. Check if this
|
|
||||||
// corresponds to a /dev/input/jsX interface, which has a better chance of
|
|
||||||
// having read permissions set, but doesn't export all of the features
|
|
||||||
// (notably, force feedback).
|
|
||||||
|
|
||||||
// We do this by checking for a js# directory inside the sysfs directory.
|
|
||||||
sprintf(path, "/sys/class/input/event%d/device", ev_index);
|
|
||||||
|
|
||||||
DIR *dir = opendir(path);
|
|
||||||
if (dir == NULL) {
|
|
||||||
if (device_cat.is_debug()) {
|
|
||||||
device_cat.debug()
|
|
||||||
<< "Error opening directory " << path << ": " << strerror(errno) << "\n";
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
dirent *entry = readdir(dir);
|
|
||||||
while (entry != NULL) {
|
|
||||||
int js_index = -1;
|
|
||||||
if (sscanf(entry->d_name, "js%d", &js_index) == 1) {
|
|
||||||
// Yes, we fonud a corresponding js device. Try adding it.
|
|
||||||
closedir(dir);
|
|
||||||
|
|
||||||
InputDevice *device = consider_add_js_device(js_index);
|
|
||||||
if (device != NULL && device_cat.is_warning()) {
|
|
||||||
// This seemed to work. Display a warning to the user indicating
|
|
||||||
// that they might want to configure udev properly.
|
|
||||||
device_cat.warning()
|
|
||||||
<< "/dev/input/event" << ev_index << " is not readable, some "
|
|
||||||
"features will be unavailable.\n";
|
|
||||||
}
|
|
||||||
_evdev_devices[ev_index] = device;
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
entry = readdir(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(dir);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether the joystick device with the given index is accessible, and
|
|
||||||
* if so, adds it. Returns the device if it was newly connected.
|
|
||||||
*
|
|
||||||
* This is only used on Linux as a fallback interface for when an evdev device
|
|
||||||
* cannot be accessed.
|
|
||||||
*/
|
|
||||||
InputDevice *InputDeviceManager::
|
|
||||||
consider_add_js_device(int js_index) {
|
|
||||||
char path[64];
|
|
||||||
sprintf(path, "/dev/input/js%d", js_index);
|
|
||||||
|
|
||||||
if (access(path, R_OK) == 0) {
|
|
||||||
PT(LinuxJoystickDevice) device = new LinuxJoystickDevice(js_index);
|
|
||||||
if (device_cat.is_debug()) {
|
|
||||||
device_cat.debug()
|
|
||||||
<< "Discovered joydev input device " << *device << "\n";
|
|
||||||
}
|
|
||||||
InputDevice *device_p = device.p();
|
|
||||||
|
|
||||||
if (device->is_connected()) {
|
|
||||||
_connected_devices.add_device(MOVE(device));
|
|
||||||
} else {
|
|
||||||
// Wait for activity on the device before it is considered connected.
|
|
||||||
_inactive_devices.add_device(MOVE(device));
|
|
||||||
}
|
|
||||||
return device_p;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Description: Returns all currently connected devices.
|
* Description: Returns all currently connected devices.
|
||||||
*/
|
*/
|
||||||
@ -348,105 +176,4 @@ remove_device(InputDevice *device) {
|
|||||||
*/
|
*/
|
||||||
void InputDeviceManager::
|
void InputDeviceManager::
|
||||||
update() {
|
update() {
|
||||||
#ifdef PHAVE_LINUX_INPUT_H
|
|
||||||
// Check for any devices that may be disconnected and need to be probed in
|
|
||||||
// order to see whether they have been reconnected.
|
|
||||||
InputDeviceSet inactive_devices;
|
|
||||||
{
|
|
||||||
LightMutexHolder holder(_lock);
|
|
||||||
inactive_devices = _inactive_devices;
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < inactive_devices.size(); ++i) {
|
|
||||||
InputDevice *device = inactive_devices[i];
|
|
||||||
if (device != NULL && !device->is_connected()) {
|
|
||||||
device->poll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We use inotify to tell us whether a device was added, removed, or has
|
|
||||||
// changed permissions to allow us to access it.
|
|
||||||
unsigned int avail = 0;
|
|
||||||
ioctl(_inotify_fd, FIONREAD, &avail);
|
|
||||||
if (avail == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char buffer[avail];
|
|
||||||
int n_read = read(_inotify_fd, buffer, avail);
|
|
||||||
if (n_read < 0) {
|
|
||||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
||||||
// No data available for now.
|
|
||||||
|
|
||||||
} else {
|
|
||||||
device_cat.error() << "read: " << strerror(errno) << "\n";
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LightMutexHolder holder(_lock);
|
|
||||||
|
|
||||||
// Iterate over the events in the buffer.
|
|
||||||
char *ptr = buffer;
|
|
||||||
char *end = buffer + avail;
|
|
||||||
while (ptr < end) {
|
|
||||||
inotify_event *event = (inotify_event *)ptr;
|
|
||||||
|
|
||||||
string name(event->name);
|
|
||||||
|
|
||||||
if (event->mask & IN_DELETE) {
|
|
||||||
// The device was deleted. If we have it, remove it.
|
|
||||||
|
|
||||||
int index = -1;
|
|
||||||
if (sscanf(event->name, "event%d", &index) == 1) {
|
|
||||||
// Check if we have this evdev device. If so, disconnect it.
|
|
||||||
if (index < _evdev_devices.size()) {
|
|
||||||
PT(InputDevice) device = _evdev_devices[index];
|
|
||||||
if (device != NULL) {
|
|
||||||
_evdev_devices[index] = NULL;
|
|
||||||
_inactive_devices.remove_device(device);
|
|
||||||
if (_connected_devices.remove_device(device)) {
|
|
||||||
throw_event("disconnect-device", device.p());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device_cat.is_debug()) {
|
|
||||||
device_cat.debug()
|
|
||||||
<< "Removed input device " << *device << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (event->mask & (IN_CREATE | IN_ATTRIB)) {
|
|
||||||
// The device was created, or it was chmodded to be accessible. We need
|
|
||||||
// to check for the latter since it seems that the device may get the
|
|
||||||
// IN_CREATE event before the driver gets the permissions set properly.
|
|
||||||
|
|
||||||
int index = -1;
|
|
||||||
if (sscanf(event->name, "event%d", &index) == 1) {
|
|
||||||
InputDevice *device = consider_add_evdev_device(index);
|
|
||||||
if (device != NULL && device->is_connected()) {
|
|
||||||
throw_event("connect-device", device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr += sizeof(inotify_event) + event->len;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__APPLE__) && !defined(CPPPARSER)
|
|
||||||
void InputDeviceManager::
|
|
||||||
on_match_device(void *ctx, IOReturn result, void *sender, IOHIDDeviceRef device) {
|
|
||||||
InputDeviceManager *mgr = (InputDeviceManager *)ctx;
|
|
||||||
nassertv(mgr != nullptr);
|
|
||||||
nassertv(device);
|
|
||||||
|
|
||||||
PT(InputDevice) input_device = new IOKitInputDevice(device);
|
|
||||||
if (device_cat.is_debug()) {
|
|
||||||
device_cat.debug()
|
|
||||||
<< "Discovered input device " << *input_device << "\n";
|
|
||||||
}
|
|
||||||
mgr->add_device(input_device);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
@ -62,9 +62,6 @@ protected:
|
|||||||
LightMutex _lock;
|
LightMutex _lock;
|
||||||
|
|
||||||
#ifdef PHAVE_LINUX_INPUT_H
|
#ifdef PHAVE_LINUX_INPUT_H
|
||||||
int _inotify_fd;
|
|
||||||
|
|
||||||
pvector<InputDevice *> _evdev_devices;
|
|
||||||
InputDeviceSet _inactive_devices;
|
InputDeviceSet _inactive_devices;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
294
panda/src/device/linuxInputDeviceManager.cxx
Normal file
294
panda/src/device/linuxInputDeviceManager.cxx
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
/**
|
||||||
|
* PANDA 3D SOFTWARE
|
||||||
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||||
|
*
|
||||||
|
* All use of this software is subject to the terms of the revised BSD
|
||||||
|
* license. You should have received a copy of this license along
|
||||||
|
* with this source code in a file named "LICENSE."
|
||||||
|
*
|
||||||
|
* @file linuxInputDeviceManager.cxx
|
||||||
|
* @author rdb
|
||||||
|
* @date 2018-01-25
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "inputDeviceManager.h"
|
||||||
|
#include "throw_event.h"
|
||||||
|
|
||||||
|
#ifdef PHAVE_LINUX_INPUT_H
|
||||||
|
|
||||||
|
#include "evdevInputDevice.h"
|
||||||
|
#include "linuxJoystickDevice.h"
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the device manager by scanning which devices are currently
|
||||||
|
* connected and setting up any platform-dependent structures necessary for
|
||||||
|
* listening for future device connect events.
|
||||||
|
*/
|
||||||
|
LinuxInputDeviceManager::
|
||||||
|
LinuxInputDeviceManager() {
|
||||||
|
// Use inotify to watch /dev/input for hotplugging of devices.
|
||||||
|
_inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||||
|
|
||||||
|
if (_inotify_fd < 0) {
|
||||||
|
device_cat.error()
|
||||||
|
<< "Error initializing inotify: " << strerror(errno) << "\n";
|
||||||
|
|
||||||
|
} else if (inotify_add_watch(_inotify_fd, "/dev/input", IN_CREATE | IN_ATTRIB | IN_DELETE) < 0) {
|
||||||
|
device_cat.error()
|
||||||
|
<< "Error adding inotify watch on /dev/input: " << strerror(errno) << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan /dev/input for a list of input devices.
|
||||||
|
DIR *dir = opendir("/dev/input");
|
||||||
|
if (dir) {
|
||||||
|
vector_int indices;
|
||||||
|
dirent *entry = readdir(dir);
|
||||||
|
while (entry != nullptr) {
|
||||||
|
int index = -1;
|
||||||
|
if (entry->d_type == DT_CHR && sscanf(entry->d_name, "event%d", &index) == 1) {
|
||||||
|
indices.push_back(index);
|
||||||
|
}
|
||||||
|
entry = readdir(dir);
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
// We'll want to sort the devices by index, since the order may be
|
||||||
|
// meaningful (eg. for the Xbox wireless receiver).
|
||||||
|
std::sort(indices.begin(), indices.end());
|
||||||
|
_evdev_devices.resize(indices.back() + 1, nullptr);
|
||||||
|
|
||||||
|
vector_int::const_iterator it;
|
||||||
|
for (it = indices.begin(); it != indices.end(); ++it) {
|
||||||
|
consider_add_evdev_device(*it);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
device_cat.error()
|
||||||
|
<< "Error opening directory /dev/input: " << strerror(errno) << "\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes any resources that the device manager was using to listen for events.
|
||||||
|
*/
|
||||||
|
LinuxInputDeviceManager::
|
||||||
|
~LinuxInputDeviceManager() {
|
||||||
|
if (_inotify_fd >= 0) {
|
||||||
|
close(_inotify_fd);
|
||||||
|
_inotify_fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the event device with the given index is accessible, and if
|
||||||
|
* so, adds it. Returns the device if it was newly connected.
|
||||||
|
*
|
||||||
|
* This is the preferred interface for input devices on Linux.
|
||||||
|
*/
|
||||||
|
InputDevice *LinuxInputDeviceManager::
|
||||||
|
consider_add_evdev_device(int ev_index) {
|
||||||
|
if (ev_index < _evdev_devices.size()) {
|
||||||
|
if (_evdev_devices[ev_index] != nullptr) {
|
||||||
|
// We already have this device. FIXME: probe it and add it to the
|
||||||
|
// list of connected devices?
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Make room to store this index.
|
||||||
|
_evdev_devices.resize(ev_index + 1, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we can directly read the event device.
|
||||||
|
char path[64];
|
||||||
|
sprintf(path, "/dev/input/event%d", ev_index);
|
||||||
|
|
||||||
|
if (access(path, R_OK) == 0) {
|
||||||
|
PT(InputDevice) device = new EvdevInputDevice(this, ev_index);
|
||||||
|
if (device_cat.is_debug()) {
|
||||||
|
device_cat.debug()
|
||||||
|
<< "Discovered evdev input device " << *device << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
_evdev_devices[ev_index] = device;
|
||||||
|
|
||||||
|
if (device->is_connected()) {
|
||||||
|
_connected_devices.add_device(MOVE(device));
|
||||||
|
} else {
|
||||||
|
// Wait for activity on the device before it is considered connected.
|
||||||
|
_inactive_devices.add_device(MOVE(device));
|
||||||
|
}
|
||||||
|
return _evdev_devices[ev_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nope. The permissions haven't been configured to allow it. Check if this
|
||||||
|
// corresponds to a /dev/input/jsX interface, which has a better chance of
|
||||||
|
// having read permissions set, but doesn't export all of the features
|
||||||
|
// (notably, force feedback).
|
||||||
|
|
||||||
|
// We do this by checking for a js# directory inside the sysfs directory.
|
||||||
|
sprintf(path, "/sys/class/input/event%d/device", ev_index);
|
||||||
|
|
||||||
|
DIR *dir = opendir(path);
|
||||||
|
if (dir == nullptr) {
|
||||||
|
if (device_cat.is_debug()) {
|
||||||
|
device_cat.debug()
|
||||||
|
<< "Error opening directory " << path << ": " << strerror(errno) << "\n";
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
dirent *entry = readdir(dir);
|
||||||
|
while (entry != nullptr) {
|
||||||
|
int js_index = -1;
|
||||||
|
if (sscanf(entry->d_name, "js%d", &js_index) == 1) {
|
||||||
|
// Yes, we fonud a corresponding js device. Try adding it.
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
InputDevice *device = consider_add_js_device(js_index);
|
||||||
|
if (device != nullptr && device_cat.is_warning()) {
|
||||||
|
// This seemed to work. Display a warning to the user indicating
|
||||||
|
// that they might want to configure udev properly.
|
||||||
|
device_cat.warning()
|
||||||
|
<< "/dev/input/event" << ev_index << " is not readable, some "
|
||||||
|
"features will be unavailable.\n";
|
||||||
|
}
|
||||||
|
_evdev_devices[ev_index] = device;
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
entry = readdir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the joystick device with the given index is accessible, and
|
||||||
|
* if so, adds it. Returns the device if it was newly connected.
|
||||||
|
*
|
||||||
|
* This is only used on Linux as a fallback interface for when an evdev device
|
||||||
|
* cannot be accessed.
|
||||||
|
*/
|
||||||
|
InputDevice *LinuxInputDeviceManager::
|
||||||
|
consider_add_js_device(int js_index) {
|
||||||
|
char path[64];
|
||||||
|
sprintf(path, "/dev/input/js%d", js_index);
|
||||||
|
|
||||||
|
if (access(path, R_OK) == 0) {
|
||||||
|
PT(LinuxJoystickDevice) device = new LinuxJoystickDevice(this, js_index);
|
||||||
|
if (device_cat.is_debug()) {
|
||||||
|
device_cat.debug()
|
||||||
|
<< "Discovered joydev input device " << *device << "\n";
|
||||||
|
}
|
||||||
|
InputDevice *device_p = device.p();
|
||||||
|
|
||||||
|
if (device->is_connected()) {
|
||||||
|
_connected_devices.add_device(MOVE(device));
|
||||||
|
} else {
|
||||||
|
// Wait for activity on the device before it is considered connected.
|
||||||
|
_inactive_devices.add_device(MOVE(device));
|
||||||
|
}
|
||||||
|
return device_p;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polls the system to see if there are any new devices. In some
|
||||||
|
* implementations this is a no-op.
|
||||||
|
*/
|
||||||
|
void LinuxInputDeviceManager::
|
||||||
|
update() {
|
||||||
|
// Check for any devices that may be disconnected and need to be probed in
|
||||||
|
// order to see whether they have been reconnected.
|
||||||
|
InputDeviceSet inactive_devices;
|
||||||
|
{
|
||||||
|
LightMutexHolder holder(_lock);
|
||||||
|
inactive_devices = _inactive_devices;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < inactive_devices.size(); ++i) {
|
||||||
|
InputDevice *device = inactive_devices[i];
|
||||||
|
if (device != nullptr && !device->is_connected()) {
|
||||||
|
device->poll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use inotify to tell us whether a device was added, removed, or has
|
||||||
|
// changed permissions to allow us to access it.
|
||||||
|
unsigned int avail = 0;
|
||||||
|
ioctl(_inotify_fd, FIONREAD, &avail);
|
||||||
|
if (avail == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buffer[avail];
|
||||||
|
int n_read = read(_inotify_fd, buffer, avail);
|
||||||
|
if (n_read < 0) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
// No data available for now.
|
||||||
|
|
||||||
|
} else {
|
||||||
|
device_cat.error() << "read: " << strerror(errno) << "\n";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LightMutexHolder holder(_lock);
|
||||||
|
|
||||||
|
// Iterate over the events in the buffer.
|
||||||
|
char *ptr = buffer;
|
||||||
|
char *end = buffer + avail;
|
||||||
|
while (ptr < end) {
|
||||||
|
inotify_event *event = (inotify_event *)ptr;
|
||||||
|
|
||||||
|
string name(event->name);
|
||||||
|
|
||||||
|
if (event->mask & IN_DELETE) {
|
||||||
|
// The device was deleted. If we have it, remove it.
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
if (sscanf(event->name, "event%d", &index) == 1) {
|
||||||
|
// Check if we have this evdev device. If so, disconnect it.
|
||||||
|
if (index < _evdev_devices.size()) {
|
||||||
|
PT(InputDevice) device = _evdev_devices[index];
|
||||||
|
if (device != nullptr) {
|
||||||
|
device->set_connected(false);
|
||||||
|
_evdev_devices[index] = nullptr;
|
||||||
|
_inactive_devices.remove_device(device);
|
||||||
|
if (_connected_devices.remove_device(device)) {
|
||||||
|
throw_event("disconnect-device", device.p());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_cat.is_debug()) {
|
||||||
|
device_cat.debug()
|
||||||
|
<< "Removed input device " << *device << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (event->mask & (IN_CREATE | IN_ATTRIB)) {
|
||||||
|
// The device was created, or it was chmodded to be accessible. We need
|
||||||
|
// to check for the latter since it seems that the device may get the
|
||||||
|
// IN_CREATE event before the driver gets the permissions set properly.
|
||||||
|
|
||||||
|
int index = -1;
|
||||||
|
if (sscanf(event->name, "event%d", &index) == 1) {
|
||||||
|
InputDevice *device = consider_add_evdev_device(index);
|
||||||
|
if (device != nullptr && device->is_connected()) {
|
||||||
|
throw_event("connect-device", device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr += sizeof(inotify_event) + event->len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PHAVE_LINUX_INPUT_H
|
45
panda/src/device/linuxInputDeviceManager.h
Normal file
45
panda/src/device/linuxInputDeviceManager.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* PANDA 3D SOFTWARE
|
||||||
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||||
|
*
|
||||||
|
* All use of this software is subject to the terms of the revised BSD
|
||||||
|
* license. You should have received a copy of this license along
|
||||||
|
* with this source code in a file named "LICENSE."
|
||||||
|
*
|
||||||
|
* @file linuxInputDeviceManager.h
|
||||||
|
* @author rdb
|
||||||
|
* @date 2018-01-25
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LINUXINPUTDEVICEMANAGER_H
|
||||||
|
#define LINUXINPUTDEVICEMANAGER_H
|
||||||
|
|
||||||
|
#include "inputDeviceManager.h"
|
||||||
|
|
||||||
|
#ifdef PHAVE_LINUX_INPUT_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class keeps track of all the devices on a system, and sends out events
|
||||||
|
* when a device has been hot-plugged.
|
||||||
|
*/
|
||||||
|
class EXPCL_PANDA_DEVICE LinuxInputDeviceManager FINAL : public InputDeviceManager {
|
||||||
|
private:
|
||||||
|
LinuxInputDeviceManager();
|
||||||
|
~LinuxInputDeviceManager();
|
||||||
|
|
||||||
|
InputDevice *consider_add_evdev_device(int index);
|
||||||
|
InputDevice *consider_add_js_device(int index);
|
||||||
|
|
||||||
|
virtual void update();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int _inotify_fd;
|
||||||
|
|
||||||
|
pvector<InputDevice *> _evdev_devices;
|
||||||
|
|
||||||
|
friend class InputDeviceManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PHAVE_LINUX_INPUT_H
|
||||||
|
|
||||||
|
#endif
|
@ -17,6 +17,7 @@
|
|||||||
#ifdef PHAVE_LINUX_INPUT_H
|
#ifdef PHAVE_LINUX_INPUT_H
|
||||||
|
|
||||||
#include "gamepadButton.h"
|
#include "gamepadButton.h"
|
||||||
|
#include "linuxInputDeviceManager.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/joystick.h>
|
#include <linux/joystick.h>
|
||||||
@ -27,7 +28,8 @@ TypeHandle LinuxJoystickDevice::_type_handle;
|
|||||||
* Creates a new device using the Linux joystick device with the given index.
|
* Creates a new device using the Linux joystick device with the given index.
|
||||||
*/
|
*/
|
||||||
LinuxJoystickDevice::
|
LinuxJoystickDevice::
|
||||||
LinuxJoystickDevice(int index) :
|
LinuxJoystickDevice(LinuxInputDeviceManager *manager, int index) :
|
||||||
|
_manager(manager),
|
||||||
_fd(-1),
|
_fd(-1),
|
||||||
_index(index),
|
_index(index),
|
||||||
_dpad_x_axis(-1),
|
_dpad_x_axis(-1),
|
||||||
@ -79,8 +81,9 @@ do_poll() {
|
|||||||
// If we got events, we are obviously connected. Mark us so.
|
// If we got events, we are obviously connected. Mark us so.
|
||||||
if (!_is_connected) {
|
if (!_is_connected) {
|
||||||
_is_connected = true;
|
_is_connected = true;
|
||||||
InputDeviceManager *mgr = InputDeviceManager::get_global_ptr();
|
if (_manager != nullptr) {
|
||||||
mgr->add_device(this);
|
_manager->add_device(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,15 @@
|
|||||||
|
|
||||||
#ifdef PHAVE_LINUX_INPUT_H
|
#ifdef PHAVE_LINUX_INPUT_H
|
||||||
|
|
||||||
|
class LinuxInputDeviceManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a type of device that uses the Linux /dev/input/js# API to read
|
* This is a type of device that uses the Linux /dev/input/js# API to read
|
||||||
* data from a game controller.
|
* data from a game controller.
|
||||||
*/
|
*/
|
||||||
class EXPCL_PANDA_DEVICE LinuxJoystickDevice : public InputDevice {
|
class EXPCL_PANDA_DEVICE LinuxJoystickDevice : public InputDevice {
|
||||||
public:
|
PUBLISHED:
|
||||||
LinuxJoystickDevice(int index);
|
LinuxJoystickDevice(LinuxInputDeviceManager *manager, int index);
|
||||||
virtual ~LinuxJoystickDevice();
|
virtual ~LinuxJoystickDevice();
|
||||||
|
|
||||||
bool check_events() const;
|
bool check_events() const;
|
||||||
@ -36,6 +38,8 @@ private:
|
|||||||
bool process_events();
|
bool process_events();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
LinuxInputDeviceManager *_manager;
|
||||||
|
|
||||||
int _fd;
|
int _fd;
|
||||||
int _index;
|
int _index;
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "inputDeviceNode.cxx"
|
#include "inputDeviceNode.cxx"
|
||||||
#include "inputDeviceSet.cxx"
|
#include "inputDeviceSet.cxx"
|
||||||
#include "ioKitInputDevice.cxx"
|
#include "ioKitInputDevice.cxx"
|
||||||
|
#include "linuxInputDeviceManager.cxx"
|
||||||
#include "linuxJoystickDevice.cxx"
|
#include "linuxJoystickDevice.cxx"
|
||||||
#include "winInputDeviceManager.cxx"
|
#include "winInputDeviceManager.cxx"
|
||||||
#include "winRawInputDevice.cxx"
|
#include "winRawInputDevice.cxx"
|
||||||
|
@ -1245,7 +1245,7 @@ open_raw_mice() {
|
|||||||
string fn = fnb.str();
|
string fn = fnb.str();
|
||||||
int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
|
int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
|
||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
EvdevInputDevice *device = new EvdevInputDevice(fd);
|
EvdevInputDevice *device = new EvdevInputDevice(nullptr, fd);
|
||||||
nassertd(device != NULL) continue;
|
nassertd(device != NULL) continue;
|
||||||
|
|
||||||
if (device->has_pointer()) {
|
if (device->has_pointer()) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user