Compare commits
2 Commits
9b43f600b8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
c9f8325da9
|
|||
|
2bb5ce0327
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,7 +4,7 @@
|
||||
|
||||
### C++ template
|
||||
# Prerequisites
|
||||
*.d
|
||||
#*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
|
||||
135
CONTRIBUTING.md
Normal file
135
CONTRIBUTING.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# Contributing to OpenVPC Shift Tool
|
||||
|
||||
Thank you for your interest in contributing to the OpenVPC Shift Tool! This document provides guidelines and instructions for contributing to the project.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please be respectful and considerate of others when contributing to this project. We aim to foster an inclusive and welcoming community.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Fork the repository on GitHub
|
||||
2. Clone your fork locally:
|
||||
```
|
||||
git clone https://github.com/YOUR-USERNAME/vpc-shift-tool.git
|
||||
cd vpc-shift-tool
|
||||
```
|
||||
3. Add the original repository as an upstream remote:
|
||||
```
|
||||
git remote add upstream https://github.com/RavenX8/vpc-shift-tool.git
|
||||
```
|
||||
4. Create a new branch for your changes:
|
||||
```
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
|
||||
## Development Environment Setup
|
||||
|
||||
1. Install the Rust toolchain from [rustup.rs](https://rustup.rs/)
|
||||
2. Install dependencies:
|
||||
- Windows: No additional dependencies required
|
||||
- Linux: `sudo apt install libudev-dev pkg-config` (Ubuntu/Debian)
|
||||
|
||||
3. Build the project:
|
||||
```
|
||||
cargo build
|
||||
```
|
||||
|
||||
4. Run the application:
|
||||
```
|
||||
cargo run
|
||||
```
|
||||
|
||||
## Making Changes
|
||||
|
||||
1. Make your changes to the codebase
|
||||
2. Write or update tests as necessary
|
||||
3. Ensure all tests pass:
|
||||
```
|
||||
cargo test
|
||||
```
|
||||
4. Format your code:
|
||||
```
|
||||
cargo fmt
|
||||
```
|
||||
5. Run the linter:
|
||||
```
|
||||
cargo clippy
|
||||
```
|
||||
|
||||
## Commit Guidelines
|
||||
|
||||
- Use clear and descriptive commit messages
|
||||
- Reference issue numbers in your commit messages when applicable
|
||||
- Keep commits focused on a single change
|
||||
- Use the present tense ("Add feature" not "Added feature")
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
1. Update your fork with the latest changes from upstream:
|
||||
```
|
||||
git fetch upstream
|
||||
git rebase upstream/main
|
||||
```
|
||||
2. Push your changes to your fork:
|
||||
```
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
3. Create a pull request through the GitHub interface
|
||||
4. Ensure your PR description clearly describes the changes and their purpose
|
||||
5. Link any related issues in the PR description
|
||||
|
||||
## Code Style
|
||||
|
||||
- Follow the Rust style guidelines
|
||||
- Use meaningful variable and function names
|
||||
- Add comments for complex logic
|
||||
- Document public functions and types
|
||||
|
||||
## Project Structure
|
||||
|
||||
- `src/main.rs`: Application entry point and main structure
|
||||
- `src/about.rs`: About screen information
|
||||
- `src/config.rs`: Configuration handling
|
||||
- `src/device.rs`: Device representation and management
|
||||
- `src/hid_worker.rs`: HID communication worker thread
|
||||
- `src/state.rs`: Application state management
|
||||
- `src/ui.rs`: User interface components
|
||||
- `src/util.rs`: Utility functions and constants
|
||||
|
||||
## Adding Support for New Devices
|
||||
|
||||
If you're adding support for new device types:
|
||||
|
||||
1. Update the device detection logic in `device.rs`
|
||||
2. Add any necessary report format definitions in `util.rs`
|
||||
3. Test with the actual hardware if possible
|
||||
4. Document the new device support in your PR
|
||||
|
||||
## Testing
|
||||
|
||||
- Write unit tests for new functionality
|
||||
- Test on both Windows and Linux if possible
|
||||
- Test with actual VirPil hardware if available
|
||||
|
||||
## Documentation
|
||||
|
||||
- Update the README.md with any new features or changes
|
||||
- Document new functions and types with rustdoc comments
|
||||
- Update TECHNICAL.md for significant architectural changes
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
If you find a bug or have a suggestion for improvement:
|
||||
|
||||
1. Check if the issue already exists in the [GitHub Issues](https://github.com/RavenX8/vpc-shift-tool/issues)
|
||||
2. If not, create a new issue with:
|
||||
- A clear title and description
|
||||
- Steps to reproduce the issue
|
||||
- Expected and actual behavior
|
||||
- System information (OS, Rust version, etc.)
|
||||
- Screenshots if applicable
|
||||
|
||||
## License
|
||||
|
||||
By contributing to this project, you agree that your contributions will be licensed under the project's [GNU General Public License v3.0](LICENSE).
|
||||
92
Cargo.lock
generated
92
Cargo.lock
generated
@@ -377,7 +377,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -412,7 +412,7 @@ checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -565,7 +565,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -701,7 +701,7 @@ dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -889,7 +889,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1073,7 +1073,7 @@ checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1228,7 +1228,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1279,7 +1279,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1682,7 +1682,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1937,6 +1937,26 @@ dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mock-it"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c79f6245a4564f117ae4e640a829a7b425e641ca3e6ea279d74a9caf05d2daf"
|
||||
dependencies = [
|
||||
"mock-it_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mock-it_codegen"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8a887ee7c909093b773c59ee57412f0fd29d2f262905eeea721cfc31a38e18f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "23.1.0"
|
||||
@@ -2043,7 +2063,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2370,7 +2390,7 @@ dependencies = [
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2401,7 +2421,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2704,7 +2724,7 @@ checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2727,7 +2747,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2764,6 +2784,7 @@ dependencies = [
|
||||
"fast_config",
|
||||
"hidapi",
|
||||
"log",
|
||||
"mock-it",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -2890,6 +2911,17 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.96"
|
||||
@@ -2909,7 +2941,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2961,7 +2993,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2972,7 +3004,7 @@ checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3046,7 +3078,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3193,7 +3225,7 @@ dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -3228,7 +3260,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@@ -3560,7 +3592,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3571,7 +3603,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3979,7 +4011,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4039,7 +4071,7 @@ checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
"zbus-lockstep",
|
||||
"zbus_xml",
|
||||
"zvariant",
|
||||
@@ -4054,7 +4086,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
@@ -4100,7 +4132,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4120,7 +4152,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@@ -4143,7 +4175,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4168,7 +4200,7 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
"zvariant_utils",
|
||||
]
|
||||
|
||||
@@ -4180,5 +4212,5 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
11
Cargo.toml
11
Cargo.toml
@@ -5,6 +5,14 @@ edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[[bin]]
|
||||
name = "shift_tool"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lib]
|
||||
name = "vpc_shift_tool"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.4", features = ["derive"] }
|
||||
eframe = "0.30.0"
|
||||
@@ -21,3 +29,6 @@ chrono = "0.4.40"
|
||||
logging = []
|
||||
|
||||
default = ["logging"]
|
||||
|
||||
[dev-dependencies]
|
||||
mock-it = "0.9.0"
|
||||
|
||||
150
INSTALL.md
Normal file
150
INSTALL.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# Installation Guide for OpenVPC Shift Tool
|
||||
|
||||
This guide provides detailed instructions for installing and setting up the OpenVPC Shift Tool on different operating systems.
|
||||
|
||||
## Windows Installation
|
||||
|
||||
### Using Pre-built Binary
|
||||
|
||||
1. Download the Windows build artifact (`Windows-x86_64_build`) from the [GitHub Actions](https://github.com/RavenX8/vpc-shift-tool/actions) page by selecting the most recent successful workflow run
|
||||
2. The downloaded artifact is the executable binary itself (shift_tool.exe)
|
||||
3. Place it in a location of your choice and run it
|
||||
|
||||
### Building from Source on Windows
|
||||
|
||||
1. Install the Rust toolchain from [rustup.rs](https://rustup.rs/)
|
||||
2. Open Command Prompt or PowerShell
|
||||
3. Clone the repository:
|
||||
```
|
||||
git clone https://github.com/RavenX8/vpc-shift-tool.git
|
||||
cd vpc-shift-tool
|
||||
```
|
||||
4. Build the release version:
|
||||
```
|
||||
cargo build --release
|
||||
```
|
||||
5. The executable will be available at `target\release\shift_tool.exe`
|
||||
|
||||
## Linux Installation
|
||||
|
||||
### Using Pre-built Binary
|
||||
|
||||
1. Download the Linux build artifact (`Linux-x86_64_build`) from the [GitHub Actions](https://github.com/RavenX8/vpc-shift-tool/actions) page by selecting the most recent successful workflow run
|
||||
2. The downloaded artifact is the executable binary itself, no extraction needed. Just make it executable:
|
||||
```
|
||||
chmod +x Linux-x86_64_build
|
||||
# Optionally rename it to something more convenient
|
||||
mv Linux-x86_64_build shift_tool
|
||||
```
|
||||
3. Create and install the udev rules for device access:
|
||||
```
|
||||
sudo mkdir -p /etc/udev/rules.d/
|
||||
# Create the udev rule file
|
||||
echo -e '# Virpil Control devices\nSUBSYSTEM=="usb", ATTRS{idVendor}=="3344", TAG+="uaccess", GROUP:="input"' | sudo tee /etc/udev/rules.d/70-vpc.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
```
|
||||
4. Make the binary executable and run it:
|
||||
```
|
||||
chmod +x shift_tool
|
||||
./shift_tool
|
||||
```
|
||||
|
||||
### Building from Source on Linux
|
||||
|
||||
1. Install dependencies:
|
||||
```
|
||||
# Ubuntu/Debian
|
||||
sudo apt install build-essential libudev-dev pkg-config
|
||||
|
||||
# Fedora
|
||||
sudo dnf install gcc libudev-devel pkgconfig
|
||||
|
||||
# Arch Linux
|
||||
sudo pacman -S base-devel
|
||||
```
|
||||
|
||||
2. Install the Rust toolchain from [rustup.rs](https://rustup.rs/)
|
||||
|
||||
3. Clone the repository:
|
||||
```
|
||||
git clone https://github.com/RavenX8/vpc-shift-tool.git
|
||||
cd vpc-shift-tool
|
||||
```
|
||||
|
||||
4. Build and install using the Makefile:
|
||||
```
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Or manually:
|
||||
```
|
||||
cargo build --release
|
||||
sudo cp target/release/shift_tool /usr/local/bin/
|
||||
sudo mkdir -p /etc/udev/rules.d/
|
||||
sudo cp udev/rules.d/70-vpc.rules /etc/udev/rules.d/
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
```
|
||||
|
||||
## Verifying Installation
|
||||
|
||||
After installation, you can verify that the application can detect your VirPil devices:
|
||||
|
||||
1. Connect your VirPil device(s) to your computer
|
||||
2. Launch the OpenVPC Shift Tool
|
||||
3. Click the "Refresh Devices" button
|
||||
4. Your devices should appear in the dropdown menus
|
||||
|
||||
If devices are not detected:
|
||||
|
||||
- On Windows, ensure you have the correct drivers installed
|
||||
- On Linux, verify the udev rules are installed correctly and you've reloaded the rules
|
||||
- Check that your devices are properly connected and powered on
|
||||
|
||||
## Configuration Location
|
||||
|
||||
The application stores its configuration in:
|
||||
|
||||
- Windows: `%APPDATA%\shift_tool.json`
|
||||
- Linux: `~/.config/shift_tool.json`
|
||||
|
||||
## Uninstallation
|
||||
|
||||
### Windows
|
||||
|
||||
Simply delete the application files.
|
||||
|
||||
### Linux
|
||||
|
||||
If installed using the Makefile:
|
||||
```
|
||||
sudo rm /usr/local/bin/shift_tool
|
||||
sudo rm /etc/udev/rules.d/70-vpc.rules
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Linux Permission Issues
|
||||
|
||||
If you encounter permission issues accessing the devices on Linux:
|
||||
|
||||
1. Ensure the udev rules are installed correctly
|
||||
2. Add your user to the input group:
|
||||
```
|
||||
sudo usermod -a -G input $USER
|
||||
```
|
||||
3. Log out and log back in, or reboot your system
|
||||
4. Verify your user is in the input group:
|
||||
```
|
||||
groups $USER
|
||||
```
|
||||
|
||||
### Windows Device Access Issues
|
||||
|
||||
If the application cannot access devices on Windows:
|
||||
|
||||
1. Ensure no other application (like the official VPC software) is currently using the devices
|
||||
2. Try running the application as Administrator (right-click, "Run as Administrator")
|
||||
3. Check Device Manager to ensure the devices are properly recognized by Windows
|
||||
2
Makefile
2
Makefile
@@ -25,7 +25,7 @@ install:
|
||||
install -d $(DESTDIR)$(PREFIX)/bin
|
||||
install -d $(DESTDIR)/etc/udev/rules.d
|
||||
install -m 0755 target/$(target)/$(prog)$(extension) $(DESTDIR)$(PREFIX)/bin
|
||||
install -m 0644 udev/rules.d/99-vpc.rules $(DESTDIR)/etc/udev/rules.d
|
||||
install -m 0644 udev/rules.d/70-vpc.rules $(DESTDIR)/etc/udev/rules.d
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
|
||||
110
README.md
110
README.md
@@ -1,2 +1,110 @@
|
||||
# vpc-shift-tool
|
||||
# OpenVPC - Shift Tool
|
||||
|
||||
A free and open-source alternative to the VPC Shift Tool bundled with the VirPil control software package.
|
||||
|
||||
## Overview
|
||||
|
||||
OpenVPC Shift Tool is a utility designed for VirPil flight simulation controllers. It allows you to combine button inputs from multiple VirPil devices using logical operations (OR, AND, XOR), creating a "shift state" that can be sent to receiver devices. This enables more complex control schemes and button combinations for flight simulators.
|
||||
|
||||
## Features
|
||||
|
||||
- Connect to multiple VirPil devices simultaneously
|
||||
- Configure source devices that provide button inputs
|
||||
- Set up receiver devices that receive the combined shift state
|
||||
- Choose between different logical operations (OR, AND, XOR) for each bit
|
||||
- Automatic device detection for VirPil hardware
|
||||
- Configuration saving and loading
|
||||
- Cross-platform support (Windows and Linux)
|
||||
|
||||
## Installation
|
||||
|
||||
### Pre-built Binaries
|
||||
|
||||
Pre-built binaries for Windows and Linux are available in the GitHub Actions artifacts for each commit. You can find them by:
|
||||
|
||||
1. Going to the [Actions tab](https://github.com/RavenX8/vpc-shift-tool/actions)
|
||||
2. Selecting the most recent successful workflow run
|
||||
3. Downloading the appropriate artifact for your platform (Linux-x86_64_build or Windows-x86_64_build)
|
||||
|
||||
### Building from Source
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
- Rust toolchain (install from [rustup.rs](https://rustup.rs/))
|
||||
- For Linux: libudev-dev package
|
||||
|
||||
#### Build Steps
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/RavenX8/vpc-shift-tool.git
|
||||
cd vpc-shift-tool
|
||||
|
||||
# Build the release version
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
The compiled binary will be available in `target/release/`.
|
||||
|
||||
### Linux Installation
|
||||
|
||||
On Linux, you need to install udev rules to access VirPil devices without root privileges:
|
||||
|
||||
```bash
|
||||
# Using the Makefile
|
||||
sudo make install
|
||||
|
||||
# Or manually
|
||||
sudo cp udev/rules.d/70-vpc.rules /etc/udev/rules.d/
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
1. Launch the application
|
||||
2. The main interface shows three sections:
|
||||
- **Sources**: Devices that provide button inputs
|
||||
- **Rules**: Logical operations to apply to each bit
|
||||
- **Receivers**: Devices that receive the combined shift state
|
||||
|
||||
3. Add source devices by selecting them from the dropdown menu
|
||||
4. Configure which bits are active for each source
|
||||
5. Set the logical operation (OR, AND, XOR) for each bit in the Rules section
|
||||
6. Add receiver devices that will receive the combined shift state
|
||||
7. Click "Start" to begin the shift operation
|
||||
|
||||
## Configuration
|
||||
|
||||
The application automatically saves your configuration to:
|
||||
- Windows: `%APPDATA%\shift_tool.json`
|
||||
- Linux: `~/.config/shift_tool.json`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Device Not Detected
|
||||
|
||||
- Ensure your VirPil devices are properly connected
|
||||
- On Linux, verify udev rules are installed correctly
|
||||
- Try refreshing the device list with the "Refresh Devices" button
|
||||
|
||||
### Permission Issues on Linux
|
||||
|
||||
If you encounter permission issues accessing the devices on Linux:
|
||||
|
||||
1. Ensure the udev rules are installed correctly
|
||||
2. Log out and log back in, or reboot your system
|
||||
3. Verify your user is in the "input" group: `groups $USER`
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v3.0
|
||||
|
||||
## Author
|
||||
|
||||
RavenX8
|
||||
|
||||
## Links
|
||||
|
||||
- [GitHub Repository](https://github.com/RavenX8/vpc-shift-tool)
|
||||
- [Gitea Repository](https://gitea.azgstudio.com/Raven/vpc-shift-tool)
|
||||
|
||||
116
TECHNICAL.md
Normal file
116
TECHNICAL.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# OpenVPC Shift Tool - Technical Documentation
|
||||
|
||||
This document provides technical details about the OpenVPC Shift Tool application, its architecture, and how it works internally.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The application is built using Rust with the following main components:
|
||||
|
||||
1. **GUI Layer**: Uses the `eframe` crate (egui) for cross-platform GUI
|
||||
2. **HID Communication**: Uses the `hidapi` crate to communicate with VirPil devices
|
||||
3. **Configuration Management**: Uses the `fast_config` crate for saving/loading settings
|
||||
4. **Worker Thread**: Background thread that handles device communication
|
||||
|
||||
## Core Components
|
||||
|
||||
### Main Application Structure
|
||||
|
||||
The main application is represented by the `ShiftTool` struct in `src/main.rs`, which contains:
|
||||
|
||||
- State management
|
||||
- Device list
|
||||
- Shared state between UI and worker thread
|
||||
- Configuration data
|
||||
|
||||
### Modules
|
||||
|
||||
- **about.rs**: Contains application information and about screen text
|
||||
- **config.rs**: Configuration data structures and serialization
|
||||
- **device.rs**: Device representation and management
|
||||
- **hid_worker.rs**: Background worker thread for HID communication
|
||||
- **state.rs**: Application state enum
|
||||
- **ui.rs**: User interface drawing and event handling
|
||||
- **util.rs**: Utility functions and constants
|
||||
|
||||
## Data Flow
|
||||
|
||||
1. The application scans for VirPil devices (vendor ID 0x3344)
|
||||
2. User selects source and receiver devices in the UI
|
||||
3. When "Start" is clicked, a worker thread is spawned
|
||||
4. The worker thread:
|
||||
- Opens connections to all configured devices
|
||||
- Reads input from source devices
|
||||
- Applies logical operations based on configuration
|
||||
- Writes the resulting shift state to receiver devices
|
||||
5. Shared state (protected by mutexes) is used to communicate between the UI and worker thread
|
||||
|
||||
## Device Communication
|
||||
|
||||
### Device Detection
|
||||
|
||||
Devices are detected using the HID API, filtering for VirPil's vendor ID (0x3344). The application creates `VpcDevice` objects for each detected device, which include:
|
||||
|
||||
- Vendor ID and Product ID
|
||||
- Device name and firmware version
|
||||
- Serial number
|
||||
- Usage page/ID
|
||||
|
||||
### HID Protocol
|
||||
|
||||
The application supports different report formats based on device firmware versions. The worker thread:
|
||||
|
||||
1. Reads HID reports from source devices
|
||||
2. Extracts button states from the reports
|
||||
3. Applies logical operations (OR, AND, XOR) to combine states
|
||||
4. Formats the combined state into HID reports
|
||||
5. Sends the reports to receiver devices
|
||||
|
||||
## Configuration
|
||||
|
||||
Configuration is stored in JSON format using the `fast_config` crate. The configuration includes:
|
||||
|
||||
- Source devices (vendor ID, product ID, serial number, enabled bits)
|
||||
- Receiver devices (vendor ID, product ID, serial number, enabled bits)
|
||||
- Shift modifiers (logical operations for each bit)
|
||||
|
||||
## Threading Model
|
||||
|
||||
The application uses a main UI thread and a separate worker thread:
|
||||
|
||||
1. **Main Thread**: Handles UI rendering and user input
|
||||
2. **Worker Thread**: Performs HID communication in the background
|
||||
|
||||
Thread synchronization is achieved using:
|
||||
- `Arc<Mutex<T>>` for shared state
|
||||
- `Arc<(Mutex<bool>, Condvar)>` for signaling thread termination
|
||||
|
||||
## Linux-Specific Features
|
||||
|
||||
On Linux, the application requires udev rules to access HID devices without root privileges. The rule is installed to `/etc/udev/rules.d/70-vpc.rules` and contains:
|
||||
|
||||
```
|
||||
# Virpil Control devices
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="3344", TAG+="uaccess", GROUP:="input"
|
||||
```
|
||||
|
||||
## Building and Deployment
|
||||
|
||||
The application can be built using Cargo:
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
A Makefile is provided for easier installation on Linux, which:
|
||||
1. Builds the application
|
||||
2. Installs the binary to `/usr/local/bin`
|
||||
3. Installs udev rules to `/etc/udev/rules.d/`
|
||||
|
||||
## Future Development
|
||||
|
||||
Potential areas for enhancement:
|
||||
- Support for additional device types
|
||||
- More complex logical operations
|
||||
- Custom button mapping
|
||||
- Profile management
|
||||
- Integration with game APIs
|
||||
78
src/lib.rs
Normal file
78
src/lib.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
// Export modules for testing
|
||||
pub mod about;
|
||||
pub mod config;
|
||||
pub mod device;
|
||||
pub mod hid_worker;
|
||||
pub mod state;
|
||||
pub mod ui;
|
||||
pub mod util;
|
||||
|
||||
// Re-export main struct and types for testing
|
||||
pub use crate::config::ConfigData;
|
||||
pub use crate::device::VpcDevice;
|
||||
pub use crate::state::State;
|
||||
|
||||
// Constants
|
||||
pub const PROGRAM_TITLE: &str = "OpenVPC - Shift Tool";
|
||||
pub const INITIAL_WIDTH: f32 = 740.0;
|
||||
pub const INITIAL_HEIGHT: f32 = 260.0;
|
||||
|
||||
// Type aliases for shared state
|
||||
pub use std::sync::{Arc, Condvar, Mutex};
|
||||
pub type SharedStateFlag = Arc<(Mutex<bool>, Condvar)>;
|
||||
pub type SharedDeviceState = Arc<Mutex<u16>>;
|
||||
|
||||
// Args struct for command line parsing
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
pub struct Args {
|
||||
#[arg(short, long, default_value_t = false)]
|
||||
pub skip_firmware: bool,
|
||||
}
|
||||
|
||||
// Wrapper for ConfigData to match the actual structure
|
||||
pub use fast_config::Config;
|
||||
|
||||
// The main application struct
|
||||
pub struct ShiftTool {
|
||||
// State
|
||||
pub state: State,
|
||||
pub thread_state: SharedStateFlag, // Is the worker thread running?
|
||||
|
||||
// Device Data
|
||||
pub device_list: Vec<VpcDevice>, // List of discovered compatible devices
|
||||
|
||||
// Shared state between UI and Worker Thread
|
||||
pub shift_state: SharedDeviceState, // Current shift state
|
||||
pub source_states: Vec<SharedDeviceState>, // Current state of each source device
|
||||
pub receiver_states: Vec<SharedDeviceState>, // Current state of each receiver device
|
||||
|
||||
// Configuration
|
||||
pub config: Config<ConfigData>,
|
||||
pub selected_source: usize,
|
||||
pub selected_receiver: usize,
|
||||
}
|
||||
|
||||
// Implementations for ShiftTool
|
||||
impl ShiftTool {
|
||||
// Add a new source state tracking object
|
||||
pub fn add_source_state(&mut self) {
|
||||
self.source_states.push(Arc::new(Mutex::new(0)));
|
||||
}
|
||||
|
||||
// Add a new receiver state tracking object
|
||||
pub fn add_receiver_state(&mut self) {
|
||||
self.receiver_states.push(Arc::new(Mutex::new(0)));
|
||||
}
|
||||
|
||||
// Get the current thread status
|
||||
pub fn get_thread_status(&self) -> bool {
|
||||
let &(ref lock, _) = &*self.thread_state;
|
||||
match lock.lock() {
|
||||
Ok(guard) => *guard,
|
||||
Err(_) => false, // Return false if the mutex is poisoned
|
||||
}
|
||||
}
|
||||
}
|
||||
197
tests/basic_tests.rs
Normal file
197
tests/basic_tests.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
use vpc_shift_tool::config::{ConfigData, ShiftModifiers, ModifiersArray};
|
||||
use vpc_shift_tool::device::{SavedDevice, VpcDevice};
|
||||
use vpc_shift_tool::state::State;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
fn test_config_data_default() {
|
||||
// Test that the default ConfigData is created correctly
|
||||
let config = ConfigData::default();
|
||||
|
||||
// Check that sources and receivers are empty
|
||||
assert_eq!(config.sources.len(), 0);
|
||||
assert_eq!(config.receivers.len(), 0);
|
||||
|
||||
// Check that shift_modifiers has the default value (all OR)
|
||||
for i in 0..8 {
|
||||
assert_eq!(config.shift_modifiers[i], ShiftModifiers::OR);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shift_modifiers_display() {
|
||||
// Test the Display implementation for ShiftModifiers
|
||||
assert_eq!(format!("{}", ShiftModifiers::OR), "OR");
|
||||
assert_eq!(format!("{}", ShiftModifiers::AND), "AND");
|
||||
assert_eq!(format!("{}", ShiftModifiers::XOR), "XOR");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_saved_device_default() {
|
||||
// Test that the default SavedDevice is created correctly
|
||||
let device = SavedDevice::default();
|
||||
|
||||
assert_eq!(device.vendor_id, 0);
|
||||
assert_eq!(device.product_id, 0);
|
||||
assert_eq!(device.serial_number, "");
|
||||
assert_eq!(device.state_enabled, [true; 8]); // All bits enabled by default
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_state_enum() {
|
||||
// Test that the State enum has the expected variants
|
||||
let initializing = State::Initialising;
|
||||
let about = State::About;
|
||||
let running = State::Running;
|
||||
|
||||
// Test that the variants are different
|
||||
assert_ne!(initializing, about);
|
||||
assert_ne!(initializing, running);
|
||||
assert_ne!(about, running);
|
||||
|
||||
// Test equality with same variant
|
||||
assert_eq!(initializing, State::Initialising);
|
||||
assert_eq!(about, State::About);
|
||||
assert_eq!(running, State::Running);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_with_devices() {
|
||||
// Test creating a ConfigData with sources and receivers
|
||||
let mut config = ConfigData::default();
|
||||
|
||||
// Create some test devices
|
||||
let device1 = SavedDevice {
|
||||
vendor_id: 0x3344,
|
||||
product_id: 0x0001,
|
||||
serial_number: "123456".to_string(),
|
||||
state_enabled: [true, false, true, false, true, false, true, false],
|
||||
};
|
||||
|
||||
let device2 = SavedDevice {
|
||||
vendor_id: 0x3344,
|
||||
product_id: 0x0002,
|
||||
serial_number: "654321".to_string(),
|
||||
state_enabled: [false, true, false, true, false, true, false, true],
|
||||
};
|
||||
|
||||
// Add devices to sources and receivers
|
||||
config.sources.push(device1.clone());
|
||||
config.receivers.push(device2.clone());
|
||||
|
||||
// Check that the devices were added correctly
|
||||
assert_eq!(config.sources.len(), 1);
|
||||
assert_eq!(config.receivers.len(), 1);
|
||||
|
||||
assert_eq!(config.sources[0].vendor_id, 0x3344);
|
||||
assert_eq!(config.sources[0].product_id, 0x0001);
|
||||
assert_eq!(config.sources[0].serial_number, "123456");
|
||||
assert_eq!(config.sources[0].state_enabled, [true, false, true, false, true, false, true, false]);
|
||||
|
||||
assert_eq!(config.receivers[0].vendor_id, 0x3344);
|
||||
assert_eq!(config.receivers[0].product_id, 0x0002);
|
||||
assert_eq!(config.receivers[0].serial_number, "654321");
|
||||
assert_eq!(config.receivers[0].state_enabled, [false, true, false, true, false, true, false, true]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_modifiers_array() {
|
||||
// Test the ModifiersArray implementation
|
||||
let mut modifiers = ModifiersArray::default();
|
||||
|
||||
// Check default values
|
||||
for i in 0..8 {
|
||||
assert_eq!(modifiers[i], ShiftModifiers::OR);
|
||||
}
|
||||
|
||||
// Test setting values
|
||||
modifiers[0] = ShiftModifiers::AND;
|
||||
modifiers[4] = ShiftModifiers::XOR;
|
||||
|
||||
// Check the modified values
|
||||
assert_eq!(modifiers[0], ShiftModifiers::AND);
|
||||
assert_eq!(modifiers[4], ShiftModifiers::XOR);
|
||||
|
||||
// Check that other values remain unchanged
|
||||
for i in 1..4 {
|
||||
assert_eq!(modifiers[i], ShiftModifiers::OR);
|
||||
}
|
||||
for i in 5..8 {
|
||||
assert_eq!(modifiers[i], ShiftModifiers::OR);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vpc_device_default() {
|
||||
// Test the default VpcDevice implementation
|
||||
let device = VpcDevice::default();
|
||||
|
||||
assert_eq!(device.full_name, "");
|
||||
assert_eq!(*device.name, "-NO CONNECTION (Select device from list)-");
|
||||
assert_eq!(*device.firmware, "");
|
||||
assert_eq!(device.vendor_id, 0);
|
||||
assert_eq!(device.product_id, 0);
|
||||
assert_eq!(device.serial_number, "");
|
||||
assert_eq!(device.usage, 0);
|
||||
assert_eq!(device.active, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vpc_device_display() {
|
||||
// Test the Display implementation for VpcDevice
|
||||
|
||||
// Test default device
|
||||
let device = VpcDevice::default();
|
||||
assert_eq!(format!("{}", device), "-NO CONNECTION (Select device from list)-");
|
||||
|
||||
// Test a real device
|
||||
let device = VpcDevice {
|
||||
full_name: "3344:0001:123456".to_string(),
|
||||
name: Rc::new("VPC MongoosT-50CM3".to_string()),
|
||||
firmware: Rc::new("VIRPIL Controls 20240101".to_string()),
|
||||
vendor_id: 0x3344,
|
||||
product_id: 0x0001,
|
||||
serial_number: "123456".to_string(),
|
||||
usage: 0,
|
||||
active: false,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{}", device),
|
||||
"VID:3344 PID:0001 VPC MongoosT-50CM3 (SN:123456 FW:VIRPIL Controls 20240101)"
|
||||
);
|
||||
|
||||
// Test a device with empty serial number
|
||||
let device = VpcDevice {
|
||||
full_name: "3344:0001:no_sn".to_string(),
|
||||
name: Rc::new("VPC MongoosT-50CM3".to_string()),
|
||||
firmware: Rc::new("VIRPIL Controls 20240101".to_string()),
|
||||
vendor_id: 0x3344,
|
||||
product_id: 0x0001,
|
||||
serial_number: "".to_string(),
|
||||
usage: 0,
|
||||
active: false,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{}", device),
|
||||
"VID:3344 PID:0001 VPC MongoosT-50CM3 (SN:N/A FW:VIRPIL Controls 20240101)"
|
||||
);
|
||||
|
||||
// Test a device with empty firmware
|
||||
let device = VpcDevice {
|
||||
full_name: "3344:0001:123456".to_string(),
|
||||
name: Rc::new("VPC MongoosT-50CM3".to_string()),
|
||||
firmware: Rc::new("".to_string()),
|
||||
vendor_id: 0x3344,
|
||||
product_id: 0x0001,
|
||||
serial_number: "123456".to_string(),
|
||||
usage: 0,
|
||||
active: false,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{}", device),
|
||||
"VID:3344 PID:0001 VPC MongoosT-50CM3 (SN:123456 FW:N/A)"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user