1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
//! Contains logics to feed/update vJoy devices. // TODO: review feedings into unused axes (C API will still accept such call with no error) #[cfg(test)] mod tests { use super::*; use crate::test_env::TEST_DEVICE_1; use crate::test_env::TEST_DEVICE_2; use serial_test::serial; #[test] #[serial] fn acquire_relinquish() { assert!(VJDOwnership::acquire(TEST_DEVICE_1)); assert!(VJDOwnership::acquire(TEST_DEVICE_2)); assert_eq!(VJDStatus::Own, VJDInfo::get_status(TEST_DEVICE_1)); assert_eq!(VJDStatus::Own, VJDInfo::get_status(TEST_DEVICE_2)); assert!(VJDOwnership::relinquish(TEST_DEVICE_1)); assert!(VJDOwnership::relinquish(TEST_DEVICE_2)); assert_eq!(VJDStatus::Free, VJDInfo::get_status(TEST_DEVICE_1)); assert_eq!(VJDStatus::Free, VJDInfo::get_status(TEST_DEVICE_2)); } #[test] #[serial] fn set_axis_checked_success() { VJDOwnership::acquire(TEST_DEVICE_1); VJDOwnership::acquire(TEST_DEVICE_2); assert!(VJDSeqFeed::set_axis(TEST_DEVICE_1, VJDAxis::X, 0)); //assert!(!VJDSeqFeed::set_axis(TEST_DEVICE_1, VJDAxis::Y, 16000)); assert!(VJDSeqFeed::set_axis(TEST_DEVICE_1, VJDAxis::Ry, 16000)); assert!(VJDSeqFeed::set_axis(TEST_DEVICE_1, VJDAxis::Slider1, 32000)); VJDOwnership::relinquish(TEST_DEVICE_1); VJDOwnership::relinquish(TEST_DEVICE_2); } } use super::info::VJDInfo; use crate::{ffi::*, vjoy_base::driver::VJGeneral}; /** Holder of utility methods to manage devices acquisition and relinquishment. */ pub struct VJDOwnership(()); impl VJDOwnership { /** Acquire the specified device and change his state to [`VJDStatus::Own`].\ Only a device in state [`VJDStatus::Free`] can be acquired. Returns `true` if acquisition was authorized, `false` otherwise. */ pub fn acquire(device: VJDevice) -> bool { unsafe { AcquireVJD(device) } } /** Relinquish the previously acquired specified device and change his state to [`VJDStatus::Free`].\ Only a device in state [`VJDStatus::Own`] can be relinquish. Returns `true` if relinquishment was authorized, `false` otherwise. */ pub fn relinquish(device: VJDevice) -> bool { if VJDInfo::get_status(device) != VJDStatus::Own { false } else { unsafe { RelinquishVJD(device) } true } } } /** Holder of utility methods to feed/update vJoy devices in the most efficient way by using [`VJDPosition`]. Feeds vJoy devices by directly providing [`VJDPosition`]s. This is the most efficient way, because you can make changes in batch to a position and then push the updated position at the proper time. Another strategy exist with [`VJDSeqFeed`] which won't force you keep track of a position, but is less efficient. See [`VJDSeqFeed`] for more details. */ pub struct VJDPosFeed(()); impl VJDPosFeed { /** Send the position data of the device encoded in [`VJDPosition`] to vJoy. Only a device in state [`VJDStatus::Own`] can have his position updated. Returns `true` if the operation succeeds, `false` otherwise. */ pub fn send_position(position: &VJDPosition) -> bool { unsafe { UpdateVJD(position.get_device(), &mut position.get_position()) } } } /** Holder of utility methods to feed/update vJoy devices in a less efficient way by using sequential updates. Feeds vJoy devices sequentially. This is the less efficient way, each methods' call will in the internal implementation make a call to UpdateVJD(...). Another strategy exist with [`VJDPosFeed`] which will force you keep track of a [`VJDPosition`], but is more efficient. See [`VJDPosFeed`] for more details. */ pub struct VJDSeqFeed(()); impl VJDSeqFeed { /** Resets all the controls of the specified device to a set of values.\ Returns `true` if the operation succeeds, `false` otherwise. These values are hard coded in the vJoy interface DLL and are currently set as follows: - Axes X, Y & Z: middle point. - All other axes: 0. - POV switches: neutral. - Buttons: not pressed. */ pub fn reset(device: VJDevice) -> bool { // ResetVJD() is bugged... implement a custom code // unsafe { ResetVJD(device) } let was_owned = match VJDInfo::get_status(device) { VJDStatus::Own => true, VJDStatus::Free => false, _ => return false, }; if !was_owned { VJDOwnership::acquire(device); } VJDPosFeed::send_position(&VJDPosition::new(device)); if !was_owned { VJDOwnership::relinquish(device); } true } /** Resets all the controls of all devices to a set of values. Please note: vJoy public API doesn't tell us if it succeeds or not. */ pub fn reset_all() { // ResetAll() is bugged... implement a custom code // unsafe { ResetAll() } // TODO: ?return bool with which devices failed (non blocking loop: if a device // fails, the following may succeed) for n in 1..=VJGeneral::MAX_DEVICES { VJDSeqFeed::reset(VJDevice::get_from(n).unwrap()); } } /** Resets all buttons to a released state in the specified device. */ pub fn reset_btns(device: VJDevice) { // return bool is not needed, internal implementation only return // false when providing a wrong device number, which is impossible // because we provide it by a controlled enum unsafe { ResetButtons(device) }; } /** Resets all POV switches to a neutral state in the specified device. */ pub fn reset_povs(device: VJDevice) { // return bool is not needed, internal implementation only return // false when providing a wrong device number, which is impossible // because we provide it by a controlled enum unsafe { ResetPovs(device) }; } // TODO: check range 0x1-0x8000 for setaxis and update doc /** Write a value to the given axis of the specified device. Only a device in state [`VJDStatus::Own`] can have his axes altered. Returns `true` if the operation succeeds, `false` otherwise. Value can be in the range of 0 to 32767. Middle point is at 16384. */ // Value range is annonced 1 to 32768 in the vJoy doc, but the reality // when tested is 0 to 32767. See this thread for more details: // https://vjoy.freeforums.net/thread/15/axis-value-range pub fn set_axis(device: VJDevice, axis: VJDAxis, value: i32) -> bool { unsafe { SetAxis(value, device, axis) } } /** Set a given button of the specified device pressed or released. Only a device in state [`VJDStatus::Own`] can have his buttons altered. Returns `true` if the operation succeeds, `false` otherwise. Button number can be in the range 1 to 128. */ pub fn set_btn(device: VJDevice, button_number: VJDButton, state: VJDButtonState) -> bool { unsafe { SetBtn(state, device, button_number) } } /** Write a discrete direction to a given discrete POV of the specified device. Returns `true` if the operation succeeds, `false` otherwise. */ pub fn set_disc_pov( device: VJDevice, pov_number: VJDPovNumber, disc_direction: VJDPovDisc, ) -> bool { unsafe { SetDiscPov(disc_direction, device, pov_number) } } /** Write a value to a given continuous POV of the specified device. Returns `true` if the operation succeeds, `false` otherwise. Value can be in the range 0 to 35999, neutral is [`u32::MAX`].\ A value is measured in units of one-hundredth a degree. */ pub fn set_cont_pov(device: VJDevice, pov_number: VJDPovNumber, value: u32) -> bool { unsafe { SetContPov(value, device, pov_number) } } }