]> Devoid-pointer.net GitWeb - FFBChecker.git/commitdiff
First step towards modularizing for multiple FFB backends.
authorMichal Malý <madcatxster@devoid-pointer.net>
Wed, 29 Jul 2015 22:44:49 +0000 (00:44 +0200)
committerMichal Malý <madcatxster@devoid-pointer.net>
Wed, 29 Jul 2015 22:44:49 +0000 (00:44 +0200)
12 files changed:
CMakeLists.txt
deviceprober.h
ffbdevice.cpp
ffbdevice.h
linuxdeviceprober.cpp [moved from deviceprober.cpp with 70% similarity]
linuxdeviceprober.h [new file with mode: 0644]
linuxffbdevice.cpp [new file with mode: 0644]
linuxffbdevice.h [new file with mode: 0644]
main.cpp
mainwindow.cpp
mainwindow.h
mainwindow.ui

index a452a5c3038d25c3629efa0a7a957a3ba8f1a395..512ee97ea7d0dd1040a90fb5581d11ec21f5762f 100644 (file)
@@ -10,6 +10,12 @@ endif()
 
 find_package(Qt5Widgets)
 
+include(FindPkgConfig)
+pkg_search_module(SDL2 sdl2)
+if (SDL2_FOUND)
+    add_definitions("-DFFBC_HAVE_SDL2")
+endif()
+
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTOUIC ON)
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
@@ -17,7 +23,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
 set(FFBChecker_SRCS
     conditioneffectsettings.cpp
     constanteffectsettings.cpp
-    deviceprober.cpp
     effectsettings.cpp
     envelopesettings.cpp
     ffbconditioneffect.cpp
@@ -37,14 +42,22 @@ set(FFBChecker_SRCS
     ffbrumbleeffect.cpp
     ffbrumbleeffectparameters.cpp
     globalsettings.cpp
+    linuxdeviceprober.cpp
+    linuxffbdevice.cpp
     main.cpp
     mainwindow.cpp
     periodiceffectsettings.cpp
     rampeffectsettings.cpp
     rumbleeffectsettings.cpp)
 
-include_directories(
-  ${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+if (SDL2_FOUND)
+    include_directories(${SDL2_INCLUDE_DIRS})
+endif()
 
 add_executable(FFBChecker ${FFBChecker_SRCS})
 target_link_libraries(FFBChecker Qt5::Widgets)
+
+if (SDL2_FOUND)
+    target_link_libraries(FFBChecker ${SDL2_LIBRARIES})
+endif()
index 778d28eb828f2eab752f700e18257e15a62d2deb..4d5c30dc13564f66f1472ebe7a0f9fb340b9bc8b 100644 (file)
@@ -3,33 +3,34 @@
 
 #include "ffbdevice.h"
 #include <memory>
-#include <QtCore/QDir>
+#include <QtCore/QVariant>
 #include <QtCore/QObject>
 
-class DeviceProber : public QObject
+class DeviceProber
 {
-  Q_OBJECT
 public:
   struct DeviceInfo {
-    QString path;
+    QVariant id;
     QString name;
   };
   typedef QList<DeviceInfo> DeviceList;
 
-  explicit DeviceProber(QObject* parent = 0);
-  DeviceList listDevices();
-  std::shared_ptr<FFBDevice> openDevice(const QString& path);
-
-private:
-  std::list<std::shared_ptr<FFBDevice>> m_openedDevices;
-
-  static const QString DEVICE_NODES_PATH;
-  static const QString res_ffbdeviceErrCap;
+  enum class DeviceInterfaces : unsigned int {
+    NONE,
+    LINUX,
+    SDL2
+  };
 
-signals:
+  virtual void closeAllDevices() = 0;
+  virtual DeviceList listDevices() = 0;
+  virtual std::shared_ptr<FFBDevice> openDevice(const QString& id) = 0;
 
-public slots:
+  const DeviceInterfaces type;
 
+protected:
+  explicit DeviceProber() : type(DeviceInterfaces::NONE) {}
 };
 
+
 #endif // DEVICEPROBER_H
+
index 3b0efa8bd40dc5b31e6040c6c735bf8a3869a85d..a2424af227dd8eb98c72d401a2b0bbce18b6ad62 100644 (file)
@@ -1,21 +1,5 @@
 #include "ffbdevice.h"
 #include "ffbeffectfactory.h"
-#include <QtWidgets/QMessageBox>
-#include <QDebug>
-
-#define CHECK_EFFECT_IDX(idx) if (idx < 0 || idx > c_maxEffectCount) return false
-
-const quint8 FFBDevice::BITS_PER_LONG = sizeof(unsigned long) * 8;
-
-FFBDevice::FFBDevice(const int fd, const QString& path, const int maxEffectCount, QObject* parent) :
-  QObject(parent),
-  c_fd(fd),
-  c_maxEffectCount(maxEffectCount),
-  c_path(path)
-{
-  for (int i = 0; i < maxEffectCount; i++)
-    m_effects.push_back(FFBEffectFactory::createEffect(FFBEffectTypes::NONE));
-}
 
 const std::vector<ConditionSubtypes>& FFBDevice::availableConditionSubtypesList() const
 {
@@ -34,7 +18,10 @@ const std::vector<PeriodicWaveforms>& FFBDevice::availableWaveformsList() const
 
 const std::shared_ptr<FFBEffectParameters> FFBDevice::effectParameters(const int idx)
 {
-  if (idx >= c_maxEffectCount)
+  if (idx >= c_maxEffectCount || idx < 0)
+    return nullptr;
+
+  if (m_effects[idx] == nullptr)
     return nullptr;
 
   return m_effects[idx]->parameters();
@@ -42,6 +29,9 @@ const std::shared_ptr<FFBEffectParameters> FFBDevice::effectParameters(const int
 
 FFBEffect::FFBEffectStatus FFBDevice::effectStatusByIdx(const int idx) const
 {
+  if (idx >= c_maxEffectCount || idx < 0)
+    return FFBEffect::FFBEffectStatus::NOT_LOADED;
+
   if (m_effects[idx] == nullptr)
     return FFBEffect::FFBEffectStatus::NOT_LOADED;
 
@@ -50,6 +40,12 @@ FFBEffect::FFBEffectStatus FFBDevice::effectStatusByIdx(const int idx) const
 
 FFBEffectTypes FFBDevice::effectTypeByEffectIdx(const int idx) const
 {
+  if (idx >= c_maxEffectCount || idx < 0)
+    return FFBEffectTypes::NONE;
+
+  if (m_effects[idx] == nullptr)
+    return FFBEffectTypes::NONE;
+
   return m_effects[idx]->type();
 }
 
@@ -68,202 +64,3 @@ bool FFBDevice::hasPeriodicWaveform(PeriodicWaveforms id) const
 
   return false;
 }
-
-bool FFBDevice::queryDeviceCapabilities()
-{
-  unsigned long caps[4];
-  int ret = ioctl(c_fd, EVIOCGBIT(EV_FF, sizeof(caps)), caps);
-  if (ret < 0)
-    return false;
-
-  /* Query FFB effects this device can do */
-  if (testBit(FF_CONSTANT, caps))
-    m_availableEffects.push_back(FFBEffectTypes::CONSTANT);
-  if (testBit(FF_PERIODIC, caps))
-    m_availableEffects.push_back(FFBEffectTypes::PERIODIC);
-  if (testBit(FF_RAMP, caps))
-    m_availableEffects.push_back(FFBEffectTypes::RAMP);
-  if (testBit(FF_SPRING, caps) || testBit(FF_FRICTION, caps) ||
-      testBit(FF_DAMPER, caps) || testBit(FF_INERTIA, caps))
-    m_availableEffects.push_back(FFBEffectTypes::CONDITION);
-  if (testBit(FF_RUMBLE, caps))
-    m_availableEffects.push_back(FFBEffectTypes::RUMBLE);
-
-  /* Query waveforms for PERIODIC if the device supports it */
-  if (hasEffect(FFBEffectTypes::PERIODIC)) {
-    if (testBit(FF_SQUARE, caps))
-      m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SQUARE);
-    if (testBit(FF_TRIANGLE, caps))
-      m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::TRIANGLE);
-    if (testBit(FF_SINE, caps))
-      m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SINE);
-    if (testBit(FF_SAW_UP, caps))
-      m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SAW_UP);
-    if (testBit(FF_SAW_DOWN, caps))
-      m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SAW_DOWN);
-  }
-
-  /* Query condition effect subtypes */
-  if (testBit(FF_SPRING, caps))
-    m_availableConditionSubtypes.push_back(ConditionSubtypes::SPRING);
-  if (testBit(FF_FRICTION, caps))
-    m_availableConditionSubtypes.push_back(ConditionSubtypes::FRICTION);
-  if (testBit(FF_DAMPER, caps))
-    m_availableConditionSubtypes.push_back(ConditionSubtypes::DAMPER);
-  if (testBit(FF_INERTIA, caps))
-    m_availableConditionSubtypes.push_back(ConditionSubtypes::INERTIA);
-
-  return true;
-}
-
-bool FFBDevice::removeAndEraseEffect(const int idx)
-{
-  if (m_effects[idx]->status() == FFBEffect::FFBEffectStatus::NOT_LOADED)
-    return true;
-
-  if (removeEffect(idx)) {
-    m_effects[idx] = FFBEffectFactory::createEffect(FFBEffectTypes::NONE);
-    if (m_effects[idx]->type() != FFBEffectTypes::NONE) {
-      qCritical("Unable to empty the effect slot.");
-      return false;
-    }
-  } else {
-    qCritical("Unable to stop the effect.");
-    return false;
-  }
-
-  m_effects[idx]->setStatus(FFBEffect::FFBEffectStatus::NOT_LOADED);
-  return true;
-}
-
-bool FFBDevice::removeEffect(const int idx)
-{
-  if (!stopEffect(idx))
-    return false;
-
-  int internalIdx = m_effects[idx]->internalIdx();
-  int ret = ioctl(c_fd, EVIOCRMFF, internalIdx);
-  if (ret < 0)
-    return false;
-  return true;
-}
-
-bool FFBDevice::startEffect(const int idx, const FFBEffectTypes type, std::shared_ptr<FFBEffectParameters> parameters)
-{
-  int ret;
-
-  CHECK_EFFECT_IDX(idx);
-
-  if (m_effects[idx]->status() == FFBEffect::FFBEffectStatus::NOT_LOADED) {
-    if (!uploadEffect(idx, type, parameters))
-      return false;
-  }
-  if (m_effects[idx]->status() == FFBEffect::FFBEffectStatus::PLAYING)
-    return true;
-
-  /* Start playback */
-  struct input_event evt;
-  evt.type = EV_FF;
-  evt.code = m_effects[idx]->internalIdx();
-  evt.value = m_effects[idx]->parameters()->repeat;
-
-  ret = write(c_fd, &evt, sizeof(struct input_event));
-  if (ret != sizeof(struct input_event)) {
-    QMessageBox::critical(nullptr, "FFB Device", "Effect could not have been started, error code: " + QString::number(ret));
-    qDebug() << "Effect not started" << ret;
-    return false;
-  }
-
-  m_effects[idx]->setStatus(FFBEffect::FFBEffectStatus::PLAYING);
-
-  return true;
-}
-
-bool FFBDevice::stopEffect(const int idx)
-{
-  CHECK_EFFECT_IDX(idx);
-
-  if (m_effects[idx] == nullptr)
-    return true;
-
-  if (m_effects[idx]->status() != FFBEffect::FFBEffectStatus::PLAYING)
-    return true;
-
-  int internalIdx = m_effects[idx]->internalIdx();
-
-  struct input_event evt;
-  evt.type = EV_FF;
-  evt.code = internalIdx;
-  evt.value = 0;
-
-  int ret = write(c_fd, &evt, sizeof(struct input_event));
-  if (ret != sizeof(struct input_event))
-    return false;
-
-  m_effects[idx]->setStatus(FFBEffect::FFBEffectStatus::UPLOADED);
-  return true;
-}
-
-bool FFBDevice::uploadEffect(const int idx, const FFBEffectTypes type, std::shared_ptr<FFBEffectParameters> parameters)
-{
-  struct ff_effect* kernelEff = nullptr;
-  std::shared_ptr<FFBEffect> effect = FFBEffectFactory::createEffect(type);
-
-  CHECK_EFFECT_IDX(idx);
-
-  if (effect == nullptr) {
-    qDebug() << "Unable to create effect";
-    return false;
-  }
-  if (!effect->setParameters(parameters)) {
-    qDebug() << "Unable to set effect parameters, some values are probably invalid.";
-    return false;
-  }
-
-  if (idx < 0 || idx > c_maxEffectCount) {
-    qCritical() << "Effect index out of bounds";
-    return false;
-  }
-
-  /* There is no effect in the selected slot */
-  if (m_effects[idx]->type() == FFBEffectTypes::NONE) {
-    effect->setStatus(FFBEffect::FFBEffectStatus::UPLOADED);
-    qDebug() << "Creating new effect";
-  } else {
-    if (*m_effects[idx] != *effect) {
-      if (!removeEffect(idx)) {
-        QMessageBox::critical(nullptr, "FFB Device", "Unable to remove effect");
-        return false;
-      }
-      effect->setStatus(FFBEffect::FFBEffectStatus::UPLOADED);
-      qDebug() << "Recreating effect" << idx;
-    } else {
-      effect->setInternalIdx(m_effects[idx]->internalIdx());
-      effect->setStatus(m_effects[idx]->status());
-      qDebug() << "Updating effect" << idx;
-    }
-  }
-
-  kernelEff = effect->createFFStruct();
-  if (kernelEff == nullptr) {
-    QMessageBox::critical(nullptr, "FFB Device", "ff_effect struct could not have been created. Effect not uploaded.");
-    qDebug() << "struct ff_effect not created";
-    return false;
-  }
-
-  qDebug() << kernelEff->u.condition[0].center << kernelEff->u.condition[0].deadband << kernelEff->u.condition[1].center << kernelEff->u.condition[1].deadband;
-
-  int ret = ioctl(c_fd, EVIOCSFF, kernelEff);
-  if (ret < 0) {
-    QMessageBox::critical(nullptr, "FFB Device", "Effect could not have been uploaded, error code: " + QString::number(ret));
-    qDebug() << "Effect not uploaded" << ret;
-    delete kernelEff;
-    return false;
-  }
-
-  effect->setInternalIdx(kernelEff->id);
-  delete kernelEff;
-
-  m_effects[idx] = effect;
-  return true;
-}
index 547bff453c57e6db89911bbd313f9456331aac25..45352e0ec56a5b01b081fa6ebc4350efe1f35dc1 100644 (file)
@@ -4,19 +4,9 @@
 #include "ffbeffect.h"
 #include <memory>
 #include <vector>
-#include <QtCore/QObject>
-#include <QtCore/QStringList>
-#include <fcntl.h>
-#include <unistd.h>
-#include <linux/input.h>
-#include <sys/stat.h>
-
-class FFBDevice : public QObject
-{
-  Q_OBJECT
-public:
 
-  explicit FFBDevice(const int fd, const QString& path, const int maxEffectCount, QObject* parent = 0);
+class FFBDevice {
+public:
   const std::vector<ConditionSubtypes>& availableConditionSubtypesList() const;
   const std::vector<FFBEffectTypes>& availableEffectsList() const;
   const std::vector<PeriodicWaveforms>& availableWaveformsList() const;
@@ -26,33 +16,25 @@ public:
   bool hasEffect(FFBEffectTypes id) const;
   bool hasPeriodicWaveform(PeriodicWaveforms id) const;
   inline int maxEffectCount() const { return c_maxEffectCount; }
-  inline const QString& path() const { return c_path; }
-  bool queryDeviceCapabilities();
-  bool removeAndEraseEffect(const int idx);
-  bool startEffect(const int idx, const FFBEffectTypes type, std::shared_ptr<FFBEffectParameters> parameters);
-  bool stopEffect(const int idx);
-  bool uploadEffect(const int idx, const FFBEffectTypes type, std::shared_ptr<FFBEffectParameters> parameters);
   inline PeriodicWaveforms waveformByIdx(const int idx) const { return m_availablePeriodicWaveforms[idx]; }
 
-private:
-  bool removeEffect(const int idx);
+  virtual void close() = 0;
+  virtual bool queryDeviceCapabilities() = 0;
+  virtual bool removeAndEraseEffect(const int idx) = 0;
+  virtual bool startEffect(const int idx, const FFBEffectTypes type, std::shared_ptr<FFBEffectParameters> parameters) = 0;
+  virtual bool stopEffect(const int idx) = 0;
+  virtual bool uploadEffect(const int idx, const FFBEffectTypes type, std::shared_ptr<FFBEffectParameters> parameters) = 0;
+
+protected:
+  explicit FFBDevice(const int maxEffectCount) :
+    c_maxEffectCount(maxEffectCount) {}
+
   std::vector<ConditionSubtypes> m_availableConditionSubtypes;
   std::vector<FFBEffectTypes> m_availableEffects;
   std::vector<PeriodicWaveforms> m_availablePeriodicWaveforms;
   std::vector<std::shared_ptr<FFBEffect>> m_effects;
 
-  const int c_fd;
   const int c_maxEffectCount;
-  const QString c_path;
-
-  static inline unsigned long longIdx(unsigned long bit) { return bit / BITS_PER_LONG; }
-  static inline unsigned long offset(unsigned long bit) { return bit % BITS_PER_LONG; }
-  static inline bool testBit(unsigned long bit, unsigned long* array) { return (array[longIdx(bit)] >> offset(bit)) & 1; }
-
-  static const quint8 BITS_PER_LONG;
-signals:
-
-public slots:
 
 };
 
similarity index 70%
rename from deviceprober.cpp
rename to linuxdeviceprober.cpp
index b48683309d3e8de2ef893bdc30995b1944e41d0c..6134ceeccf6d324ff0f2cbccf2ca35f74d801e82 100644 (file)
@@ -1,21 +1,23 @@
-#include "deviceprober.h"
-#include "ffbdevice.h"
+#include "linuxdeviceprober.h"
 #include <QtCore/QDebug>
+#include <QtCore/QDir>
 #include <QtWidgets/QMessageBox>
 #include <linux/input.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 
-const QString DeviceProber::DEVICE_NODES_PATH("/dev/input");
-const QString DeviceProber::res_ffbdeviceErrCap("FFB Device error");
+const QString LinuxDeviceProber::DEVICE_NODES_PATH("/dev/input");
+const QString LinuxDeviceProber::res_ffbdeviceErrCap("FFB Device error");
 
-DeviceProber::DeviceProber(QObject* parent) :
-  QObject(parent)
+void LinuxDeviceProber::closeAllDevices()
 {
+  for (std::shared_ptr<LinuxFFBDevice> dev : m_openedDevices) {
+    dev->close();
+  }
 }
 
-DeviceProber::DeviceList DeviceProber::listDevices()
+DeviceProber::DeviceList LinuxDeviceProber::listDevices()
 {
   DeviceProber::DeviceList list;
   char deviceName[64];
@@ -35,7 +37,7 @@ DeviceProber::DeviceList DeviceProber::listDevices()
       continue;
     }
 
-    dinfo.path = devicePath;
+    dinfo.id = devicePath;
     ret = ioctl(fd, EVIOCGNAME(63), deviceName);
     if (ret < 0)
       qDebug() << "Cannot get name of device" << d << strerror(errno);
@@ -48,17 +50,17 @@ DeviceProber::DeviceList DeviceProber::listDevices()
   return list;
 }
 
-std::shared_ptr<FFBDevice> DeviceProber::openDevice(const QString& path)
+std::shared_ptr<FFBDevice> LinuxDeviceProber::openDevice(const QString& id)
 {
   /* Check if the device is already opened */
-  for (std::shared_ptr<FFBDevice> dev : m_openedDevices) {
-    if (QString::compare(path, dev->path()) == 0) {
-      qDebug() << "Device" << path << "already opened";
+  for (std::shared_ptr<LinuxFFBDevice>& dev : m_openedDevices) {
+    if (QString::compare(id, dev->path()) == 0) {
+      qDebug() << "Device" << id << "already opened";
       return dev;
     }
   }
 
-  int fd = open(path.toLocal8Bit(), O_RDWR);
+  int fd = open(id.toLocal8Bit(), O_RDWR);
   if (!fd) {
     QMessageBox::critical(nullptr, res_ffbdeviceErrCap, "Cannot open device.");
     return nullptr;
@@ -77,7 +79,7 @@ std::shared_ptr<FFBDevice> DeviceProber::openDevice(const QString& path)
     return nullptr;
   }
 
-  std::shared_ptr<FFBDevice> device(new FFBDevice(fd, path, maxEffectCount));
+  std::shared_ptr<LinuxFFBDevice> device(new LinuxFFBDevice(fd, maxEffectCount, id));
   if (!device->queryDeviceCapabilities()) {
     QMessageBox::critical(nullptr, res_ffbdeviceErrCap, "Unable to query device capabilities.");
     return nullptr;
diff --git a/linuxdeviceprober.h b/linuxdeviceprober.h
new file mode 100644 (file)
index 0000000..79ab94a
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef LINUXDEVICEPROBER_H
+#define LINUXDEVICEPROBER_H
+
+#include "deviceprober.h"
+#include "linuxffbdevice.h"
+
+class LinuxDeviceProber : public DeviceProber
+{
+public:
+  explicit LinuxDeviceProber() {}
+  void closeAllDevices();
+  DeviceList listDevices();
+  std::shared_ptr<FFBDevice> openDevice(const QString& id);
+
+private:
+  std::list<std::shared_ptr<LinuxFFBDevice>> m_openedDevices;
+
+  static const QString DEVICE_NODES_PATH;
+  static const QString res_ffbdeviceErrCap;
+
+signals:
+
+public slots:
+
+};
+
+#endif // LINUXDEVICEPROBER_H
diff --git a/linuxffbdevice.cpp b/linuxffbdevice.cpp
new file mode 100644 (file)
index 0000000..c20804c
--- /dev/null
@@ -0,0 +1,230 @@
+#include "linuxffbdevice.h"
+#include "ffbeffectfactory.h"
+#include <QtWidgets/QMessageBox>
+#include <QDebug>
+#include <fcntl.h>
+#include <unistd.h>
+#include <linux/input.h>
+#include <sys/stat.h>
+
+#define CHECK_EFFECT_IDX(idx) if (idx < 0 || idx > c_maxEffectCount) return false
+
+const quint8 LinuxFFBDevice::BITS_PER_LONG = sizeof(unsigned long) * 8;
+
+LinuxFFBDevice::LinuxFFBDevice(const int fd, const int maxEffectCount, const QString path) :
+  FFBDevice(maxEffectCount),
+  c_fd(fd),
+  c_path(path)
+{
+  for (int i = 0; i < maxEffectCount; i++)
+    m_effects.push_back(FFBEffectFactory::createEffect(FFBEffectTypes::NONE));
+}
+
+void LinuxFFBDevice::close()
+{
+  for (int idx = 0; idx < c_maxEffectCount; idx++) {
+    stopEffect(idx);
+    removeAndEraseEffect(idx);
+  }
+
+  ::close(c_fd);
+}
+
+bool LinuxFFBDevice::queryDeviceCapabilities()
+{
+  unsigned long caps[4];
+  int ret = ioctl(c_fd, EVIOCGBIT(EV_FF, sizeof(caps)), caps);
+  if (ret < 0)
+    return false;
+
+  /* Query FFB effects this device can do */
+  if (testBit(FF_CONSTANT, caps))
+    m_availableEffects.push_back(FFBEffectTypes::CONSTANT);
+  if (testBit(FF_PERIODIC, caps))
+    m_availableEffects.push_back(FFBEffectTypes::PERIODIC);
+  if (testBit(FF_RAMP, caps))
+    m_availableEffects.push_back(FFBEffectTypes::RAMP);
+  if (testBit(FF_SPRING, caps) || testBit(FF_FRICTION, caps) ||
+      testBit(FF_DAMPER, caps) || testBit(FF_INERTIA, caps))
+    m_availableEffects.push_back(FFBEffectTypes::CONDITION);
+  if (testBit(FF_RUMBLE, caps))
+    m_availableEffects.push_back(FFBEffectTypes::RUMBLE);
+
+  /* Query waveforms for PERIODIC if the device supports it */
+  if (hasEffect(FFBEffectTypes::PERIODIC)) {
+    if (testBit(FF_SQUARE, caps))
+      m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SQUARE);
+    if (testBit(FF_TRIANGLE, caps))
+      m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::TRIANGLE);
+    if (testBit(FF_SINE, caps))
+      m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SINE);
+    if (testBit(FF_SAW_UP, caps))
+      m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SAW_UP);
+    if (testBit(FF_SAW_DOWN, caps))
+      m_availablePeriodicWaveforms.push_back(PeriodicWaveforms::SAW_DOWN);
+  }
+
+  /* Query condition effect subtypes */
+  if (testBit(FF_SPRING, caps))
+    m_availableConditionSubtypes.push_back(ConditionSubtypes::SPRING);
+  if (testBit(FF_FRICTION, caps))
+    m_availableConditionSubtypes.push_back(ConditionSubtypes::FRICTION);
+  if (testBit(FF_DAMPER, caps))
+    m_availableConditionSubtypes.push_back(ConditionSubtypes::DAMPER);
+  if (testBit(FF_INERTIA, caps))
+    m_availableConditionSubtypes.push_back(ConditionSubtypes::INERTIA);
+
+  return true;
+}
+
+bool LinuxFFBDevice::removeAndEraseEffect(const int idx)
+{
+  if (m_effects[idx]->status() == FFBEffect::FFBEffectStatus::NOT_LOADED)
+    return true;
+
+  if (removeEffect(idx)) {
+    m_effects[idx] = FFBEffectFactory::createEffect(FFBEffectTypes::NONE);
+    if (m_effects[idx]->type() != FFBEffectTypes::NONE) {
+      qCritical("Unable to empty the effect slot.");
+      return false;
+    }
+  } else {
+    qCritical("Unable to stop the effect.");
+    return false;
+  }
+
+  m_effects[idx]->setStatus(FFBEffect::FFBEffectStatus::NOT_LOADED);
+  return true;
+}
+
+bool LinuxFFBDevice::removeEffect(const int idx)
+{
+  if (!stopEffect(idx))
+    return false;
+
+  int internalIdx = m_effects[idx]->internalIdx();
+  int ret = ioctl(c_fd, EVIOCRMFF, internalIdx);
+  if (ret < 0)
+    return false;
+  return true;
+}
+
+bool LinuxFFBDevice::startEffect(const int idx, const FFBEffectTypes type, std::shared_ptr<FFBEffectParameters> parameters)
+{
+  int ret;
+
+  CHECK_EFFECT_IDX(idx);
+
+  if (m_effects[idx]->status() == FFBEffect::FFBEffectStatus::NOT_LOADED) {
+    if (!uploadEffect(idx, type, parameters))
+      return false;
+  }
+  if (m_effects[idx]->status() == FFBEffect::FFBEffectStatus::PLAYING)
+    return true;
+
+  /* Start playback */
+  struct input_event evt;
+  evt.type = EV_FF;
+  evt.code = m_effects[idx]->internalIdx();
+  evt.value = m_effects[idx]->parameters()->repeat;
+
+  ret = write(c_fd, &evt, sizeof(struct input_event));
+  if (ret != sizeof(struct input_event)) {
+    QMessageBox::critical(nullptr, "FFB Device", "Effect could not have been started, error code: " + QString::number(ret));
+    qDebug() << "Effect not started" << ret;
+    return false;
+  }
+
+  m_effects[idx]->setStatus(FFBEffect::FFBEffectStatus::PLAYING);
+
+  return true;
+}
+
+bool LinuxFFBDevice::stopEffect(const int idx)
+{
+  CHECK_EFFECT_IDX(idx);
+
+  if (m_effects[idx] == nullptr)
+    return true;
+
+  if (m_effects[idx]->status() != FFBEffect::FFBEffectStatus::PLAYING)
+    return true;
+
+  int internalIdx = m_effects[idx]->internalIdx();
+
+  struct input_event evt;
+  evt.type = EV_FF;
+  evt.code = internalIdx;
+  evt.value = 0;
+
+  int ret = write(c_fd, &evt, sizeof(struct input_event));
+  if (ret != sizeof(struct input_event))
+    return false;
+
+  m_effects[idx]->setStatus(FFBEffect::FFBEffectStatus::UPLOADED);
+  return true;
+}
+
+bool LinuxFFBDevice::uploadEffect(const int idx, const FFBEffectTypes type, std::shared_ptr<FFBEffectParameters> parameters)
+{
+  struct ff_effect* kernelEff = nullptr;
+  std::shared_ptr<FFBEffect> effect = FFBEffectFactory::createEffect(type);
+
+  CHECK_EFFECT_IDX(idx);
+
+  if (effect == nullptr) {
+    qDebug() << "Unable to create effect";
+    return false;
+  }
+  if (!effect->setParameters(parameters)) {
+    qDebug() << "Unable to set effect parameters, some values are probably invalid.";
+    return false;
+  }
+
+  if (idx < 0 || idx > c_maxEffectCount) {
+    qCritical() << "Effect index out of bounds";
+    return false;
+  }
+
+  /* There is no effect in the selected slot */
+  if (m_effects[idx]->type() == FFBEffectTypes::NONE) {
+    effect->setStatus(FFBEffect::FFBEffectStatus::UPLOADED);
+    qDebug() << "Creating new effect";
+  } else {
+    if (*m_effects[idx] != *effect) {
+      if (!removeEffect(idx)) {
+        QMessageBox::critical(nullptr, "FFB Device", "Unable to remove effect");
+        return false;
+      }
+      effect->setStatus(FFBEffect::FFBEffectStatus::UPLOADED);
+      qDebug() << "Recreating effect" << idx;
+    } else {
+      effect->setInternalIdx(m_effects[idx]->internalIdx());
+      effect->setStatus(m_effects[idx]->status());
+      qDebug() << "Updating effect" << idx;
+    }
+  }
+
+  kernelEff = effect->createFFStruct();
+  if (kernelEff == nullptr) {
+    QMessageBox::critical(nullptr, "FFB Device", "ff_effect struct could not have been created. Effect not uploaded.");
+    qDebug() << "struct ff_effect not created";
+    return false;
+  }
+
+  qDebug() << kernelEff->u.condition[0].center << kernelEff->u.condition[0].deadband << kernelEff->u.condition[1].center << kernelEff->u.condition[1].deadband;
+
+  int ret = ioctl(c_fd, EVIOCSFF, kernelEff);
+  if (ret < 0) {
+    QMessageBox::critical(nullptr, "FFB Device", "Effect could not have been uploaded, error code: " + QString::number(ret));
+    qDebug() << "Effect not uploaded" << ret;
+    delete kernelEff;
+    return false;
+  }
+
+  effect->setInternalIdx(kernelEff->id);
+  delete kernelEff;
+
+  m_effects[idx] = effect;
+  return true;
+}
diff --git a/linuxffbdevice.h b/linuxffbdevice.h
new file mode 100644 (file)
index 0000000..afb511a
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef LINUXFFBDEVICE_H
+#define LINUXFFBDEVICE_H
+
+#include "ffbdevice.h"
+
+class LinuxFFBDevice : public FFBDevice
+{
+public:
+
+  explicit LinuxFFBDevice(const int fd, const int maxEffectCount, const QString path);
+  const QString& path() const { return c_path; }
+
+  /* Overriden virtual functions */
+  void close();
+  bool queryDeviceCapabilities();
+  bool removeAndEraseEffect(const int idx);
+  bool startEffect(const int idx, const FFBEffectTypes type, std::shared_ptr<FFBEffectParameters> parameters);
+  bool stopEffect(const int idx);
+  bool uploadEffect(const int idx, const FFBEffectTypes type, std::shared_ptr<FFBEffectParameters> parameters);
+  inline PeriodicWaveforms waveformByIdx(const int idx) const { return m_availablePeriodicWaveforms[idx]; }
+
+private:
+  bool removeEffect(const int idx);
+
+  const int c_fd;
+  const QString c_path;
+
+  static inline unsigned long longIdx(unsigned long bit) { return bit / BITS_PER_LONG; }
+  static inline unsigned long offset(unsigned long bit) { return bit % BITS_PER_LONG; }
+  static inline bool testBit(unsigned long bit, unsigned long* array) { return (array[longIdx(bit)] >> offset(bit)) & 1; }
+
+  static const quint8 BITS_PER_LONG;
+signals:
+
+public slots:
+
+};
+
+#endif // FFBDEVICE_H
index e1a8b8b1609915b95c3c7e880195a470411b80fe..7f728054e1aad4f7e58a779610e6c57acd2143f3 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -26,8 +26,7 @@ int main(int argc, char** argv)
    GlobalSettings::init(doSanityChecks);
   }
 
-  std::shared_ptr<DeviceProber> prober(new DeviceProber);
-  MainWindow* mWin = new MainWindow(prober, VERSION_STRING);
+  MainWindow* mWin = new MainWindow(VERSION_STRING);
 
   mWin->show();
   return myApp.exec();
index 000c114f1853f9eb060c856b01bd2df5d07a838b..f8aa5488aa3cdb1f2534f7a5edc8b1238b6e4bc2 100644 (file)
@@ -1,4 +1,5 @@
 #include "globalsettings.h"
+#include "linuxdeviceprober.h"
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
 #include <QtWidgets/QMessageBox>
@@ -10,9 +11,8 @@ const QString MainWindow::res_effectPlaying("Playing");
 const QString MainWindow::res_effectUploaded("Uploaded");
 const QString MainWindow::res_inputFormatErrCap("Invalid input format.");
 
-MainWindow::MainWindow(std::shared_ptr<DeviceProber> prober, const QString& title, QWidget* parent) :
+MainWindow::MainWindow(const QString& title, QWidget* parent) :
   QMainWindow(parent),
-  m_prober(prober),
   ui(new Ui::MainWindow)
 {
   ui->setupUi(this);
@@ -32,10 +32,20 @@ MainWindow::MainWindow(std::shared_ptr<DeviceProber> prober, const QString& titl
   if (GlobalSettings::GS()->doSanityChecks)
     ui->ql_noChecksWarning->setHidden(true);
 
+  /* Fill the list of available interfaces */
+  ui->cbox_interfaces->addItem("Linux API", static_cast<std::underlying_type<DeviceProber::DeviceInterfaces>::type>(DeviceProber::DeviceInterfaces::LINUX));
+#ifdef FFBC_HAVE_SDL2
+  ui->cbox_interfaces->addItem("SDL2", static_cast<std::underlying_type<DeviceProber::DeviceInterfaces>::type>(DeviceProber::DeviceInterfaces::SDL2));
+#endif
+
+  ui->cbox_interfaces->setCurrentIndex(0);
+  createDeviceProber(DeviceProber::DeviceInterfaces::LINUX);
   fillDeviceList();
+
   connect(ui->cbox_devices, SIGNAL(activated(const int)), this, SLOT(onDeviceSelected(const int)));
   connect(ui->cbox_effectSlots, SIGNAL(activated(const int)), this, SLOT(onEffectSlotSelected(const int)));
   connect(ui->cbox_effectTypes, SIGNAL(activated(const int)), this, SLOT(onEffectTypeSelected(const int)));
+  connect(ui->cbox_interfaces, SIGNAL(activated(int)), this, SLOT(onInterfaceSelected(const int)));
   connect(ui->qpb_refreshDevices, SIGNAL(clicked()), this, SLOT(onRefreshDevicesClicked()));
   connect(ui->qpb_remove, SIGNAL(clicked()), this, SLOT(onRemoveEffectClicked()));
   connect(ui->qpb_start, SIGNAL(clicked()), this, SLOT(onStartEffectClicked()));
@@ -43,6 +53,25 @@ MainWindow::MainWindow(std::shared_ptr<DeviceProber> prober, const QString& titl
   connect(ui->qpb_upload, SIGNAL(clicked()), this, SLOT(onUploadEffectClicked()));
 }
 
+void MainWindow::createDeviceProber(const DeviceProber::DeviceInterfaces iface)
+{
+  std::shared_ptr<DeviceProber> prober;
+
+  if (m_prober != nullptr)
+    m_prober->closeAllDevices();
+
+  switch (iface) {
+  case DeviceProber::DeviceInterfaces::LINUX:
+    prober = std::make_shared<LinuxDeviceProber>();
+    break;
+  default:
+    QMessageBox::critical(this, "Cannot probe devices", "Selected interface is not supported yet.");
+    break;
+  }
+
+  m_prober = prober;
+}
+
 EffectSettings* MainWindow::effectSettingsByType(FFBEffectTypes type)
 {
   switch (type) {
@@ -90,8 +119,8 @@ void MainWindow::fillDeviceList()
     else
       name = dinfo.name;
 
-    QString tag = QString("%1 [%2]").arg(name).arg(dinfo.path);
-    ui->cbox_devices->addItem(tag, dinfo.path);
+    QString tag = QString("%1 [%2]").arg(name).arg(dinfo.id.toString());
+    ui->cbox_devices->addItem(tag, dinfo.id.toString());
   }
 }
 
@@ -178,9 +207,26 @@ void MainWindow::onEffectTypeSelected(const int cboxIdx)
   ui->qstw_effectSpecifics->setCurrentWidget(effectSettingsByType(etype));
 }
 
+void MainWindow::onInterfaceSelected(const int cboxIdx)
+{
+  Q_UNUSED(cboxIdx);
+  bool ok;
+  unsigned int rawIface;
+  DeviceProber::DeviceInterfaces iface;
+
+  rawIface = ui->cbox_interfaces->currentData().toUInt(&ok);
+  if (!ok) {
+    QMessageBox::critical(this, "Invalid data", "Invalid data passed as interface type.");
+    return;
+  }
+
+  iface = static_cast<DeviceProber::DeviceInterfaces>(rawIface);
+  createDeviceProber(iface);
+}
+
 void MainWindow::onRefreshDevicesClicked()
 {
-  fillDeviceList();
+  //fillDeviceList();
 }
 
 void MainWindow::onRemoveEffectClicked()
index 261912a495c27cf2796678160f56a80906bf8744..417d4b3114937246c90d969937ef6f50d5a93466 100644 (file)
@@ -24,15 +24,18 @@ class MainWindow : public QMainWindow
   Q_OBJECT
 
 public:
-  explicit MainWindow(std::shared_ptr<DeviceProber> prober, const QString& title, QWidget* parent = 0);
+  explicit MainWindow(const QString& title, QWidget* parent = 0);
   ~MainWindow();
 
 private:
-  enum class ErrorMessages { BAD_EFFECT_SLOT,
-                             CANT_REMOVE_EFFECT,
-                             CANT_START_EFFECT,
-                             CANT_UPLOAD_EFFECT };
+  enum class ErrorMessages {
+    BAD_EFFECT_SLOT,
+    CANT_REMOVE_EFFECT,
+    CANT_START_EFFECT,
+    CANT_UPLOAD_EFFECT
+  };
 
+  void createDeviceProber(const DeviceProber::DeviceInterfaces iface);
   EffectSettings* effectSettingsByType(FFBEffectTypes type);
   QString effectTypeToEffectName(const FFBEffectTypes type) const;
   void fillDeviceList();
@@ -48,10 +51,10 @@ private:
   std::shared_ptr<FFBDevice> m_activeDevice;
   ConditionEffectSettings* m_conditionEffSet;
   ConstantEffectSettings* m_constantEffSet;
+  std::shared_ptr<DeviceProber> m_prober;
   PeriodicEffectSettings* m_periodicEffSet;
   RampEffectSettings* m_rampEffSet;
   RumbleEffectSettings* m_rumbleEffSet;
-  std::shared_ptr<DeviceProber> m_prober;
   Ui::MainWindow* ui;
 
   static const QString res_deviceErrorCap;
@@ -64,6 +67,7 @@ private slots:
   void onDeviceSelected(const int cboxIdx);
   void onEffectSlotSelected(const int cboxIdx);
   void onEffectTypeSelected(const int cboxIdx);
+  void onInterfaceSelected(const int cboxIdx);
   void onRefreshDevicesClicked();
   void onRemoveEffectClicked();
   void onStartEffectClicked();
index 59061df8a77ced7f0a15779414c47e817311dc98..da999b91f734bf6a1a47208eb619e22bbc9b3679 100644 (file)
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>444</width>
-    <height>500</height>
+    <height>546</height>
    </rect>
   </property>
   <property name="windowTitle">
         <item row="1" column="1">
          <widget class="QComboBox" name="cbox_effectSlots"/>
         </item>
-        <item row="2" column="0" colspan="2">
+        <item row="3" column="0" colspan="2">
          <widget class="QPushButton" name="qpb_refreshDevices">
           <property name="text">
            <string>Refresh</string>
           </property>
          </widget>
         </item>
+        <item row="2" column="0">
+         <widget class="QLabel" name="ql_interface">
+          <property name="text">
+           <string>Interface:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="1">
+         <widget class="QComboBox" name="cbox_interfaces"/>
+        </item>
        </layout>
       </item>
      </layout>