Commit f9968ff4 authored by 957dd's avatar 957dd

Initial commit

parents
# Visual Studio
.vs/
[Dd]ebug/
[Rr]elease/
x64/
x86/
ARM/
ARM64/
*.aps
*.bak
*.cache
*.db
*.opendb
*.sdf
*.suo
*.user
*.userosscache
*.sln.docstates
*.exe.WebView2/
# Build Output
*.exe
*.dll
*.pdb
*.ilk
*.exp
*.lib
*.obj
*.iobj
*.ipdb
*.log
*.tlog
# Project Specific
config.ini
steer_url/x64/
steer_url/Debug/
steer_url/Release/
# External / Local
packages/
node_modules/
temp/
tmp/
# MozaR3CenterOnly - WebView2 赛车终助手
本项目是一个高性能的 Windows 应用程序,它封装了一个赛车网页 (`https://fcrs.tentmall.cn/flyPC/#/`),并集成了 **MOZA R3** 方向盘硬件,实现了用力反馈 (FFB) 和输入控制。
## 项目特性
- **500Hz 硬件轮询**:提供极速、丝滑的方向盘和踏板数据读取。
- **无边框全屏**:沉浸式 Kiosk 模式(按 **ESC** 键可一键退出到窗口模式进行设置)。
- **开机自启**:可选集成到 Windows 启动项。
- **后台自动重连**:当程序从后台切换回前台时,自动重新挂载硬件,无需手动操作。
- **个性化品牌**:完美集成自定义赛车图标。
## 开发环境与准备工作
要编译本项目,您需要 **Visual Studio 2019 或 2022**,并安装 **“使用 C++ 的桌面开发”** 工作负载。
### 1. 安装 WebView2 SDK (必须)
浏览器引擎依赖于微软的 WebView2 SDK:
1. 在 Visual Studio 中打开解决方案 (`steer_url.sln`)。
2. 在解决方案资源管理器中右键点击 **steer_url** 项目。
3. 选择 **“管理 NuGet 程序包...”**
4. 搜索 **`Microsoft.Web.WebView2`**
5. 点击 **“安装”**
### 2. MOZA SDK (已包含)
MOZA 硬件 SDK 已通过 `third_party/moza` 目录集成到项目代码中,**无需**额外下载或安装任何库。
## 编译指南
1. 将配置设置为 **Release**,平台设置为 **x64**
2. 点击 **生成 (Build)** -> **重新生成解决方案 (Rebuild Solution)**
3. 编译成功的 .exe 文件和必要的 DLL 将位于 `x64/Release/` 目录下。
## 运行与发布
发布时,请确保以下文件位于同一目录下:
- `steer_url.exe` (主程序)
- `MOZA_SDK.dll` (硬件驱动库,需从 `third_party/moza/bin/x64` 复制到此处)
- `WebView2Loader.dll` (浏览器核心)
> **贴士**:为了方便开发,您可以在 VS 项目属性的 **“生成事件 -> 生成后事件”** 中添加以下命令,让系统自动为您复制 DLL:
> `copy /Y "$(ProjectDir)third_party\moza\bin\x64\MOZA_SDK.dll" "$(OutDir)MOZA_SDK.dll"`
- `www/` (资源文件夹,包含图标)
- `config.ini` (保存您的设置)
---
*专为 MOZA R3 硬件集成定制。*

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36623.8 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "steer_url", "steer_url\steer_url.vcxproj", "{7F3B8DDD-AD8D-417B-8193-A02B136442F2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7F3B8DDD-AD8D-417B-8193-A02B136442F2}.Debug|x64.ActiveCfg = Debug|x64
{7F3B8DDD-AD8D-417B-8193-A02B136442F2}.Debug|x64.Build.0 = Debug|x64
{7F3B8DDD-AD8D-417B-8193-A02B136442F2}.Debug|x86.ActiveCfg = Debug|Win32
{7F3B8DDD-AD8D-417B-8193-A02B136442F2}.Debug|x86.Build.0 = Debug|Win32
{7F3B8DDD-AD8D-417B-8193-A02B136442F2}.Release|x64.ActiveCfg = Release|x64
{7F3B8DDD-AD8D-417B-8193-A02B136442F2}.Release|x64.Build.0 = Release|x64
{7F3B8DDD-AD8D-417B-8193-A02B136442F2}.Release|x86.ActiveCfg = Release|Win32
{7F3B8DDD-AD8D-417B-8193-A02B136442F2}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {90990069-BBDD-49BA-9099-DB1933840305}
EndGlobalSection
EndGlobal
#include "MozaController.h"
#include <iostream>
#include <chrono>
#include <cmath>
#include <algorithm>
#include <string>
#include <sstream>
namespace {
void DebugLog(const std::string& msg) {
OutputDebugStringA(("[MOZA] " + msg + "\n").c_str());
}
const char* ErrorCodeToString(ERRORCODE code) {
switch (code) {
case NORMAL: return "NORMAL";
case NOINSTALLSDK: return "NOINSTALLSDK";
case NODEVICES: return "NODEVICES";
case OUTOFRANGE: return "OUTOFRANGE";
case PARAMETERERR: return "PARAMETERERR";
case COLLECTIONCYCLEDATALOSS: return "COLLECTIONCYCLEDATALOSS";
case CREATEFFECTERR: return "CREATEFFECTERR";
case ENCODINGFAILED: return "ENCODINGFAILED";
case FFBERR: return "FFBERR";
case FIRMWARETOOOLD: return "FIRMWARETOOOLD";
case PITHOUSENOTREADY: return "PITHOUSENOTREADY";
default: return "UNKNOWN";
}
}
void PrintError(const char* step, ERRORCODE code) {
std::stringstream ss;
ss << "[ERROR] " << step << " failed: " << ErrorCodeToString(code) << " (" << static_cast<int>(code) << ")";
DebugLog(ss.str());
}
template <typename Fn>
bool TryEffectCall(const char* step, Fn&& fn) {
try {
fn();
return true;
} catch (const std::exception& e) {
std::stringstream ss;
ss << "[WARN] " << step << " exception: " << e.what();
DebugLog(ss.str());
} catch (...) {
DebugLog(std::string("[WARN] ") + step + " unknown exception.");
}
return false;
}
}
MozaController::MozaController()
: m_sdkInstalled(false), m_running(false), m_hwnd(nullptr), m_lastAngle(0.0f), m_hasLastAngle(false) {
m_lastTs = std::chrono::steady_clock::now();
m_nextRenewTime = m_lastTs;
m_nextPrintTime = m_lastTs;
}
MozaController::~MozaController() {
Stop();
}
void MozaController::InitializeHardware(HWND hWnd) {
m_hwnd = hWnd;
moza::installMozaSDK();
m_sdkInstalled = true;
m_running = false;
DebugLog("SDK Installed. Waiting for wheelbase asynchronously...");
}
void MozaController::Update() {
if (!m_sdkInstalled) return;
const auto now = std::chrono::steady_clock::now();
if (!m_running) {
if (now >= m_nextRenewTime) {
ERRORCODE err = NORMAL;
const char* wheelbase = moza::getDeviceParent(PRODUCT_WHEELBASE, err);
if (err == NORMAL && wheelbase != nullptr && wheelbase[0] != '\0') {
std::stringstream ss;
ss << "Wheelbase detected: " << wheelbase;
DebugLog(ss.str());
HWND desktopHwnd = GetDesktopWindow();
if (CreateAndStartCenterForce(m_hwnd, desktopHwnd)) {
// Standard settings
moza::setMotorFfbStrength(100);
moza::setMotorPeakTorque(100);
moza::setMotorSpringStrength(0);
moza::setMotorNaturalDamper(45);
moza::setMotorSpeedDamping(15);
moza::setMotorHandsOffProtection(0);
m_running = true;
m_nextRenewTime = now + std::chrono::milliseconds(59800);
DebugLog("Hardware Ready. Main thread control active.");
} else {
DebugLog("[ERROR] Failed to start center force. Will retry...");
}
} else {
DebugLog("Waiting for wheelbase connection...");
}
if (!m_running) {
m_nextRenewTime = now + std::chrono::milliseconds(500);
}
}
return;
}
ERRORCODE hidErr = NORMAL;
const HIDData* hid = moza::getHIDData(hidErr);
float angleDeg = 0.0f;
bool angleValid = false;
float velocityDegPerSec = 0.0f;
if (hidErr == NORMAL && hid != nullptr) {
if (!std::isnan(hid->fSteeringWheelAngle)) {
angleDeg = hid->fSteeringWheelAngle;
angleValid = true;
}
if (!std::isnan(hid->fSteeringWheelVelocity)) {
velocityDegPerSec = hid->fSteeringWheelVelocity;
}
}
if (angleValid) {
if (std::isnan(velocityDegPerSec) || velocityDegPerSec == 0.0f) {
if (m_hasLastAngle) {
const float dt = std::chrono::duration<float>(now - m_lastTs).count();
if (dt > 0.0001f) {
velocityDegPerSec = (angleDeg - m_lastAngle) / dt;
}
}
}
m_lastAngle = angleDeg;
m_lastTs = now;
m_hasLastAngle = true;
} else if (m_hasLastAngle) {
angleDeg = m_lastAngle;
angleValid = true;
}
constexpr float kDeadzoneDeg = 1.5f;
constexpr float kKp = 50.0f;
constexpr float kKd = 0.8f;
constexpr float kMaxTorque = 6500.0f;
float torque = 0.0f;
if (angleValid && std::fabs(angleDeg) > kDeadzoneDeg) {
const float command = (kKp * angleDeg + kKd * velocityDegPerSec);
torque = kMaxTorque * std::tanh(command / kMaxTorque);
}
torque = std::clamp(torque, -kMaxTorque, kMaxTorque);
if (m_centerForce) {
bool magnitudeOk = TryEffectCall("centerForce->setMagnitude", [&]() {
m_centerForce->setMagnitude(static_cast<long>(torque));
});
if (!magnitudeOk) {
DebugLog("[WARN] Effect lost (likely backgrounded). Dropping connection to standby mode...");
moza::stopForceFeedback();
m_centerForce.reset();
m_running = false;
}
}
if (m_running && now >= m_nextRenewTime) {
if (m_centerForce && !TryEffectCall("centerForce->start(renew)", [&]() { m_centerForce->start(); })) {
DebugLog("[WARN] Effect renew failed (likely backgrounded). Dropping connection to standby mode...");
moza::stopForceFeedback();
m_centerForce.reset();
m_running = false;
} else {
m_nextRenewTime = now + std::chrono::milliseconds(59800);
}
}
if (now >= m_nextPrintTime) {
std::stringstream ss;
ss << "[CENTER] angle=" << angleDeg
<< " vel=" << velocityDegPerSec
<< " torque=" << static_cast<long>(torque);
DebugLog(ss.str());
m_nextPrintTime = now + std::chrono::milliseconds(1000);
}
}
void MozaController::Stop() {
m_running = false;
if (m_centerForce) {
m_centerForce.reset();
}
moza::stopForceFeedback();
moza::removeMozaSDK();
}
bool MozaController::CreateAndStartCenterForce(HWND primaryHwnd, HWND desktopHwnd) {
ERRORCODE err = NORMAL;
// 恢复为尝试主窗口 -> 桌面窗口,不使用全局 nullptr 避免按键被驱动全局吃掉
const HWND startHandles[2] = { primaryHwnd, desktopHwnd };
const char* startNames[2] = { "app-window", "desktop-window" };
constexpr unsigned long kDurationMs = 60000;
for (int i = 0; i < 2; ++i) {
if (startHandles[i] == nullptr) continue;
err = NORMAL;
m_centerForce = moza::createWheelbaseETConstantForce(startHandles[i], err);
if (m_centerForce == nullptr || err != NORMAL) {
PrintError("createWheelbaseETConstantForce", err);
continue;
}
m_centerForce->setGain(10000);
m_centerForce->setDuration(kDurationMs);
m_centerForce->setAttackTime(5);
m_centerForce->setFadeTime(10);
m_centerForce->setMagnitude(0);
if (TryEffectCall("centerForce->start", [&]() { m_centerForce->start(); })) {
std::stringstream ss;
ss << "[INFO] Center force started with " << startNames[i];
DebugLog(ss.str());
return true;
}
}
return false;
}
#pragma once
#include <windows.h>
#include <memory>
#include <thread>
#include <atomic>
#include "mozaAPI.h"
class MozaController {
public:
MozaController();
~MozaController();
void InitializeHardware(HWND hWnd);
void Update(); // 每一帧调用的更新逻辑
void Stop();
private:
bool CreateAndStartCenterForce(HWND primaryHwnd, HWND desktopHwnd);
std::shared_ptr<RS21::direct_input::ETConstantForce> m_centerForce;
std::atomic<bool> m_running;
bool m_sdkInstalled;
HWND m_hwnd;
// 状态追踪 (用于模拟程序循环逻辑)
float m_lastAngle;
std::chrono::steady_clock::time_point m_lastTs;
std::chrono::steady_clock::time_point m_nextRenewTime;
std::chrono::steady_clock::time_point m_nextPrintTime;
bool m_hasLastAngle;
};
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 使用者 steer_url.rc
#define IDS_APP_TITLE 103
#define IDR_MAINFRAME 128
#define IDD_STEERURL_DIALOG 102
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_STEERURL 107
#define IDI_SMALL 108
#define IDC_STEERURL 109
#define IDC_MYICON 2
#ifndef IDC_STATIC
#define IDC_STATIC -1
#endif
// 新对象的下一组默认值
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 130
#define _APS_NEXT_RESOURCE_VALUE 129
#define _APS_NEXT_COMMAND_VALUE 32771
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 110
#endif
#endif
// header.h: 标准系统包含文件的包含文件,
// 或特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
// C 运行时头文件
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MOZA R3 Control Dashboard</title>
<style>
:root {
--primary: #00f2fe;
--secondary: #4facfe;
--bg: #0f172a;
--card: #1e293b;
}
body {
background: var(--bg);
color: white;
font-family: 'Outfit', -apple-system, sans-serif;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
}
.dashboard {
background: var(--card);
padding: 2rem;
border-radius: 1.5rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
border: 1px solid rgba(255, 255, 255, 0.1);
text-align: center;
backdrop-filter: blur(10px);
width: 80%;
max-width: 500px;
}
h1 {
font-size: 2rem;
margin-bottom: 0.5rem;
background: linear-gradient(to right, var(--primary), var(--secondary));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.status {
display: inline-block;
padding: 0.5rem 1rem;
background: rgba(16, 185, 129, 0.2);
color: #10b981;
border-radius: 2rem;
font-size: 0.875rem;
margin: 1rem 0;
border: 1px solid rgba(16, 185, 129, 0.2);
}
.controls {
margin-top: 2rem;
}
.info {
color: #94a3b8;
font-size: 0.9rem;
line-height: 1.5;
}
.visual {
width: 150px;
height: 150px;
margin: 1.5rem auto;
border: 4px solid var(--primary);
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
animation: pulse 2s infinite;
}
.visual::after {
content: '';
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-top: 30px solid var(--primary);
position: absolute;
top: 20px;
}
@keyframes pulse {
0% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(0, 242, 254, 0.4);
}
70% {
transform: scale(1.05);
box-shadow: 0 0 0 15px rgba(0, 242, 254, 0);
}
100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(0, 242, 254, 0);
}
}
</style>
</head>
<body>
<div class="dashboard">
<h1>MOZA R3 Center Force</h1>
<div class="status">System Active</div>
<div class="visual"></div>
<div class="info">
<p>Background controller thread is running.</p>
<p>Direct Input center force enabled for desktop and app windows.</p>
</div>
<div class="controls">
<p style="color: #64748b; font-size: 0.8rem;">UI rendered with WebView2</p>
</div>
</div>
</body>
</html>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Web.WebView2" version="1.0.3856.49" targetFramework="native" />
</packages>
\ No newline at end of file
This diff is collapsed.
#pragma once
#include "resource.h"
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{7f3b8ddd-ad8d-417b-8193-a02b136442f2}</ProjectGuid>
<RootNamespace>steerurl</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)third_party\moza\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(ProjectDir)third_party\moza\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>MOZA_SDK.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)third_party\moza\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>MOZA_SDK.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)third_party\moza\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="framework.h" />
<ClInclude Include="MozaController.h" />
<ClInclude Include="Resource.h" />
<ClInclude Include="steer_url.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="third_party\moza\include\effects.h" />
<ClInclude Include="third_party\moza\include\effects\Effect.h" />
<ClInclude Include="third_party\moza\include\effects\EffectException.h" />
<ClInclude Include="third_party\moza\include\effects\ETConstantForce.h" />
<ClInclude Include="third_party\moza\include\effects\ETDamper.h" />
<ClInclude Include="third_party\moza\include\effects\ETFriction.h" />
<ClInclude Include="third_party\moza\include\effects\ETInertia.h" />
<ClInclude Include="third_party\moza\include\effects\ETSine.h" />
<ClInclude Include="third_party\moza\include\effects\ETSpring.h" />
<ClInclude Include="third_party\moza\include\effects\WinDirectInputApiException.h" />
<ClInclude Include="third_party\moza\include\enumCode.h" />
<ClInclude Include="third_party\moza\include\hid_device.h" />
<ClInclude Include="third_party\moza\include\hid_struct.h" />
<ClInclude Include="third_party\moza\include\macros.h" />
<ClInclude Include="third_party\moza\include\mozaAPI.h" />
<ClInclude Include="third_party\moza\include\shifter_device.h" />
<ClInclude Include="third_party\moza\include\switches_device.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="MozaController.cpp" />
<ClCompile Include="steer_url.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="steer_url.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="small.ico" />
<Image Include="steer_url.ico" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\packages\Microsoft.Web.WebView2.1.0.3856.49\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\packages\Microsoft.Web.WebView2.1.0.3856.49\build\native\Microsoft.Web.WebView2.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Web.WebView2.1.0.3856.49\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Web.WebView2.1.0.3856.49\build\native\Microsoft.Web.WebView2.targets'))" />
</Target>
</Project>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="源文件\third_party">
<UniqueIdentifier>{b8208d23-10cc-4fe2-9b0d-a1043305db4c}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\third_party\moza">
<UniqueIdentifier>{8152eacf-b9f2-450a-9993-ed3de2203a27}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\third_party\moza\effect">
<UniqueIdentifier>{0fb51518-2ff6-4f0a-877e-91e076264408}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\www">
<UniqueIdentifier>{e197ad14-d224-4309-b764-b2f6706bfd46}</UniqueIdentifier>
</Filter>
<Filter Include="源文件\www\image">
<UniqueIdentifier>{ccb9fb77-adb1-4588-9ade-f376d61e1686}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="Resource.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="steer_url.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\effects\Effect.h">
<Filter>源文件\third_party\moza\effect</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\effects\EffectException.h">
<Filter>源文件\third_party\moza\effect</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\effects\ETConstantForce.h">
<Filter>源文件\third_party\moza\effect</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\effects\ETDamper.h">
<Filter>源文件\third_party\moza\effect</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\effects\ETFriction.h">
<Filter>源文件\third_party\moza\effect</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\effects\ETInertia.h">
<Filter>源文件\third_party\moza\effect</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\effects\ETSine.h">
<Filter>源文件\third_party\moza\effect</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\effects\ETSpring.h">
<Filter>源文件\third_party\moza\effect</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\effects\WinDirectInputApiException.h">
<Filter>源文件\third_party\moza\effect</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\effects.h">
<Filter>源文件\third_party\moza</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\enumCode.h">
<Filter>源文件\third_party\moza</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\hid_device.h">
<Filter>源文件\third_party\moza</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\hid_struct.h">
<Filter>源文件\third_party\moza</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\macros.h">
<Filter>源文件\third_party\moza</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\mozaAPI.h">
<Filter>源文件\third_party\moza</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\shifter_device.h">
<Filter>源文件\third_party\moza</Filter>
</ClInclude>
<ClInclude Include="third_party\moza\include\switches_device.h">
<Filter>源文件\third_party\moza</Filter>
</ClInclude>
<ClInclude Include="MozaController.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="steer_url.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="MozaController.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="steer_url.rc">
<Filter>资源文件</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Image Include="small.ico">
<Filter>资源文件</Filter>
</Image>
<Image Include="steer_url.ico">
<Filter>资源文件</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>
\ No newline at end of file
#pragma once
// // 包含 SDKDDKVer.h 可定义可用的最高版本的 Windows 平台。
// 如果希望为之前的 Windows 平台构建应用程序,在包含 SDKDDKVer.h 之前请先包含 WinSDKVer.h 并
// 将 _WIN32_WINNT 宏设置为想要支持的平台。
#include <SDKDDKVer.h>
#pragma once
#include"effects/Effect.h"
#include"effects/ETConstantForce.h"
#include"effects/ETDamper.h"
#include"effects/ETFriction.h"
#include"effects/ETInertia.h"
#include"effects/ETSine.h"
#include"effects/ETSpring.h"
#pragma once
#include <dinput.h>
#include "Effect.h"
namespace RS21::direct_input {
/**
* @if english
* @brief Constant force effect class
* 1.Specialization attribute: magnitude
* @else
* @brief The ETConstantForce class 常量力效果类
* 1. 特化属性:强度,magnitude
* @endif
*/
class WIN_API ETConstantForce: public Effect
{
public:
ETConstantForce(Device* device);
/**
* @if english
* @brief Obtain the strength of the current ETConstantForce
* @return The strength of the current ETConstantForce
* @else
* @brief 获取当前ETConstantForce的强度
* @return 当前ETConstantForce的强度
* @endif
*/
long magnitude();
/**
* @if english
* @brief set the strength of the current ETConstantForce
* @param newMagnitude The strength of the current ETConstantForce
* @else
* @brief 设置当前ETConstantForce的强度
* @param newMagnitude 设置的ETConstantForce的强度值
* @endif
*/
void setMagnitude(long newMagnitude);
protected:
/**
* @if english
* @brief Device creation force (initialization)
* @param device win Device interface instance
* @else
* @brief 设备创建力(初始化)
* @param device win设备接口实例
* @endif
*/
void downloadToDevice(LPDIRECTINPUTDEVICE8 device) override;
DICONSTANTFORCE m_diPeriodic;
};
}//RS21::directInput

#pragma once
#include <dinput.h>
#include "Effect.h"
#include <iostream>
namespace RS21::direct_input {
/**
* @if english
* @brief Damping force effect class
* @else
* @brief The ETDamper class 阻尼力效果类
* @endif
*/
class WIN_API ETDamper : public Effect
{
public:
ETDamper(Device* device);
/**
* @if english
* @brief Obtain offset for the condition
* @return Offset for the condition, in the range from - 10,000 through 10,000.
* @else
* @brief 获取条件的偏移量
* @return 条件的偏移量,范围从-10000到10000
* @endif
*/
long offset();
/**
* @if english
* @brief Obtain coefficient constant on the positive side of the offset
* @return coefficient constant on the positive side of the offset, in the range from - 10,000 through 10,000.
* @else
* @brief 获取偏移正侧的系数常数
* @return 偏移正侧的系数常数,范围从-10000到10000
* @endif
*/
long positiveCoefficient();
/**
* @if english
* @brief Obtain Coefficient constant on the negative side of the offset
* @return Coefficient constant on the negative side of the offset, in the range from - 10,000 through 10,000
* If the device does not support separate positive and negative coefficients, the value of lNegativeCoefficient is ignored,
* and the value of lPositiveCoefficient is used as both the positive and negative coefficients
* @else
* @brief 获取偏移负侧的系数常数
* @return 偏移负侧的系数常数,范围从-10000到10000。
* 如果设备不支持单独的正系数和负系数,则忽略lNegativeCoefficient的值,并将lPositiveCoefficiency的值用作正系数和负值
* @endif
*/
long negativeCoefficient();
/**
* @if english
* @brief Obtain maximum force output on the positive side of the offset
* @return Maximum force output on the positive side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* @else
* @brief 获得偏移正侧的最大力输出
* @return 偏移正侧的最大力输出,范围从0到10000。如果设备不支持力饱和,则忽略此成员的值。
* @endif
*/
unsigned long positiveSaturation();
/**
* @if english
* @brief Obtain maximum force output on the negative side of the offset
* @return Maximum force output on the negative side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* If the device does not support separate positive and negative saturation, the value of dwNegativeSaturation is ignored, and the value of dwPositiveSaturation is used as both the positive and negative saturation.
* @else
* @brief 获取偏移负侧的最大力输出
* @return 偏移负侧的最大力输出,范围从0到10000。
* 如果设备不支持力饱和,则忽略此成员的值。
* 如果设备不支持单独的正饱和和负饱和,则忽略dwNegativeSaturation的值,并使用dwPositiveSaturation值作为正饱和和负饱和。
* @endif
*/
unsigned long negativeSaturation();
/**
* @if english
* @brief Obtain region around lOffset in which the condition is not active
* @return Region around lOffset in which the condition is not active, in the range from 0 through 10,000. In other words,
* the condition is not active between lOffset minus lDeadBand and lOffset plus lDeadBand.
* @else
* @brief 获取lOffset周围条件未激活的区域
* @return lOffset周围条件未激活的区域,范围从0到10000。换句话说,在lOffset减去lDeadBand和lOffset加上lDeadBnd之间,条件不活动。
* @endif
*/
long deadBand();
/**
* @if english
* @brief set offset for the condition
* @param offset Offset for the condition, in the range from - 10,000 through 10,000.
* @else
* @brief 设置条件的偏移量
* @param offset 条件的偏移量,范围从-10000到10000
* @endif
*/
void setOffset(long offset);
/**
* @if english
* @brief set coefficient constant on the positive side of the offset
* @param newPositiveCoefficient coefficient constant on the positive side of the offset, in the range from - 10,000 through 10,000.
* @else
* @brief 设置偏移正侧的系数常数
* @param newPositiveCoefficient 偏移正侧的系数常数,范围从-10000到10000
* @endif
*/
void setPositiveCoefficient(long newPositiveCoefficient);
/**
* @if english
* @brief set Coefficient constant on the negative side of the offset
* @param newNegativeCoefficient Coefficient constant on the negative side of the offset, in the range from - 10,000 through 10,000
* If the device does not support separate positive and negative coefficients, the value of lNegativeCoefficient is ignored,
* and the value of lPositiveCoefficient is used as both the positive and negative coefficients
* @else
* @brief 设置偏移负侧的系数常数
* @param newNegativeCoefficient 偏移负侧的系数常数,范围从-10000到10000。
* 如果设备不支持单独的正系数和负系数,则忽略lNegativeCoefficient的值,并将lPositiveCoefficiency的值用作正系数和负值
* @endif
*/
void setNegativeCoefficient(long newNegativeCoefficient);
/**
* @if english
* @brief set maximum force output on the positive side of the offset
* @param newPositiveSaturation Maximum force output on the positive side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* @else
* @brief 设置偏移正侧的最大力输出
* @param newPositiveSaturation 偏移正侧的最大力输出,范围从0到10000。如果设备不支持力饱和,则忽略此成员的值。
* @endif
*/
void setPositiveSaturation(unsigned long newPositiveSaturation);
/**
* @if english
* @brief set maximum force output on the negative side of the offset
* @param newNegativeSaturation Maximum force output on the negative side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* If the device does not support separate positive and negative saturation, the value of dwNegativeSaturation is ignored, and the value of dwPositiveSaturation is used as both the positive and negative saturation.
* @else
* @brief 设置偏移负侧的最大力输出
* @param newNegativeSaturation 偏移负侧的最大力输出,范围从0到10000。
* 如果设备不支持力饱和,则忽略此成员的值。
* 如果设备不支持单独的正饱和和负饱和,则忽略dwNegativeSaturation的值,并使用dwPositiveSaturation值作为正饱和和负饱和。
* @endif
*/
void setNegativeSaturation(unsigned long newNegativeSaturation);
/**
* @if english
* @brief set region around lOffset in which the condition is not active
* @param newDeadBand Region around lOffset in which the condition is not active, in the range from 0 through 10,000. In other words,
* the condition is not active between lOffset minus lDeadBand and lOffset plus lDeadBand.
* @else
* @brief 设置lOffset周围条件未激活的区域
* @param newDeadBand lOffset周围条件未激活的区域,范围从0到10000。换句话说,在lOffset减去lDeadBand和lOffset加上lDeadBnd之间,条件不活动。
* @endif
*/
void setDeadBand(long newDeadBand);
protected:
// /**
// * @if english
// * @brief Device creation force (initialization)
// * @param device win Device interface instance
// * @else
// * @brief 设备创建力(初始化)
// * @param device win设备接口实例
// * @endif
// */
virtual void downloadToDevice(LPDIRECTINPUTDEVICE8 device) override;
DICONDITION m_diPeriodic;
};
}//RS21::directInput

#pragma once
#include <dinput.h>
#include "Effect.h"
namespace RS21::direct_input {
/**
* @if english
* @brief Mechanical friction force effect class
* @else
* @brief The ETFriction class 机械摩擦力效果类
* @endif
*/
class WIN_API ETFriction: public Effect
{
public:
ETFriction(Device* device);
//查看参数
/**
* @if english
* @brief Obtain offset for the condition
* @return Offset for the condition, in the range from - 10,000 through 10,000.
* @else
* @brief 获取条件的偏移量
* @return 条件的偏移量,范围从-10000到10000
* @endif
*/
long offset();
/**
* @if english
* @brief Obtain coefficient constant on the positive side of the offset
* @return coefficient constant on the positive side of the offset, in the range from - 10,000 through 10,000.
* @else
* @brief 获取偏移正侧的系数常数
* @return 偏移正侧的系数常数,范围从-10000到10000
* @endif
*/
long positiveCoefficient();
/**
* @if english
* @brief Obtain Coefficient constant on the negative side of the offset
* @return Coefficient constant on the negative side of the offset, in the range from - 10,000 through 10,000
* If the device does not support separate positive and negative coefficients, the value of lNegativeCoefficient is ignored,
* and the value of lPositiveCoefficient is used as both the positive and negative coefficients
* @else
* @brief 获取偏移负侧的系数常数
* @return 偏移负侧的系数常数,范围从-10000到10000。
* 如果设备不支持单独的正系数和负系数,则忽略lNegativeCoefficient的值,并将lPositiveCoefficiency的值用作正系数和负值
* @endif
*/
long negativeCoefficient();
/**
* @if english
* @brief Obtain maximum force output on the positive side of the offset
* @return Maximum force output on the positive side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* @else
* @brief 获得偏移正侧的最大力输出
* @return 偏移正侧的最大力输出,范围从0到10000。如果设备不支持力饱和,则忽略此成员的值。
* @endif
*/
unsigned long positiveSaturation();
/**
* @if english
* @brief Obtain maximum force output on the negative side of the offset
* @return Maximum force output on the negative side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* If the device does not support separate positive and negative saturation, the value of dwNegativeSaturation is ignored, and the value of dwPositiveSaturation is used as both the positive and negative saturation.
* @else
* @brief 获取偏移负侧的最大力输出
* @return 偏移负侧的最大力输出,范围从0到10000。
* 如果设备不支持力饱和,则忽略此成员的值。
* 如果设备不支持单独的正饱和和负饱和,则忽略dwNegativeSaturation的值,并使用dwPositiveSaturation值作为正饱和和负饱和。
* @endif
*/
unsigned long negativeSaturation();
/**
* @if english
* @brief Obtain region around lOffset in which the condition is not active
* @return Region around lOffset in which the condition is not active, in the range from 0 through 10,000. In other words,
* the condition is not active between lOffset minus lDeadBand and lOffset plus lDeadBand.
* @else
* @brief 获取lOffset周围条件未激活的区域
* @return lOffset周围条件未激活的区域,范围从0到10000。换句话说,在lOffset减去lDeadBand和lOffset加上lDeadBnd之间,条件不活动。
* @endif
*/
long deadBand();
/**
* @if english
* @brief set offset for the condition
* @param offset Offset for the condition, in the range from - 10,000 through 10,000.
* @else
* @brief 设置条件的偏移量
* @param offset 条件的偏移量,范围从-10000到10000
* @endif
*/
void setOffset(long offset);
/**
* @if english
* @brief set coefficient constant on the positive side of the offset
* @param newPositiveCoefficient coefficient constant on the positive side of the offset, in the range from - 10,000 through 10,000.
* @else
* @brief 设置偏移正侧的系数常数
* @param newPositiveCoefficient 偏移正侧的系数常数,范围从-10000到10000
* @endif
*/
void setPositiveCoefficient(long newPositiveCoefficient);
/**
* @if english
* @brief set Coefficient constant on the negative side of the offset
* @param newNegativeCoefficient Coefficient constant on the negative side of the offset, in the range from - 10,000 through 10,000
* If the device does not support separate positive and negative coefficients, the value of lNegativeCoefficient is ignored,
* and the value of lPositiveCoefficient is used as both the positive and negative coefficients
* @else
* @brief 设置偏移负侧的系数常数
* @param newNegativeCoefficient 偏移负侧的系数常数,范围从-10000到10000。
* 如果设备不支持单独的正系数和负系数,则忽略lNegativeCoefficient的值,并将lPositiveCoefficiency的值用作正系数和负值
* @endif
*/
void setNegativeCoefficient(long newNegativeCoefficient);
/**
* @if english
* @brief set maximum force output on the positive side of the offset
* @param newPositiveSaturation Maximum force output on the positive side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* @else
* @brief 设置偏移正侧的最大力输出
* @param newPositiveSaturation 偏移正侧的最大力输出,范围从0到10000。如果设备不支持力饱和,则忽略此成员的值。
* @endif
*/
void setPositiveSaturation(unsigned long newPositiveSaturation);
/**
* @if english
* @brief set maximum force output on the negative side of the offset
* @param newNegativeSaturation Maximum force output on the negative side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* If the device does not support separate positive and negative saturation, the value of dwNegativeSaturation is ignored, and the value of dwPositiveSaturation is used as both the positive and negative saturation.
* @else
* @brief 设置偏移负侧的最大力输出
* @param newNegativeSaturation 偏移负侧的最大力输出,范围从0到10000。
* 如果设备不支持力饱和,则忽略此成员的值。
* 如果设备不支持单独的正饱和和负饱和,则忽略dwNegativeSaturation的值,并使用dwPositiveSaturation值作为正饱和和负饱和。
* @endif
*/
void setNegativeSaturation(unsigned long newNegativeSaturation);
/**
* @if english
* @brief set region around lOffset in which the condition is not active
* @param newDeadBand Region around lOffset in which the condition is not active, in the range from 0 through 10,000. In other words,
* the condition is not active between lOffset minus lDeadBand and lOffset plus lDeadBand.
* @else
* @brief 设置lOffset周围条件未激活的区域
* @param newDeadBand lOffset周围条件未激活的区域,范围从0到10000。换句话说,在lOffset减去lDeadBand和lOffset加上lDeadBnd之间,条件不活动。
* @endif
*/
void setDeadBand(long newDeadBand);
protected:
virtual void downloadToDevice(LPDIRECTINPUTDEVICE8 device) override;
DICONDITION m_diPeriodic;
};
}//rs21::direct_input

#pragma once
#include "Effect.h"
namespace RS21::direct_input {
/**
* @if english
* @brief Inertia effect class
* @else
* @brief The ETDamper class 惯量效果类
* @endif
*/
class WIN_API ETInertia: public Effect
{
public:
ETInertia(Device* device);
//查看参数
/**
* @if english
* @brief Obtain offset for the condition
* @return Offset for the condition, in the range from - 10,000 through 10,000.
* @else
* @brief 获取条件的偏移量
* @return 条件的偏移量,范围从-10000到10000
* @endif
*/
long offset();
/**
* @if english
* @brief Obtain coefficient constant on the positive side of the offset
* @return coefficient constant on the positive side of the offset, in the range from - 10,000 through 10,000.
* @else
* @brief 获取偏移正侧的系数常数
* @return 偏移正侧的系数常数,范围从-10000到10000
* @endif
*/
long positiveCoefficient();
/**
* @if english
* @brief Obtain Coefficient constant on the negative side of the offset
* @return Coefficient constant on the negative side of the offset, in the range from - 10,000 through 10,000
* If the device does not support separate positive and negative coefficients, the value of lNegativeCoefficient is ignored,
* and the value of lPositiveCoefficient is used as both the positive and negative coefficients
* @else
* @brief 获取偏移负侧的系数常数
* @return 偏移负侧的系数常数,范围从-10000到10000。
* 如果设备不支持单独的正系数和负系数,则忽略lNegativeCoefficient的值,并将lPositiveCoefficiency的值用作正系数和负值
* @endif
*/
long negativeCoefficient();
/**
* @if english
* @brief Obtain maximum force output on the positive side of the offset
* @return Maximum force output on the positive side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* @else
* @brief 获得偏移正侧的最大力输出
* @return 偏移正侧的最大力输出,范围从0到10000。如果设备不支持力饱和,则忽略此成员的值。
* @endif
*/
unsigned long positiveSaturation();
/**
* @if english
* @brief Obtain maximum force output on the negative side of the offset
* @return Maximum force output on the negative side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* If the device does not support separate positive and negative saturation, the value of dwNegativeSaturation is ignored, and the value of dwPositiveSaturation is used as both the positive and negative saturation.
* @else
* @brief 获取偏移负侧的最大力输出
* @return 偏移负侧的最大力输出,范围从0到10000。
* 如果设备不支持力饱和,则忽略此成员的值。
* 如果设备不支持单独的正饱和和负饱和,则忽略dwNegativeSaturation的值,并使用dwPositiveSaturation值作为正饱和和负饱和。
* @endif
*/
unsigned long negativeSaturation();
/**
* @if english
* @brief Obtain region around lOffset in which the condition is not active
* @return Region around lOffset in which the condition is not active, in the range from 0 through 10,000. In other words,
* the condition is not active between lOffset minus lDeadBand and lOffset plus lDeadBand.
* @else
* @brief 获取lOffset周围条件未激活的区域
* @return lOffset周围条件未激活的区域,范围从0到10000。换句话说,在lOffset减去lDeadBand和lOffset加上lDeadBnd之间,条件不活动。
* @endif
*/
long deadBand();
/**
* @if english
* @brief set offset for the condition
* @param offset Offset for the condition, in the range from - 10,000 through 10,000.
* @else
* @brief 设置条件的偏移量
* @param offset 条件的偏移量,范围从-10000到10000
* @endif
*/
void setOffset(long offset);
/**
* @if english
* @brief set coefficient constant on the positive side of the offset
* @param newPositiveCoefficient coefficient constant on the positive side of the offset, in the range from - 10,000 through 10,000.
* @else
* @brief 设置偏移正侧的系数常数
* @param newPositiveCoefficient 偏移正侧的系数常数,范围从-10000到10000
* @endif
*/
void setPositiveCoefficient(long newPositiveCoefficient);
/**
* @if english
* @brief set Coefficient constant on the negative side of the offset
* @param newNegativeCoefficient Coefficient constant on the negative side of the offset, in the range from - 10,000 through 10,000
* If the device does not support separate positive and negative coefficients, the value of lNegativeCoefficient is ignored,
* and the value of lPositiveCoefficient is used as both the positive and negative coefficients
* @else
* @brief 设置偏移负侧的系数常数
* @param newNegativeCoefficient 偏移负侧的系数常数,范围从-10000到10000。
* 如果设备不支持单独的正系数和负系数,则忽略lNegativeCoefficient的值,并将lPositiveCoefficiency的值用作正系数和负值
* @endif
*/
void setNegativeCoefficient(long newNegativeCoefficient);
/**
* @if english
* @brief set maximum force output on the positive side of the offset
* @param newPositiveSaturation Maximum force output on the positive side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* @else
* @brief 设置偏移正侧的最大力输出
* @param newPositiveSaturation 偏移正侧的最大力输出,范围从0到10000。如果设备不支持力饱和,则忽略此成员的值。
* @endif
*/
void setPositiveSaturation(unsigned long newPositiveSaturation);
/**
* @if english
* @brief set maximum force output on the negative side of the offset
* @param newNegativeSaturation Maximum force output on the negative side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* If the device does not support separate positive and negative saturation, the value of dwNegativeSaturation is ignored, and the value of dwPositiveSaturation is used as both the positive and negative saturation.
* @else
* @brief 设置偏移负侧的最大力输出
* @param newNegativeSaturation 偏移负侧的最大力输出,范围从0到10000。
* 如果设备不支持力饱和,则忽略此成员的值。
* 如果设备不支持单独的正饱和和负饱和,则忽略dwNegativeSaturation的值,并使用dwPositiveSaturation值作为正饱和和负饱和。
* @endif
*/
void setNegativeSaturation(unsigned long newNegativeSaturation);
/**
* @if english
* @brief set region around lOffset in which the condition is not active
* @param newDeadBand Region around lOffset in which the condition is not active, in the range from 0 through 10,000. In other words,
* the condition is not active between lOffset minus lDeadBand and lOffset plus lDeadBand.
* @else
* @brief 设置lOffset周围条件未激活的区域
* @param newDeadBand lOffset周围条件未激活的区域,范围从0到10000。换句话说,在lOffset减去lDeadBand和lOffset加上lDeadBnd之间,条件不活动。
* @endif
*/
void setDeadBand(long newDeadBand);
protected:
virtual void downloadToDevice(LPDIRECTINPUTDEVICE8 device) override;
DICONDITION m_diPeriodic;
};
}//rs21::direct_input

#pragma once
#include "Effect.h"
#include <dinput.h>
#include <iostream>
namespace RS21::direct_input{
/**
* @if english
* @brief Sinusoidal force effect class
* @else
* @brief The ETSine class 正弦力效果类
* @endif
*/
class WIN_API ETSine : public Effect
{
public:
ETSine(Device* device);
/**
* @if english
* @brief Obtain the strength of the current ETSine
* @return The strength of the current ETSine
* @else
* @brief magnitude 获取当前ETSine的强度
* @return 当前ETSine的强度
* @endif
*/
unsigned long magnitude();
/**
* @if english
* @brief set the strength of the current ETSine
* @param newMagnitude The strength of the current ETSine
* @else
* @brief setMagnitude 设置当前ETSine的强度
* @param newMagnitude 设置的ETSine的强度值
* @endif
*/
void setMagnitude(unsigned long newMagnitude);
/**
* @if english
* @brief Obtain offset for the condition
* @return Offset for the condition, in the range from - 10,000 through 10,000.
* @else
* @brief 获取条件的偏移量
* @return 条件的偏移量,范围从-10000到10000
* @endif
*/
long offset();
/**
* @if english
* @brief set offset for the condition
* @param offset Offset for the condition, in the range from - 10,000 through 10,000.
* @else
* @brief 设置条件的偏移量
* @param offset 条件的偏移量,范围从-10000到10000
* @endif
*/
void setOffset(long newOffset);
/**
* @if english
* @brief Obtain the current phase of ETSine
* @return Current phase of ETSine
* @else
* @brief phase 获取当前ETSine相位
* @return 当前ETSine的相位
* @endif
*/
unsigned long phase();
/**
* @if english
* @brief set the current phase of ETSine
* @param newPhase current phase of ETSine
* @else
* @brief 设置当前ETSine的相位
* @param newPhase 设置的ETSine的相位
* @endif
*/
void setPhase(unsigned long newPhase);
/**
* @if english
* @brief Obtain the cycle of the current ETSine
* @return cycle of the current ETSine
* @else
* @brief 获取当前ETSine的周期
* @return 当前ETSine的周期
* @endif
*/
unsigned long period();
/**
* @if english
* @brief set the cycle of the current ETSine
* @param newPhase cycle of the current ETSine
* @else
* @brief 设置当前ETSine的周期
* @param newPeriod 设置的周期
* @endif
*/
void setPeriod(unsigned long newPeriod);
protected:
virtual void downloadToDevice(LPDIRECTINPUTDEVICE8 device) override;
DIPERIODIC m_diPeriodic;
};
}//RS21::directInput

#pragma once
#include "Effect.h"
namespace RS21::direct_input {
/**
* @if english
* @brief Centripetal force effect class
* @else
* @brief The ETSpring class 回中力效果类
* @endif
*/
class WIN_API ETSpring: public Effect
{
public:
ETSpring(Device* device);
//查看参数
/**
* @if english
* @brief Obtain offset for the condition
* @return Offset for the condition, in the range from - 10,000 through 10,000.
* @else
* @brief 获取条件的偏移量
* @return 条件的偏移量,范围从-10000到10000
* @endif
*/
long offset();
/**
* @if english
* @brief Obtain coefficient constant on the positive side of the offset
* @return coefficient constant on the positive side of the offset, in the range from - 10,000 through 10,000.
* @else
* @brief 获取偏移正侧的系数常数
* @return 偏移正侧的系数常数,范围从-10000到10000
* @endif
*/
long positiveCoefficient();
/**
* @if english
* @brief Obtain Coefficient constant on the negative side of the offset
* @return Coefficient constant on the negative side of the offset, in the range from - 10,000 through 10,000
* If the device does not support separate positive and negative coefficients, the value of lNegativeCoefficient is ignored,
* and the value of lPositiveCoefficient is used as both the positive and negative coefficients
* @else
* @brief 获取偏移负侧的系数常数
* @return 偏移负侧的系数常数,范围从-10000到10000。
* 如果设备不支持单独的正系数和负系数,则忽略lNegativeCoefficient的值,并将lPositiveCoefficiency的值用作正系数和负值
* @endif
*/
long negativeCoefficient();
/**
* @if english
* @brief Obtain maximum force output on the positive side of the offset
* @return Maximum force output on the positive side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* @else
* @brief 获得偏移正侧的最大力输出
* @return 偏移正侧的最大力输出,范围从0到10000。如果设备不支持力饱和,则忽略此成员的值。
* @endif
*/
unsigned long positiveSaturation();
/**
* @if english
* @brief Obtain maximum force output on the negative side of the offset
* @return Maximum force output on the negative side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* If the device does not support separate positive and negative saturation, the value of dwNegativeSaturation is ignored, and the value of dwPositiveSaturation is used as both the positive and negative saturation.
* @else
* @brief 获取偏移负侧的最大力输出
* @return 偏移负侧的最大力输出,范围从0到10000。
* 如果设备不支持力饱和,则忽略此成员的值。
* 如果设备不支持单独的正饱和和负饱和,则忽略dwNegativeSaturation的值,并使用dwPositiveSaturation值作为正饱和和负饱和。
* @endif
*/
unsigned long negativeSaturation();
/**
* @if english
* @brief Obtain region around lOffset in which the condition is not active
* @return Region around lOffset in which the condition is not active, in the range from 0 through 10,000. In other words,
* the condition is not active between lOffset minus lDeadBand and lOffset plus lDeadBand.
* @else
* @brief 获取lOffset周围条件未激活的区域
* @return lOffset周围条件未激活的区域,范围从0到10000。换句话说,在lOffset减去lDeadBand和lOffset加上lDeadBnd之间,条件不活动。
* @endif
*/
long deadBand();
/**
* @if english
* @brief set offset for the condition
* @param offset Offset for the condition, in the range from - 10,000 through 10,000.
* @else
* @brief 设置条件的偏移量
* @param offset 条件的偏移量,范围从-10000到10000
* @endif
*/
void setOffset(long offset);
/**
* @if english
* @brief set coefficient constant on the positive side of the offset
* @param newPositiveCoefficient coefficient constant on the positive side of the offset, in the range from - 10,000 through 10,000.
* @else
* @brief 设置偏移正侧的系数常数
* @param newPositiveCoefficient 偏移正侧的系数常数,范围从-10000到10000
* @endif
*/
void setPositiveCoefficient(long newPositiveCoefficient);
/**
* @if english
* @brief set Coefficient constant on the negative side of the offset
* @param newNegativeCoefficient Coefficient constant on the negative side of the offset, in the range from - 10,000 through 10,000
* If the device does not support separate positive and negative coefficients, the value of lNegativeCoefficient is ignored,
* and the value of lPositiveCoefficient is used as both the positive and negative coefficients
* @else
* @brief 设置偏移负侧的系数常数
* @param newNegativeCoefficient 偏移负侧的系数常数,范围从-10000到10000。
* 如果设备不支持单独的正系数和负系数,则忽略lNegativeCoefficient的值,并将lPositiveCoefficiency的值用作正系数和负值
* @endif
*/
void setNegativeCoefficient(long newNegativeCoefficient);
/**
* @if english
* @brief set maximum force output on the positive side of the offset
* @param newPositiveSaturation Maximum force output on the positive side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* @else
* @brief 设置偏移正侧的最大力输出
* @param newPositiveSaturation 偏移正侧的最大力输出,范围从0到10000。如果设备不支持力饱和,则忽略此成员的值。
* @endif
*/
void setPositiveSaturation(unsigned long newPositiveSaturation);
/**
* @if english
* @brief set maximum force output on the negative side of the offset
* @param newNegativeSaturation Maximum force output on the negative side of the offset, in the range from 0 through 10,000.
* If the device does not support force saturation, the value of this member is ignored.
* If the device does not support separate positive and negative saturation, the value of dwNegativeSaturation is ignored, and the value of dwPositiveSaturation is used as both the positive and negative saturation.
* @else
* @brief 设置偏移负侧的最大力输出
* @param newNegativeSaturation 偏移负侧的最大力输出,范围从0到10000。
* 如果设备不支持力饱和,则忽略此成员的值。
* 如果设备不支持单独的正饱和和负饱和,则忽略dwNegativeSaturation的值,并使用dwPositiveSaturation值作为正饱和和负饱和。
* @endif
*/
void setNegativeSaturation(unsigned long newNegativeSaturation);
/**
* @if english
* @brief set region around lOffset in which the condition is not active
* @param newDeadBand Region around lOffset in which the condition is not active, in the range from 0 through 10,000. In other words,
* the condition is not active between lOffset minus lDeadBand and lOffset plus lDeadBand.
* @else
* @brief 设置lOffset周围条件未激活的区域
* @param newDeadBand lOffset周围条件未激活的区域,范围从0到10000。换句话说,在lOffset减去lDeadBand和lOffset加上lDeadBnd之间,条件不活动。
* @endif
*/
void setDeadBand(long newDeadBand);
protected:
virtual void downloadToDevice(LPDIRECTINPUTDEVICE8 device) override;
DICONDITION m_diPeriodic;
};
}//rs21::direct_input
#pragma once
#include "../macros.h"
#include <cstdint>
#include <string>
#include <dinput.h>
#include <memory>
#include <map>
namespace RS21::direct_input{
class Device;
extern unsigned int DI_MSECONDS;
/**
* @if english
* @brief Effect Abstract Class
* 1. Provide API for effect start and end
* 2. Provide interfaces for obtaining and modifying general parameters
* @else
* @brief The Effect class 效果抽象类
* 1. 提供效果开始、结束API
* 2. 提供通用参数的获取、修改接口
* @endif
*/
class WIN_API Effect
{
public:
/**
* @if english
* @brief start Force effect
* @else
* @brief 力效果启动
* @endif
*/
void start();
/**
* @if english
* @brief stop Force effect
* @else
* @brief stop 停止力效果
* @endif
*/
void stop();
/**
* @if english
* @brief Set the power level
* @param newAttackLevel Power level
* @else
* @brief 设置发力级别
* @param newAttackLevel 发力级别
* @endif
*/
void setAttackLevel(const unsigned long newAttackLevel = 0);
/**
* @if english
* @brief Obtain Power Level
* @return Power level
* @else
* @brief 获取发力级别
* @return 发力级别
* @endif
*/
unsigned long attackLevel() const;
/**
* @if english
* @brief Obtain Power time
* @return Power time,Unit: ms
* @else
* @brief 获取发力时间
* @return 发力时间,单位:ms
* @endif
*/
unsigned long attackTime() const;
/**
* @if english
* @brief set Power time
* @param newAttackTime Power time,Unit: ms
* @else
* @brief 设置发力时间
* @param newAttackTime 发力时间,单位:ms
* @endif
*/
void setAttackTime(const unsigned long newAttackTime = 500);//单位:s
/**
* @if english
* @brief Obtain attenuation level
* @return attenuation level
* @else
* @brief fadeLevel 获取衰减级别
* @return 衰减级别
* @endif
*/
unsigned long fadeLevel() const;
/**
* @if english
* @brief Set attenuation level
* @param newFadeLevel attenuation level
* @else
* @brief 设置衰减级别
* @param newFadeLevel 衰减级别
* @endif
*/
void setFadeLevel(const unsigned long newFadeLevel = 0);
/**
* @if english
* @brief Obtain attenuation time
* @return attenuation time,Unit: ms
* @else
* @brief fadeTime 获取衰减时间,单位:ms
* @return 衰减时间
* @endif
*/
unsigned long fadeTime()const;
/**
* @if english
* @brief Set attenuation time
* @param newFadeLevel attenuation time,Unit: ms
* @else
* @brief setFadeTime 设置衰减时间
* @param newFadeTime 衰减时间,单位:ms
* @endif
*/
void setFadeTime(const unsigned long newFadeTime = 1000 );//
/**
* @if english
* @brief Obtain attenuation duration
* @return attenuation duration,Unit: ms
* @else
* @brief duration 获取效果持续时间
* @return 效果持续时间,单位:ms
* @endif
*/
unsigned long duration() const;
/**
* @if english
* @brief set attenuation duration
* @param newDuration attenuation duration,Unit: ms
* @else
* @brief setDuration 设置效果持续时间
* @param newDuration 效果持续时间,单位:ms
* @endif
*/
void setDuration(const unsigned long newDuration = 2000);
/**
* @if english
* @brief Obtain sampling period
* @return sampling period,Unit: ms
* @else
* @brief 获取采样周期
* @return 采样周期,单位:ms
* @endif
*/
unsigned long samplePeriod() const;
/**
* @if english
* @brief set sampling period
* @param newSamplePeriod sampling period,Unit: ms
* @else
* @brief setSamplePeriod 设置采样周期
* @param newSamplePeriod 采样周期值,单位:ms
* @endif
*/
void setSamplePeriod(const unsigned long newSamplePeriod = 0);
/**
* @if english
* @brief Obtain Gain
* @return Gain value (according to report descriptor: 0-10000)
* @else
* @brief 获取增益
* @return 增益值(根据报告描述符:0~10000)
* @endif
*/
unsigned long gain() const;
/**
* @if english
* @brief set Gain
* @return newGain Gain value (according to report descriptor: 0-10000)
* @else
* @brief 设置增益值
* @param newGain 增益值(根据报告描述符:0~10000)
*/
void setGain(const unsigned long newGain = DI_FFNOMINALMAX);
/**
* @if english
* @brief Obtain departure effect button
* @return departure effect button
* @else
* @brief 获取出发效果按钮
* @return 效果按钮
* @endif
*/
unsigned long triggerButton() const;
/**
* @if english
* @brief set departure effect button
* @param newButtonIndex departure effect button
* @else
* @brief 设置触发效果按钮
* @param newButtonIndex 触发效果按钮
* @endif
*/
void setTriggerButton(const unsigned long newButtonIndex = DIEB_NOTRIGGER);
/**
* @if english
* @brief Obtain trigger effect repetition interval
* @return Trigger effect repetition interval
* @else
* @brief 获取触发效果重复间隔
* @return 触发效果重复间隔
* @endif
*/
unsigned long triggerRepeatInterval() const;
/**
* @if english
* @brief Set trigger effect repetition interval
* @param newRepeatInterval Trigger effect interval
* @else
* @brief 设置触发效果间隔
* @param newRepeatInterval 触发效果间隔
* @endif
*/
void setTriggerRepeatInterval(const unsigned long newRepeatInterval = 0);
// void setCAxes(const unsigned long newCAxes = 2);
/**
* @if english
* @brief Number of effect related axes
* @return number of axles
* @else
* @brief 效果相关轴数
* @return 轴数
* @endif
*/
unsigned long rgdAxesCount();
/**
* @if english
* @brief Obtain relevant axis array
* @return Related Axis Array
* @else
* @brief 获取相关轴数组
* @return 相关轴数组
* @endif
*/
unsigned long* rgdAxes();
/**
* @if english
* @brief Set related axes
* @param axes Related axis array pointer
* @param size Length of related axis array
* @else
* @brief 设置相关轴
* @param axes 相关轴数组指针
* @param size 相关轴数组长度
* @endif
*/
void setRgdAxes(unsigned long* axes, unsigned long size);//设置轴,运行时不能修改,不建议修改,使用默认
/**
* @if english
* @brief Obtain the angle of the effect in the x-axis
* @return The angle of the effect on the x-axis
* @else
* @brief 获取effect在x轴向的角度
* @return effect在x轴的角度
* @endif
*/
long xDirection();
/**
* @if english
* @brief Set the angle of the effect in the x-axis
* @param newDegree The angle of the effect on the x-axis
* @else
* @brief 设置effect在x轴向的角度
* @param newDegree x轴向的角度
* @endif
*/
void setXDirection(long newDegree = 1 );//设置x轴方向,单位:度
// void setEnvelope();//设置封装
// void virtual setCbTypeSpecificParams() = 0;//设置特殊参数长度
// void virtual setTypeSpecificParams() = 0;//设置特殊参数长度
/**
* @if english
* @brief Obtain the name of the current effect
* @return The name of the current effect
* @else
* @brief 获取当前effect的名称
* @return 当前effect的名称
* @endif
*/
std::string effectName() const;
/**
* @if english
* @brief set the name of the current effect
* @param newEffectName The name of the effect
* @else
* @brief 设置effect别名
* @param newEffectName effect的别名
* @endif
*/
void setEffectName(const std::string &newEffectName);
/**
* @if english
* @brief Obtain the index of the current effect
* @return The index of the current effect
* @else
* @brief 获取当前effect的index
* @return 当前effect的index
* @endif
*/
int index() const;
/**
* @if english
* @brief set the index of the current effect
* @param The index of the current effect,index >= 0
* @else
* @brief 设置当前effect的index
* @param newIndex 当前effect的index,index >= 0
* @endif
*/
void setIndex( unsigned int newIndex);
bool m_isRunning = false;
protected:
Effect(Device* device);//抽象类,但是没有虚函数
~Effect();//生命周期由device管理
virtual void downloadToDevice(LPDIRECTINPUTDEVICE8 device) = 0;
std::map<std::string, std::string> allGeneralEffectInfo();
protected:
uint16_t m_effectId;
std::string m_effectName;
LPDIRECTINPUTEFFECT m_effect = NULL;
DIEFFECT m_diEffect; //effect_info
DIENVELOPE m_diEnvelope; //effect_instance
LONG* m_lDirection = nullptr;
DWORD *m_dwAxes = nullptr;
Device* m_device = nullptr;//parent
unsigned int m_index = 0;
};
}//RS21::directInput

#pragma once
#include "../macros.h"
#include <exception>
#include <string>
namespace RS21::direct_input {
class WIN_API EffectException : public std::exception
{
public:
EffectException(const std::string& errorMessage);
EffectException(const char* errorMessage);
virtual const char* what() const override{return m_errorStr.c_str();}
private:
std::string m_errorStr;
};
}//namespace
#pragma once
#include "../macros.h"
#include <exception>
#include <string>
#include "Windows.h" //DWORD
#include "winbase.h"
namespace RS21::direct_input {
class WIN_API WinDirectInputApiException : public std::exception
{
public:
WinDirectInputApiException(HRESULT newHResult, std::string additionalString);
WinDirectInputApiException(HRESULT newHResult, const char* additionalString);
long hResult() const throw()
{
return m_hResult;
}
virtual const char* what() const throw() override
{
return m_errorString.c_str();
}
private:
HRESULT m_hResult;
std::string m_errorString;
std::string ConvertWStringToString(wchar_t* wstr);
};
}//rs21::direct_input
#ifndef ENUMCODE_H
#define ENUMCODE_H
enum ERRORCODE
{
NORMAL = 0,
NOINSTALLSDK,
NODEVICES,
OUTOFRANGE,
PARAMETERERR,
COLLECTIONCYCLEDATALOSS, //采集周期太长导致数据丢失
CREATEFFECTERR,
ENCODINGFAILED,
FFBERR,
FIRMWARETOOOLD, //固件版本过低
PITHOUSENOTREADY, // PitHouse未准备好
};
enum PRODUCTTYPE
{
PRODUCT_WHEELBASE = 0,
PRODUCT_STEERINGWHEEL,
PRODUCT_DISPLAYSCREEN,
PRODUCT_PEDALS,
PRODUCT_METER,
PRODUCT_ADAPTER,
PRODUCT_HANDBRAKE,
PRODUCT_GEARSHIFTER,
PRODUCT_UNKNOWDEVICE,
};
#endif // BASESINGLETON_H
#pragma once
#include "macros.h"
#include <string>
#include <vector>
namespace moza {
/**
* @if english
* @brief HID device class
* @else
* @brief HID设备类
* @endif
*/
class WIN_API HidDevice
{
public:
/**
* @if english
* @brief Default constructor
* @else
* @brief 无参构造
* @endif
*/
HidDevice();
/**
* @if english
* @brief Constructs with a device instance path
* @param path device instance path
* @else
* @brief 从一个设备实例路径构造
* @param path 设备实例路径
* @endif
*/
explicit HidDevice(const std::string& path);
/**
* @if english
* @brief Destructor, automatically closes the device and releases resources
* @else
* @brief 析构函数,内部会自动关闭设备并释放资源
* @endif
*/
virtual ~HidDevice();
// Copying is disabled
HidDevice(const HidDevice&) = delete;
HidDevice& operator=(const HidDevice&) = delete;
// Move only
HidDevice(HidDevice&& other) noexcept;
HidDevice& operator=(HidDevice&& other) noexcept;
/**
* @if english
* @brief Get the device instance path
* @return device instance path
* @else
* @brief 获取设备实例路径
* @return 设备实例路径
* @endif
*/
const std::string& path() const;
/**
* @if english
* @brief Set the device instance path
* @param path device instance path
* @else
* @brief 设置设备实例路径
* @param path 设备实例路径
* @endif
*/
void setPath(const std::string& path);
/**
* @if english
* @brief Check if the device is already opened (only indicates the opened state, does not guarantee the connection state; use isConnected function to check the connection state)
* @return Returns true if the device is opened; otherwise, returns false
* @else
* @brief 判断设备是否已被打开(仅表示打开状态,不保证连接状态,若需检查连接状态请使用isConnected函数)
* @return 如果设备已经打开,则返回true;否则返回false
* @endif
*/
bool isOpen() const;
/**
* @if english
* @brief Check if the device is in a connected state, useful for determining if the device is disconnected
* @note Internally, this is determined based on the error code of the last device operation.
* It is recommended to use this function if a call to the button state retrieval function returns empty,
* to confirm whether the device has been disconnected
* @return Returns true if the device is connected; otherwise, returns false
* @else
* @brief 判断设备是否处于已连接状态,可用于判断设备是否断连
* @note 内部通过上一次设备操作的错误码判断。建议在执行获取按钮状态函数后返回值为空时,通过此函数加以判断设备是否已经断开连接
* @return 如果设备已连接,则返回true;否则返回false
* @endif
*/
bool isConnected() const;
/**
* @if english
* @brief Open the device using the device instance path
* @return Returns true if the device was successfully opened; otherwise, returns false
* @else
* @brief 通过设备实例路径打开设备
* @return 如果设备打开成功,则返回true;否则返回false
* @endif
*/
virtual bool open();
/**
* @if english
* @brief Close the device
* @else
* @brief 关闭设备
* @endif
*/
virtual void close();
private:
class HidDevicePrivate* m_impl;
protected:
size_t getReportSize() const;
size_t getNumInputReports() const;
std::vector<uint8_t> read(size_t size) const;
std::vector<uint8_t> readLatestReport() const;
};
inline HidDevice::HidDevice(HidDevice&& other) noexcept : m_impl() { std::swap(m_impl, other.m_impl); }
inline HidDevice& HidDevice::operator=(HidDevice&& other) noexcept
{
if (this != &other)
{
std::swap(m_impl, other.m_impl);
}
return *this;
}
}
#ifndef HID_STRUCT_H
#define HID_STRUCT_H
#include <vector>
enum ROCKERMODE
{
UNKNOW = 0,
CROSSKEY,
KEY,
};
enum KNOBMODE
{
UNKNOWKNOB = 0,
KNOB,
MULTIKEY,
};
enum CLUTCHPICKMODE
{
UNKNOWCLUTCHPICK = 0,
COMPOSITEAXIS,
INDEPENDENTAXIS,
};
enum ROCKEREDIR
{
UP = 0,
RIGHTUP,
RIGHT,
RIGHTDOWM,
DOWM,
LEFTDOWM,
LEFT,
LEFTUP,
NONEDIR
};
//enum ERRORCODE
//{
// NORMAL = 0,
// NOINSTALLSDK,
// NODEVICES,
// COLLECTIONCYCLEDATALOSS,
//};
struct HIDButton
{
bool startValue = 0;
int changeNum = -1;
bool isPressed() const{
if(changeNum == -1)
return startValue;
return startValue || changeNum;
};
bool lastPressState() const{
return (changeNum & 1) == 0 || changeNum == -1 ? startValue : !startValue;
}
int pressNum(){
return changeNum == -1 ? 0 : changeNum/2 + changeNum&0x01;
}
};
//没有操作为空,8同样表示没有操作,但vector第一个数据不会为8
struct HIDRocker
{
std::vector<ROCKEREDIR> rockerDatas;
ROCKEREDIR lastDir() const
{
return rockerDatas.empty()? NONEDIR : rockerDatas.back();
}
};
struct HIDKnob
{
std::vector<int> knobDatas;
int getOffset() const{
int res = 0;
for(auto var : knobDatas)
res += var;
return res;
}
};
struct HIDMultiSegmentKnob : public HIDKnob
{
int getLastKey() const
{
return knobDatas.empty()? 0 : knobDatas.back();
}
};
enum GEAR
{
R = 0,
GEAR1st,
GEAR2nd,
GEAR3rf,
GEAR4th,
GEAR5th,
GEAR6th,
GEAR7th,
GEAR0th,
};
struct HIDData
{
float fSteeringWheelAngle = NAN; // 方向盘角度
float fSteeringWheelVelocity = NAN; // 方向盘速度(度每秒,基座固件版本需大于等于1.2.4.x)
float fSteeringWheelAcceleration = NAN; // 方向盘加速度(度每二次方秒,基座固件版本需大于等于1.2.4.x)
int16_t steeringWheelAxle = 0x8001;
int16_t clutchSynthesisShaft = 0x8001;
int16_t clutchIndependentShaftL = 0x8001;
int16_t clutchIndependentShaftR = 0x8001;
int16_t throttle = 0x8000;
int16_t clutch = 0x8000;
int16_t brake = 0x8000;
int16_t handbrake = 0x8001;
HIDButton buttons[128];
HIDRocker leftRocker5_8;
HIDRocker rightRocker9_12;
HIDKnob knobL45_46;
HIDKnob knobR47_48;
// HIDKnob knob2L;
// HIDKnob knob2R;
HIDMultiSegmentKnob multiSegmentKnob26_27or53_64;
HIDMultiSegmentKnob multiSegmentKnob28_29or65_76;
HIDMultiSegmentKnob multiSegmentKnob30_31or77_88;
HIDMultiSegmentKnob multiSegmentKnob39_40or89_100;
HIDMultiSegmentKnob multiSegmentKnob43_44or101_112;
GEAR shift = GEAR0th;
bool buttonHandbrake = false;
};
#endif // HID_STRUCT_H
#pragma once
#ifdef DLL_EXPORTS
#define WIN_API __declspec(dllexport)
#else
#define WIN_API __declspec(dllimport)
#endif
This diff is collapsed.
#pragma once
#include "./hid_device.h"
namespace moza {
class WIN_API ShifterDevice : public HidDevice
{
public:
using HidDevice::HidDevice;
/**
* @if english
* @brief Get the current gear of the shifter
* @return The current gear
* @retval -1 for reverse gear, 0 for neutral gear, and 1-7 for forward gears.
* During gear shifting, since the neutral position is passed through, a value of 0 may appear during this process.
* @warning This function waits for the HID report while reading data, until valid data is received or an error occurs.
This means the execution time of this function may be relatively long, and it is not recommended to call it in the main thread.
* @else
* @brief 获取换挡器的当前档位
* @return 当前档位
* @retval -1为倒档,0为空档,1-7为前进档。在切换档位时,因为中途会经过空档,所以在这个过程中会出现0的值
* @warning 该函数在读取数据时会等待HID报文,直到读取到有效数据或者发生错误。这意味着该函数的执行时间可能会比较长,不适合在主线程中调用
* @endif
*/
int getCurrentGear() const;
};
}
#pragma once
#include "./hid_device.h"
enum ERRORCODE;
namespace moza {
/**
* @if english
* @brief Enumeration of switch indices for MOZA Switches devices
* @note The enumeration values can be directly used as indices for the switch state array
* @note Toggle Switch is a latching switch (it remains triggered after being pressed);
* Rotary Switch is a position switch (it stays at different positions or settings).
* @else
* @brief MOZA Switches(多功能组合开关)设备的开关索引枚举
* @note 枚举值可直接用作开关状态数组的下标
* @note Toggle Switch是保持型开关(按下触发后一直保持);Rotary Switch是拨动型开关(保持在不同挡位/位置)
* @endif
*/
enum SwitchesIndex
{
HeadlightOff = 0, /* Rotary Switch 1 */
HeadlightPark, /* Rotary Switch 1 */
HeadlightHigh, /* Rotary Switch 1 */
HighBeam, /* Rotary Switch 2 special */
FlasherOff, /* Rotary Switch 2 special */
Flasher, /* Rotary Switch 2 special */
FogLight, /* Toggle Switch */
TurnRight, /* Rotary Switch 3 */
TurnSignalOff, /* Rotary Switch 3 */
TurnLeft, /* Rotary Switch 3 */
RearWiperOff, /* Rotary Switch 4 */
RearWiperSpray, /* Rotary Switch 4 */
RearWiperWash, /* Rotary Switch 4 toggle */
WiperSensitivity1, /* Rotary Switch 5 */
WiperSensitivity2, /* Rotary Switch 5 */
WiperSensitivity3, /* Rotary Switch 5 */
WiperSensitivity4, /* Rotary Switch 5 */
WiperSensitivity5, /* Rotary Switch 5 */
FrontWiperWash, /* Toggle Switch */
FrontWiperSingle, /* Rotary Switch 6 toggle */
FrontWiperOff, /* Rotary Switch 6 */
FrontWiperInterval, /* Rotary Switch 6 */
FrontWiperLow, /* Rotary Switch 6 */
FrontWiperHigh, /* Rotary Switch 6 */
CruiseOnOff, /* Toggle Switch */
CruiseDecrease, /* Toggle Switch */
CruiseIncrease, /* Toggle Switch */
CruiseCancel, /* Toggle Switch */
MAX_SWITCHES_INDEX
};
/**
* @if english
* @brief Group of switches, specifically for Rotary Switches. Each group corresponds to the different positions of a single Rotary Switch
* @else
* @brief 开关组,仅针对Rotary Switch旋转型开关,每组就是一个Rotary Switch对应在不同位置
* @endif
*/
const static std::vector<SwitchesIndex> SWITCHES_GROUPS[] = {
{HeadlightOff, HeadlightPark, HeadlightHigh}, /* Rotary Switch Group 1 */
{HighBeam, FlasherOff, Flasher}, /* Rotary Switch Group 2 */
{TurnRight, TurnSignalOff, TurnLeft}, /* Rotary Switch Group 3 */
{RearWiperOff, RearWiperSpray, RearWiperWash}, /* Rotary Switch Group 4 */
{WiperSensitivity1, WiperSensitivity2, WiperSensitivity3, WiperSensitivity4, WiperSensitivity5}, /* Rotary Switch Group 5 */
{FrontWiperSingle, FrontWiperOff, FrontWiperInterval, FrontWiperLow, FrontWiperHigh}, /* Rotary Switch Group 6 */
};
/**
* @if english
* @brief MOZA Switches device class
* @else
* @brief MOZA Switches(多功能组合开关)设备类
* @endif
*/
class WIN_API SwitchesDevice : public HidDevice
{
friend class SwitchesDevicePrivate;
public:
/**
* @if english
* @brief Default constructor
* @else
* @brief 无参构造
* @endif
*/
SwitchesDevice();
/**
* @if english
* @brief Constructs with a device instance path
* @param path device instance path
* @else
* @brief 从一个设备实例路径构造
* @param path 设备实例路径
* @endif
*/
explicit SwitchesDevice(const std::string& path);
~SwitchesDevice() override;
SwitchesDevice(const SwitchesDevice&) = delete;
SwitchesDevice& operator=(const SwitchesDevice&) = delete;
SwitchesDevice(SwitchesDevice&& other) noexcept;
SwitchesDevice& operator=(SwitchesDevice&& other) noexcept;
/**
* @if english
* @brief Open the device
* @return True if the device is successfully opened; false otherwise
* @else
* @brief 打开设备
* @return 设备是否成功打开
* @endif
*/
bool open() override;
/**
* @if english
* @brief Close the device
* @else
* @brief 关闭设备
* @endif
*/
void close() override;
/**
* @if english
* @brief Check if the rotary switch states are ready
* @return Returns true if the states are ready; otherwise, returns false
* @note This function is non-blocking when reading data
* @else
* @brief 检查旋转型开关的状态是否准备完毕
* @return 如果准备完毕则返回true;否则返回false
* @note 该函数在读取数据是非阻塞的
* @endif
*/
bool isRotarySwitchStateReady() const;
/**
* @if english
* @brief Get the current switches state of the device
* @param err Error code
* @return A vector of device switches state
* @retval Each element in the vector represents the state of a switch.
* The index starts from 0 and corresponds to the switch numbers in MOZA Pit House minus 1.
* Alternatively, refer to the moza::SwitchesIndex enum values.
* @note Some switch state data needs to be retrieved from Pit House. Establishing communication between the SDK and Pit House takes time to complete.
* Therefore, the switch states obtained by this function may be inaccurate before the communication is fully established.
* You can check whether the preparation is complete by using the return value of the `isRotarySwitchStateReady` function or the error code `err`.
* This function is non-blocking when reading data
* @else
* @brief 获取设备当前的开关状态
* @param err 错误码
* @return 设备的开关状态数组
* @retval 数组的每一项都表示一个开关的状态。下标从0开始,对应MOZA Pit House中的开关编号-1,也可以参考moza::SwitchesIndex枚举值
* @note 开关状态部分数据需要从PitHouse获取,而SDK与PitHouse的通信建立需要一定的时间才能完成,所以在通信未完成时该函数获取到的开关状态不准确;
* 可以通过`isRotarySwitchStateReady`函数返回值或者错误码`err`判断是否准备完毕;
* 该函数在读取数据是非阻塞的。
* @endif
*/
std::vector<uint8_t> getStateInfo(ERRORCODE& err) const;
/**
* @if english
* @brief Get the current switches state of the device (retrieved solely from the HID reports sent by the device)
* @return A vector of device switches state
* @retval Each element in the vector represents the state of a switch.
* The index starts from 0 and corresponds to the switch numbers in MOZA Pit House minus 1.
* Alternatively, refer to the moza::SwitchesIndex enum values.
* @note It is generally recommended to use the `getStateInfo` function to obtain the current switch state of the device.
* This function retrieves the switch state solely by reading the HID report, which does not include the device's initial state.
* It can be used when the device firmware is not supported or when PitHouse cannot be connected.
* This function is non-blocking when reading data.
* @else
* @brief 获取设备当前的开关状态(仅通过设备发送的HID报告来获取)
* @return 设备的开关状态数组
* @retval 数组的每一项都表示一个开关的状态。下标从0开始,对应MOZA Pit House中的开关编号-1,也可以参考moza::SwitchesIndex枚举值
* @note 通常情况下建议使用`getStateInfo`函数来获取当前设备的开关状态。该函数仅通过读取HID报告来获取开关状态,这将无法获取到设备的初始状态。可以在设备固件不支持时或无法连接PitHouse时使用该函数。
* 该函数在读取数据是非阻塞的。
* @endif
*/
std::vector<uint8_t> getStateInfoByHid() const;
private:
SwitchesDevicePrivate* m_impl;
};
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment