Modernization of the legacy vJoy-based RIO cockpit interface for Win10/11, removing the vJoy dependency in favor of a custom VHF/UMDF HID driver, rewritten in C#/.NET 8 as a background tray app with per-game profiles. - Reorganize: legacy C++ -> legacy/, cockpit art -> docs/reference/ - RioJoy.sln: src/RioJoy.Core (lib) + src/RioJoy.Tray (tray app), net8.0-windows x64 - driver/ placeholder for the RioGamepad WDK driver - docs/PLAN.md (7-phase plan; profiles + serial-yield model) - docs/PROTOCOL.md (RIO wire format + iRIO input-map reference) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2553 lines
64 KiB
C++
2553 lines
64 KiB
C++
// riovjoy2.cpp : This file contains the 'main' function. Program execution begins and ends there.
|
|
//
|
|
|
|
|
|
#include <windows.h>
|
|
#include <iostream>
|
|
#include <thread>
|
|
#include <iomanip>
|
|
#include "SimpleIni.h"
|
|
#include "vjoyinterface.h"
|
|
#include "public.h"
|
|
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#pragma warning(disable : 4996)
|
|
#define ESC 27
|
|
|
|
class C232Comm
|
|
{
|
|
private:
|
|
HANDLE m_hCom;
|
|
int m_nPort;
|
|
DCB m_dcb;
|
|
COMMTIMEOUTS m_commTimeOuts;
|
|
COMMCONFIG m_cc;
|
|
COMMPROP m_cp;
|
|
public:
|
|
C232Comm();
|
|
virtual ~C232Comm();
|
|
BOOL IsValid() const { return m_hCom != INVALID_HANDLE_VALUE; }
|
|
BOOL Open(int nPort, DWORD dwBaudRate = CBR_9600, BYTE dwParity = NOPARITY, BYTE bByteSize = 8, BYTE dwStopBits = ONESTOPBIT);
|
|
void Close();
|
|
int rxCom(void* pData, int nLen);
|
|
BOOL rxComLoop(void* pData, int nLen);
|
|
int txCom(const void* pData, int nLen);
|
|
BOOL txComLoop(const void* pData, int nLen);
|
|
void locProcessCommError();
|
|
int CommunicationConfig(HWND hWndParent);
|
|
};
|
|
const char g_szPortFormat[] = "\\\\.\\com%d"; // fh trying to resolve C2664 compile error by explicitly converting the char[32] sz to LPCWSTR lpwsz
|
|
|
|
class CPlasma : public C232Comm
|
|
{
|
|
public:
|
|
DWORD m_dwStart;
|
|
int m_nOldRank;
|
|
int m_nOldScore;
|
|
enum {
|
|
ATTR_NORMAL = 0,
|
|
ATTR_HALF = 1,
|
|
ATTR_LINE = 2,
|
|
ATTR_REVERSE = 4,
|
|
ATTR_FLASH = 8,
|
|
ATTR_PROTECTED = 128
|
|
};
|
|
enum { PLASMA_OFF, PLASMA_ON, PLASMA_FLASH };
|
|
|
|
int m_nGlobalState;
|
|
// Time m_timeLastPlasma2;
|
|
public:
|
|
CPlasma() {
|
|
Open(2);
|
|
m_dwStart = GetTickCount();
|
|
PlasmaClear();
|
|
PlasmaCursor(CPlasma::PLASMA_OFF);
|
|
m_nOldRank = INT_MIN;
|
|
m_nOldScore = INT_MIN;
|
|
|
|
m_nGlobalState = -1;
|
|
}
|
|
virtual ~CPlasma() {
|
|
PlasmaCursor(CPlasma::PLASMA_OFF);
|
|
PlasmaClear();
|
|
Close();
|
|
}
|
|
public:
|
|
void SendPacket(const BYTE* pbPacket, char nLen);
|
|
void PlasmaClear();
|
|
|
|
void PlasmaCursor(char n = PLASMA_OFF);
|
|
void PlasmaCursorHome();
|
|
void PlasmaCursorX(char n);
|
|
void PlasmaCursorY(char n);
|
|
|
|
void PlasmaFontAttr(char n);
|
|
void PlasmaFont(char n);
|
|
|
|
void PlasmaText(const char* szText);
|
|
POINT GetFontSize(int Font);
|
|
|
|
void PlasmaBoxDraw(RECT tag);
|
|
void PlasmaBoxFill(RECT tag);
|
|
void PlasmaPosText(const char* szText, char x = 0, char y = 0, char Attr = 0, char Font = 5);
|
|
void PlasmaScoreDraw(const char* Rank, const char* Score, BOOL bFlag);
|
|
|
|
void DoPlasma(char nMode);
|
|
private:
|
|
void PlasmaDisplay(int Gamestate, const char* szName, const char* Rank, const char* Score);
|
|
};
|
|
|
|
const char* sINI{ new char[32] {"c:\\games\\RIO\\RIO.ini"} };
|
|
const char* sGreeting{ new char[16] {"RIOvJoy v.03"} };
|
|
const char* sPlayer{ new char[16] {"Player"} };
|
|
const char* sDisplay{ new char[24] {"Pod PowerUp Detected"} };
|
|
const char* newDesktop{ new char[32] {"C:\\games\\RIO\\VWE2.bmp"} };
|
|
bool* invertX{ new bool{0} };
|
|
bool* invertY{ new bool{0} };
|
|
bool* invertZ{ new bool{0} };
|
|
bool* invertXR{ new bool{0} };
|
|
bool* invertYR{ new bool{0} };
|
|
bool* enableZR{ new bool{0} };
|
|
bool* invertZR{ new bool{0} };
|
|
int* iRIO{ new int[112] {} };
|
|
|
|
LONG g_Throttle;
|
|
LONG g_LeftPedal;
|
|
LONG g_RightPedal;
|
|
LONG g_JoystickY;
|
|
LONG g_JoystickX;
|
|
LONG g_Rudder;
|
|
LONG g_ThrottleResult = -1L;
|
|
LONG g_HatResult = -1L;
|
|
|
|
LONG g_ThrottleStart = INT_MIN;
|
|
LONG g_LeftPedalStart = INT_MAX;
|
|
LONG g_RightPedalStart = INT_MAX;
|
|
LONG g_JoystickXLast = 16383L;
|
|
LONG g_JoystickYLast = 16383L;
|
|
LONG g_ThrottleLast = 16383L;
|
|
LONG g_LeftPedalLast = 16383L;
|
|
LONG g_RightPedalLast = 16383L;
|
|
LONG g_RudderLast = 16838L;
|
|
LONG maxt = 0;
|
|
LONG mint = 0;
|
|
LONG maxl = 0;
|
|
LONG minl = 0;
|
|
LONG maxr = 0;
|
|
LONG minr = 0;
|
|
LONG maxx = 0;
|
|
LONG minx = 0;
|
|
LONG maxy = 0;
|
|
LONG miny = 0;
|
|
BOOL g_rawaxes = false;
|
|
bool enablecount = false;
|
|
int count = 0;
|
|
int now = GetTickCount();
|
|
int last = GetTickCount();
|
|
int lastAnalogEvent = GetTickCount();
|
|
|
|
|
|
static int g_nOpenComState = 0;
|
|
|
|
#define ASCII_XON 0x11
|
|
#define ASCII_XOFF 0x13
|
|
#define ACK_CHAR 0xFC
|
|
#define NAK_CHAR 0xFD
|
|
#define RESTART_CHAR 0xFE
|
|
#define IDLE_CHAR 0xFF
|
|
|
|
static HANDLE g_hCom = INVALID_HANDLE_VALUE;
|
|
static OVERLAPPED wos;
|
|
static OVERLAPPED ros;
|
|
static OVERLAPPED wcos;//WaitCommEvent
|
|
static HANDLE g_hCommWatchThread = NULL;
|
|
static HANDLE g_hWatchEvent = NULL;
|
|
static HANDLE g_hLampEvent = NULL;
|
|
static DWORD g_dwThreadID;
|
|
static BYTE packetbytes[16];
|
|
static int packetbyteremain = 0;
|
|
static int chinpacket = 0;
|
|
static int g_naButtonThrottles[8];
|
|
static BYTE g_baRIOLengthsA[] = {
|
|
0, // 0.CheckRequest
|
|
0, // 1.VersionRequest
|
|
0, // 2.AnalogRequest
|
|
1, // 3.ResetRequest
|
|
2, // 4.LampRequest
|
|
2, // 5.CheckReply
|
|
2, // 6.VersionReply
|
|
10, // 7.AnalogReply
|
|
1, // 8.ButtonPressed
|
|
1, // 9.ButtonReleased
|
|
2, // 10.KeyPressed
|
|
2, // 11.KeyReleased
|
|
1, // 12.TestModeChange
|
|
};
|
|
int g_nRIOPacketCountA = (sizeof(g_baRIOLengthsA) / sizeof(g_baRIOLengthsA[0]));
|
|
BYTE* g_pbRIOLengths = NULL;
|
|
int g_nRIOPacketCount = 0;
|
|
enum RIOCommand {
|
|
rio_CheckRequest = 0x80, // 128
|
|
rio_VersionRequest, // 129
|
|
rio_AnalogRequest, // 130
|
|
rio_ResetRequest, // 131
|
|
rio_LampRequest, // 132
|
|
rio_CheckReply, // 133
|
|
rio_VersionReply, // 134
|
|
rio_AnalogReply, // 135
|
|
rio_ButtonPressed, // 136
|
|
rio_ButtonReleased, // 137
|
|
rio_KeyPressed, // 138
|
|
rio_KeyReleased, // 139
|
|
rio_TestModeChange, // 140
|
|
};
|
|
enum RIOStatusType {
|
|
BoardOk,
|
|
BoardMissing,
|
|
BoardBad,
|
|
LampBad,
|
|
RestartCount,
|
|
AbandonCount,
|
|
FullBufferCount
|
|
};
|
|
enum LampState {
|
|
solid = 0, flashSlow = 1, flashMed = 2, flashFast = 3,
|
|
state1Off = 0x00, state1Dim = 0x04, state1Bright = 0x0C,
|
|
state2Off = 0x00, state2Dim = 0x10, state2Bright = 0x30,
|
|
};
|
|
enum {
|
|
LampSolidOff = solid + state1Off + state2Off,
|
|
LampSolidDim = solid + state1Dim + state2Dim,
|
|
LampSolidBright = solid + state1Bright + state2Bright
|
|
};
|
|
DWORD g_dwLastBytesToWrite = 0;
|
|
DWORD g_dCancelCount = 0;
|
|
BYTE g_baLastPacket[20];
|
|
static bool _continue;
|
|
|
|
CSimpleIniA ini = new CSimpleIni;
|
|
|
|
///////////////////////////
|
|
// forward declarations //
|
|
///////////////////////////
|
|
void PrintMenu();
|
|
bool ReadyvJoy();
|
|
char* getCmdOption(char** begin, char** end, const std::string& option);
|
|
bool cmdOptionExists(char** begin, char** end, const std::string& option);
|
|
|
|
BOOL SetupConnection(HANDLE hCom);
|
|
HANDLE OpenConnection(int port);
|
|
BOOL CloseConnection();
|
|
void TransmitCommChar2(HANDLE g_hCom, int ch);
|
|
int ReadCommBlock(LPSTR lpszBlock, int nMaxLength);
|
|
BOOL WriteCommBlock(LPSTR lpByte, DWORD dwBytesToWrite);
|
|
DWORD FAR PASCAL CommWatchProc(LPSTR lpData);
|
|
short CombinePair(BYTE low_value, BYTE high_value);
|
|
void AnalogEvent(BYTE* pData);
|
|
void ButtonEvent(BYTE* pData);
|
|
void KeypadEvent(BYTE* pData);
|
|
BOOL SendPacket(const BYTE* ba, int length);
|
|
BOOL SendCommand(const BYTE* pbPacket);
|
|
BOOL SetLamp(int lampNumber, int state);
|
|
void AnalogAllReset();
|
|
void GeneralReset();
|
|
void ResetThrottle();
|
|
void ResetLeftPedal();
|
|
void ResetRightPedal();
|
|
void ResetVerticalJoystick();
|
|
void ResetHorizontalJoystick();
|
|
void RequestCheck();
|
|
void CheckReply(BYTE* pData);
|
|
const char* GetLampName(int lamp_number);
|
|
const char* GetBoardName(int board_number);
|
|
void RequestVersion();
|
|
void VersionReply(BYTE* pData);
|
|
void RequestAnalogUpdate();
|
|
void UpdateJoystick();
|
|
void UpdateThrottle();
|
|
void UpdatePadal();
|
|
void TestModeChange(BYTE* pData);
|
|
void RIOcmd(unsigned char ucValue);
|
|
static void Press_V2(unsigned char iAddress);
|
|
static void Release_V2(unsigned char iAddress);
|
|
static void ModKeySHIFT(bool bIsPressed);
|
|
static void ModKeyCTRL(bool bIsPressed);
|
|
static void ModKeyALT(bool bIsPressed);
|
|
static void Key(unsigned char ucwVk, bool bIsPressed, bool bIsExtended);
|
|
static void Mouse(unsigned char ucValue, bool bIsPressed);
|
|
|
|
///////////////////
|
|
// Main function //
|
|
///////////////////
|
|
int main(int argc, char** argv)
|
|
{
|
|
|
|
_continue = true; // the show stopper set to false and watch the world burn
|
|
std::cout << "Useage: riovjoy.exe -f [ini file path] -p [\"Pilot\"] [console]\n";
|
|
|
|
// check the command line for any arguments
|
|
if (cmdOptionExists(argv, argv + argc, "console"))
|
|
{
|
|
std::cout << "console enabled" << std::endl;
|
|
}
|
|
else {
|
|
HWND hWnd = GetConsoleWindow();
|
|
ShowWindow(hWnd, SW_MINIMIZE);
|
|
}
|
|
|
|
char* inifilename = getCmdOption(argv, argv + argc, "-f");
|
|
if (inifilename)
|
|
{
|
|
std::cout << "Will try " << inifilename << std::endl;
|
|
sINI = inifilename;
|
|
}
|
|
else {
|
|
std::cout << "Will try default C:\\games\\RIO\\RIO.ini\n";
|
|
}
|
|
|
|
char* oPlayer{ new char[16] {} };
|
|
oPlayer = getCmdOption(argv, argv + argc, "-p");
|
|
if (oPlayer)
|
|
{
|
|
std::cout << "player name supplied: " << oPlayer << std::endl;
|
|
sPlayer = oPlayer;
|
|
}
|
|
else {
|
|
std::cout << "no player name supplied\n";
|
|
}
|
|
|
|
//Read in INI config data
|
|
switch (ini.LoadFile(sINI))
|
|
{
|
|
case SI_OK:
|
|
std::cout << "Found: " << sINI << std::endl;
|
|
break;
|
|
case SI_UPDATED:
|
|
std::cout << "updated INI file\n";
|
|
break;
|
|
case SI_INSERTED:
|
|
std::cout << "A new value was inserted\n";
|
|
break;
|
|
case SI_FAIL:
|
|
std::cout << "INI Generic failure\n";
|
|
break;
|
|
case SI_NOMEM:
|
|
std::cout << "Ran Out of memory error loading INI\n";
|
|
break;
|
|
case SI_FILE:
|
|
std::cout << "INI File not found\n";
|
|
break;
|
|
};
|
|
|
|
// set all of the varables
|
|
newDesktop = ini.GetValue("Desktop", "File", "C:\\games\\RIO\\VWE2.bmp", 0);
|
|
sGreeting = ini.GetValue("Plasma", "Greeting", "RIOvJoy2 v.03", 0);
|
|
*invertX = ini.GetBoolValue("JoyStick", "invertX", false);
|
|
*invertY = ini.GetBoolValue("JoyStick", "invertY", false);
|
|
*invertZ = ini.GetBoolValue("JoyStick", "invertZ", false);
|
|
*invertXR = ini.GetBoolValue("JoyStick", "invertXR", false);
|
|
*invertYR = ini.GetBoolValue("JoyStick", "invertYR", false);
|
|
*enableZR = ini.GetBoolValue("JoyStick", "enableZR", true);
|
|
*invertZR = ini.GetBoolValue("JoyStick", "invertZR", false);
|
|
iRIO[0] = ini.GetLongValue("Buttons", "RIO00", 0x8049);
|
|
iRIO[1] = ini.GetLongValue("Buttons", "RIO01", 0x805A);
|
|
iRIO[2] = ini.GetLongValue("Buttons", "RIO02", 0x8055);
|
|
iRIO[3] = ini.GetLongValue("Buttons", "RIO03", 0x804C);
|
|
iRIO[4] = ini.GetLongValue("Buttons", "RIO04", 0x8926);
|
|
iRIO[5] = ini.GetLongValue("Buttons", "RIO05", 0x8928);
|
|
iRIO[6] = ini.GetLongValue("Buttons", "RIO06", 0x8927);
|
|
iRIO[7] = ini.GetLongValue("Buttons", "RIO07", 0x8925);
|
|
iRIO[8] = ini.GetLongValue("Buttons", "RIO08", 0x8051);
|
|
iRIO[9] = ini.GetLongValue("Buttons", "RIO09", 0x8059);
|
|
iRIO[10] = ini.GetLongValue("Buttons", "RIO0A", 0x8045);
|
|
iRIO[11] = ini.GetLongValue("Buttons", "RIO0B", 0x8052);
|
|
iRIO[12] = ini.GetLongValue("Buttons", "RIO0C", 0x8823);
|
|
iRIO[13] = ini.GetLongValue("Buttons", "RIO0D", 0x80BE);
|
|
iRIO[14] = ini.GetLongValue("Buttons", "RIO0E", 0x80BC);
|
|
iRIO[15] = ini.GetLongValue("Buttons", "RIO0F", 0x8824);
|
|
iRIO[16] = ini.GetLongValue("Buttons", "RIO10", 0xC000);
|
|
iRIO[17] = ini.GetLongValue("Buttons", "RIO11", 0xC001);
|
|
iRIO[18] = ini.GetLongValue("Buttons", "RIO12", 0xC002);
|
|
iRIO[19] = ini.GetLongValue("Buttons", "RIO13", 0xC003);
|
|
iRIO[20] = ini.GetLongValue("Buttons", "RIO14", 0xC004);
|
|
iRIO[21] = ini.GetLongValue("Buttons", "RIO15", 0x8009);
|
|
iRIO[22] = ini.GetLongValue("Buttons", "RIO16", 0x0);
|
|
iRIO[23] = ini.GetLongValue("Buttons", "RIO17", 0x0);
|
|
iRIO[24] = ini.GetLongValue("Buttons", "RIO18", 0x800D);
|
|
iRIO[25] = ini.GetLongValue("Buttons", "RIO19", 0x8012);
|
|
iRIO[26] = ini.GetLongValue("Buttons", "RIO1A", 0x8011);
|
|
iRIO[27] = ini.GetLongValue("Buttons", "RIO1B", 0x8010);
|
|
iRIO[28] = ini.GetLongValue("Buttons", "RIO1C", 0x88AF);
|
|
iRIO[29] = ini.GetLongValue("Buttons", "RIO1D", 0x88AE);
|
|
iRIO[30] = ini.GetLongValue("Buttons", "RIO1E", 0x0);
|
|
iRIO[31] = ini.GetLongValue("Buttons", "RIO1F", 0x0);
|
|
iRIO[32] = ini.GetLongValue("Buttons", "RIO20", 0x8827);
|
|
iRIO[33] = ini.GetLongValue("Buttons", "RIO21", 0x8828);
|
|
iRIO[34] = ini.GetLongValue("Buttons", "RIO22", 0x8826);
|
|
iRIO[35] = ini.GetLongValue("Buttons", "RIO23", 0x8825);
|
|
iRIO[36] = ini.GetLongValue("Buttons", "RIO24", 0x804E);
|
|
iRIO[37] = ini.GetLongValue("Buttons", "RIO25", 0x804D);
|
|
iRIO[38] = ini.GetLongValue("Buttons", "RIO26", 0x804C);
|
|
iRIO[39] = ini.GetLongValue("Buttons", "RIO27", 0x804B);
|
|
iRIO[40] = ini.GetLongValue("Buttons", "RIO28", 0x8056);
|
|
iRIO[41] = ini.GetLongValue("Buttons", "RIO29", 0x8055);
|
|
iRIO[42] = ini.GetLongValue("Buttons", "RIO2A", 0x8054);
|
|
iRIO[43] = ini.GetLongValue("Buttons", "RIO2B", 0x8053);
|
|
iRIO[44] = ini.GetLongValue("Buttons", "RIO2C", 0x804A);
|
|
iRIO[45] = ini.GetLongValue("Buttons", "RIO2D", 0x8049);
|
|
iRIO[46] = ini.GetLongValue("Buttons", "RIO2E", 0x8048);
|
|
iRIO[47] = ini.GetLongValue("Buttons", "RIO2F", 0x8047);
|
|
iRIO[48] = ini.GetLongValue("Buttons", "RIO30", 0x805A);
|
|
iRIO[49] = ini.GetLongValue("Buttons", "RIO31", 0x8059);
|
|
iRIO[50] = ini.GetLongValue("Buttons", "RIO32", 0x8058);
|
|
iRIO[51] = ini.GetLongValue("Buttons", "RIO33", 0x8057);
|
|
iRIO[52] = ini.GetLongValue("Buttons", "RIO34", 0x8052);
|
|
iRIO[53] = ini.GetLongValue("Buttons", "RIO35", 0x8051);
|
|
iRIO[54] = ini.GetLongValue("Buttons", "RIO36", 0x8050);
|
|
iRIO[55] = ini.GetLongValue("Buttons", "RIO37", 0x804F);
|
|
iRIO[56] = ini.GetLongValue("Buttons", "RIO38", 0x4000);
|
|
iRIO[57] = ini.GetLongValue("Buttons", "RIO39", 0x4001);
|
|
iRIO[58] = ini.GetLongValue("Buttons", "RIO3A", 0x4002);
|
|
iRIO[59] = ini.GetLongValue("Buttons", "RIO3B", 0x4003);
|
|
iRIO[60] = ini.GetLongValue("Buttons", "RIO3C", 0x4004);
|
|
iRIO[61] = ini.GetLongValue("Buttons", "RIO3D", 0x801B);
|
|
iRIO[62] = ini.GetLongValue("Buttons", "RIO3E", 0x4005);
|
|
iRIO[63] = ini.GetLongValue("Buttons", "RIO3F", 0x1008);
|
|
iRIO[64] = ini.GetLongValue("Buttons", "RIO40", 0x1001);
|
|
iRIO[65] = ini.GetLongValue("Buttons", "RIO41", 0x2002);
|
|
iRIO[66] = ini.GetLongValue("Buttons", "RIO42", 0x2000);
|
|
iRIO[67] = ini.GetLongValue("Buttons", "RIO43", 0x2001);
|
|
iRIO[68] = ini.GetLongValue("Buttons", "RIO44", 0x2003);
|
|
iRIO[69] = ini.GetLongValue("Buttons", "RIO45", 0x1004);
|
|
iRIO[70] = ini.GetLongValue("Buttons", "RIO46", 0x1003);
|
|
iRIO[71] = ini.GetLongValue("Buttons", "RIO47", 0x1002);
|
|
iRIO[72] = ini.GetLongValue("Buttons", "RIO48", 0x0);
|
|
iRIO[73] = ini.GetLongValue("Buttons", "RIO49", 0x0);
|
|
iRIO[74] = ini.GetLongValue("Buttons", "RIO4A", 0x0);
|
|
iRIO[75] = ini.GetLongValue("Buttons", "RIO4B", 0x0);
|
|
iRIO[76] = ini.GetLongValue("Buttons", "RIO4C", 0x0);
|
|
iRIO[77] = ini.GetLongValue("Buttons", "RIO4D", 0x0);
|
|
iRIO[78] = ini.GetLongValue("Buttons", "RIO4E", 0x0);
|
|
iRIO[79] = ini.GetLongValue("Buttons", "RIO4F", 0x0);
|
|
iRIO[80] = ini.GetLongValue("Buttons", "RIO50", 0x30);
|
|
iRIO[81] = ini.GetLongValue("Buttons", "RIO51", 0x31);
|
|
iRIO[82] = ini.GetLongValue("Buttons", "RIO52", 0x32);
|
|
iRIO[83] = ini.GetLongValue("Buttons", "RIO53", 0x33);
|
|
iRIO[84] = ini.GetLongValue("Buttons", "RIO54", 0x34);
|
|
iRIO[85] = ini.GetLongValue("Buttons", "RIO55", 0x35);
|
|
iRIO[86] = ini.GetLongValue("Buttons", "RIO56", 0x36);
|
|
iRIO[87] = ini.GetLongValue("Buttons", "RIO57", 0x37);
|
|
iRIO[88] = ini.GetLongValue("Buttons", "RIO58", 0x38);
|
|
iRIO[89] = ini.GetLongValue("Buttons", "RIO59", 0x39);
|
|
iRIO[90] = ini.GetLongValue("Buttons", "RIO5A", 0x41);
|
|
iRIO[91] = ini.GetLongValue("Buttons", "RIO5B", 0x42);
|
|
iRIO[92] = ini.GetLongValue("Buttons", "RIO5C", 0x43);
|
|
iRIO[93] = ini.GetLongValue("Buttons", "RIO5D", 0x44);
|
|
iRIO[94] = ini.GetLongValue("Buttons", "RIO5E", 0x45);
|
|
iRIO[95] = ini.GetLongValue("Buttons", "RIO5F", 0x46);
|
|
iRIO[96] = ini.GetLongValue("Buttons", "RIO60", 0x1051);
|
|
iRIO[97] = ini.GetLongValue("Buttons", "RIO61", 0x1052);
|
|
iRIO[98] = ini.GetLongValue("Buttons", "RIO62", 0x1053);
|
|
iRIO[99] = ini.GetLongValue("Buttons", "RIO63", 0x1054);
|
|
iRIO[100] = ini.GetLongValue("Buttons", "RIO64", 0x1055);
|
|
iRIO[101] = ini.GetLongValue("Buttons", "RIO65", 0x1056);
|
|
iRIO[102] = ini.GetLongValue("Buttons", "RIO66", 0x1057);
|
|
iRIO[103] = ini.GetLongValue("Buttons", "RIO67", 0x1058);
|
|
iRIO[104] = ini.GetLongValue("Buttons", "RIO68", 0x1059);
|
|
iRIO[105] = ini.GetLongValue("Buttons", "RIO69", 0x105A);
|
|
iRIO[106] = ini.GetLongValue("Buttons", "RIO6A", 0x105B);
|
|
iRIO[107] = ini.GetLongValue("Buttons", "RIO6B", 0x105C);
|
|
iRIO[108] = ini.GetLongValue("Buttons", "RIO6C", 0x105D);
|
|
iRIO[109] = ini.GetLongValue("Buttons", "RIO6D", 0x105E);
|
|
iRIO[110] = ini.GetLongValue("Buttons", "RIO6E", 0x105F);
|
|
iRIO[111] = ini.GetLongValue("Buttons", "RIO6F", 0x1060);
|
|
|
|
// if the Vjoy setup fails set _continue to false and let the program die.
|
|
if (!ReadyvJoy()) {
|
|
// std::cout << "vJoy setup failed, exiting\n";
|
|
// _continue = false;
|
|
// return 1;
|
|
}
|
|
|
|
//start the RIO shinanigins
|
|
|
|
OpenConnection(1);
|
|
RequestVersion();
|
|
RequestCheck();
|
|
|
|
|
|
Sleep(1000); // to allow the comread thread to get set up and rolling the analog updates
|
|
for (int b = 0; b < 72; b++) {
|
|
if (iRIO[b] & 0x8000)
|
|
SetLamp(b, LampSolidDim);
|
|
}
|
|
|
|
|
|
// try to open com2 to plasma display, else no display for you
|
|
CPlasma* _Plasma = new CPlasma;
|
|
if (_Plasma->IsValid()) {
|
|
std::cout << "Com2 open\n";
|
|
_Plasma->PlasmaPosText(sDisplay);
|
|
std::string playergreeting = std::string(sGreeting) + "," + std::string(sPlayer);
|
|
Sleep(3000);
|
|
_Plasma->PlasmaClear();
|
|
_Plasma->PlasmaPosText(playergreeting.c_str());
|
|
}
|
|
else {
|
|
std::cout << "No Display for you" << std::endl;
|
|
}
|
|
|
|
/////////////////////////////////////////////
|
|
// code block to check the desktop image //
|
|
/////////////////////////////////////////////
|
|
//get current desktop image, store for replacement
|
|
wchar_t * worgDesktop = new wchar_t[MAX_PATH];
|
|
SystemParametersInfoW(SPI_GETDESKWALLPAPER, MAX_PATH, worgDesktop, 0);
|
|
//set desktop from ini
|
|
int wchar_numnew = MultiByteToWideChar( CP_UTF8 , 0 , newDesktop , -1 , NULL , 0 );
|
|
wchar_t* wnewDesktop = new wchar_t[wchar_numnew];
|
|
MultiByteToWideChar(CP_UTF8, 0, newDesktop, -1, wnewDesktop, wchar_numnew);
|
|
bool setnewdesktop = SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, wnewDesktop, SPIF_UPDATEINIFILE);
|
|
wnewDesktop = nullptr;
|
|
/////////////////////////////////////////////
|
|
|
|
//Main while wait loop
|
|
while (_continue) // wait loop
|
|
{
|
|
|
|
PrintMenu();
|
|
|
|
}
|
|
|
|
// quit signal clean up
|
|
// close com2 to plasma
|
|
if (_Plasma->IsValid()) {
|
|
std::cout << "Closing Com2 to Plasma\n";
|
|
_Plasma->~CPlasma();
|
|
}
|
|
|
|
// end RIO
|
|
GeneralReset();
|
|
CloseConnection();
|
|
|
|
|
|
// relinquish vjoy device
|
|
RelinquishVJD(1);
|
|
std::cout << "Relinquished vJoy Device\n";
|
|
|
|
// set desktop back to original
|
|
SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, worgDesktop, SPIF_UPDATEINIFILE);
|
|
worgDesktop = nullptr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/////////////////
|
|
// Functions //
|
|
/////////////////
|
|
|
|
void PrintMenu()
|
|
{
|
|
char myChoice = 'Z';
|
|
|
|
std::cout << "********** Menu **********" << std::endl;
|
|
std::cout << "(0): Reset all analog axes" << std::endl;
|
|
std::cout << "(1): Reset Throttle analog axes" << std::endl;
|
|
std::cout << "(2): Reset LeftPedal analog axes" << std::endl;
|
|
std::cout << "(3): Reset RightPedal analog axes" << std::endl;
|
|
std::cout << "(4): Reset VerticalJoystick analog axes" << std::endl;
|
|
std::cout << "(5): Reset HorizontalJoystick analog axes" << std::endl;
|
|
std::cout << "(6): Reset Digital" << std::endl;
|
|
std::cout << "(7): RequestVersion and status" << std::endl;
|
|
std::cout << "(8): enable/disable axes readout" << std::endl;
|
|
std::cout << "(9): enable/disable analog Frequency readout" << std::endl;
|
|
std::cout << "(Q): quit" << std::endl;
|
|
std::cin >> myChoice;
|
|
|
|
switch (myChoice)
|
|
{
|
|
case '0':
|
|
RIOcmd(0);
|
|
break;
|
|
case '1':
|
|
RIOcmd(1);
|
|
break;
|
|
case '2':
|
|
RIOcmd(2);
|
|
break;
|
|
case '3':
|
|
RIOcmd(3);
|
|
break;
|
|
case '4':
|
|
RIOcmd(4);
|
|
break;
|
|
case '5':
|
|
RIOcmd(5);
|
|
break;
|
|
case '6':
|
|
RIOcmd(6);
|
|
break;
|
|
case '7':
|
|
RIOcmd(7);
|
|
break;
|
|
case '8':
|
|
RIOcmd(8);
|
|
break;
|
|
case '9':
|
|
RIOcmd(9);
|
|
break;
|
|
case 'Q':
|
|
case 'q':
|
|
_continue = false;
|
|
std::cout << "Closing Application" << std::endl;
|
|
break;
|
|
default:
|
|
std::cout << "ERROR! You have selected an invalid choice." << std::endl;
|
|
break;
|
|
}
|
|
}
|
|
bool ReadyvJoy() // make ready the vJoy interface
|
|
{
|
|
BYTE id = 1; // ID of the target vjoy device (Default is 1)
|
|
UINT iInterface = 1; // Default target vJoy device
|
|
// WORD VerDll, VerDrv;
|
|
|
|
if (!vJoyEnabled())
|
|
{
|
|
std::cout << "vJoy driver not enabled: Failed Getting vJoy attributes.\n";
|
|
}
|
|
else
|
|
{
|
|
// std::cout << "vJoyEnabled on this system\n";
|
|
};
|
|
|
|
//if (!DriverMatch(&VerDll, &VerDrv)) {
|
|
// std::cout << "vJoy Driver version " << VerDrv << " does not match vJoyInterface DLL version " << VerDll << std::endl;
|
|
//}
|
|
//else {
|
|
// std::cout << "Driver and DLL match version" << VerDrv;
|
|
//};
|
|
|
|
// Get the state of the requested device
|
|
VjdStat status = GetVJDStatus(iInterface);
|
|
switch (status)
|
|
{
|
|
case VJD_STAT_OWN:
|
|
// std::cout << "vJoy Device " << iInterface << " is already owned by this feeder";
|
|
break;
|
|
case VJD_STAT_FREE:
|
|
// std::cout << "vJoy Device " << iInterface << " is free";
|
|
break;
|
|
case VJD_STAT_BUSY:
|
|
std::cout << "vJoy Device " << iInterface << " is already owned by another feeder Cannot continue";
|
|
return false;
|
|
break;
|
|
case VJD_STAT_MISS:
|
|
std::cout << "vJoy Device " << iInterface << " is not installed or disabled\nCannot continue";
|
|
return false;
|
|
break;
|
|
default:
|
|
std::cout << "vJoy Device " << iInterface << " general error\nCannot continue";
|
|
return false;
|
|
break;
|
|
};
|
|
// Check which axes are supported
|
|
BOOL AxisX = GetVJDAxisExist(iInterface, HID_USAGE_X);
|
|
BOOL AxisY = GetVJDAxisExist(iInterface, HID_USAGE_Y);
|
|
BOOL AxisZ = GetVJDAxisExist(iInterface, HID_USAGE_Z);
|
|
BOOL AxisRX = GetVJDAxisExist(iInterface, HID_USAGE_RX);
|
|
BOOL AxisRY = GetVJDAxisExist(iInterface, HID_USAGE_RY);
|
|
BOOL AxisRZ = GetVJDAxisExist(iInterface, HID_USAGE_RZ);
|
|
// Get the number of buttons supported and hats by this vJoy device
|
|
int nButtons = GetVJDButtonNumber(iInterface);
|
|
int DiscPovNumber = GetVJDDiscPovNumber(iInterface);
|
|
|
|
if (!AxisX || !AxisY || !AxisZ || !AxisRX || !AxisRY || !AxisRZ || DiscPovNumber != 1 || nButtons != 96) {
|
|
std::cout << "vJoy Interface not corectly setup, Please enable X,Y,Z,RX,RY,RZ, one descrete Pov Hat, and 96 buttons";
|
|
// return false;
|
|
}
|
|
|
|
// Acquire the target
|
|
if ((status == VJD_STAT_OWN) || ((status == VJD_STAT_FREE) && (!AcquireVJD(iInterface))))
|
|
{
|
|
std::cout << "Failed to acquire vJoy device, exiting";
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
std::cout << "Acquired: vJoy device." << std::endl;
|
|
ResetVJD(1);
|
|
return true;
|
|
}
|
|
}
|
|
char* getCmdOption(char** begin, char** end, const std::string& option)
|
|
{
|
|
char** itr = std::find(begin, end, option);
|
|
if (itr != end && ++itr != end)
|
|
{
|
|
return *itr;
|
|
}
|
|
return 0;
|
|
}
|
|
bool cmdOptionExists(char** begin, char** end, const std::string& option)
|
|
{
|
|
return std::find(begin, end, option) != end;
|
|
}
|
|
BOOL SetupConnection(HANDLE hCom)
|
|
{
|
|
DCB dcb;
|
|
BOOL fRetVal;
|
|
dcb.DCBlength = sizeof(DCB);
|
|
|
|
GetCommState(hCom, &dcb);
|
|
dcb.BaudRate = CBR_9600;
|
|
dcb.ByteSize = 8;
|
|
dcb.Parity = 0;
|
|
dcb.StopBits = 0;
|
|
dcb.fOutxDsrFlow = 0;
|
|
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
|
dcb.fOutxCtsFlow = 0;
|
|
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
|
dcb.fInX = 0;
|
|
dcb.fOutX = 0;
|
|
dcb.XonChar = ASCII_XON;
|
|
dcb.XoffChar = ASCII_XOFF;
|
|
dcb.XonLim = 100;
|
|
dcb.XoffLim = 100;
|
|
dcb.fBinary = TRUE;
|
|
dcb.fParity = TRUE;
|
|
|
|
fRetVal = SetCommState(hCom, &dcb);
|
|
if (fRetVal == 0)return FALSE;
|
|
|
|
|
|
// get any early notifications
|
|
fRetVal = SetCommMask(hCom, EV_RXCHAR);
|
|
if (fRetVal == 0)return FALSE;
|
|
|
|
// setup device buffers
|
|
fRetVal = SetupComm(hCom, 2, 2);
|
|
if (fRetVal == 0)return FALSE;
|
|
|
|
// purge any information in the buffer
|
|
fRetVal = PurgeComm(hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
|
|
if (fRetVal == 0)return FALSE;
|
|
|
|
// set up for overlapped I/O
|
|
COMMTIMEOUTS CommTimeOuts = { 0, };
|
|
CommTimeOuts.ReadIntervalTimeout = 1;
|
|
CommTimeOuts.ReadTotalTimeoutMultiplier = 1;
|
|
CommTimeOuts.ReadTotalTimeoutConstant = 0;
|
|
CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
|
|
CommTimeOuts.WriteTotalTimeoutConstant = 0;
|
|
fRetVal = SetCommTimeouts(hCom, &CommTimeOuts);
|
|
if (fRetVal == 0)return FALSE;
|
|
return TRUE;
|
|
}
|
|
HANDLE OpenConnection(int port)
|
|
{
|
|
g_pbRIOLengths = &g_baRIOLengthsA[0];
|
|
g_nRIOPacketCount = g_nRIOPacketCountA;
|
|
|
|
|
|
//port: 1~n
|
|
//char portname[8];
|
|
//wsprintf(portname, "COM%d", port);
|
|
|
|
//const char* portprefix = "COM";
|
|
//LPWSTR portname = std::strcat(portprefix, port);
|
|
|
|
|
|
DWORD dwFAs = FILE_ATTRIBUTE_NORMAL;
|
|
|
|
dwFAs |= FILE_FLAG_OVERLAPPED;
|
|
|
|
//g_hCom = CreateFile( portname, GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING, dwFAs, NULL );
|
|
g_hCom = CreateFile(L"COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, dwFAs, NULL);
|
|
if (g_hCom != INVALID_HANDLE_VALUE) {
|
|
if (SetupConnection(g_hCom)) {
|
|
ros.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
wos.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
g_hWatchEvent = CreateEvent(0, 0, 0, 0);
|
|
g_hLampEvent = CreateEvent(0, FALSE, 0, 0);
|
|
g_hCommWatchThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0, (LPTHREAD_START_ROUTINE)CommWatchProc, NULL, CREATE_SUSPENDED, &g_dwThreadID);
|
|
if (g_hCommWatchThread) {
|
|
//"Normally, were produced by Thread."
|
|
//////////////////////////////////////////////
|
|
//All OK
|
|
//Exit Point <======
|
|
EscapeCommFunction(g_hCom, SETDTR);
|
|
Sleep(50);
|
|
EscapeCommFunction(g_hCom, CLRDTR);
|
|
Sleep(1000);
|
|
SetThreadPriority(g_hCommWatchThread, THREAD_PRIORITY_TIME_CRITICAL);
|
|
ResumeThread(g_hCommWatchThread);
|
|
g_nOpenComState = 1;
|
|
|
|
return g_hCom;
|
|
}
|
|
}
|
|
CloseConnection();
|
|
}
|
|
|
|
return INVALID_HANDLE_VALUE;
|
|
}
|
|
BOOL CloseConnection()
|
|
{
|
|
if (g_hCom != INVALID_HANDLE_VALUE) {
|
|
SetCommMask(g_hCom, 0);
|
|
|
|
//Terminate Watch Thread
|
|
if (g_hCommWatchThread) {
|
|
SetEvent(g_hWatchEvent);
|
|
WaitForSingleObject(g_hCommWatchThread, 1000 /*INFINITE*/); //comwatch thread not responding to temination signal
|
|
g_hCommWatchThread = NULL;
|
|
}
|
|
|
|
if (g_hWatchEvent) {
|
|
CloseHandle(g_hWatchEvent);
|
|
g_hWatchEvent = NULL;
|
|
}
|
|
|
|
if (g_hLampEvent) {
|
|
CloseHandle(g_hLampEvent);
|
|
g_hLampEvent = NULL;
|
|
}
|
|
|
|
EscapeCommFunction(g_hCom, CLRDTR);
|
|
|
|
// purge any outstanding reads/writes and close device handle
|
|
PurgeComm(g_hCom, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
|
|
CloseHandle(g_hCom);
|
|
g_hCom = INVALID_HANDLE_VALUE;
|
|
|
|
// clean up event objects
|
|
|
|
if (ros.hEvent) {
|
|
CloseHandle(ros.hEvent);
|
|
ros.hEvent = NULL;
|
|
}
|
|
if (wos.hEvent) {
|
|
CloseHandle(wos.hEvent);
|
|
wos.hEvent = NULL;
|
|
}
|
|
g_nOpenComState = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
void TransmitCommChar2(HANDLE g_hCom, int ch)
|
|
{
|
|
TransmitCommChar(g_hCom, (char)ch);
|
|
}
|
|
int ReadCommBlock(LPSTR lpszBlock, int nMaxLength)
|
|
{
|
|
BOOL fReadStat;
|
|
COMSTAT ComStat;
|
|
DWORD dwErrorFlags;
|
|
DWORD dwLength;
|
|
DWORD dwLength2;
|
|
DWORD dwError;
|
|
|
|
// only try to read number of bytes in queue
|
|
ClearCommError(g_hCom, &dwErrorFlags, &ComStat);
|
|
dwLength = __min((DWORD)nMaxLength, ComStat.cbInQue);
|
|
dwLength2 = __min((DWORD)nMaxLength, ComStat.cbInQue);
|
|
|
|
if (dwLength > 0) {
|
|
fReadStat = ReadFile(g_hCom, lpszBlock, dwLength, &dwLength2, &ros);
|
|
|
|
for (int i = 0; i < (int)dwLength; i++) {
|
|
|
|
int ch = (BYTE)lpszBlock[i];
|
|
|
|
|
|
//"Loops as received letters".
|
|
//"Currently there is no packet receiving .. .. The state can come any control characters ."
|
|
|
|
if (packetbyteremain != 0) {
|
|
//"The letters arrived for the packet .. get the rest of the characters ."
|
|
if (ch & 0x80) {
|
|
packetbyteremain = 0;
|
|
chinpacket = 0;
|
|
continue;
|
|
}
|
|
packetbyteremain--;
|
|
packetbytes[chinpacket] = ch;
|
|
chinpacket++;
|
|
if (packetbyteremain == 0) {
|
|
//"A packet was completed . ... Respond immediately arrived ."
|
|
chinpacket--; // exclude check byte
|
|
int packettype = packetbytes[0];
|
|
BYTE bCheckByte = 0;
|
|
for (int kkk = 0; kkk < chinpacket; kkk++) {
|
|
bCheckByte += packetbytes[kkk] & 0x7f;
|
|
}
|
|
static bool s_bool = true;
|
|
if (s_bool || (bCheckByte & 0x7f) == packetbytes[chinpacket]) {
|
|
switch (packettype) {
|
|
//PC to RIO
|
|
case rio_CheckRequest:
|
|
case rio_VersionRequest:
|
|
case rio_AnalogRequest:
|
|
case rio_ResetRequest:
|
|
case rio_LampRequest:
|
|
//RIO to PC
|
|
case rio_CheckReply:
|
|
if (ComStat.cbOutQue == 0)
|
|
TransmitCommChar2(g_hCom, ACK_CHAR);
|
|
CheckReply(packetbytes);
|
|
break;
|
|
case rio_VersionReply:
|
|
if (ComStat.cbOutQue == 0)
|
|
TransmitCommChar2(g_hCom, ACK_CHAR);
|
|
VersionReply(packetbytes);
|
|
break;
|
|
case rio_TestModeChange:
|
|
if (ComStat.cbOutQue == 0)
|
|
TransmitCommChar2(g_hCom, ACK_CHAR);
|
|
TestModeChange(packetbytes);
|
|
break;
|
|
case rio_AnalogReply:
|
|
if (ComStat.cbOutQue == 0)
|
|
TransmitCommChar2(g_hCom, ACK_CHAR);
|
|
AnalogEvent(packetbytes);
|
|
break;
|
|
case rio_ButtonPressed:
|
|
case rio_ButtonReleased:
|
|
if (ComStat.cbOutQue == 0)
|
|
TransmitCommChar2(g_hCom, ACK_CHAR);
|
|
ButtonEvent(packetbytes);
|
|
break;
|
|
case rio_KeyPressed:
|
|
case rio_KeyReleased:
|
|
if (ComStat.cbOutQue == 0)
|
|
TransmitCommChar2(g_hCom, ACK_CHAR);
|
|
KeypadEvent(packetbytes);
|
|
|
|
|
|
}
|
|
}
|
|
else {
|
|
if (ComStat.cbOutQue == 0) {
|
|
if ((packettype == rio_ButtonPressed) || (packettype == rio_ButtonReleased))
|
|
TransmitCommChar2(g_hCom, NAK_CHAR);
|
|
else
|
|
TransmitCommChar2(g_hCom, ACK_CHAR);
|
|
}
|
|
}
|
|
// TransmitCommChar2(g_hCom,0xFC);
|
|
// WriteTTYBlock(hWnd,"|",1);
|
|
chinpacket = 0;
|
|
|
|
//packetbytes"Clear the buffer."
|
|
}
|
|
else {
|
|
//"Packet has not yet arrived . The characters do nothing."
|
|
;
|
|
}
|
|
}
|
|
else {
|
|
chinpacket = 0;
|
|
if ((rio_CheckRequest <= ch) && (ch < (rio_CheckRequest + g_nRIOPacketCount))) {
|
|
packetbyteremain = g_pbRIOLengths[ch - rio_CheckRequest] + 1;
|
|
chinpacket = 0;
|
|
packetbytes[chinpacket] = ch;
|
|
chinpacket++;
|
|
}
|
|
else {
|
|
WriteCommBlock(NULL, ch);
|
|
}
|
|
}
|
|
}//for(int i=0;i<(int)dwLength;i++)
|
|
|
|
|
|
//"After awards"
|
|
if (!fReadStat) {
|
|
if (GetLastError() == ERROR_IO_PENDING) {
|
|
while (!GetOverlappedResult(g_hCom, &ros, &dwLength, TRUE)) {
|
|
dwError = GetLastError();
|
|
if (dwError == ERROR_IO_INCOMPLETE)
|
|
// normal result if not finished
|
|
continue;
|
|
else {
|
|
// an error occurred, try to recover
|
|
ClearCommError(g_hCom, &dwErrorFlags, &ComStat);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// some other error occurred
|
|
dwLength = 0;
|
|
ClearCommError(g_hCom, &dwErrorFlags, &ComStat);
|
|
}
|
|
}
|
|
}
|
|
return dwLength;
|
|
}
|
|
BOOL WriteCommBlock(LPSTR lpByte, DWORD dwBytesToWrite)
|
|
{
|
|
if (lpByte == NULL) {
|
|
switch (dwBytesToWrite) {
|
|
case ACK_CHAR:
|
|
g_dwLastBytesToWrite = 0;
|
|
break;
|
|
case NAK_CHAR:
|
|
if (g_dwLastBytesToWrite != 0) {
|
|
if (g_baLastPacket[0] == rio_LampRequest) {
|
|
}
|
|
g_dwLastBytesToWrite = 0;
|
|
}
|
|
break;
|
|
case RESTART_CHAR:
|
|
break;
|
|
case IDLE_CHAR:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
//Normal Variables
|
|
BOOL fWriteStat;
|
|
DWORD dwBytesWritten;
|
|
DWORD dwBytesSent = 0;
|
|
//Error Report
|
|
DWORD dwErrorFlags;
|
|
COMSTAT ComStat;
|
|
|
|
if (g_dwLastBytesToWrite == 0) {
|
|
g_dCancelCount = 0;
|
|
// g_dwLastBytesToWrite = dwBytesToWrite;
|
|
memcpy(g_baLastPacket, lpByte, dwBytesToWrite);
|
|
}
|
|
else {
|
|
if (lpByte[0] == rio_LampRequest) {
|
|
}
|
|
if (++g_dCancelCount >= 5) {
|
|
g_dwLastBytesToWrite = 0;
|
|
}
|
|
return FALSE;
|
|
}
|
|
fWriteStat = WriteFile(g_hCom, lpByte, dwBytesToWrite, &dwBytesWritten, &wos);
|
|
|
|
if (fWriteStat == FALSE) {
|
|
if (GetLastError() == ERROR_IO_PENDING) {
|
|
while (!GetOverlappedResult(g_hCom, &wos, &dwBytesWritten, TRUE)) {
|
|
if (GetLastError() == ERROR_IO_INCOMPLETE) {
|
|
dwBytesSent += dwBytesWritten;
|
|
Sleep(0);//Yield the process to the others
|
|
}
|
|
else {
|
|
ClearCommError(g_hCom, &dwErrorFlags, &ComStat);
|
|
g_dwLastBytesToWrite = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ClearCommError(g_hCom, &dwErrorFlags, &ComStat);
|
|
g_dwLastBytesToWrite = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
DWORD FAR PASCAL CommWatchProc(LPSTR lpData)
|
|
{
|
|
wcos.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
HANDLE events[3] = { wcos.hEvent,g_hWatchEvent,g_hLampEvent };
|
|
|
|
while (1) {
|
|
DWORD dwEvtMask = 0;
|
|
|
|
WaitCommEvent(g_hCom, &dwEvtMask, &wcos);
|
|
|
|
DWORD ret = WaitForMultipleObjects(3, events, FALSE, 55 ); // this timeout value is the defaultwait for requesting an analog update
|
|
if (ret == WAIT_OBJECT_0) {
|
|
//An Comm Event has arrived.
|
|
DWORD bytesread;
|
|
|
|
GetOverlappedResult(g_hCom, &wcos, &bytesread, FALSE);
|
|
|
|
BYTE abIn[16];
|
|
ReadCommBlock((LPSTR)abIn, 16);
|
|
//((dwEvtMask & EV_RXCHAR) == EV_RXCHAR)
|
|
|
|
}
|
|
else if (ret == WAIT_OBJECT_0 + 1) {
|
|
//Terminate This Thread.
|
|
goto terminate;
|
|
}
|
|
else if (ret == WAIT_OBJECT_0 + 2) {
|
|
//g_hLampEvent is set.
|
|
// g_pRIOMain->RunMain();
|
|
}
|
|
else if (ret == WAIT_FAILED) {
|
|
//can't be reached here.!!!
|
|
//break;
|
|
}
|
|
else if (ret == WAIT_TIMEOUT) {
|
|
if (packetbyteremain == 0) {
|
|
int currenttick = GetTickCount();
|
|
int timeouttick = lastAnalogEvent + 5000;
|
|
if (timeouttick < currenttick) { // more than 5000ms since last AnalogEvent was seen send digital reset to try to recover
|
|
std::cout << "more than 1000ms since last AnalogEvent was seen send digital reset to try to recover" << std::endl;
|
|
std::cout << "timeouttick:currenttick " << timeouttick << ":" << currenttick << std::endl;
|
|
RIOcmd(6);
|
|
}
|
|
|
|
RequestAnalogUpdate();
|
|
|
|
}
|
|
else {
|
|
}
|
|
}
|
|
}
|
|
terminate:
|
|
// get rid of event handle
|
|
|
|
CloseHandle(wcos.hEvent);
|
|
return(TRUE);
|
|
}
|
|
short CombinePair(BYTE low_value, BYTE high_value)
|
|
{
|
|
short result;
|
|
result = (short)(((short)(low_value & 0x7F)) | (((short)high_value) << 7));
|
|
if (result & 0x2000)
|
|
{
|
|
result |= ~0x3FFF;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
void AnalogEvent(BYTE* pData)
|
|
{
|
|
|
|
for (int i = 1; i <= 10; i++) {
|
|
if (pData[i] == 0xFE) {
|
|
//AnalogAllReset ();
|
|
return;
|
|
}
|
|
}
|
|
|
|
g_Throttle = CombinePair(pData[1], pData[2]);
|
|
g_LeftPedal = CombinePair(pData[3], pData[4]);
|
|
g_RightPedal = CombinePair(pData[5], pData[6]);
|
|
g_JoystickY = CombinePair(pData[7], pData[8]);
|
|
g_JoystickX = CombinePair(pData[9], pData[10]);
|
|
|
|
maxt = (g_Throttle > maxt) ? g_Throttle : maxt;
|
|
mint = (g_Throttle < mint) ? g_Throttle : mint;
|
|
maxl = (g_LeftPedal > maxl) ? g_LeftPedal : maxl;
|
|
minl = (g_LeftPedal < minl) ? g_LeftPedal : minl;
|
|
maxr = (g_RightPedal > maxr) ? g_RightPedal : maxr;
|
|
minr = (g_RightPedal < minr) ? g_RightPedal : minr;
|
|
maxx = (g_JoystickX > maxx) ? g_JoystickX : maxx;
|
|
minx = (g_JoystickX < minx) ? g_JoystickX : minx;
|
|
maxy = (g_JoystickY > maxy) ? g_JoystickY : maxy;
|
|
miny = (g_JoystickY < miny) ? g_JoystickY : miny;
|
|
lastAnalogEvent = GetTickCount();
|
|
if (g_rawaxes) {
|
|
std::cout << "min:cur:max" <<
|
|
" T:" << std::setw(4) << mint << ":" << std::setw(4) << g_Throttle << ":" << std::setw(4) << maxt <<
|
|
" L:" << std::setw(4) << minl << ":" << std::setw(4) << g_LeftPedal << ":" << std::setw(4) << maxl <<
|
|
" R:" << std::setw(4) << minr << ":" << std::setw(4) << g_RightPedal << ":" << std::setw(4) << maxr <<
|
|
" Y:" << std::setw(4) << miny << ":" << std::setw(4) << g_JoystickY << ":" << std::setw(4) << maxy <<
|
|
" X:" << std::setw(4) << minx << ":" << std::setw(4) << g_JoystickX << ":" << std::setw(4) << maxx <<
|
|
":" << std::endl;
|
|
}
|
|
UpdateJoystick();
|
|
UpdateThrottle();
|
|
UpdatePadal();
|
|
}
|
|
void ButtonEvent(BYTE* pData)
|
|
{
|
|
|
|
BYTE state = pData[0];
|
|
BYTE index = pData[1];
|
|
|
|
switch (state)
|
|
{
|
|
case rio_ButtonPressed:
|
|
Press_V2(index);
|
|
break;
|
|
case rio_ButtonReleased:
|
|
Release_V2(index);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
void KeypadEvent(BYTE* pData)
|
|
{
|
|
|
|
BYTE state = pData[0];
|
|
BYTE pad = pData[1];
|
|
BYTE index = pData[2];
|
|
|
|
switch (state)
|
|
{
|
|
case rio_KeyPressed:
|
|
if (pad == 0)
|
|
index = index + 80;
|
|
if (pad == 1)
|
|
index = index + 96;
|
|
Press_V2(index);
|
|
break;
|
|
case rio_KeyReleased:
|
|
if (pad == 0)
|
|
index = index + 80;
|
|
if (pad == 1)
|
|
index = index + 96;
|
|
Release_V2(index);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
BOOL SendPacket(const BYTE* ba, int length)
|
|
{
|
|
|
|
return WriteCommBlock((LPSTR)ba, length);
|
|
}
|
|
BOOL SendCommand(const BYTE* pbPacket)
|
|
{
|
|
BYTE ba[256];
|
|
BYTE bCmd = *pbPacket;
|
|
int cmdindex = bCmd & 0x7F;
|
|
|
|
if (bCmd & 0x80) { /*reduce the command byte down to the index number listed in the g_baRIOLengths array */
|
|
if (cmdindex < g_nRIOPacketCount) {
|
|
int nLeft = 1/* 1byte command */ + g_pbRIOLengths[cmdindex]; /* plus any additional bytes for arguments to the commands*/
|
|
|
|
BYTE bCheck = 0;
|
|
BYTE* p = ba;
|
|
|
|
while (nLeft > 0) {
|
|
BYTE b = *pbPacket++;
|
|
*p++ = b;
|
|
bCheck += b & 0x7F;
|
|
nLeft--;
|
|
}
|
|
|
|
*p++ = bCheck & 0x7F;
|
|
|
|
return SendPacket(ba, p - ba);
|
|
}
|
|
else {
|
|
//unknown command
|
|
}
|
|
}
|
|
else {
|
|
;//error
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
BOOL SetLamp(int lampNumber, int state)
|
|
{
|
|
static BYTE request_lamp_string[] = { rio_LampRequest, 0, 0, 0 };
|
|
|
|
request_lamp_string[1] = (BYTE)(lampNumber & 0x7F);
|
|
request_lamp_string[2] = (BYTE)(state & 0x7F);
|
|
|
|
return SendCommand(request_lamp_string);
|
|
}
|
|
void AnalogAllReset()
|
|
{
|
|
ResetThrottle();
|
|
ResetLeftPedal();
|
|
ResetRightPedal();
|
|
ResetVerticalJoystick();
|
|
ResetHorizontalJoystick();
|
|
}
|
|
void GeneralReset()
|
|
{
|
|
static BYTE request_reset_string[] = { rio_ResetRequest, 0 };
|
|
SendCommand(request_reset_string);
|
|
}
|
|
void ResetThrottle()
|
|
{
|
|
static BYTE request_throttle_string[] = { rio_ResetRequest, 1 };
|
|
SendCommand(request_throttle_string);
|
|
}
|
|
void ResetLeftPedal()
|
|
{
|
|
static BYTE request_lpedal_string[] = { rio_ResetRequest, 2 };
|
|
SendCommand(request_lpedal_string);
|
|
}
|
|
void ResetRightPedal()
|
|
{
|
|
static BYTE request_rpedal_string[] = { rio_ResetRequest, 3 };
|
|
SendCommand(request_rpedal_string);
|
|
}
|
|
void ResetVerticalJoystick()
|
|
{
|
|
static BYTE request_vstick_string[] = { rio_ResetRequest, 4 };
|
|
SendCommand(request_vstick_string);
|
|
}
|
|
void ResetHorizontalJoystick()
|
|
{
|
|
static BYTE request_hstick_string[] = { rio_ResetRequest, 5 };
|
|
SendCommand(request_hstick_string);
|
|
}
|
|
void RequestCheck()
|
|
{
|
|
static BYTE request_check_string[] = { rio_CheckRequest };
|
|
SendCommand(request_check_string);
|
|
}
|
|
void CheckReply(BYTE* pData) {
|
|
|
|
switch (pData[1])
|
|
{
|
|
case BoardMissing:
|
|
std::cout << "missing_board : " << int(pData[2]) << " : " << GetBoardName(int(pData[2])) << std::endl;
|
|
break;
|
|
|
|
case BoardBad:
|
|
std::cout << "dead_board : " << int(pData[2]) << std::endl;
|
|
break;
|
|
|
|
case LampBad:
|
|
std::cout << "RIODeadLamp : " << GetLampName(int(pData[2])) << std::endl;
|
|
break;
|
|
|
|
case RestartCount:
|
|
std::cout << "remoteRetryCount:" << int(pData[2]) << std::endl;
|
|
break;
|
|
|
|
case AbandonCount:
|
|
std::cout << "remoteAbandonCount:" << int(pData[2]) << std::endl;
|
|
break;
|
|
|
|
case FullBufferCount:
|
|
std::cout << "remoteFullBufferCount:" << int(pData[2]) << std::endl;
|
|
break;
|
|
}
|
|
}
|
|
const char* GetLampName(int lamp_number)
|
|
{
|
|
const char* lamp_name[] =
|
|
{
|
|
"AuxLowerRight8",
|
|
"AuxLowerRight7",
|
|
"AuxLowerRight6",
|
|
"AuxLowerRight5",
|
|
"AuxLowerRight4",
|
|
"AuxLowerRight3",
|
|
"AuxLowerRight2",
|
|
"AuxLowerRight1",
|
|
"AuxLowerLeft8",
|
|
"AuxLowerLeft7",
|
|
"AuxLowerLeft6",
|
|
"AuxLowerLeft5",
|
|
"AuxLowerLeft4",
|
|
"AuxLowerLeft3",
|
|
"AuxLowerLeft2",
|
|
"AuxLowerLeft1",
|
|
"Secondary1", //=0x10
|
|
"Secondary2",
|
|
"Secondary3",
|
|
"Secondary4",
|
|
"Secondary5",
|
|
"Secondary6",
|
|
"TeslaRelay3",
|
|
"undefined_0x17",
|
|
"Secondary7", //=0x18
|
|
"Secondary8",
|
|
"Secondary9",
|
|
"Secondary10",
|
|
"Secondary11",
|
|
"Secondary12",
|
|
"TeslaRelay1",
|
|
"TeslaRelay2",
|
|
"AuxUpperCenter8", //=0x20
|
|
"AuxUpperCenter7",
|
|
"AuxUpperCenter6",
|
|
"AuxUpperCenter5",
|
|
"AuxUpperCenter4",
|
|
"AuxUpperCenter3",
|
|
"AuxUpperCenter2",
|
|
"AuxUpperCenter1",
|
|
"AuxUpperLeft8", //=0x28
|
|
"AuxUpperLeft7",
|
|
"AuxUpperLeft6",
|
|
"AuxUpperLeft5",
|
|
"AuxUpperLeft4",
|
|
"AuxUpperLeft3",
|
|
"AuxUpperLeft2",
|
|
"AuxUpperLeft1",
|
|
"AuxUpperRight8", //=0x30
|
|
"AuxUpperRight7",
|
|
"AuxUpperRight6",
|
|
"AuxUpperRight5",
|
|
"AuxUpperRight4",
|
|
"AuxUpperRight3",
|
|
"AuxUpperRight2",
|
|
"AuxUpperRight1",
|
|
"PanicButton", //=0x38
|
|
"IcomAmpEnableRelay",//=0x39
|
|
"IcomIncRelay", //=0x3A
|
|
"IcomPTTRelay", //=0x03B
|
|
"IcomDecRelay", //=0x3C
|
|
"Eject", //=0x3D unimplemented convenience lamp
|
|
"FloorEntry", //=0x3E
|
|
"Reverse", //=0x3F
|
|
"Joystickboard1", //=0x41
|
|
"Joystickboard2", //=0x42
|
|
"Joystickboard3", //=0x43
|
|
"Joystickboard4", //=0x44
|
|
"Joystickboard5", //=0x45
|
|
"Joystickboard6", //=0x46
|
|
"Joystickboard7", //=0x47
|
|
"Joystickboard8", //=0x48
|
|
};
|
|
|
|
return lamp_name[lamp_number];
|
|
|
|
}
|
|
const char* GetBoardName(int board_number)
|
|
{
|
|
const char* board_name[] =
|
|
{
|
|
"AuxLowerRight",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"AuxLowerLeft",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Secondary1", //=0x10
|
|
"Secondary2",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"AuxUpperCenter", //=0x18
|
|
"AuxUpperLeft",
|
|
"AuxUpperRight",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"IntKeyPad", //=0x20
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"ExtKeyPad", //=0x28
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Throttel", //=0x30
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"Undefined",
|
|
"JoyStick", //=0x38
|
|
};
|
|
|
|
return board_name[board_number];
|
|
|
|
}
|
|
void RequestVersion()
|
|
{
|
|
static BYTE request_version_string[] = { rio_VersionRequest };
|
|
SendCommand(request_version_string);
|
|
}
|
|
void VersionReply(BYTE* pData) {
|
|
std::cout << "RIO Version : " << int(pData[1]) << " . " << int(pData[2]) << std::endl;
|
|
}
|
|
void RequestAnalogUpdate()
|
|
{
|
|
static BYTE request_analog_string[] = { rio_AnalogRequest };
|
|
|
|
SendCommand(request_analog_string);
|
|
if (enablecount) {
|
|
count++;
|
|
now = GetTickCount();
|
|
if (count == 10) {
|
|
|
|
std::cout << "10 analog requests took ," << (now - last) << ",MS about ," << (10000 / (now - last)) << ", per second" << std::endl;
|
|
|
|
last = now;
|
|
count = 0;
|
|
}
|
|
}
|
|
}
|
|
void UpdatePadal()
|
|
{
|
|
#define RANGE_PADAL 500L
|
|
#define DEADZONE_PADAL 10L
|
|
/*
|
|
== PADAL ==
|
|
|
|
Up : (-)
|
|
Down : (+)
|
|
|
|
------------------
|
|
Set Start Position
|
|
------------------
|
|
|
|
Down Up
|
|
======================
|
|
(+) (-)
|
|
======================
|
|
|
|
g_LeftPedalStart: INT_MAX (+)
|
|
|
|
lP = (LONG)g_LeftPedalStart - g_LeftPedal;
|
|
"lP negative result is unconditional"
|
|
*/
|
|
|
|
LONG lP = 0;
|
|
LONG rudder = 16383L;
|
|
|
|
//
|
|
// LEFT PADAL
|
|
//
|
|
|
|
if ((g_LeftPedal < -RANGE_PADAL) || (g_LeftPedal > RANGE_PADAL))
|
|
{
|
|
|
|
if (g_LeftPedal > RANGE_PADAL) {
|
|
g_LeftPedalLast = -1000L;
|
|
}
|
|
else {
|
|
g_LeftPedalLast = 0L;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_LeftPedalStart > g_LeftPedal) {
|
|
g_LeftPedalStart = g_LeftPedal;
|
|
}
|
|
|
|
lP = (LONG)g_LeftPedalStart - g_LeftPedal;
|
|
if (lP != 0) {
|
|
lP = lP * 1000 / RANGE_PADAL;
|
|
}
|
|
|
|
// Dead Zone
|
|
if (abs(lP) < DEADZONE_PADAL) {
|
|
lP = 0;
|
|
}
|
|
|
|
g_LeftPedalLast = lP;
|
|
}
|
|
g_LeftPedalLast = std::abs(g_LeftPedalLast * 32L);
|
|
|
|
if (*invertXR) {
|
|
g_LeftPedalLast = (32766L - g_LeftPedalLast);
|
|
}
|
|
|
|
if (!*enableZR) {
|
|
SetAxis(g_LeftPedalLast, 1, HID_USAGE_RX);
|
|
}
|
|
else {
|
|
SetAxis(16383L, 1, HID_USAGE_RX);
|
|
}
|
|
|
|
//
|
|
// RIGHT PADAL
|
|
//
|
|
|
|
lP = 0;
|
|
|
|
if ((g_RightPedal < -RANGE_PADAL) || (g_RightPedal > RANGE_PADAL))
|
|
{
|
|
|
|
if (g_RightPedal > RANGE_PADAL) {
|
|
g_RightPedalLast = 1000L;
|
|
}
|
|
else {
|
|
g_RightPedalLast = 0L;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_RightPedalStart > g_RightPedal) {
|
|
g_RightPedalStart = g_RightPedal;
|
|
}
|
|
|
|
lP = (LONG)g_RightPedalStart - g_RightPedal;
|
|
if (lP != 0) {
|
|
lP = lP * 1000 / RANGE_PADAL;
|
|
}
|
|
|
|
// Dead Zone
|
|
if (abs(lP) < DEADZONE_PADAL) {
|
|
lP = 0;
|
|
}
|
|
|
|
g_RightPedalLast = -lP;
|
|
}
|
|
g_RightPedalLast = std::abs(g_RightPedalLast * 32L);
|
|
|
|
if (*invertYR) {
|
|
g_RightPedalLast = (32766L - g_RightPedalLast);
|
|
}
|
|
|
|
if (!*enableZR) {
|
|
SetAxis(g_RightPedalLast, 1, HID_USAGE_RY);
|
|
}
|
|
else {
|
|
SetAxis(16383L, 1, HID_USAGE_RY);
|
|
}
|
|
|
|
rudder = 16383L - (g_LeftPedalLast / 2L) + (g_RightPedalLast / 2L);
|
|
|
|
if (*invertZR) {
|
|
rudder = (32766L - rudder);
|
|
}
|
|
|
|
if (*enableZR) {
|
|
SetAxis(rudder, 1, HID_USAGE_RZ);
|
|
}
|
|
else {
|
|
SetAxis(16383L, 1, HID_USAGE_RZ);
|
|
}
|
|
}
|
|
void UpdateThrottle()
|
|
{
|
|
// [[RIO button value range]]
|
|
// RIO board spec: (up) -800 ~ +800 (down)
|
|
// actually maximum throttle-value-range is 800, not 1600(-800 ~ 800)
|
|
// possible throttle value (g_ThrottleMax ~ (g_ThrottleMax - 800))
|
|
// g_ThrottleMax will be changedfrom input value...
|
|
//
|
|
// g_Throttle/g_ThrottleMax
|
|
#define RANGE_THROTTLE 800L
|
|
|
|
#define ZERO_THROTTLE 0L // see,,, control_mapping.cpp, 0.3
|
|
|
|
#define DEADZONE_THROTTLE 50L
|
|
|
|
/*
|
|
== THROTTLE ==
|
|
|
|
Up : (-)
|
|
Down : (+)
|
|
|
|
------------------
|
|
Set Start Position
|
|
------------------
|
|
|
|
Up Down
|
|
======================
|
|
(-) (+)
|
|
======================
|
|
|
|
g_ThrottleStart: INT_MIN (-)
|
|
*/
|
|
|
|
|
|
LONG lT;
|
|
|
|
if ((g_Throttle < -RANGE_THROTTLE) || (g_Throttle > RANGE_THROTTLE))
|
|
{
|
|
// error
|
|
if (g_Throttle < RANGE_THROTTLE) {
|
|
if (g_ThrottleResult > 0L) { // back
|
|
g_ThrottleLast = 1000L;
|
|
}
|
|
else {
|
|
g_ThrottleLast = -1000L;
|
|
}
|
|
}
|
|
else {
|
|
g_ThrottleLast = 0L;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set StartPosition
|
|
if (g_ThrottleStart < g_Throttle)
|
|
g_ThrottleStart = g_Throttle;
|
|
|
|
lT = (LONG)g_ThrottleStart - g_Throttle;
|
|
|
|
if (lT > 800L)
|
|
lT = 800L;
|
|
|
|
if (lT != 0) {
|
|
lT = lT * 1000L / 800L;
|
|
|
|
if ((lT > -DEADZONE_THROTTLE) && (lT < DEADZONE_THROTTLE)) {
|
|
g_ThrottleLast = ZERO_THROTTLE;
|
|
}
|
|
else {
|
|
if (g_ThrottleResult > 0L) { // back
|
|
g_ThrottleLast = 900L + (lT * g_ThrottleResult / 10L);
|
|
}
|
|
else { // front
|
|
g_ThrottleLast = lT * g_ThrottleResult;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
g_ThrottleLast = std::abs(g_ThrottleLast * 32L);
|
|
|
|
if (*invertZ) {
|
|
g_ThrottleLast = 32766L - g_ThrottleLast;
|
|
}
|
|
SetAxis(g_ThrottleLast, 1, HID_USAGE_Z);
|
|
}
|
|
void UpdateJoystick()
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
/*
|
|
"The meaning of 80"
|
|
|
|
"RIO (LEFT: 120 ~ RIGHT: -80) home and promised to return the number of
|
|
(LEFT uigyeong right) cut away the rest of the maximum value of 80 or greater RIGHT
|
|
The LEFT and RIGHT case you can move at the same speed"
|
|
|
|
FH changed the method to make the rate adjust to the max/min value seen from the axis
|
|
starting with that default value of 80 like the original code but then not sniping off the
|
|
rest of the range
|
|
*/
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
LONG lJx;
|
|
LONG lJy;
|
|
#define DEADZONE_JOYSTICKX_LEFT 5L
|
|
#define DEADZONE_JOYSTICKX_RIGHT 5L
|
|
LONG startingLeftrate = -80;
|
|
LONG startingrightrate = 80;
|
|
|
|
startingLeftrate = (minx < startingLeftrate) ? minx : startingLeftrate;
|
|
startingrightrate = (maxx > startingrightrate) ? maxx : startingrightrate;
|
|
|
|
|
|
const LONG sLeftRate = (LONG)((16383L) / (LONG)(abs(startingLeftrate) - DEADZONE_JOYSTICKX_LEFT));
|
|
const LONG sRightRate = (LONG)((16383L) / (LONG)(startingrightrate - DEADZONE_JOYSTICKX_RIGHT));
|
|
|
|
lJx = g_JoystickX;
|
|
|
|
if (lJx < 0L) // LEFT
|
|
{
|
|
lJx += DEADZONE_JOYSTICKX_LEFT;
|
|
|
|
if (lJx < 0L) {
|
|
// if (lJx < (-80L + DEADZONE_JOYSTICKX_LEFT))
|
|
// lJx = (-80L + DEADZONE_JOYSTICKX_LEFT);
|
|
|
|
g_JoystickXLast = (16383L - (lJx * sLeftRate));
|
|
}
|
|
else {
|
|
g_JoystickXLast = 16383L;
|
|
}
|
|
}
|
|
else if (lJx > 0L) // RIGHT
|
|
{
|
|
lJx -= DEADZONE_JOYSTICKX_RIGHT;
|
|
|
|
if (lJx > 0L) {
|
|
// if (lJx > (80L - DEADZONE_JOYSTICKX_RIGHT))
|
|
// lJx = (80L - DEADZONE_JOYSTICKX_RIGHT);
|
|
|
|
g_JoystickXLast = (16838L - (( lJx + 2 ) * sRightRate)); // add in a -2 adjustment to prevent snaping alittle to the oposit direction.
|
|
}
|
|
else {
|
|
g_JoystickXLast = 16383L;
|
|
}
|
|
}
|
|
|
|
if (*invertX) {
|
|
g_JoystickXLast = 32766L - g_JoystickXLast;
|
|
}
|
|
|
|
SetAxis(g_JoystickXLast, 1, HID_USAGE_X);
|
|
// std::cout << " X:" << std::setw(4) << minx << ":" << std::setw(4) << g_JoystickX << ":" << std::setw(4) << maxx << ":" << sLeftRate << ":" << g_JoystickXLast << ":" << sRightRate << std::endl;
|
|
|
|
/////////////////////////
|
|
|
|
#define DEADZONE_JOYSTICKY_UP 5L
|
|
#define DEADZONE_JOYSTICKY_DOWN 5L
|
|
LONG startingUprate = -80;
|
|
LONG startingDownrate = 80;
|
|
|
|
startingUprate = (miny < startingUprate) ? miny : startingUprate;
|
|
startingDownrate = (maxy > startingDownrate) ? maxy : startingDownrate;
|
|
const LONG sUpRate = (LONG)((16383L) / (LONG)(abs(startingUprate) - DEADZONE_JOYSTICKY_UP));
|
|
const LONG sDownRate = (LONG)((16383L) / (LONG)(startingDownrate - DEADZONE_JOYSTICKY_DOWN));
|
|
|
|
lJy = g_JoystickY;
|
|
|
|
if (lJy < 0L) // UP
|
|
{
|
|
lJy += DEADZONE_JOYSTICKY_UP;
|
|
|
|
if (lJy < 0L) {
|
|
// if (lJy < (-80L + DEADZONE_JOYSTICKY_UP))
|
|
// lJy = (-80L + DEADZONE_JOYSTICKY_UP);
|
|
|
|
g_JoystickYLast = (16383L - (lJy * sUpRate));
|
|
}
|
|
else {
|
|
g_JoystickYLast = 16383L;
|
|
}
|
|
}
|
|
else if (lJy > 0L) // Down
|
|
{
|
|
lJy -= DEADZONE_JOYSTICKY_DOWN;
|
|
|
|
if (lJy > 0L) {
|
|
// if (lJy > (80L - DEADZONE_JOYSTICKY_DOWN))
|
|
// lJy = (80L - DEADZONE_JOYSTICKY_DOWN);
|
|
|
|
g_JoystickYLast = (16383L - (lJy * sDownRate));
|
|
|
|
}
|
|
else {
|
|
g_JoystickYLast = 16383L;
|
|
}
|
|
}
|
|
|
|
if (*invertY) {
|
|
g_JoystickYLast = 32766L - g_JoystickYLast;
|
|
}
|
|
|
|
SetAxis(g_JoystickYLast, 1, HID_USAGE_Y);
|
|
// std::cout << "Y:" << std::setw(4) << miny << ":" << std::setw(4) << g_JoystickY << ":" << std::setw(4) << maxy << ":" << sUpRate << ":" << g_JoystickYLast << ":" << sDownRate << std::endl;
|
|
}
|
|
void TestModeChange(BYTE* pData) {
|
|
if (pData[1] != 0)
|
|
{
|
|
std::cout << "RIO entered test mode : " << int(pData[1]) << std::endl;
|
|
// TestModeActive = 1;
|
|
}
|
|
else
|
|
{
|
|
std::cout << "RIO exited test mode : " << int(pData[1]) << std::endl;
|
|
// TestModeActive = 0;
|
|
}
|
|
}
|
|
void RIOcmd(unsigned char ucValue) { // all the things we may want the rio to do that are not outputs to the computer
|
|
switch (int(ucValue)) {
|
|
case 0:
|
|
std::cout << "Reset Analog axes and clabration data" << std::endl;
|
|
AnalogAllReset();
|
|
g_ThrottleResult = -1L;
|
|
g_ThrottleStart = INT_MIN;
|
|
g_LeftPedalStart = INT_MAX;
|
|
g_RightPedalStart = INT_MAX;
|
|
g_JoystickXLast = 16383L;
|
|
g_JoystickYLast = 16383L;
|
|
g_ThrottleLast = 0L;
|
|
g_LeftPedalLast = 0L;
|
|
g_RightPedalLast = 0L;
|
|
g_RudderLast = 16838L;
|
|
maxt = 0;
|
|
mint = 0;
|
|
maxl = 0;
|
|
minl = 0;
|
|
maxr = 0;
|
|
minr = 0;
|
|
maxx = 0;
|
|
minx = 0;
|
|
maxy = 0;
|
|
miny = 0;
|
|
break;
|
|
case 1:
|
|
std::cout << "ResetThrottle" << std::endl;
|
|
ResetThrottle();
|
|
g_ThrottleResult = -1L;
|
|
g_ThrottleStart = INT_MIN;
|
|
g_ThrottleLast = 0L;
|
|
maxt = 0;
|
|
mint = 0;
|
|
break;
|
|
case 2:
|
|
std::cout << "ResetLeftPedal" << std::endl;
|
|
ResetLeftPedal();
|
|
g_LeftPedalStart = INT_MAX;
|
|
g_LeftPedalLast = 0L;
|
|
maxl = 0;
|
|
minl = 0;
|
|
break;
|
|
case 3:
|
|
std::cout << "ResetRightPedal" << std::endl;
|
|
ResetRightPedal();
|
|
g_RightPedalStart = INT_MAX;
|
|
g_RightPedalLast = 0L;
|
|
maxr = 0;
|
|
minr = 0;
|
|
break;
|
|
case 4:
|
|
std::cout << "ResetVerticalJoystick" << std::endl;
|
|
ResetVerticalJoystick();
|
|
g_JoystickYLast = 16383L;
|
|
maxy = 0;
|
|
miny = 0;
|
|
break;
|
|
case 5:
|
|
std::cout << "ResetHorizontalJoystick" << std::endl;
|
|
ResetHorizontalJoystick();
|
|
g_JoystickXLast = 16383L;
|
|
maxx = 0;
|
|
minx = 0;
|
|
break;
|
|
case 6:
|
|
std::cout << "GeneralReset" << std::endl;
|
|
GeneralReset();
|
|
for (int b = 0; b < 72; b++) {
|
|
if (iRIO[b] & 0x8000)
|
|
SetLamp(b, LampSolidDim);
|
|
}
|
|
break;
|
|
case 7:
|
|
std::cout << "RequestVersion and RequestCheck" << std::endl;
|
|
RequestVersion();
|
|
RequestCheck();
|
|
break;
|
|
case 8:
|
|
std::cout << "enable/disable axes readout" << std::endl;
|
|
g_rawaxes = !g_rawaxes;
|
|
break;
|
|
case 9:
|
|
std::cout << "enable/disable polling Frequency output" << std::endl;
|
|
last = GetTickCount();// set the "last" time to be now so that the first output does not count all of the time since program start
|
|
enablecount = !enablecount;
|
|
break;
|
|
default:
|
|
std::cout << "unimplimented option : " << int(ucValue) << std::endl;
|
|
break;
|
|
}
|
|
}
|
|
static void Press_V2(unsigned char iAddress)
|
|
{
|
|
int iValue = iRIO[iAddress]; //
|
|
bool bIsDim = iValue & 0x8000; // would it be faster/better to just calculate the values as needed
|
|
bool bIsMouse = iValue & 0x4000; // or express them out like this and pass values?
|
|
bool bIsHat = iValue & 0x2000;
|
|
bool bIsJoy = iValue & 0x1000;
|
|
bool bIsExt = iValue & 0x0800;
|
|
bool bIsALT = iValue & 0x0400;
|
|
bool bIsCTRL = iValue & 0x0200;
|
|
bool bIsSHIFT = iValue & 0x0100;
|
|
unsigned char ucValue = iValue & 0xFF;
|
|
|
|
if (!bIsJoy && !bIsHat && !bIsMouse) //this must then be a Keyboard press
|
|
{
|
|
if (bIsSHIFT) { ModKeySHIFT(1); } //take care of any mod keys
|
|
if (bIsCTRL) { ModKeyCTRL(1); }
|
|
if (bIsALT) { ModKeyALT(1); }
|
|
|
|
Key(ucValue, 1, bIsExt); // Then press the Key
|
|
}
|
|
if (bIsJoy && bIsHat && bIsMouse) { //this must then be a RIO command
|
|
RIOcmd(ucValue);
|
|
return;
|
|
}
|
|
if (bIsJoy) { // call the vJoy button function with press and Value
|
|
SetBtn(TRUE, 1, ucValue);
|
|
}
|
|
else if (bIsHat) { // call the vJoy hat function with direction information
|
|
SetDiscPov(ucValue, 1, 1);
|
|
}
|
|
else if (bIsMouse) { // call the mouse function as a press (1)
|
|
Mouse(ucValue, 1);
|
|
}
|
|
// Work on button lighting
|
|
if (iRIO[iAddress] & 0x8000)
|
|
SetLamp(iAddress, LampSolidBright);
|
|
}
|
|
static void Release_V2(unsigned char iAddress)
|
|
{
|
|
int iValue = iRIO[iAddress];
|
|
bool bIsDim = iValue & 0x8000; // would it be faster/better to just calculate the values as needed
|
|
bool bIsMouse = iValue & 0x4000; // or express them out like this and pass values?
|
|
bool bIsHat = iValue & 0x2000;
|
|
bool bIsJoy = iValue & 0x1000;
|
|
bool bIsExt = iValue & 0x0800;
|
|
bool bIsALT = iValue & 0x0400;
|
|
bool bIsCTRL = iValue & 0x0200;
|
|
bool bIsSHIFT = iValue & 0x0100;
|
|
unsigned char ucValue = iValue & 0xFF;
|
|
|
|
if (!bIsJoy && !bIsHat && !bIsMouse) //this must then be a Keyboard press
|
|
{
|
|
Key(ucValue, 0, bIsExt); // release the Key
|
|
|
|
if (bIsALT) { ModKeyALT(0); } //take care of any mod keys
|
|
if (bIsCTRL) { ModKeyCTRL(0); }
|
|
if (bIsSHIFT) { ModKeySHIFT(0); }
|
|
|
|
}
|
|
if (bIsJoy && bIsHat && bIsMouse) { //this must then be a RIO command
|
|
|
|
return;
|
|
}
|
|
if (bIsJoy) { // call the vJoy button function with release and Value
|
|
SetBtn(FALSE, 1, ucValue);
|
|
|
|
}
|
|
else if (bIsHat) { // call the vJoy hat function with centering information
|
|
SetDiscPov(-1, 1, 1);
|
|
|
|
}
|
|
else if (bIsMouse) { // call the mouse function as a release (0)
|
|
Mouse(ucValue, 0);
|
|
}
|
|
//work on button lighting
|
|
if (iRIO[iAddress] & 0x8000)
|
|
SetLamp(iAddress, LampSolidDim);
|
|
|
|
}
|
|
static void ModKeySHIFT(bool bIsPressed)
|
|
{
|
|
INPUT sInput = { 0 };
|
|
|
|
sInput.type = INPUT_KEYBOARD;
|
|
if (!bIsPressed) { sInput.ki.dwFlags = KEYEVENTF_KEYUP; }
|
|
sInput.ki.wVk = VK_SHIFT;
|
|
sInput.ki.wScan = 0x2A;
|
|
SendInput(1, &sInput, sizeof(sInput));
|
|
|
|
}
|
|
static void ModKeyCTRL(bool bIsPressed)
|
|
{
|
|
INPUT cInput = { 0 };
|
|
cInput.type = INPUT_KEYBOARD;
|
|
if (!bIsPressed) { cInput.ki.dwFlags = KEYEVENTF_KEYUP; }
|
|
cInput.ki.wVk = VK_CONTROL;
|
|
cInput.ki.wScan = 0x1D;
|
|
SendInput(1, &cInput, sizeof(cInput));
|
|
|
|
}
|
|
static void ModKeyALT(bool bIsPressed)
|
|
{
|
|
INPUT aInput = { 0 };
|
|
aInput.type = INPUT_KEYBOARD;
|
|
if (!bIsPressed) { aInput.ki.dwFlags = KEYEVENTF_KEYUP; }
|
|
aInput.ki.wVk = VK_MENU;
|
|
aInput.ki.wScan = 0x38;
|
|
SendInput(1, &aInput, sizeof(aInput));
|
|
|
|
}
|
|
static void Key(unsigned char ucwVk, bool bIsPressed, bool bIsExtended)
|
|
{
|
|
//INPUT kInput = { 0 };
|
|
//kInput.type = INPUT_KEYBOARD;
|
|
//if (!bIsPressed) { kInput.ki.dwFlags = KEYEVENTF_KEYUP; }
|
|
//if (bIsExtended) { kInput.ki.dwFlags = kInput.ki.dwFlags | KEYEVENTF_EXTENDEDKEY; }
|
|
//kInput.ki.wVk = ucwVk;
|
|
//kInput.ki.wScan = MapVirtualKey(kInput.ki.wVk, MAPVK_VK_TO_VSC);
|
|
//SendInput(1, &kInput, sizeof(kInput));
|
|
INPUT kInput = { 0 };
|
|
kInput.type = INPUT_KEYBOARD;
|
|
kInput.ki.dwFlags = KEYEVENTF_SCANCODE;
|
|
if (!bIsPressed) { kInput.ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_SCANCODE; }
|
|
if (bIsExtended) { kInput.ki.dwFlags = kInput.ki.dwFlags | KEYEVENTF_EXTENDEDKEY; }
|
|
kInput.ki.wVk = 0;
|
|
kInput.ki.wScan = MapVirtualKey(ucwVk, MAPVK_VK_TO_VSC);
|
|
kInput.ki.time = 0;
|
|
SendInput(1, &kInput, sizeof(kInput));
|
|
|
|
}
|
|
static void Mouse(unsigned char ucValue, bool bIsPressed)
|
|
{
|
|
INPUT mInput = { 0 };
|
|
mInput.type = INPUT_MOUSE;
|
|
if (ucValue == 0) { //do the UP thing
|
|
if (bIsPressed) {
|
|
mInput.mi.dwFlags = MOUSEEVENTF_MOVE;
|
|
mInput.mi.dx = -50;
|
|
SendInput(1, &mInput, sizeof(mInput));
|
|
}
|
|
else {
|
|
// do nothing?
|
|
}
|
|
|
|
}
|
|
else if (ucValue == 1) { //do the RIGHT thing
|
|
if (bIsPressed) {
|
|
mInput.mi.dwFlags = MOUSEEVENTF_MOVE;
|
|
mInput.mi.dy = 50;
|
|
SendInput(1, &mInput, sizeof(mInput));
|
|
}
|
|
else {
|
|
//do nothing?
|
|
}
|
|
|
|
}
|
|
else if (ucValue == 2) { //do the DOWN thing
|
|
if (bIsPressed) {
|
|
mInput.mi.dwFlags = MOUSEEVENTF_MOVE;
|
|
mInput.mi.dx = 50;
|
|
SendInput(1, &mInput, sizeof(mInput));
|
|
}
|
|
else {
|
|
// do mothing?
|
|
}
|
|
|
|
}
|
|
else if (ucValue == 3) { //do the LEFT thing
|
|
if (bIsPressed) {
|
|
mInput.mi.dwFlags = MOUSEEVENTF_MOVE;
|
|
mInput.mi.dy = -50;
|
|
SendInput(1, &mInput, sizeof(mInput));
|
|
}
|
|
else {
|
|
// do nothing?
|
|
}
|
|
|
|
}
|
|
else if (ucValue == 4) { //do the Lclick thing
|
|
if (bIsPressed) {
|
|
mInput.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | KEYEVENTF_EXTENDEDKEY;
|
|
}
|
|
else {
|
|
mInput.mi.dwFlags = MOUSEEVENTF_LEFTUP | KEYEVENTF_EXTENDEDKEY;
|
|
}
|
|
SendInput(1, &mInput, sizeof(mInput));
|
|
|
|
}
|
|
else if (ucValue == 5) { //do the Rclick thing
|
|
if (bIsPressed) {
|
|
mInput.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN | KEYEVENTF_EXTENDEDKEY;
|
|
}
|
|
else {
|
|
mInput.mi.dwFlags = MOUSEEVENTF_RIGHTUP | KEYEVENTF_EXTENDEDKEY;
|
|
}
|
|
SendInput(1, &mInput, sizeof(mInput));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void CPlasma::SendPacket(const BYTE* pbPacket, char nLen)
|
|
{
|
|
if (IsValid())
|
|
txComLoop(pbPacket, nLen);
|
|
}
|
|
void CPlasma::PlasmaClear()
|
|
{
|
|
char string[] = { ESC, '@' };
|
|
SendPacket((BYTE*)string, 2);
|
|
}
|
|
void CPlasma::PlasmaCursor(char n)
|
|
{
|
|
char string[] = { ESC, 'G', char(n) };
|
|
SendPacket((BYTE*)string, 3);
|
|
}
|
|
void CPlasma::PlasmaCursorHome()
|
|
{
|
|
char string[] = { ESC, 'L' };
|
|
SendPacket((BYTE*)string, 2);
|
|
}
|
|
void CPlasma::PlasmaCursorX(char n)
|
|
{
|
|
char string[] = { ESC, 'R', n };
|
|
SendPacket((BYTE*)string, 3);
|
|
}
|
|
void CPlasma::PlasmaCursorY(char n)
|
|
{
|
|
char string[] = { ESC, 'Q', n };
|
|
SendPacket((BYTE*)string, 3);
|
|
}
|
|
void CPlasma::PlasmaFontAttr(char n)
|
|
{
|
|
char string[] = { ESC, 'H', (BYTE)n };
|
|
SendPacket((BYTE*)string, 3);
|
|
}
|
|
void CPlasma::PlasmaFont(char n)
|
|
{
|
|
char string[] = { 27 /*ESC*/, 'K', n };
|
|
SendPacket((BYTE*)string, 3);
|
|
}
|
|
void CPlasma::PlasmaText(const char* szText)
|
|
{
|
|
if (!szText)
|
|
return;
|
|
|
|
int len = strlen(szText);
|
|
|
|
if (len >= 256)
|
|
return;
|
|
|
|
SendPacket((BYTE*)szText, (char)len);
|
|
}
|
|
POINT CPlasma::GetFontSize(int Font)
|
|
{
|
|
POINT size = { 0, 0 };
|
|
|
|
switch (Font)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
size.x = 5;
|
|
size.y = 7;
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
size.x = 10;
|
|
size.y = 14;
|
|
break;
|
|
case 6:
|
|
case 7:
|
|
size.x = 5;
|
|
size.y = 7;
|
|
break;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
void CPlasma::PlasmaBoxDraw(RECT tag)
|
|
{
|
|
char string[] = { ESC, 'X', (char)tag.left, (char)tag.top, (char)tag.right, (char)tag.bottom };
|
|
SendPacket((BYTE*)string, 6);
|
|
}
|
|
void CPlasma::PlasmaBoxFill(RECT tag)
|
|
{
|
|
char string[] = { ESC, 'x', 0, (char)tag.left, (char)tag.top, (char)tag.right, (char)tag.bottom };
|
|
SendPacket((BYTE*)string, 7);
|
|
}
|
|
void CPlasma::PlasmaPosText(const char* szText, char x, char y, char Attr, char Font)
|
|
{
|
|
if (!szText)
|
|
return;
|
|
|
|
int len = strlen(szText);
|
|
|
|
if (len <= 0)
|
|
return;
|
|
if (Font != 2) // !Score
|
|
{
|
|
if (len <= 9) {
|
|
Font = 5;
|
|
}
|
|
else if (len <= 20) {
|
|
Font = 2;
|
|
}
|
|
else {
|
|
Font = 2;
|
|
len = 20;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (len > 20)
|
|
len = 20;
|
|
}
|
|
|
|
if (x == 0 && y == 0) {
|
|
x = 56 - ((len * GetFontSize(Font).x) / 2);
|
|
y = 15 - ((GetFontSize(Font).y) / 2);
|
|
}
|
|
|
|
PlasmaCursorX(x);
|
|
PlasmaCursorY(y);
|
|
PlasmaFontAttr(Attr);
|
|
PlasmaFont(Font);
|
|
|
|
if (len >= 256)
|
|
return;
|
|
|
|
SendPacket((const BYTE*)szText, len);
|
|
}
|
|
void CPlasma::PlasmaScoreDraw(const char* Rank, const char* Score, BOOL bFlag)
|
|
{
|
|
int CurRank = atoi(Rank);
|
|
int CurScore = atoi(Score);
|
|
|
|
RECT tag1 = { 27,19,42,30 };
|
|
RECT tag2 = { 42,19,93,30 };
|
|
|
|
RECT tag3 = { 27,19,93,30 }; //Fill
|
|
|
|
if (bFlag == TRUE)
|
|
{
|
|
PlasmaBoxFill(tag3);
|
|
|
|
PlasmaBoxDraw(tag1);
|
|
PlasmaBoxDraw(tag2);
|
|
}
|
|
|
|
int len_Rank = strlen(Rank);
|
|
int len_Score = strlen(Score);
|
|
|
|
//{
|
|
if (len_Rank <= 2 && len_Rank > 0)
|
|
if (len_Score <= 8 && len_Score > 0)
|
|
{
|
|
if (CurRank != m_nOldRank)
|
|
{
|
|
PlasmaPosText(" ", 29, 20, 0, 2);
|
|
if (len_Rank == 1)
|
|
PlasmaPosText(Rank, 33, 20, 0, 2);
|
|
else
|
|
PlasmaPosText(Rank, 29, 20, 0, 2);
|
|
}
|
|
|
|
if (m_nOldScore != CurScore)
|
|
{
|
|
PlasmaPosText(" ", 44, 20, 0, 2);
|
|
PlasmaPosText(Score, (char)(85 - (len_Score * 5)), 20, 0, 2);
|
|
}
|
|
}
|
|
|
|
m_nOldRank = CurRank;
|
|
m_nOldScore = CurScore;
|
|
//}
|
|
}
|
|
void CPlasma::DoPlasma(char nMode)
|
|
{
|
|
|
|
switch (nMode)
|
|
{
|
|
case 0:
|
|
m_nGlobalState = -1;
|
|
break;
|
|
case 1:
|
|
if ((m_nGlobalState != 0) && (m_nGlobalState != 2))
|
|
return;
|
|
break;
|
|
case 2:
|
|
if ((m_nGlobalState != 1) && (m_nGlobalState != 2))
|
|
return;
|
|
break;
|
|
case 9:
|
|
if ((m_nGlobalState != 1) && (m_nGlobalState != 2))
|
|
return;
|
|
PlasmaDisplay(0, NULL, NULL, NULL);
|
|
m_nGlobalState = -1;
|
|
return;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
}
|
|
void CPlasma::PlasmaDisplay(int Gamestate, const char* szName/*9*/, const char* Rank/*2*/, const char* Score/*8*/)
|
|
{
|
|
static BOOL BoxFlag = FALSE;
|
|
static BOOL NameFlag = FALSE;
|
|
|
|
if (Gamestate == 0) // end
|
|
{
|
|
NameFlag = TRUE;
|
|
PlasmaClear();
|
|
}
|
|
else if (Gamestate == 1) // ready
|
|
{
|
|
if (NameFlag) {
|
|
PlasmaPosText(" ");
|
|
PlasmaPosText(szName);
|
|
}
|
|
|
|
NameFlag = FALSE;
|
|
BoxFlag = TRUE;
|
|
m_nOldRank = INT_MIN;
|
|
m_nOldScore = INT_MIN;
|
|
}
|
|
else if (Gamestate == 2) // start
|
|
{
|
|
PlasmaScoreDraw(Rank, Score, BoxFlag);
|
|
BoxFlag = FALSE;
|
|
}
|
|
}
|
|
|
|
C232Comm::C232Comm()
|
|
: m_hCom(INVALID_HANDLE_VALUE), m_nPort(0)
|
|
{
|
|
}
|
|
C232Comm::~C232Comm()
|
|
{
|
|
Close();
|
|
}
|
|
BOOL C232Comm::Open(int nPort, DWORD dwBaudRate/* = CBR_9600*/, BYTE byParity/* = NOPARITY*/, BYTE bByteSize/* = 8*/, BYTE byStopBits/* = ONESTOPBIT*/)
|
|
{
|
|
m_nPort = nPort;
|
|
|
|
char sz[32];
|
|
|
|
sprintf_s(sz, g_szPortFormat, m_nPort);
|
|
// fh trying to resolve C2664 compile error by explicitly converting the char[32] sz to LPCWSTR lpwsz,
|
|
wchar_t wsz[32];
|
|
std::mbstowcs(wsz, sz, std::strlen(sz) + 1);
|
|
LPWSTR lpwsz = wsz;
|
|
m_hCom = CreateFile(lpwsz, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
|
if (IsValid()) {
|
|
m_cp.wPacketLength = sizeof(COMMPROP); /* get comm properties */
|
|
if (GetCommProperties(m_hCom, &m_cp)) {
|
|
if (GetCommState(m_hCom, &m_dcb)) {
|
|
m_dcb.BaudRate = (DWORD)dwBaudRate;
|
|
m_dcb.ByteSize = bByteSize; // ONESTOPBIT TWOSTOPBITS
|
|
m_dcb.Parity = byParity; // NOPARITY EVENPARITY ODDPARITY
|
|
m_dcb.StopBits = byStopBits;
|
|
|
|
m_dcb.fOutxCtsFlow = 0;
|
|
m_dcb.fOutxDsrFlow = 0;
|
|
m_dcb.fRtsControl = 0;
|
|
m_dcb.fDtrControl = DTR_CONTROL_ENABLE; // HANDSHAKE; // 9;
|
|
|
|
m_dcb.fOutX = 0;
|
|
m_dcb.fInX = 0;
|
|
|
|
if (SetCommState(m_hCom, &m_dcb)) {
|
|
memset(&m_commTimeOuts, 0, sizeof(m_commTimeOuts));
|
|
|
|
m_commTimeOuts.ReadIntervalTimeout = MAXDWORD;
|
|
m_commTimeOuts.ReadTotalTimeoutMultiplier = 0;
|
|
m_commTimeOuts.ReadTotalTimeoutConstant = 0;
|
|
m_commTimeOuts.WriteTotalTimeoutMultiplier = 5;
|
|
m_commTimeOuts.WriteTotalTimeoutConstant = 5;
|
|
|
|
if (SetupComm(m_hCom, 2048 * 5, 1024)) {
|
|
if (SetCommTimeouts(m_hCom, &m_commTimeOuts)) {
|
|
if (GetCommTimeouts(m_hCom, &m_commTimeOuts)) {
|
|
if (GetCommState(m_hCom, &m_dcb)) {
|
|
m_dcb.fDtrControl = DTR_CONTROL_DISABLE;
|
|
Sleep(10);
|
|
SetCommState(m_hCom, &m_dcb);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
}
|
|
}
|
|
else {
|
|
}
|
|
}
|
|
else {
|
|
}
|
|
}
|
|
else {
|
|
}
|
|
locProcessCommError();
|
|
Close();
|
|
}
|
|
else {
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
void C232Comm::Close()
|
|
{
|
|
if (IsValid()) {
|
|
CloseHandle(m_hCom);
|
|
m_hCom = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
int C232Comm::rxCom(void* pData, int nLen)
|
|
{
|
|
if (!ReadFile(m_hCom, pData, (DWORD)nLen, (LPDWORD)&nLen, (LPOVERLAPPED)NULL)) {
|
|
locProcessCommError();
|
|
return -1;
|
|
}
|
|
/* clean out any pending bytes in the receive buffer */
|
|
PurgeComm(m_hCom, PURGE_RXCLEAR);
|
|
|
|
return nLen;
|
|
}
|
|
BOOL C232Comm::rxComLoop(void* pData, int nLen)
|
|
{
|
|
while (nLen > 0) {
|
|
int nRead = rxCom(pData, nLen);
|
|
if (nRead == -1)
|
|
return FALSE;
|
|
nLen -= nRead;
|
|
pData = &((BYTE*)pData)[nRead];
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
int C232Comm::txCom(const void* pData, int nLen)
|
|
{
|
|
|
|
if (!WriteFile(m_hCom, pData, (DWORD)nLen, (LPDWORD)&nLen, NULL)) {
|
|
locProcessCommError();
|
|
return -1;
|
|
}
|
|
|
|
return nLen;
|
|
}
|
|
int C232Comm::txComLoop(const void* pData, int nLen)
|
|
{
|
|
while (nLen > 0) {
|
|
int nSent = txCom(pData, nLen);
|
|
if (nSent == -1)
|
|
return FALSE;
|
|
nLen -= nSent;
|
|
pData = &((BYTE*)pData)[nSent];
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
void C232Comm::locProcessCommError()
|
|
{
|
|
|
|
DWORD dwError;
|
|
COMSTAT cs;
|
|
/* clear error */
|
|
ClearCommError(m_hCom, &dwError, &cs);
|
|
|
|
}
|
|
int C232Comm::CommunicationConfig(HWND hWndParent)
|
|
{
|
|
if (IsValid()) {
|
|
DWORD dwCC = sizeof(m_cc);
|
|
m_cc.dwSize = dwCC;
|
|
|
|
if (GetCommConfig(m_hCom, &m_cc, &dwCC)) {
|
|
char sz[32];
|
|
|
|
sprintf_s(sz, g_szPortFormat, m_nPort);
|
|
// fh trying to resolve C2664 compile error by explicitly converting the char[32] sz to LPCWSTR lpwsz,
|
|
wchar_t wsz[32];
|
|
std::mbstowcs(wsz, sz, std::strlen(sz) + 1);
|
|
LPWSTR lpwsz = wsz;
|
|
if (CommConfigDialog(lpwsz, hWndParent, &m_cc)) {
|
|
if (SetCommState(m_hCom, &m_cc.dcb)) {
|
|
// write new settings
|
|
return +1;
|
|
}
|
|
locProcessCommError();
|
|
return 0;
|
|
}
|
|
locProcessCommError();
|
|
return -1;
|
|
}
|
|
else {
|
|
locProcessCommError();
|
|
return -2;
|
|
}
|
|
}
|
|
else {
|
|
return -3;
|
|
}
|
|
}
|