From 9a18ef852beee37ecf073718fbb527c89cb70337 Mon Sep 17 00:00:00 2001
From: ChenZhuo <chenchao@htsdfp.com>
Date: Thu, 3 Apr 2025 16:30:09 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=9B=BE=E9=A9=AC=E6=96=AF?=
 =?UTF-8?q?=E7=89=B9=E6=91=87=E6=9D=86=E6=8E=A7=E5=88=B6=E5=8A=9F=E8=83=BD?=
 =?UTF-8?q?=E6=A1=86=E6=9E=B6=EF=BC=8C=E6=8C=89=E9=92=AE=E6=9C=AA=E5=85=B3?=
 =?UTF-8?q?=E8=81=94=E5=90=8A=E8=88=B1=E6=8E=A7=E5=88=B6=E9=94=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/CMakeLists.txt          |   9 ++
 src/commandwidget.cpp       |  66 +++++++++++++++
 src/commandwidget.h         |   6 ++
 src/directinputjoystick.cpp | 164 ++++++++++++++++++++++++++++++++++++
 src/directinputjoystick.h   |  47 +++++++++++
 src/joystickcontroller.cpp  | 106 +++++++++++++++++++++++
 src/joystickcontroller.h    |  41 +++++++++
 src/mainwindow.cpp          |  37 ++++++++
 src/mainwindow.h            |  15 +++-
 src/video/readstream.cpp    |   1 +
 src/video/readstream.h      |   1 -
 11 files changed, 491 insertions(+), 2 deletions(-)
 create mode 100644 src/directinputjoystick.cpp
 create mode 100644 src/directinputjoystick.h
 create mode 100644 src/joystickcontroller.cpp
 create mode 100644 src/joystickcontroller.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ef4ecb7..cce6152 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,9 +13,14 @@ qt_add_executable(VideoClient
     videoControl.h videoControl.ui videoControl.cpp
     wprogressbar.h wprogressbar.cpp
     webmapwidget.h webmapwidget.cpp
+    joystickcontroller.h joystickcontroller.cpp
+    directinputjoystick.h directinputjoystick.cpp
+
 
 )
 
+
+
 qt6_add_resources(VideoClient "resources"
     PREFIX "/"
     FILES "resources/res.qrc"
@@ -39,7 +44,11 @@ target_link_libraries(VideoClient
         Qt6::Network
         Qt6::SerialPort
         Qt6::WebEngineWidgets
+        #Qt6::Gamepad
         video
+        winmm
+        dinput8
+        dxguid
         # QWindowKit::Widgets
         # WidgetFrame
 )
diff --git a/src/commandwidget.cpp b/src/commandwidget.cpp
index 829c58b..7138715 100644
--- a/src/commandwidget.cpp
+++ b/src/commandwidget.cpp
@@ -145,3 +145,69 @@ void CommandWidget::on_pushButton_16_clicked() {
 void CommandWidget::on_pushButton_15_clicked() {
     buttonResponse(0xC9);
 }
+
+
+void CommandWidget::onButtonChanged(int button, bool pressed)
+{
+    qDebug() << "Button" << button << (pressed ? "Pressed" : "Released");
+    // switch (button)
+    // {
+    // case 0:
+    //     on_pushButton_clicked();
+    //     break;
+    // case 1:
+    //     on_pushButton_2_clicked();
+    //     break;
+    // case 2:
+    //     on_pushButton_3_clicked();
+    //     break;
+    // case 3:
+    //     on_pushButton_clicked();
+    //     break;
+    // case 4:
+    //     on_pushButton_2_clicked();
+    //     break;
+    // case 5:
+    //     on_pushButton_3_clicked();
+    //     break;
+    // case 6:
+    //     on_pushButton_clicked();
+    //     break;
+    // case 7:
+    //     on_pushButton_2_clicked();
+    //     break;
+    // case 8:
+    //     on_pushButton_3_clicked();
+    //     break;
+    // case 9:
+    //     on_pushButton_clicked();
+    //     break;
+    // case 10:
+    //     on_pushButton_2_clicked();
+    //     break;
+    // case 11:
+    //     on_pushButton_3_clicked();
+    //     break;
+    // case 12:
+    //     on_pushButton_clicked();
+    //     break;
+    // case 13:
+    //     on_pushButton_2_clicked();
+    //     break;
+    // case 14:
+    //     on_pushButton_3_clicked();
+    //     break;
+
+    // }
+
+}
+
+
+void CommandWidget::onDirectionChanged(int direction){
+
+    direction!=42949672?qDebug() << "direction" << direction:qDebug() << "center";
+
+
+}
+
+
diff --git a/src/commandwidget.h b/src/commandwidget.h
index f042863..17a4a4c 100644
--- a/src/commandwidget.h
+++ b/src/commandwidget.h
@@ -122,6 +122,12 @@ private slots:
 
     void on_pushButton_15_clicked();
 
+public slots:
+
+    void onButtonChanged(int button, bool pressed);
+    void onDirectionChanged(int dirction);
+
+
 private:
     Ui::CommandWidget *ui;
     QUdpSocket *m_commandUdpSocket = nullptr;
diff --git a/src/directinputjoystick.cpp b/src/directinputjoystick.cpp
new file mode 100644
index 0000000..c90f49b
--- /dev/null
+++ b/src/directinputjoystick.cpp
@@ -0,0 +1,164 @@
+#include "DirectInputJoystick.h"
+#include <QDebug>
+
+DirectInputJoystick::DirectInputJoystick(QObject *parent) : QObject(parent) {
+    // 确保 COM 库在主线程初始化
+    CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+}
+
+DirectInputJoystick::~DirectInputJoystick() {
+    cleanup();
+    CoUninitialize();
+}
+
+bool DirectInputJoystick::initialize() {
+    HRESULT hr = DirectInput8Create(
+        GetModuleHandle(nullptr),
+        DIRECTINPUT_VERSION,
+        IID_IDirectInput8,
+        (VOID**)&m_pDirectInput,
+        nullptr
+        );
+
+    if (FAILED(hr)) {
+        emit errorOccurred("无法创建 DirectInput 对象");
+        return false;
+    }
+
+    // 枚举所有摇杆设备
+    hr = m_pDirectInput->EnumDevices(
+        DI8DEVCLASS_GAMECTRL,
+        enumJoysticksCallback,
+        this,
+        DIEDFL_ATTACHEDONLY
+        );
+
+    return SUCCEEDED(hr);
+}
+
+BOOL CALLBACK DirectInputJoystick::enumJoysticksCallback(const DIDEVICEINSTANCE* instance, VOID* context) {
+    DirectInputJoystick* joystick = static_cast<DirectInputJoystick*>(context);
+    return joystick->setupDevice();
+}
+
+bool DirectInputJoystick::setupDevice() {
+    if (!m_pDirectInput) return false;
+
+    // 创建设备
+    HRESULT hr = m_pDirectInput->CreateDevice(
+        GUID_Joystick,
+        &m_pJoystick,
+        nullptr
+        );
+
+    if (FAILED(hr)) {
+        emit errorOccurred("无法创建摇杆设备");
+        return false;
+    }
+
+    // 设置数据格式
+    hr = m_pJoystick->SetDataFormat(&c_dfDIJoystick);
+    if (FAILED(hr)) {
+        emit errorOccurred("设置数据格式失败");
+        return false;
+    }
+
+    // 设置协作级别
+    hr = m_pJoystick->SetCooperativeLevel(
+        nullptr, // 无窗口句柄
+        DISCL_BACKGROUND | DISCL_NONEXCLUSIVE
+        );
+
+    // 枚举轴并设置范围
+    hr = m_pJoystick->EnumObjects(enumAxesCallback, this, DIDFT_AXIS);
+    if (FAILED(hr)) {
+        emit errorOccurred("枚举摇杆轴失败");
+        return false;
+    }
+
+    // 创建事件对象
+    m_hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
+    hr = m_pJoystick->SetEventNotification(m_hEvent);
+    if (FAILED(hr)) {
+        emit errorOccurred("设置事件通知失败");
+        return false;
+    }
+
+    return true;
+}
+
+BOOL CALLBACK DirectInputJoystick::enumAxesCallback(const DIDEVICEOBJECTINSTANCE* doi, VOID* context) {
+    DirectInputJoystick* joystick = static_cast<DirectInputJoystick*>(context);
+    DIPROPRANGE diprg;
+    diprg.diph.dwSize = sizeof(DIPROPRANGE);
+    diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+    diprg.diph.dwHow = DIPH_BYID;
+    diprg.diph.dwObj = doi->dwType;
+    diprg.lMin = -1000;
+    diprg.lMax = 1000;
+
+    HRESULT hr = joystick->m_pJoystick->SetProperty(DIPROP_RANGE, &diprg.diph);
+    return SUCCEEDED(hr);
+}
+
+void DirectInputJoystick::startListening() {
+    if (!m_pJoystick || m_isRunning) return;
+
+    m_isRunning = true;
+    m_workerThread = QThread::create([this]() {
+        m_pJoystick->Acquire();
+
+        while (m_isRunning) {
+            WaitForSingleObject(m_hEvent, INFINITE);
+
+            DIJOYSTATE state;
+            HRESULT hr = m_pJoystick->GetDeviceState(sizeof(DIJOYSTATE), &state);
+            if (SUCCEEDED(hr)) {
+                // 处理轴数据
+                double x = state.lX / 1000.0;
+                double y = state.lY / 1000.0;
+                double z = state.lZ / 1000.0;
+                emit axisChanged(0, x);
+                emit axisChanged(1, y);
+                emit axisChanged(2, z);
+
+                // 处理按钮
+                for (int i = 0; i < 32; ++i) {
+                    if(lastState.rgbButtons[i]!=state.rgbButtons[i])
+                    {
+                        bool pressed = (state.rgbButtons[i] & 0x80) != 0;
+
+                        emit buttonChanged(i, pressed);
+                    }
+
+                }
+                if(lastState.rgdwPOV[0]!=state.rgdwPOV[0])
+                {
+                    emit directionChanged(state.rgdwPOV[0]/100);
+
+                }
+            }
+            lastState = state;
+        }
+    });
+
+    connect(m_workerThread, &QThread::finished, m_workerThread, &QThread::deleteLater);
+    m_workerThread->start();
+}
+
+void DirectInputJoystick::cleanup() {
+    m_isRunning = false;
+    if (m_hEvent) {
+        CloseHandle(m_hEvent);
+        m_hEvent = nullptr;
+    }
+    if (m_pJoystick) {
+        m_pJoystick->Unacquire();
+        m_pJoystick->Release();
+        m_pJoystick = nullptr;
+    }
+    if (m_pDirectInput) {
+        m_pDirectInput->Release();
+        m_pDirectInput = nullptr;
+    }
+}
diff --git a/src/directinputjoystick.h b/src/directinputjoystick.h
new file mode 100644
index 0000000..d85bdc1
--- /dev/null
+++ b/src/directinputjoystick.h
@@ -0,0 +1,47 @@
+#ifndef DIRECTINPUTJOYSTICK_H
+#define DIRECTINPUTJOYSTICK_H
+
+#include <QObject>
+#include <windows.h>
+#include <dinput.h>
+#include <QThread>
+
+
+class DirectInputJoystick : public QObject
+{
+    Q_OBJECT
+public:
+    explicit DirectInputJoystick(QObject *parent = nullptr);
+    ~DirectInputJoystick();
+
+
+    bool initialize();
+    void startListening();
+
+
+signals:
+    // 轴变化信号(axis: 0-X, 1-Y, 2-Z; value: -1.0 ~ 1.0)
+    void axisChanged(int axis, double value);
+    // 按钮状态变化(button: 按钮编号,从0开始;pressed: 是否按下)
+    void buttonChanged(int button, bool pressed);
+    // 方向按钮状态变化(direction: 按钮编号,上下左右;pressed: 是否按下)
+    void directionChanged(int direction);
+    // 错误信号
+    void errorOccurred(const QString &message);
+
+
+private:
+    bool setupDevice();
+    void cleanup();
+    static BOOL CALLBACK enumAxesCallback(const DIDEVICEOBJECTINSTANCE* doi, VOID* context);
+    static BOOL CALLBACK enumJoysticksCallback(const DIDEVICEINSTANCE* instance, VOID* context);
+
+    DIJOYSTATE lastState;
+    LPDIRECTINPUT8 m_pDirectInput = nullptr;
+    LPDIRECTINPUTDEVICE8 m_pJoystick = nullptr;
+    HANDLE m_hEvent = nullptr;
+    QThread *m_workerThread;
+    bool m_isRunning = false;
+};
+
+#endif // DIRECTINPUTJOYSTICK_H
diff --git a/src/joystickcontroller.cpp b/src/joystickcontroller.cpp
new file mode 100644
index 0000000..039dacf
--- /dev/null
+++ b/src/joystickcontroller.cpp
@@ -0,0 +1,106 @@
+#include "joystickcontroller.h"
+#include <QDebug>
+
+
+
+
+JoystickController::JoystickController(int joystickId, QObject *parent)
+    : QObject{parent}, m_joystickId(joystickId), m_isConnected(false)
+{
+
+    // 初始化定时器
+    m_timer = new QTimer(this);
+    connect(m_timer, &QTimer::timeout, this, &JoystickController::pollJoystick);
+
+    // 检测设备是否存在
+    UINT numDevs = joyGetNumDevs();
+    if (numDevs == 0) {
+        emit errorOccurred("系统未检测到任何摇杆设备");
+        return;
+    }
+    // 获取设备能力
+    MMRESULT capsResult = joyGetDevCaps(m_joystickId, &m_joyCaps, sizeof(JOYCAPS));
+    if (capsResult != JOYERR_NOERROR) {
+        emit errorOccurred(QString("无法获取摇杆设备能力,错误代码: %1").arg(capsResult));
+        return;
+    }
+
+
+    m_isConnected = true;
+    qDebug() << "摇杆设备初始化成功,名称:" << QString::fromWCharArray(m_joyCaps.szPname);
+
+
+
+}
+
+
+JoystickController::~JoystickController()
+{
+    stopPolling();
+}
+
+
+
+bool JoystickController::isConnected() const
+{
+    return m_isConnected;
+}
+
+
+void JoystickController::startPolling(int intervalMs)
+{
+    if (m_isConnected) {
+        m_timer->start(intervalMs);
+    }
+}
+
+void JoystickController::stopPolling()
+{
+    m_timer->stop();
+}
+
+
+void JoystickController::pollJoystick()
+{
+    JOYINFOEX joyInfo;
+    joyInfo.dwSize = sizeof(JOYINFOEX);
+    joyInfo.dwFlags = JOY_RETURNALL;
+
+    MMRESULT result = joyGetPosEx(m_joystickId, &joyInfo);
+    if (result != JOYERR_NOERROR) {
+        emit errorOccurred(QString("读取摇杆数据失败,错误代码: %1").arg(result));
+        m_isConnected = false;
+        stopPolling();
+        return;
+    }
+
+    // 处理轴数据
+    normalizeAxisValues(joyInfo);
+
+    // 处理按钮状态
+    for (int i = 0; i < m_joyCaps.wNumButtons; ++i) {
+        bool isPressed = (joyInfo.dwButtons & (1 << i)) != 0;
+        emit buttonChanged(i + 1, isPressed); // 按钮ID从1开始
+    }
+
+}
+
+
+void JoystickController::normalizeAxisValues(const JOYINFOEX &joyInfo)
+{
+    // X/Y轴归一化处理
+    double x = (joyInfo.dwXpos - 32768) / 32768.0;
+    double y = (joyInfo.dwYpos - 32768) / 32768.0;
+    emit axisChanged(1, x); // 假设轴1为X轴
+    emit axisChanged(2, y); // 轴2为Y轴
+
+    // 其他轴(如Z轴、Rudder等)
+    if (m_joyCaps.wCaps & JOYCAPS_HASZ) {
+        double z = (joyInfo.dwZpos - 32768) / 32768.0;
+        emit axisChanged(3, z);
+    }
+}
+
+
+
+
diff --git a/src/joystickcontroller.h b/src/joystickcontroller.h
new file mode 100644
index 0000000..f43dc88
--- /dev/null
+++ b/src/joystickcontroller.h
@@ -0,0 +1,41 @@
+#ifndef JOYSTICKCONTROLLER_H
+#define JOYSTICKCONTROLLER_H
+
+#include <QObject>
+#include <QTimer>
+#include <windows.h>
+#include <mmsystem.h>
+
+class JoystickController : public QObject
+{
+    Q_OBJECT
+public:
+    explicit JoystickController(int joystickId = JOYSTICKID1, QObject *parent = nullptr);
+    ~JoystickController();
+
+    bool isConnected() const;
+    void startPolling(int intervalMs = 50);
+    void stopPolling();
+
+
+signals:
+    // 摇杆轴变化信号(范围:-1.0 ~ 1.0)
+    void axisChanged(int axis, double value);
+    // 按钮状态变化信号(按钮ID, 是否按下)
+    void buttonChanged(int button, bool pressed);
+    // 错误信号
+    void errorOccurred(const QString &message);
+
+
+private slots:
+    void pollJoystick();
+private:
+    void normalizeAxisValues(const JOYINFOEX &joyInfo);
+
+    QTimer *m_timer;
+    int m_joystickId;
+    bool m_isConnected;
+    JOYCAPS m_joyCaps;
+};
+
+#endif // JOYSTICKCONTROLLER_H
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index 5f2a291..7d37689 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -32,6 +32,7 @@ MainWindow::MainWindow(QWidget *parent)
         qDebug() << "Open  file fail " << Qt::endl;
     }
 
+
     // QList<QString> list;
     // list.append("rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid");
     // list.append("rtsp://182.92.130.23/nmyj/video");
@@ -60,6 +61,8 @@ MainWindow::MainWindow(QWidget *parent)
 
     initHideButton();
 
+    //initJoyStick();
+    initDtJoyStick();
     setSavedVideoDir();
     // ui->stackedWidget->setCurrentIndex(1);
     // ui->videoWidget1->play(list.at(0));
@@ -123,6 +126,8 @@ void MainWindow::initSignalConnection() {
 
     connect(ui->videoControlWidget, &VideoControl::stopVideoSignal, this,
             &MainWindow::closeVideoSlot);
+
+
 }
 
 void MainWindow::initNotifyMessageConnection() {
@@ -141,6 +146,38 @@ void MainWindow::initChangeVideoLayoutConnection() {
             });
 }
 
+
+
+void MainWindow::onAxisChanged(int axis, double value)
+{
+    qDebug() << "Axis" << axis << "Value:" << value;
+}
+
+
+
+
+// void MainWindow::initJoyStick()
+// {
+//     m_joystick = new JoystickController(JOYSTICKID1,this);
+//     connect(m_joystick, &JoystickController::axisChanged, this, &MainWindow::onAxisChanged);
+//     connect(m_joystick, &JoystickController::buttonChanged, this, &MainWindow::onButtonChanged);
+//     if (m_joystick->isConnected()) {
+//         m_joystick->startPolling();
+//     }
+// }
+
+void MainWindow::initDtJoyStick()
+{
+    m_dtjoystick = new DirectInputJoystick(this);
+
+    if (m_dtjoystick->initialize()) {
+        connect(m_dtjoystick, &DirectInputJoystick::buttonChanged, ui->commandWidget, &CommandWidget::onButtonChanged);
+        connect(m_dtjoystick, &DirectInputJoystick::directionChanged, ui->commandWidget, &CommandWidget::onDirectionChanged);
+      //  connect(m_dtjoystick, &DirectInputJoystick::buttonChanged, this, &MainWindow::onButtonChanged);
+        m_dtjoystick->startListening();
+    }
+}
+
 void MainWindow::initNotifyManager() {
     m_notifyManager = new NotifyManager(this, this);
     m_notifyManager->setMaxCount(5);
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 0412624..3355d85 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -16,7 +16,9 @@
 #include "videoControl.h"
 #include "videowidget.h"
 #include "webmapwidget.h"
-
+//#include "joystickapi.h"
+#include "joystickcontroller.h"
+#include "DirectInputJoystick.h"
 #ifdef Q_OS_WIN
 #include "windows.h"
 #include "windowsx.h"
@@ -68,6 +70,14 @@ private:
     void setSavedVideoDir();
 
     void initHideButton();
+   // void initJoyStick();
+    void initDtJoyStick();
+
+
+private slots:
+
+    void onAxisChanged(int axis, double value);
+
 
 private slots:
     void showSettingDlgSlot();
@@ -92,5 +102,8 @@ private:
     QToolButton *hideCommandWidgetBtn = nullptr;
 
     WebMapWidget *m_mapWidget = nullptr;
+ //   JoystickController *m_joystick  = nullptr;
+    DirectInputJoystick *m_dtjoystick;
+
 };
 #endif  // MAINWINDOW_H
diff --git a/src/video/readstream.cpp b/src/video/readstream.cpp
index 9a3162f..d58a47e 100644
--- a/src/video/readstream.cpp
+++ b/src/video/readstream.cpp
@@ -200,6 +200,7 @@ bool ReadStream::initSocket(QString ip, int port) {
             }
         }
     }
+
 }
 
 bool ReadStream::initSavedRawFile(QString fileDir, QString uavName) {
diff --git a/src/video/readstream.h b/src/video/readstream.h
index dd4a465..5155316 100644
--- a/src/video/readstream.h
+++ b/src/video/readstream.h
@@ -12,7 +12,6 @@
 
 #include "avpacketqueuemanager.h"
 #include "decodestream.h"
-#include "ffmpeginclude.h"
 #include "pushstream.h"
 #include "savestream.h"