Compare commits

...

61 Commits

Author SHA1 Message Date
Erimel
17d81bb05c Merge branch 'main' into fingertracking 2025-09-26 18:45:45 -04:00
Aed
f2b4d468c2 Added Done button to end of mounting calibration (#1557) 2025-09-23 22:59:22 +02:00
Eiren Rain
1c0f5c381b Update CODEOWNERS (#1558) 2025-09-23 23:54:01 +03:00
lucas lelievre
fb25421ab0 OOPS 2025-09-23 20:23:51 +00:00
lucas lelievre
4890b4a71c Update CODEOWNERS 2025-09-23 22:17:57 +02:00
lucas lelievre
e38732a81c Remove secondary colors usage + improve spacing (#1554) 2025-09-22 21:27:07 +02:00
Eiren Rain
2a78354b17 Disable update prompt on Android (#1547) 2025-09-19 19:55:27 +03:00
Eiren Rain
3fb4347277 Fix Stagered fw update detection (#1544) 2025-09-19 19:55:17 +03:00
Eiren Rain
081e88ead4 Update gui/src/hooks/firmware-update.ts
Co-authored-by: Butterscotch! <bscotchvanilla@gmail.com>
2025-09-18 15:34:56 +03:00
Uriel
73f41f8fc6 Disable update prompt on Android 2025-09-17 19:32:30 -03:00
loucass003
f233f59079 Fix Stagered fw update detection 2025-09-16 23:24:53 +02:00
Eiren Rain
9644b00690 fix: use transaction id in RPC reponses (#1527) 2025-09-16 18:54:12 +03:00
Eiren Rain
543e319c25 HID support on Android (#1532) 2025-09-16 14:57:24 +03:00
Eiren Rain
b7ef70f5c6 Bump actions/setup-java from 4 to 5 (#1522) 2025-09-16 14:25:42 +03:00
Eiren Rain
af171f8812 Fix Android serial support (#1533) 2025-09-16 14:25:11 +03:00
dependabot[bot]
8ec3cade06 Bump actions/setup-java from 4 to 5
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-16 11:23:12 +00:00
Eiren Rain
bec8be46ca Add folder icon option for the file input (#1536) 2025-09-16 14:22:47 +03:00
Eiren Rain
ac7f809132 Bump actions/setup-node from 4 to 5 (#1540) 2025-09-16 14:22:10 +03:00
Eiren Rain
fcb241fab8 Bump actions/labeler from 5 to 6 (#1541) 2025-09-16 14:21:55 +03:00
Eiren Rain
fbdcf2fa2d Race condition patch for constraints (#1535) 2025-09-16 14:21:13 +03:00
Butterscotch!
73c821e07b Merge remote-tracking branch 'upstream/main' into bscotch/android-serial-fix 2025-09-11 16:07:57 -04:00
Butterscotch!
422ddd7ee8 Write serial directly on Android 2025-09-11 16:07:49 -04:00
Butterscotch!
943ad974ec Limit Android serial buffer to 1024 chars 2025-09-11 16:03:57 -04:00
dependabot[bot]
12a5d59e89 Bump actions/labeler from 5 to 6
Bumps [actions/labeler](https://github.com/actions/labeler) from 5 to 6.
- [Release notes](https://github.com/actions/labeler/releases)
- [Commits](https://github.com/actions/labeler/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/labeler
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 14:22:53 +00:00
dependabot[bot]
85286651dd Bump actions/setup-node from 4 to 5
Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 5.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 14:22:49 +00:00
Uriel
e9b3efe3d5 Add folder icon option for the file input 2025-09-05 14:13:54 -04:00
Butterscotch!
75fa1aa1e2 Messy race condition patch for constraints 2025-09-04 06:50:34 -04:00
Butterscotch!
7d642a21f5 Use a read buffer for Android serial
The serial reader on Android does not have a big enough buffer for our serial output, causing issues when parsing it. We can buffer until we reach a newline character to make parsing work.
2025-09-04 06:35:30 -04:00
Butterscotch!
4e1421180c Ensure serial port closure 2025-09-04 04:33:39 -04:00
Butterscotch!
f0a72645d7 Clear request more & update usbReceiver comment 2025-09-04 04:27:20 -04:00
Butterscotch!
86b8e0a904 Fix serial staying open after disconnect 2025-09-04 04:18:52 -04:00
Butterscotch!
789e6a6962 Make ACTION_USB_PERMISSION const 2025-09-04 02:37:52 -04:00
Butterscotch!
f03b300d72 Use val for usbReceiver 2025-09-04 02:26:36 -04:00
Butterscotch!
6b0822c0f6 Rename HID "Service" to "Manager" 2025-09-04 02:26:08 -04:00
Butterscotch!
d9774cab87 Extra requestingPermission handling for new device 2025-09-04 02:24:21 -04:00
Butterscotch!
f07f9f3718 Add comments for USB attach/detach events 2025-09-04 02:22:23 -04:00
Butterscotch!
486be0973b Use USB events for serial & re-request permission 2025-09-04 02:17:28 -04:00
Butterscotch!
9246dd00d3 Additional comments 2025-09-04 01:41:01 -04:00
Butterscotch!
baf515791d Fix HID service comments 2025-09-04 01:37:15 -04:00
Butterscotch!
876450d764 Make Android USB host mode optional 2025-09-04 01:34:22 -04:00
Butterscotch!
44e90e255b Rename HID classes for clarity 2025-09-04 01:34:06 -04:00
Butterscotch!
41026ab851 Move deviceIdLookup to HIDCommon 2025-09-04 01:28:26 -04:00
Butterscotch!
d8509c431d Remove device reattach handling
Events should be good enough :3
2025-09-04 01:05:35 -04:00
Butterscotch!
66df65eb80 Use Android USB events for HID instead of polling 2025-09-04 00:31:43 -04:00
Butterscotch!
30641a5809 Reconnect HID 2025-09-04 00:31:43 -04:00
Butterscotch!
574523daec Persist UsbDeviceConnection 2025-09-04 00:31:43 -04:00
Butterscotch!
1913605d16 De-duplicate HID code 2025-09-04 00:31:42 -04:00
Butterscotch!
8aeecab51f Implement awful reading 2025-09-04 00:29:45 -04:00
Butterscotch!
9fd5d6a187 Initial Android USB HID support 2025-09-04 00:29:45 -04:00
peelz
0c09c22306 fix: use transaction id in RPC reponses 2025-09-01 13:46:48 -04:00
Erimel
da202e4123 Log when trying to send unsupported input to openvr 2025-08-10 21:53:08 -04:00
Erimel
4e861a58ef Merge branch 'main' into fingertracking 2025-08-10 21:16:51 -04:00
Erimel
072da46a33 Merge branch 'main' into fingertracking 2025-07-22 20:17:57 -04:00
Erimel
c9dac8ebe3 tap inputs 2025-07-22 20:16:44 -04:00
Erimel
0221cf3985 Inputs WIP 2025-07-11 22:08:47 -04:00
Erimel
dd676d5cca Remove single tap 2025-07-11 21:13:59 -04:00
Erimel
6d56af3fb3 driver protocol version check and shareable bones 2025-07-03 22:20:02 -04:00
Erimel
365437531b Send finger rotations to driver 2025-06-25 22:21:10 -04:00
Erimel
78b53224a8 WIP getting SlimeVR hand controllers to work in VR 2025-06-21 22:00:45 -04:00
Erimel
166d2b48f1 Add support for fingers protobuf data for the driver 2025-06-21 18:50:12 -04:00
Erimel
878fb42c13 Fix protobuf_update.bat script and update to 4.31.1 2025-06-21 16:56:06 -04:00
115 changed files with 6845 additions and 2499 deletions

32
.github/CODEOWNERS vendored
View File

@@ -2,23 +2,23 @@
* @Eirenliel
# Make everyone be able to approve SolarXR submodule changes
/solarxr-protocol @ButterscotchV @Erimelowo @ImUrX @loucass003
/solarxr-protocol @ButterscotchV @Erimelowo @loucass003
# Make Loucas and Uriel the owners of all GUI stuff
/gui/ @ImUrX @loucass003
/pnpm-lock.yaml @ImUrX @loucass003
/pnpm-workspace.yaml @ImUrX @loucass003
# Make Loucass the owner of all GUI stuff
/gui/ @loucass003
/pnpm-lock.yaml @loucass003
/pnpm-workspace.yaml @loucass003
# Uriel and Erimel responsible for i18n
/gui/public/i18n/ @ImUrX @Erimelowo
/gui/src/i18n/ @ImUrX @Erimelowo
/l10n.toml @ImUrX @Erimelowo
# loucass003 and Erimel responsible for i18n
/gui/public/i18n/ @loucass003 @Erimelowo
/gui/src/i18n/ @loucass003 @Erimelowo
/l10n.toml @loucass003 @Erimelowo
/gui/src/components/settings/ @Erimelowo @ImUrX @loucass003
/gui/src/components/settings/ @Erimelowo @loucass003
# Rust part of the GUI
/gui/src-tauri/ @ImUrX
/Cargo.lock @ImUrX
/gui/src-tauri/ @loucass003
/Cargo.lock @loucass003
# Some server code~
/server/ @ButterscotchV @Eirenliel @Erimelowo
@@ -32,7 +32,7 @@
/server/src/main/java/dev/slimevr/filtering/ @Erimelowo
# Linux files
*.nix @ImUrX
/flake.lock @ImUrX
/dev.slimevr.SlimeVR.metainfo.xml @ImUrX
/.envrc @ImUrX
*.nix @loucass003
/flake.lock @loucass003
/dev.slimevr.SlimeVR.metainfo.xml @loucass003
/.envrc @loucass003

View File

@@ -27,7 +27,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -85,7 +85,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with:
node-version: '22.x'

View File

@@ -27,7 +27,7 @@ jobs:
run: git fetch --tags origin --recurse-submodules=no --force
- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'adopt'
@@ -56,7 +56,7 @@ jobs:
run: git fetch --tags origin --recurse-submodules=no --force
- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'adopt'
@@ -95,7 +95,7 @@ jobs:
run: git fetch --tags origin --recurse-submodules=no --force
- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'adopt'
@@ -105,7 +105,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -190,7 +190,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -266,7 +266,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'
@@ -351,7 +351,7 @@ jobs:
- uses: pnpm/action-setup@v4
- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version-file: '.node-version'
cache: 'pnpm'

View File

@@ -17,6 +17,6 @@ jobs:
pull-requests: write
steps:
- uses: actions/labeler@v5
- uses: actions/labeler@v6
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -1125,6 +1125,7 @@ onboarding-automatic_mounting-preparation-v2-step-2 = 3. Hold the position until
onboarding-automatic_mounting-put_trackers_on-title = Put on your trackers
onboarding-automatic_mounting-put_trackers_on-description = To calibrate mounting orientations, we're gonna use the trackers you just assigned. Put on all your trackers, you can see which are which in the figure to the right.
onboarding-automatic_mounting-put_trackers_on-next = I have all my trackers on
onboarding-automatic_mounting-return-home = Done
## Tracker manual proportions setupa
onboarding-manual_proportions-back = Go Back to Reset tutorial

View File

@@ -206,6 +206,11 @@ export default function App() {
}, []);
useEffect(() => {
// don't show update stuff when on android
if (window.__ANDROID__?.isThere()) {
setUpdateFound('');
return;
}
async function fetchReleases() {
const releases = await fetch(
`https://api.github.com/repos/${GH_REPO}/releases`

View File

@@ -66,9 +66,7 @@ export function WidgetsComponent() {
bodyPartsToReset="fingers"
></ResetButton>
<ClearMountingButton></ClearMountingButton>
{(typeof __ANDROID__ === 'undefined' || !__ANDROID__?.isThere()) && (
<BVHButton></BVHButton>
)}
{!window.__ANDROID__?.isThere() && <BVHButton></BVHButton>}
<TrackingPauseButton></TrackingPauseButton>
</div>
<div className="w-full">

View File

@@ -28,7 +28,7 @@ export function ArrowLink({
};
return classNames(
variantsMap[variant],
'flex gap-2 hover:fill-background-10 hover:text-background-10 fill-background-30 text-background-30'
'flex gap-2 hover:fill-background-10 fill-background-30 text-background-10'
);
}, [variant]);

View File

@@ -53,7 +53,7 @@ export function CheckBox({
className={classNames(
{
'rounded-lg': outlined,
'text-background-30': !outlined || disabled,
'text-background-10': !outlined || disabled,
'bg-background-60': outlined && color === 'primary',
'bg-background-70': outlined && color === 'secondary',
'bg-background-50': outlined && color === 'tertiary',

View File

@@ -12,6 +12,8 @@ import { FileIcon } from './icon/FileIcon';
import { UploadFileIcon } from './icon/UploadFileIcon';
import { Typography } from './Typography';
import { CloseIcon } from './icon/CloseIcon';
import { FolderIcon } from './icon/FolderIcon';
import { UploadFolderIcon } from './icon/UploadFolderIcon';
interface InputProps {
variant?: 'primary' | 'secondary';
@@ -22,9 +24,11 @@ interface InputProps {
export const FileInputContentBlank = ({
isDragging,
label,
directory = false,
}: {
isDragging: boolean;
label: string;
directory?: boolean;
}) => {
return (
<div
@@ -36,7 +40,11 @@ export const FileInputContentBlank = ({
)}
>
<span className="flex items-center space-x-2 pointer-events-none">
<UploadFileIcon isDragging={isDragging} />
{directory ? (
<UploadFolderIcon isDragging={isDragging} />
) : (
<UploadFileIcon isDragging={isDragging} />
)}
<div>
<Localized
id={label}
@@ -57,10 +65,12 @@ export const FileInputContentBlank = ({
export const FileInputContentFile = ({
importedFileName,
directory = false,
onClearPicker,
}: {
importedFileName: string;
onClearPicker: () => any;
directory?: boolean;
}) => {
return (
<div
@@ -72,7 +82,7 @@ export const FileInputContentFile = ({
>
<div className="flex items-center space-x-2">
<div className="flex items-center space-x-2 px-4">
<FileIcon />
{directory ? <FolderIcon /> : <FileIcon />}
<span>{importedFileName}</span>
</div>
<span className="flex-grow"></span>

View File

@@ -47,19 +47,19 @@ export const InputInside = forwardRef<
const classes = useMemo(() => {
const variantsMap = {
primary: classNames({
'placeholder:text-background-30 bg-background-60 border-background-60':
'placeholder:text-background-10 placeholder:italic bg-background-60 border-background-60':
!disabled,
'text-background-30 placeholder:text-background-30 border-background-70 bg-background-70':
disabled,
}),
secondary: classNames({
'placeholder:text-background-30 bg-background-50 border-background-50':
'placeholder:text-background-10 placeholder:italic bg-background-50 border-background-50':
!disabled,
'text-background-40 placeholder:text-background-40 border-background-70 bg-background-70':
disabled,
}),
tertiary: classNames({
'placeholder:text-background-30 bg-background-40 border-background-40':
'placeholder:text-background-10 placeholder:italic bg-background-40 border-background-40':
!disabled,
'text-background-30 placeholder:text-background-30 border-background-70 bg-background-70':
disabled,

View File

@@ -51,9 +51,7 @@ export function Radio({
<div className="flex flex-col gap-2 pointer-events-none">
{children ? children : <Typography bold>{label}</Typography>}
{description && (
<Typography variant="standard" color="secondary">
{description}
</Typography>
<Typography variant="standard">{description}</Typography>
)}
</div>
</label>

View File

@@ -24,10 +24,11 @@ export function InnerTauriFileInput({
<div ref={ref} onClick={async () => onChange(await open({ directory }))}>
{value !== null
? FileInputContentFile({
directory,
importedFileName: value,
onClearPicker: () => onChange(null),
})
: FileInputContentBlank({ isDragging: false, label })}
: FileInputContentBlank({ isDragging: false, label, directory })}
</div>
);
}

View File

@@ -0,0 +1,12 @@
export function FolderIcon({ width = 24 }: { width?: number }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
width={width}
>
<path d="M19.5 21a3 3 0 0 0 3-3v-4.5a3 3 0 0 0-3-3h-15a3 3 0 0 0-3 3V18a3 3 0 0 0 3 3h15ZM1.5 10.146V6a3 3 0 0 1 3-3h5.379a2.25 2.25 0 0 1 1.59.659l2.122 2.121c.14.141.331.22.53.22H19.5a3 3 0 0 1 3 3v1.146A4.483 4.483 0 0 0 19.5 9h-15a4.483 4.483 0 0 0-3 1.146Z" />
</svg>
);
}

View File

@@ -0,0 +1,25 @@
import classNames from 'classnames';
export function UploadFolderIcon({
width = 24,
isDragging = false,
}: {
width?: number;
isDragging?: boolean;
}) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 5.5499997 4.7600007"
width={width}
fill="currentColor"
className={classNames('transition-transform', isDragging && 'scale-150')}
>
<path
fill-rule="evenodd"
d="m 4.76,4.76 c 0.44,0 0.79,-0.36 0.79,-0.79 v -2.39 c 0,-0.44 -0.36,-0.79 -0.79,-0.79 H 3.34 c -0.05,0 -0.1,-0.02 -0.14,-0.06 L 2.64,0.17 C 2.53,0.06 2.38,0 2.22,0 H 0.79 C 0.35,0 0,0.36 0,0.79 V 3.97 C 0,4.41 0.36,4.76 0.79,4.76 Z M 2.58,3.71 c 0,0.26 0.4,0.26 0.4,0 V 2.6 L 3.44,3.06 C 3.63,3.23 3.89,2.97 3.72,2.78 L 2.93,1.99 c -0.08,-0.08 -0.2,-0.08 -0.28,0 L 1.86,2.78 C 1.65,2.97 1.95,3.26 2.14,3.06 L 2.6,2.6 Z"
clip-rule="evenodd"
/>
</svg>
);
}

View File

@@ -240,7 +240,7 @@ export function AddImusStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-add_imus_step-description')}
</Typography>
</div>
@@ -297,7 +297,7 @@ export function AddImusStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -53,7 +53,7 @@ export function BoardPinsStep({
<>
<div className="flex flex-col w-full justify-between text-background-10">
<div className="flex flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-board_pins_step-description')}
</Typography>
</div>
@@ -172,7 +172,7 @@ export function BoardPinsStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -68,7 +68,7 @@ export function BuildStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-grow flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-build_step-description')}
</Typography>
</div>
@@ -82,7 +82,7 @@ export function BuildStep({
: SlimeState.SAD
}
></LoaderIcon>
<Typography variant="section-title" color="secondary">
<Typography variant="section-title">
{l10n.getString('firmware_tool-build-' + buildStatus.status)}
</Typography>
</div>
@@ -91,7 +91,7 @@ export function BuildStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -85,9 +85,7 @@ function FirmwareToolContent() {
.getString('firmware_tool-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>

View File

@@ -22,7 +22,7 @@ export function FlashBtnStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-grow flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-flashbtn_step-description')}
</Typography>
{defaultConfig?.boardConfig.type ===

View File

@@ -160,7 +160,7 @@ function SerialDevicesList({
</Localized>
{Object.keys(devices).length === 0 ? (
<Localized id="firmware_tool-flash_method_serial-no_devices">
<Typography variant="standard" color="secondary"></Typography>
<Typography variant="standard"></Typography>
</Localized>
) : (
<Dropdown
@@ -274,7 +274,7 @@ function OTADevicesList({
</Localized>
{devices.length === 0 && (
<Localized id="firmware_tool-flash_method_ota-no_devices">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
)}
<div className="grid xs-settings:grid-cols-2 mobile-settings:grid-cols-1 gap-2">
@@ -347,7 +347,7 @@ export function FlashingMethodStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-grow flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-flash_method_step-description')}
</Typography>
</div>
@@ -416,7 +416,7 @@ export function FlashingMethodStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -164,7 +164,7 @@ export function FlashingStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-grow flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-flashing_step-description')}
</Typography>
</div>

View File

@@ -27,7 +27,7 @@ export function SelectBoardStep({
<>
<div className="flex flex-col w-full">
<div className="flex flex-grow flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-board_step-description')}
</Typography>
</div>
@@ -91,7 +91,7 @@ export function SelectBoardStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -40,7 +40,7 @@ export function SelectFirmwareStep({
<>
<div className="flex flex-col w-full">
<div className="flex justify-between items-center mobile:flex-col gap-4">
<Typography color="secondary">
<Typography>
{l10n.getString('firmware_tool-select_firmware_step-description')}
</Typography>
<div>
@@ -109,7 +109,7 @@ export function SelectFirmwareStep({
<div className="flex justify-center flex-col items-center gap-3 h-44">
<LoaderIcon slimeState={SlimeState.JUMPY}></LoaderIcon>
<Localized id="firmware_tool-loading">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
)}

View File

@@ -320,7 +320,7 @@ export function FirmwareUpdate() {
<Typography variant="section-title"></Typography>
</Localized>
<Localized id="firmware_update-devices-description">
<Typography variant="standard" color="secondary"></Typography>
<Typography variant="standard"></Typography>
</Localized>
<div className="flex flex-col gap-4 overflow-y-auto xs:max-h-[530px]">
{devices.length === 0 &&

View File

@@ -28,8 +28,8 @@ export function SkipSetupButton({
<button
type="button"
className={classNames(
'text-background-40 hover:text-background-30',
'stroke-background-40 hover:stroke-background-30',
'text-background-10 hover:text-background-20',
'stroke-background-10 hover:stroke-background-20',
'absolute xs:-top-10 xs:right-4 mobile:top-0 mobile:right-0'
)}
onClick={onClick}

View File

@@ -132,9 +132,7 @@ export function CalibrationTutorialPage() {
id="onboarding-calibration_tutorial-description-v1"
elems={{ b: <b></b> }}
>
<Typography color="secondary">
Description on calibration of IMU
</Typography>
<Typography>Description on calibration of IMU</Typography>
</Localized>
<div>
<div className="xs:hidden flex flex-row justify-center">

View File

@@ -221,10 +221,10 @@ export function ConnectTrackersPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-connect_tracker-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-connect_tracker-description-p0-v1')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-connect_tracker-description-p1-v1')}
</Typography>
<div className="flex flex-col gap-2 py-5">
@@ -281,7 +281,7 @@ export function ConnectTrackersPage() {
{l10n.getString('onboarding-connect_tracker-usb')}
</Typography>
<div className="flex fill-background-10 gap-1">
<Typography color="secondary">
<Typography>
{l10n.getString(statusLabelMap[provisioningStatus])}
</Typography>
</div>
@@ -319,7 +319,7 @@ export function ConnectTrackersPage() {
</div>
</div>
<div style={{ gridArea: 't' }} className="flex items-center px-5">
<Typography color="secondary" bold>
<Typography bold>
{l10n.getString('onboarding-connect_tracker-connected_trackers', {
amount: connectedIMUTrackers.length,
})}

View File

@@ -20,7 +20,7 @@ export function DonePage() {
{l10n.getString('onboarding-done-title')}
</Typography>
<div className="flex flex-col items-center">
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-done-description')}
</Typography>
</div>

View File

@@ -22,7 +22,7 @@ export function EnterVRPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-enter_vr-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-enter_vr-description')}
</Typography>
</div>

View File

@@ -125,7 +125,7 @@ export function ResetTutorialPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-reset_tutorial')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-reset_tutorial-explanation')}
</Typography>
<div className="flex">
@@ -165,7 +165,7 @@ export function ResetTutorialPage() {
curIndex >= order.length && 'hidden'
)}
>
<Typography whitespace="whitespace-pre-line" color="secondary">
<Typography whitespace="whitespace-pre-line">
{l10n.getString(`onboarding-reset_tutorial-${curIndex}`, {
taps: tapSettings[curIndex],
})}
@@ -184,7 +184,7 @@ export function ResetTutorialPage() {
curIndex >= order.length && 'hidden'
)}
>
<Typography whitespace="whitespace-pre-line" color="secondary">
<Typography whitespace="whitespace-pre-line">
{l10n.getString(`onboarding-reset_tutorial-${curIndex}`, {
taps: tapSettings[curIndex],
})}

View File

@@ -35,9 +35,7 @@ export function WifiCredsPage() {
.getString('onboarding-wifi_creds-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
{!state.alonePage && (

View File

@@ -32,7 +32,7 @@ export function AutomaticProportionsPage() {
{l10n.getString('onboarding-automatic_proportions-title')}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-description'
)}

View File

@@ -52,7 +52,7 @@ export function ScaledProportionsPage() {
{l10n.getString('onboarding-scaled_proportions-title')}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-scaled_proportions-description')}
</Typography>
</div>

View File

@@ -72,7 +72,7 @@ export function CheckFloorHeightStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-check_floor_height-description'
)}
@@ -81,7 +81,7 @@ export function CheckFloorHeightStep({
id="onboarding-automatic_proportions-check_floor_height-calculation_warning-v2"
elems={{ u: <span className="underline"></span> }}
>
<Typography color="secondary" bold>
<Typography bold>
Press the button to get your height!
</Typography>
</Localized>

View File

@@ -60,7 +60,7 @@ export function CheckHeightStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-check_height-description-v2'
)}
@@ -69,7 +69,7 @@ export function CheckHeightStep({
id="onboarding-automatic_proportions-check_height-calculation_warning-v3"
elems={{ u: <span className="underline"></span> }}
>
<Typography color="secondary" bold>
<Typography bold>
Press the button to get your height!
</Typography>
</Localized>

View File

@@ -12,7 +12,7 @@ export function DoneStep({ variant }: { variant: 'onboarding' | 'alone' }) {
<Typography variant="section-title">
{l10n.getString('onboarding-automatic_proportions-done-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-automatic_proportions-done-description')}
</Typography>
</div>

View File

@@ -26,13 +26,13 @@ export function PreparationStep({
</Typography>
<div>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-0">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-1">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-2">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
</div>

View File

@@ -28,7 +28,7 @@ export function PutTrackersOnStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-put_trackers_on-description'
)}

View File

@@ -97,7 +97,7 @@ export function Recording({
'onboarding-automatic_proportions-recording-description-p0'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-recording-description-p1'
)}
@@ -110,7 +110,7 @@ export function Recording({
.split('\n')
.map((line, i) => (
<li key={i}>
<Typography color="secondary">{line}</Typography>
<Typography>{line}</Typography>
</li>
))}
</>
@@ -137,7 +137,7 @@ export function Recording({
)
.otherwise(() => undefined)}
></ProgressBar>
<Typography color="secondary">
<Typography>
{match([hasCalibration, hasRecording])
.returnType<ReactNode>()
.with([ProcessStatus.PENDING, ProcessStatus.FULFILLED], () =>

View File

@@ -31,7 +31,7 @@ export function RequirementsStep({
.split('\n')
.map((line, i) => (
<li key={i}>
<Typography color="secondary">{line}</Typography>
<Typography>{line}</Typography>
</li>
))}
</>

View File

@@ -63,7 +63,7 @@ export function StartRecording({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-start_recording-description'
)}
@@ -76,7 +76,7 @@ export function StartRecording({
.split('\n')
.map((line, i) => (
<li key={i}>
<Typography color="secondary">{line}</Typography>
<Typography>{line}</Typography>
</li>
))}
</>

View File

@@ -42,7 +42,7 @@ export function VerifyResultsStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_proportions-verify_results-description'
)}
@@ -65,7 +65,7 @@ export function VerifyResultsStep({
>
{bodyParts?.map(({ bone, label, value }) => (
<div className="flex justify-between" key={bone}>
<Typography color="secondary">{label}</Typography>
<Typography>{label}</Typography>
<Typography bold sentryMask>
{(value * 100).toFixed(2)} CM
</Typography>

View File

@@ -12,7 +12,7 @@ export function DoneStep({ variant }: { variant: 'onboarding' | 'alone' }) {
<Typography variant="section-title">
{l10n.getString('onboarding-scaled_proportions-done-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-scaled_proportions-done-description')}
</Typography>
</div>

View File

@@ -90,7 +90,7 @@ export function ManualHeightStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-scaled_proportions-manual_height-description-v2'
)}

View File

@@ -26,7 +26,7 @@ export function ResetProportionsStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-scaled_proportions-reset_proportion-description'
)}

View File

@@ -33,7 +33,7 @@ export function AutomaticMountingPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-automatic_mounting-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-automatic_mounting-description')}
</Typography>
</div>

View File

@@ -99,7 +99,7 @@ export function ManualMountingPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-manual_mounting')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-manual_mounting-description')}
</Typography>
<TipBox>{l10n.getString('tips-find_tracker')}</TipBox>

View File

@@ -17,16 +17,12 @@ export function MountingChoose() {
return (
<>
<div className="flex flex-col gap-5 h-full items-center w-full xs:justify-center relative overflow-y-auto px-4 pb-4">
<div className="flex flex-col gap-4 justify-center">
<div className="flex flex-col gap-8 justify-center">
<div className="xs:w-10/12 xs:max-w-[666px]">
<Typography variant="main-title">
{l10n.getString('onboarding-choose_mounting')}
</Typography>
<Typography
variant="standard"
color="secondary"
whitespace="whitespace-pre-line"
>
<Typography variant="standard" whitespace="whitespace-pre-line">
{l10n.getString('onboarding-choose_mounting-description')}
</Typography>
</div>
@@ -37,12 +33,19 @@ export function MountingChoose() {
>
<div
className={classNames(
'rounded-lg p-4 flex',
'rounded-lg p-4 flex relative',
!state.alonePage && 'bg-background-70',
state.alonePage && 'bg-background-60'
)}
>
<div className="flex flex-col gap-4">
<div className="bg-accent-background-30 absolute -left-4 -top-5 p-1.5 rounded-lg">
<Typography variant="vr-accessible" italic>
{l10n.getString(
'onboarding-choose_mounting-auto_mounting-label-v2'
)}
</Typography>
</div>
<div className="flex flex-col gap-4 ">
<div className="flex flex-grow flex-col gap-4 max-w-sm">
<div>
<Typography variant="main-title" bold>
@@ -50,14 +53,9 @@ export function MountingChoose() {
'onboarding-choose_mounting-auto_mounting'
)}
</Typography>
<Typography variant="vr-accessible" italic>
{l10n.getString(
'onboarding-choose_mounting-auto_mounting-label-v2'
)}
</Typography>
</div>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-choose_mounting-auto_mounting-description'
)}
@@ -98,14 +96,9 @@ export function MountingChoose() {
'onboarding-choose_mounting-manual_mounting'
)}
</Typography>
<Typography variant="vr-accessible" italic>
{l10n.getString(
'onboarding-choose_mounting-manual_mounting-label-v2'
)}
</Typography>
</div>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-choose_mounting-manual_mounting-description'
)}

View File

@@ -20,7 +20,7 @@ export function DoneStep({
<Typography variant="section-title">
{l10n.getString('onboarding-automatic_mounting-done-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-automatic_mounting-done-description')}
</Typography>
</div>
@@ -38,6 +38,11 @@ export function DoneStep({
{l10n.getString('onboarding-automatic_mounting-next')}
</Button>
)}
{variant === 'alone' && (
<Button className="flex gap-3" variant="primary" to="/">
{l10n.getString('onboarding-automatic_mounting-return-home')}
</Button>
)}
</div>
<SkeletonVisualizerWidget />

View File

@@ -27,12 +27,12 @@ export function MountingResetStep({
)}
</Typography>
<div className="flex flex-col gap-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_mounting-mounting_reset-step-0'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_mounting-mounting_reset-step-1'
)}

View File

@@ -26,13 +26,13 @@ export function PreparationStep({
</Typography>
<div>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-0">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-1">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-2">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
</div>

View File

@@ -28,7 +28,7 @@ export function PutTrackersOnStep({
)}
</Typography>
<div>
<Typography color="secondary">
<Typography>
{l10n.getString(
'onboarding-automatic_mounting-put_trackers_on-description'
)}

View File

@@ -136,7 +136,7 @@ export function StayAlignedSetup() {
<Typography variant="main-title">
{l10n.getString('onboarding-stay_aligned-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-stay_aligned-description')}
</Typography>
</div>

View File

@@ -11,7 +11,7 @@ export function DoneStep({ goTo }: VerticalStepComponentProps) {
<Typography variant="main-title"></Typography>
</Localized>
<Localized id="onboarding-stay_aligned-done-description-2">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
<div className="flex gap-3 justify-between">

View File

@@ -16,13 +16,13 @@ export function PreparationStep({
<div className="flex flex-col flex-grow justify-between py-2 gap-2">
<div className="flex flex-col gap-1">
<Localized id="onboarding-automatic_mounting-preparation-v2-step-0">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-1">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
<Localized id="onboarding-automatic_mounting-preparation-v2-step-2">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
<Localized id="onboarding-stay_aligned-preparation-tip">

View File

@@ -19,7 +19,7 @@ export function PutTrackersOnStep({ nextStep }: VerticalStepComponentProps) {
<div className="flex flex-grow flex-col gap-4">
<div>
<Localized id="onboarding-stay_aligned-put_trackers_on-description">
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
<div className="flex">

View File

@@ -30,9 +30,7 @@ function PosePage({
<div className="flex flex-col py-2">
<div className="flex flex-col gap-2">
{descriptionKeys.map((descriptionKey) => (
<Typography color="secondary">
{l10n.getString(descriptionKey)}
</Typography>
<Typography>{l10n.getString(descriptionKey)}</Typography>
))}
</div>
<div className="flex pt-1 items-center fill-background-50 justify-center px-12">

View File

@@ -11,16 +11,16 @@ export function VerifyMountingStep({
<div className="flex flex-grow flex-col gap-4 py-2">
<div className="flex flex-col gap-2">
<Localized id="onboarding-stay_aligned-verify_mounting-step-0">
<Typography color="secondary" />
<Typography />
</Localized>
<Localized id="onboarding-stay_aligned-verify_mounting-step-1">
<Typography color="secondary" />
<Typography />
</Localized>
<Localized id="onboarding-stay_aligned-verify_mounting-step-2">
<Typography color="secondary" />
<Typography />
</Localized>
<Localized id="onboarding-stay_aligned-verify_mounting-step-3">
<Typography color="secondary" />
<Typography />
</Localized>
</div>
<div className="flex gap-3 justify-between">

View File

@@ -43,7 +43,7 @@ const ItemContent = ({
mode,
})}
</Typography>
<Typography variant="standard" color="secondary">
<Typography variant="standard">
{l10n.getString('onboarding-assign_trackers-option-description', {
mode,
})}

View File

@@ -284,11 +284,11 @@ export function TrackersAssignPage() {
<Typography variant="main-title">
{l10n.getString('onboarding-assign_trackers-title')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-assign_trackers-description')}
</Typography>
<div className="flex gap-1">
<Typography color="secondary">
<Typography>
{l10n.getString('onboarding-assign_trackers-assigned', {
assigned: assignedTrackers.length,
trackers: trackers.length,

View File

@@ -63,11 +63,11 @@ export function AdvancedSettings() {
<div className="grid gap-4">
<div className="sm:grid sm:grid-cols-[1.75fr,_1fr] items-center">
<div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-utils-advanced-reset-gui')}
</Typography>
<div className="flex flex-col">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-utils-advanced-reset-gui-description'
)}
@@ -95,11 +95,11 @@ export function AdvancedSettings() {
<div className="sm:grid sm:grid-cols-[1.75fr,_1fr] items-center">
<div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-utils-advanced-reset-server')}
</Typography>
<div className="flex flex-col">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-utils-advanced-reset-server-description'
)}
@@ -132,11 +132,11 @@ export function AdvancedSettings() {
<div className="sm:grid sm:grid-cols-[1.75fr,_1fr] items-center">
<div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-utils-advanced-reset-all')}
</Typography>
<div className="flex flex-col">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-utils-advanced-reset-all-description'
)}
@@ -168,11 +168,11 @@ export function AdvancedSettings() {
<div className="sm:grid sm:grid-cols-[1.75fr,_1fr] items-center">
<div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-utils-advanced-open_data-v1')}
</Typography>
<div className="flex flex-col">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-utils-advanced-open_data-description-v1'
)}
@@ -188,11 +188,11 @@ export function AdvancedSettings() {
<div className="sm:grid sm:grid-cols-[1.75fr,_1fr] items-center">
<div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-utils-advanced-open_logs')}
</Typography>
<div className="flex flex-col">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-utils-advanced-open_logs-description'
)}

View File

@@ -459,7 +459,7 @@ export function GeneralSettings() {
<Typography variant="main-title">
{l10n.getString('settings-general-steamvr')}
</Typography>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-steamvr-subtitle')}
</Typography>
<div className="flex flex-col py-2">
@@ -467,13 +467,11 @@ export function GeneralSettings() {
.getString('settings-general-steamvr-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</div>
<div className="flex flex-col pt-4"></div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-steamvr-trackers-tracker_toggling'
)}
@@ -485,9 +483,7 @@ export function GeneralSettings() {
)
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</div>
<CheckBox
@@ -608,7 +604,7 @@ export function GeneralSettings() {
<Typography variant="main-title">
{l10n.getString('settings-general-tracker_mechanics')}
</Typography>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-tracker_mechanics-filtering')}
</Typography>
<div className="flex flex-col pt-2 pb-4">
@@ -618,9 +614,7 @@ export function GeneralSettings() {
)
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</div>
<Typography>
@@ -690,7 +684,7 @@ export function GeneralSettings() {
/>
</div>
<div className="flex flex-col pt-5 pb-3">
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-tracker_mechanics-save_mounting_reset'
)}
@@ -699,7 +693,7 @@ export function GeneralSettings() {
id="settings-general-tracker_mechanics-save_mounting_reset-description"
elems={{ b: <b></b> }}
>
<Typography color="secondary"></Typography>
<Typography></Typography>
</Localized>
</div>
<CheckBox
@@ -725,19 +719,19 @@ export function GeneralSettings() {
<Typography variant="main-title">
{l10n.getString('settings-general-fk_settings')}
</Typography>
<div className="flex flex-col pt-2 pb-4">
<Typography bold>
<div className="flex flex-col pt-2 pb-4 gap-2">
<Typography variant="section-title">
{l10n.getString(
'settings-general-fk_settings-leg_tweak-skating_correction'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-leg_tweak-skating_correction-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-4">
<div className="grid sm:grid-cols-1 gap-2 pb-4">
<CheckBox
variant="toggle"
outlined
@@ -761,18 +755,18 @@ export function GeneralSettings() {
</div>
<div className="flex flex-col pt-2 pb-2">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-fk_settings-leg_fk')}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-leg_tweak-floor_clip-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<div className="grid sm:grid-cols-1 gap-2 pb-3">
<CheckBox
variant="toggle"
outlined
@@ -784,7 +778,7 @@ export function GeneralSettings() {
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-leg_tweak-foot_plant-description'
)}
@@ -802,7 +796,7 @@ export function GeneralSettings() {
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-leg_tweak-toe_snap-description'
)}
@@ -821,10 +815,10 @@ export function GeneralSettings() {
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-fk_settings-arm_fk')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-arm_fk-description'
)}
@@ -843,110 +837,114 @@ export function GeneralSettings() {
</div>
<div className="flex flex-col pt-2">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-fk_settings-reset_settings')}
</Typography>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-reset_settings-reset_hmd_pitch-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetHmdPitch"
label={l10n.getString(
'settings-general-fk_settings-reset_settings-reset_hmd_pitch'
)}
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet-description-v1'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-1 gap-3 pb-3">
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetMountingFeet"
label={l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet-v1'
)}
/>
<div className="grid grid-cols-2 gap-2">
<div className="flex flex-col gap-2">
<Typography>
{l10n.getString(
'settings-general-fk_settings-reset_settings-reset_hmd_pitch-description'
)}
</Typography>
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetHmdPitch"
label={l10n.getString(
'settings-general-fk_settings-reset_settings-reset_hmd_pitch'
)}
/>
</div>
<div className="flex flex-col gap-2 justify-end">
<Typography>
{l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet-description-v1'
)}
</Typography>
<CheckBox
variant="toggle"
outlined
control={control}
name="resetsSettings.resetMountingFeet"
label={l10n.getString(
'settings-general-fk_settings-leg_fk-reset_mounting_feet-v1'
)}
/>
</div>
</div>
</div>
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-arm_fk-reset_mode-description'
)}
</Typography>
<div className="grid md:grid-cols-2 flex-col gap-3 pt-2 pb-3">
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-back'
<div>
<Typography>
{l10n.getString(
'settings-general-fk_settings-arm_fk-reset_mode-description'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-back-description'
)}
value={'0'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-forward'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-forward-description'
)}
value={'1'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_up'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_up-description'
)}
value={'2'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_down'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_down-description'
)}
value={'3'}
></Radio>
</Typography>
<div className="grid md:grid-cols-2 flex-col gap-3 pt-2 pb-3">
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-back'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-back-description'
)}
value={'0'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-forward'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-forward-description'
)}
value={'1'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_up'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_up-description'
)}
value={'2'}
></Radio>
<Radio
control={control}
name="resetsSettings.armsMountingResetMode"
label={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_down'
)}
description={l10n.getString(
'settings-general-fk_settings-arm_fk-tpose_down-description'
)}
value={'3'}
></Radio>
</div>
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
<div className="flex flex-col pt-2 pb-1">
<Typography variant="section-title">
{l10n.getString(
'settings-general-fk_settings-enforce_joint_constraints'
)}
</Typography>
<Typography color="secondary">
{l10n.getString(
'settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description'
)}
</Typography>
<div className="pt-2">
<Typography>
{l10n.getString(
'settings-general-fk_settings-enforce_joint_constraints-enforce_constraints-description'
)}
</Typography>
</div>
</div>
<div className="grid sm:grid-cols-1 pb-3">
<CheckBox
@@ -963,12 +961,12 @@ export function GeneralSettings() {
{config?.debug && (
<>
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-fk_settings-skeleton_settings-toggles'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-skeleton_settings-description'
)}
@@ -1003,14 +1001,14 @@ export function GeneralSettings() {
)}
/>
</div>
<div className="flex flex-col pt-2 pb-3">
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
<div className="flex flex-col">
<div className="flex flex-col pt-2 pb-3 gap-2">
<Typography variant="section-title">
{l10n.getString(
'settings-general-fk_settings-skeleton_settings-ratios'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-skeleton_settings-ratios-description'
)}
@@ -1112,12 +1110,12 @@ export function GeneralSettings() {
</div>
<div className="flex flex-col pt-2 pb-3">
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-fk_settings-self_localization-title'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-fk_settings-self_localization-description'
)}
@@ -1147,11 +1145,12 @@ export function GeneralSettings() {
<Typography variant="main-title">
{l10n.getString('settings-general-gesture_control')}
</Typography>
<Typography bold>
{l10n.getString('settings-general-gesture_control-subtitle')}
</Typography>
<div className="flex flex-col pt-2 pb-4">
<Typography color="secondary">
<div className="flex flex-col pt-2 pb-4 gap-2">
<Typography variant="section-title">
{l10n.getString('settings-general-gesture_control-subtitle')}
</Typography>
<Typography>
{l10n.getString('settings-general-gesture_control-description')}
</Typography>
</div>
@@ -1267,12 +1266,12 @@ export function GeneralSettings() {
/>
</div>
<div className="grid sm:grid-cols-1 gap-2 pt-2">
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-gesture_control-numberTrackersOverThreshold'
)}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-gesture_control-numberTrackersOverThreshold-description'
)}

View File

@@ -130,11 +130,14 @@ export function InterfaceSettings() {
{l10n.getString('settings-interface-notifications')}
</Typography>
<Typography bold>
{l10n.getString('settings-general-interface-serial_detection')}
</Typography>
<div className="pt-2">
<Typography variant="section-title">
{l10n.getString('settings-general-interface-serial_detection')}
</Typography>
</div>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-interface-serial_detection-description'
)}
@@ -152,11 +155,11 @@ export function InterfaceSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-interface-feedback_sound')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-interface-feedback_sound-description'
)}
@@ -187,13 +190,13 @@ export function InterfaceSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-interface-connected_trackers_warning'
)}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-interface-connected_trackers_warning-description'
)}
@@ -221,130 +224,134 @@ export function InterfaceSettings() {
<Typography variant="main-title">
{l10n.getString('settings-interface-behavior')}
</Typography>
<div className="pt-2">
{isTrayAvailable && (
<>
<Typography variant="section-title">
{l10n.getString('settings-general-interface-use_tray')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography>
{l10n.getString(
'settings-general-interface-use_tray-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.useTray"
label={l10n.getString(
'settings-general-interface-use_tray-label'
)}
/>
</div>
</>
)}
{isTrayAvailable && (
<>
<Typography bold>
{l10n.getString('settings-general-interface-use_tray')}
<Typography variant="section-title">
{l10n.getString('settings-general-interface-discord_presence')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography>
{l10n.getString(
'settings-general-interface-discord_presence-description'
)}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.discordPresence"
label={l10n.getString(
'settings-general-interface-discord_presence-label'
)}
/>
</div>
<Typography variant="section-title">
{l10n.getString('settings-general-interface-dev_mode')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography>
{l10n.getString(
'settings-general-interface-dev_mode-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.devmode"
label={l10n.getString(
'settings-general-interface-dev_mode-label'
)}
/>
</div>
<Typography variant="section-title">
{l10n.getString('settings-interface-behavior-error_tracking')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Localized
id={
'settings-interface-behavior-error_tracking-description_v2'
}
elems={{
b: <b></b>,
}}
>
<Typography whitespace="whitespace-pre-line"></Typography>
</Localized>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.errorTracking"
label={l10n.getString(
'settings-interface-behavior-error_tracking-label'
)}
/>
</div>
{isTauri() && (
<>
<Typography variant="section-title">
{l10n.getString(
'settings-general-interface-use_tray-description'
'settings-interface-behavior-bvh_directory'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.useTray"
label={l10n.getString(
'settings-general-interface-use_tray-label'
)}
/>
</div>
</>
)}
<Typography bold>
{l10n.getString('settings-general-interface-discord_presence')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
{l10n.getString(
'settings-general-interface-discord_presence-description'
)}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Localized
id={
'settings-interface-behavior-bvh_directory-description'
}
>
<Typography></Typography>
</Localized>
</div>
<div className="grid gap-3 pb-5">
<TauriFileInput
name="behavior.bvhDirectory"
rules={{
required: false,
}}
control={control}
label="settings-interface-behavior-bvh_directory-label"
directory
/>
</div>
</>
)}
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.discordPresence"
label={l10n.getString(
'settings-general-interface-discord_presence-label'
)}
/>
</div>
<Typography bold>
{l10n.getString('settings-general-interface-dev_mode')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
{l10n.getString(
'settings-general-interface-dev_mode-description'
)}
</Typography>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.devmode"
label={l10n.getString(
'settings-general-interface-dev_mode-label'
)}
/>
</div>
<Typography bold>
{l10n.getString('settings-interface-behavior-error_tracking')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Localized
id={'settings-interface-behavior-error_tracking-description_v2'}
elems={{
b: <b></b>,
}}
>
<Typography
color="secondary"
whitespace="whitespace-pre-line"
></Typography>
</Localized>
</div>
<div className="grid sm:grid-cols-2 pb-4">
<CheckBox
variant="toggle"
control={control}
outlined
name="behavior.errorTracking"
label={l10n.getString(
'settings-interface-behavior-error_tracking-label'
)}
/>
</div>
{isTauri() && (
<>
<Typography bold>
{l10n.getString('settings-interface-behavior-bvh_directory')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Localized
id={'settings-interface-behavior-bvh_directory-description'}
>
<Typography color="secondary"></Typography>
</Localized>
</div>
<div className="grid gap-3 pb-5">
<TauriFileInput
name="behavior.bvhDirectory"
rules={{
required: false,
}}
control={control}
label="settings-interface-behavior-bvh_directory-label"
directory
/>
</div>
</>
)}
</>
</SettingsPagePaneLayout>
@@ -356,11 +363,13 @@ export function InterfaceSettings() {
<Typography variant="main-title">
{l10n.getString('settings-interface-appearance')}
</Typography>
<Typography bold>
{l10n.getString('settings-interface-appearance-decorations')}
</Typography>
<div className="pt-2">
<Typography variant="section-title">
{l10n.getString('settings-interface-appearance-decorations')}
</Typography>
</div>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-interface-appearance-decorations-description'
)}
@@ -379,7 +388,7 @@ export function InterfaceSettings() {
</div>
<div className="pb-4">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-interface-theme')}
</Typography>
<div className="flex flex-wrap gap-3 pt-2">
@@ -440,13 +449,13 @@ export function InterfaceSettings() {
</div>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-interface-show-navbar-onboarding'
)}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-general-interface-show-navbar-onboarding-description'
)}
@@ -464,11 +473,11 @@ export function InterfaceSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-interface-appearance-font')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-interface-appearance-font-description'
)}
@@ -513,11 +522,11 @@ export function InterfaceSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-interface-appearance-font_size')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-interface-appearance-font_size-description'
)}
@@ -541,11 +550,11 @@ export function InterfaceSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-general-interface-lang')}
</Typography>
<div className="flex flex-col pt-1 pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-general-interface-lang-description')}
</Typography>
</div>

View File

@@ -95,7 +95,7 @@ export function MagnetometerToggleSetting({
return settingType === 'general' ? (
<>
<div className="flex flex-col pt-5 pb-3" id={id}>
<Typography bold>
<Typography variant="section-title">
{l10n.getString(
'settings-general-tracker_mechanics-use_mag_on_all_trackers'
)}
@@ -104,10 +104,7 @@ export function MagnetometerToggleSetting({
id="settings-general-tracker_mechanics-use_mag_on_all_trackers-description"
elems={{ b: <b></b> }}
>
<Typography
color="secondary"
whitespace="whitespace-pre-line"
></Typography>
<Typography whitespace="whitespace-pre-line"></Typography>
</Localized>
</div>
<CheckBox
@@ -139,10 +136,7 @@ export function MagnetometerToggleSetting({
),
}}
>
<Typography
color="secondary"
whitespace="whitespace-pre-line"
></Typography>
<Typography whitespace="whitespace-pre-line"></Typography>
</Localized>
<div className="flex">
<CheckBox

View File

@@ -110,17 +110,15 @@ export function OSCRouterSettings() {
.getString('settings-osc-router-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-router-enable')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-router-enable-description')}
</Typography>
</div>
@@ -133,7 +131,7 @@ export function OSCRouterSettings() {
label={l10n.getString('settings-osc-router-enable-label')}
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-router-network')}
</Typography>
<div className="flex flex-col pb-2">
@@ -142,9 +140,7 @@ export function OSCRouterSettings() {
.getString('settings-osc-router-network-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>
@@ -176,11 +172,11 @@ export function OSCRouterSettings() {
></Input>
</Localized>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-router-network-address')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-osc-router-network-address-description'
)}

View File

@@ -240,9 +240,7 @@ export function Serial() {
.getString('settings-serial-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>

View File

@@ -137,17 +137,15 @@ export function VMCSettings() {
.getString('settings-osc-vmc-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-enable')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vmc-enable-description')}
</Typography>
</div>
@@ -160,7 +158,7 @@ export function VMCSettings() {
label={l10n.getString('settings-osc-vmc-enable-label')}
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-network')}
</Typography>
<div className="flex flex-col pb-2">
@@ -169,9 +167,7 @@ export function VMCSettings() {
.getString('settings-osc-vmc-network-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>
@@ -203,11 +199,11 @@ export function VMCSettings() {
></Input>
</Localized>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-network-address')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vmc-network-address-description')}
</Typography>
</div>
@@ -227,11 +223,11 @@ export function VMCSettings() {
label=""
></Input>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-vrm')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vmc-vrm-description')}
</Typography>
</div>
@@ -254,11 +250,11 @@ export function VMCSettings() {
></FileInput>
{/* For some reason, linux (GNOME) is detecting the VRM file is a VRML */}
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-anchor_hip')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vmc-anchor_hip-description')}
</Typography>
</div>
@@ -271,11 +267,11 @@ export function VMCSettings() {
label={l10n.getString('settings-osc-vmc-anchor_hip-label')}
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vmc-mirror_tracking')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vmc-mirror_tracking-description')}
</Typography>
</div>

View File

@@ -140,17 +140,15 @@ export function VRCOSCSettings() {
.getString('settings-osc-vrchat-description-v1')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vrchat-enable')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vrchat-enable-description')}
</Typography>
</div>
@@ -164,18 +162,16 @@ export function VRCOSCSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vrchat-oscqueryEnabled')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n
.getString('settings-osc-vrchat-oscqueryEnabled-description')
.split('\n')
.map((line, i) => (
<Typography color="secondary" key={i}>
{line}
</Typography>
<Typography key={i}>{line}</Typography>
))}
</Typography>
</div>
@@ -191,11 +187,11 @@ export function VRCOSCSettings() {
/>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vrchat-network')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-osc-vrchat-network-description-v1')}
</Typography>
</div>
@@ -227,11 +223,11 @@ export function VRCOSCSettings() {
></Input>
</Localized>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vrchat-network-address')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-osc-vrchat-network-address-description-v1'
)}
@@ -253,11 +249,11 @@ export function VRCOSCSettings() {
label=""
></Input>
</div>
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-osc-vrchat-network-trackers')}
</Typography>
<div className="flex flex-col pb-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-osc-vrchat-network-trackers-description'
)}

View File

@@ -40,9 +40,7 @@ function StaAlignedPoseModal({
</div>
<div className="flex flex-col gap-1">
{descriptionKeys.map((descriptionKey) => (
<Typography color="secondary">
{l10n.getString(descriptionKey)}
</Typography>
<Typography>{l10n.getString(descriptionKey)}</Typography>
))}
</div>
<div className="flex pt-1 items-center fill-background-50 justify-center px-12">

View File

@@ -182,10 +182,10 @@ export function StayAlignedSettings({
{l10n.getString('settings-stay_aligned')}
</Typography>
<div className="mt-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-stay_aligned-description')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('settings-stay_aligned-setup-description')}
</Typography>
<div className="flex mt-2">
@@ -199,7 +199,7 @@ export function StayAlignedSettings({
</div>
</div>
<div className="mt-6">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-stay_aligned-general-label')}
</Typography>
<div className="grid sm:grid-cols-2 gap-3 mt-2">
@@ -224,11 +224,11 @@ export function StayAlignedSettings({
</div>
</div>
<div className="mt-6">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-stay_aligned-relaxed_poses-label')}
</Typography>
<div className="mt-2">
<Typography color="secondary">
<Typography>
{l10n.getString(
'settings-stay_aligned-relaxed_poses-description'
)}
@@ -305,11 +305,11 @@ export function StayAlignedSettings({
</div>
</div>
<div className="mt-6">
<Typography bold>
<Typography variant="section-title">
{l10n.getString('settings-stay_aligned-debug-label')}
</Typography>
<div className="mt-2">
<Typography color="secondary">
<Typography>
{l10n.getString('settings-stay_aligned-debug-description')}
</Typography>
</div>

View File

@@ -49,7 +49,7 @@ export function SingleTrackerBodyAssignmentMenu({
<Typography variant="mobile-title" bold>
{l10n.getString('body_assignment_menu')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('body_assignment_menu-description')}
</Typography>
<div className="flex">

View File

@@ -7,7 +7,7 @@ export function TrackerBattery({
value,
voltage,
disabled,
textColor = 'secondary',
textColor = 'primary',
}: {
/**
* a [0, 1] value range is expected

View File

@@ -99,7 +99,7 @@ export function TrackerPartCard({
<WarningIcon></WarningIcon>
</div>
)}
<Typography color="secondary">
<Typography variant="section-title">
{l10n.getString('body_part-' + BodyPart[role])}
</Typography>
{td?.map(({ tracker }, index) => (
@@ -110,7 +110,7 @@ export function TrackerPartCard({
/>
))}
{!td && (
<Typography>
<Typography color="text-background-30">
{l10n.getString('tracker-part_card-unassigned')}
</Typography>
)}

View File

@@ -211,10 +211,10 @@ export function TrackerSettingsPage() {
</Typography>
</Localized>
<div className="flex gap-2">
<Typography color="secondary">
<Typography>
v{tracker?.device?.hardwareInfo?.firmwareVersion}
</Typography>
<Typography color="secondary">-</Typography>
<Typography>-</Typography>
{updateUnavailable && (
<Localized id="tracker-settings-update-unavailable">
<Typography>Cannot be updated (DIY)</Typography>
@@ -272,7 +272,7 @@ export function TrackerSettingsPage() {
<div className="flex flex-col bg-background-70 p-3 rounded-lg gap-2 overflow-x-auto">
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-manufacturer')}
</Typography>
<Typography>
@@ -280,13 +280,13 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-display_name')}
</Typography>
<Typography>{tracker?.tracker.info?.displayName}</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-custom_name')}
</Typography>
<Typography sentry-mask>
@@ -294,9 +294,7 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
{l10n.getString('tracker-infos-url')}
</Typography>
<Typography>{l10n.getString('tracker-infos-url')}</Typography>
<Typography>
udp://
{IPv4.fromNumber(
@@ -305,7 +303,7 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-hardware_identifier')}
</Typography>
<Typography>
@@ -313,7 +311,7 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-data_support')}
</Typography>
<Typography>
@@ -323,9 +321,7 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
{l10n.getString('tracker-infos-imu')}
</Typography>
<Typography>{l10n.getString('tracker-infos-imu')}</Typography>
<Typography>
{tracker?.tracker.info?.imuType
? ImuType[tracker?.tracker.info?.imuType]
@@ -333,13 +329,13 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-board_type')}
</Typography>
<Typography>{boardType}</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-magnetometer')}
</Typography>
<Typography>
@@ -352,7 +348,7 @@ export function TrackerSettingsPage() {
</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-network_version')}
</Typography>
<Typography>
@@ -377,7 +373,7 @@ export function TrackerSettingsPage() {
<Typography variant="section-title">
{l10n.getString('tracker-settings-assignment_section')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'tracker-settings-assignment_section-description'
)}
@@ -419,7 +415,7 @@ export function TrackerSettingsPage() {
<Typography variant="section-title">
{l10n.getString('tracker-settings-mounting_section')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString(
'tracker-settings-mounting_section-description'
)}
@@ -468,7 +464,7 @@ export function TrackerSettingsPage() {
<Typography variant="section-title">
{l10n.getString('tracker-settings-name_section')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-settings-name_section-description')}
</Typography>
<Input
@@ -488,7 +484,7 @@ export function TrackerSettingsPage() {
<Typography variant="section-title">
{l10n.getString('tracker-settings-forget')}
</Typography>
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-settings-forget-description')}
</Typography>
<Button

View File

@@ -35,7 +35,7 @@ export function TrackerStatus({ status }: { status: number }) {
<div className="flex flex-col justify-center">
<div className={classNames('w-2 h-2 rounded-full', statusClass)}></div>
</div>
<Typography color="secondary" whitespace="whitespace-nowrap">
<Typography whitespace="whitespace-nowrap">
{l10n.getString(statusLabel)}
</Typography>
</div>

View File

@@ -130,7 +130,7 @@ export function VRCWarningsPage() {
<Typography variant="main-title" />
</Localized>
<Localized id={'vrc_config-page-desc'}>
<Typography variant="standard" color="secondary" />
<Typography variant="standard" />
</Localized>
</div>
<div className="w-full mt-4 gap-2 flex flex-col">
@@ -142,7 +142,7 @@ export function VRCWarningsPage() {
<Typography variant="section-title" />
</Localized>
<Localized id="vrc_config-page-big_menu-desc">
<Typography color="secondary" />
<Typography />
</Localized>
<Table>
<SettingRow
@@ -248,7 +248,7 @@ export function VRCWarningsPage() {
<Typography variant="section-title" />
</Localized>
<Localized id="vrc_config-page-wrist_menu-desc">
<Typography color="secondary" />
<Typography />
</Localized>
<Table>
<SettingRow
@@ -304,7 +304,7 @@ export function VRCWarningsPage() {
a: <A href="https://docs.slimevr.dev/tools/vrchat-config.html"></A>,
}}
>
<Typography color="secondary" />
<Typography />
</Localized>
</div>
</div>

View File

@@ -75,9 +75,7 @@ export function DeveloperModeWidget() {
return (
<form className="bg-background-60 flex flex-col w-full rounded-md px-2">
<div className="mt-2 px-1">
<Typography color="secondary">
{l10n.getString('widget-developer_mode')}
</Typography>
<Typography>{l10n.getString('widget-developer_mode')}</Typography>
</div>
{Object.entries(toggles).map(makeToggle)}
</form>

View File

@@ -128,7 +128,7 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) {
{tracker.position && (
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('widget-imu_visualizer-position')}
</Typography>
<Typography>{formatVector3(tracker.position, 2)}</Typography>
@@ -136,14 +136,14 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) {
)}
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('widget-imu_visualizer-rotation_raw')}
</Typography>
<Typography>{formatVector3(rotationRaw, 2)}</Typography>
</div>
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('widget-imu_visualizer-rotation_preview')}
</Typography>
<Typography>{formatVector3(rotationIdent, 2)}</Typography>
@@ -151,7 +151,7 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) {
{tracker.linearAcceleration && (
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('widget-imu_visualizer-acceleration')}
</Typography>
<Typography>
@@ -162,7 +162,7 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) {
{tracker.rawMagneticVector && (
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('tracker-infos-magnetometer')}
</Typography>
<Typography>{formatVector3(tracker.rawMagneticVector, 1)}</Typography>
@@ -171,7 +171,7 @@ export function IMUVisualizerWidget({ tracker }: { tracker: TrackerDataT }) {
{!!tracker.stayAligned && (
<div className="flex justify-between">
<Typography color="secondary">
<Typography>
{l10n.getString('widget-imu_visualizer-stay_aligned')}
</Typography>
<StayAlignedInfo color="primary" tracker={tracker} />

View File

@@ -68,9 +68,7 @@ export function OverlayWidget() {
return !loading ? (
<form className="bg-background-60 flex flex-col w-full rounded-md px-2">
<div className="mt-2 px-1">
<Typography color="secondary">
{l10n.getString('widget-overlay')}
</Typography>
<Typography>{l10n.getString('widget-overlay')}</Typography>
</div>
<CheckBox
control={control}

View File

@@ -27,6 +27,14 @@ const hash = (str: string) => {
const firstAsset = (assets: any[], name: string) =>
assets.find((asset: any) => asset.name === name && asset.browser_download_url);
const todaysRange = (deployData: [number, Date][]): number => {
let maxRange = 0;
for (const [range, date] of deployData) {
if (Date.now() >= date.getTime()) maxRange = range;
}
return maxRange;
};
const checkUserCanUpdate = async (url: string, fwVersion: string) => {
if (!url) return true;
const deployDataJson = JSON.parse(
@@ -57,11 +65,7 @@ const checkUserCanUpdate = async (url: string, fwVersion: string) => {
)
return false; // Dates in the wrong order / cancel
const todayUpdateRange = deployData.find(([, date], index) => {
if (index === 0 && Date.now() < date.getTime()) return true;
return Date.now() >= date.getTime();
})?.[0];
const todayUpdateRange = todaysRange(deployData);
if (!todayUpdateRange) return false;
const uniqueUserKey = `${await hostname()}-${await locale()}-${platform()}-${version()}`;

10
gui/src/vite-env.d.ts vendored
View File

@@ -4,14 +4,14 @@
declare const __COMMIT_HASH__: string;
declare const __VERSION_TAG__: string;
declare const __GIT_CLEAN__: boolean;
declare const __ANDROID__:
| {
isThere: () => boolean;
}
| undefined;
interface Window {
readonly isTauri: boolean;
readonly __ANDROID__:
| {
isThere: () => boolean;
}
| undefined;
}
declare module 'tailwind-gradient-mask-image';

View File

@@ -2,6 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.usb.host" android:required="false" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
@@ -26,7 +28,11 @@
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
<service

View File

@@ -8,6 +8,8 @@ import androidx.appcompat.app.AppCompatActivity
import dev.slimevr.Keybinding
import dev.slimevr.VRServer
import dev.slimevr.android.serial.AndroidSerialHandler
import dev.slimevr.android.tracking.trackers.hid.AndroidHIDManager
import dev.slimevr.tracking.trackers.Tracker
import io.eiren.util.logging.LogManager
import io.ktor.http.CacheControl
import io.ktor.http.CacheControl.Visibility
@@ -60,6 +62,15 @@ fun main(activity: AppCompatActivity) {
},
)
vrServer.start()
// Start service for USB HID trackers
val androidHidManager = AndroidHIDManager(
"Sensors HID service",
{ tracker: Tracker -> vrServer.registerTracker(tracker) },
activity,
)
androidHidManager.start()
Keybinding(vrServer)
vrServer.join()
LogManager.closeLogger()

View File

@@ -1,10 +1,13 @@
package dev.slimevr.android.serial
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.UsbManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialPort
import com.hoho.android.usbserial.driver.UsbSerialProber
@@ -15,10 +18,8 @@ import io.eiren.util.logging.LogManager
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
import java.util.stream.Stream
import kotlin.concurrent.timerTask
import kotlin.streams.asSequence
import kotlin.streams.asStream
import dev.slimevr.serial.SerialPort as SlimeSerialPort
@@ -43,12 +44,11 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
private var usbIoManager: SerialInputOutputManager? = null
private val listeners: MutableList<SerialListener> = CopyOnWriteArrayList()
private val getDevicesTimer = Timer("GetDevicesTimer")
private var watchingNewDevices = false
private var lastKnownPorts = setOf<SerialPortWrapper>()
private val manager = activity.getSystemService(Context.USB_SERVICE) as UsbManager
private var currentPort: SerialPortWrapper? = null
private var requestingPermission: String = ""
private var readBuffer: StringBuilder = StringBuilder(1024)
override val isConnected: Boolean
get() = currentPort?.port?.isOpen ?: false
@@ -60,37 +60,70 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
.filter { isKnownBoard(it) }
.asStream()
val usbReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
UsbManager.ACTION_USB_DEVICE_ATTACHED, UsbManager.ACTION_USB_DEVICE_DETACHED -> {
// Use device from `UsbManager.EXTRA_DEVICE` if this is a problem
detectNewPorts()
}
ACTION_USB_PERMISSION -> {
// TODO: We can probably receive this event in the server to avoid
// polling, but for now we can just ignore it. (Note: This event is
// not currently registered, so it will never fire.)
}
}
}
}
init {
startWatchingNewDevices()
val intentFilter = IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED)
intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
// Listen for USB device attach/detach
ContextCompat.registerReceiver(
activity,
usbReceiver,
intentFilter,
ContextCompat.RECEIVER_NOT_EXPORTED,
)
// Detect initial serial ports
detectNewPorts()
}
private fun getPorts(): List<UsbSerialDriver> = UsbSerialProber.getDefaultProber().findAllDrivers(manager)
private fun startWatchingNewDevices() {
if (watchingNewDevices) return
watchingNewDevices = true
getDevicesTimer.scheduleAtFixedRate(
timerTask {
try {
detectNewPorts()
} catch (t: Throwable) {
LogManager.severe(
"[SerialHandler] Error while watching for new devices, cancelling the \"getDevicesTimer\".",
t,
)
getDevicesTimer.cancel()
}
},
0,
3000,
)
}
private fun onNewDevice(port: SerialPortWrapper) {
// If we missed clearing this on disconnect/close, clear it on discovery
if (requestingPermission == port.portLocation) {
requestingPermission = ""
}
LogManager.info("[SerialHandler] Device added: ${port.descriptivePortName}")
listeners.forEach { it.onNewSerialDevice(port) }
}
private fun onDeviceDel(port: SerialPortWrapper) {
// Remove permission request on disconnect so reconnecting re-requests
if (requestingPermission == port.portLocation) {
requestingPermission = ""
}
// If we're currently using this port, close it
currentPort?.portLocation.let { currentPortLocation ->
if (currentPortLocation == port.portLocation) {
closeSerial()
}
}
// If this port is still open for whatever reason, close it
if (port.port.isOpen) {
port.port.close()
}
LogManager.info("[SerialHandler] Device removed: ${port.descriptivePortName}")
listeners.forEach { it.onSerialDeviceDeleted(port) }
}
@@ -154,9 +187,11 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
flags,
)
if (requestingPermission != newPort.portLocation) {
println("Requesting permission for ${newPort.portLocation}")
LogManager.info("[SerialHandler] Requesting permission for ${newPort.portLocation}")
manager.requestPermission(newPort.port.device, usbPermissionIntent)
requestingPermission = newPort.portLocation
} else {
LogManager.info("[SerialHandler] Already requested permission for ${newPort.portLocation}, skipping")
}
LogManager.warning(
"[SerialHandler] Can't open serial port ${newPort.descriptivePortName}, invalid permissions",
@@ -164,11 +199,13 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
return false
}
// If we have permission, we aren't requesting anymore
requestingPermission = ""
val connection = manager.openDevice(newPort.port.device)
if (connection == null) {
LogManager.warning(
"[SerialHandler] Can't open serial port ${newPort.descriptivePortName}, connection failed",
)
return false
}
@@ -186,7 +223,7 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
@Synchronized
private fun writeSerial(serialText: String, print: Boolean = false) {
try {
usbIoManager?.writeAsync("${serialText}\n".toByteArray())
currentPort?.port?.write("${serialText}\n".toByteArray(), 0)
if (print) {
addLog("-> $serialText\n")
}
@@ -224,6 +261,8 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
usbIoManager?.stop()
usbIoManager = null
currentPort = null
requestingPermission = ""
readBuffer.clear()
} catch (e: Exception) {
LogManager.warning(
"[SerialHandler] Error closing port ${currentPort?.descriptivePortName}",
@@ -233,7 +272,7 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
}
override fun write(buff: ByteArray) {
usbIoManager?.writeAsync(buff)
currentPort?.port?.write(buff, 0)
}
@Synchronized
@@ -251,14 +290,21 @@ class AndroidSerialHandler(val activity: AppCompatActivity) :
override fun onNewData(data: ByteArray?) {
if (data != null) {
val s = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(data)).toString()
addLog(s, false)
// Collect serial in a buffer until newline (or character limit)
// This is somewhat of a workaround for Android serial buffer being smaller
// than on desktop, so we don't read full lines and it causes parsing issues
readBuffer.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(data)))
if (readBuffer.contains('\n') || readBuffer.length >= 1024) {
addLog(readBuffer.toString(), false)
readBuffer.clear()
}
}
}
override fun onRunError(e: java.lang.Exception?) {}
companion object {
private val ACTION_USB_PERMISSION = "dev.slimevr.USB_PERMISSION"
private const val ACTION_USB_PERMISSION = "dev.slimevr.USB_PERMISSION"
}
}

View File

@@ -0,0 +1,93 @@
package dev.slimevr.android.tracking.trackers.hid
import android.hardware.usb.UsbConstants
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbEndpoint
import android.hardware.usb.UsbInterface
import android.hardware.usb.UsbManager
import java.io.Closeable
/**
* A wrapper over Android's [UsbDevice] for HID devices.
*/
class AndroidHIDDevice(hidDevice: UsbDevice, usbManager: UsbManager) : Closeable {
val deviceName = hidDevice.deviceName
val serialNumber = hidDevice.serialNumber
val manufacturerName = hidDevice.manufacturerName
val productName = hidDevice.productName
val hidInterface: UsbInterface
val endpointIn: UsbEndpoint
val endpointOut: UsbEndpoint?
val deviceConnection: UsbDeviceConnection
init {
hidInterface = findHidInterface(hidDevice)!!
val (endpointIn, endpointOut) = findHidIO(hidInterface)
this.endpointIn = endpointIn!!
this.endpointOut = endpointOut
deviceConnection = usbManager.openDevice(hidDevice)!!
deviceConnection.claimInterface(hidInterface, true)
}
override fun close() {
deviceConnection.releaseInterface(hidInterface)
deviceConnection.close()
}
companion object {
/**
* Find the HID interface.
*
* @return
* Return the HID interface if found, otherwise null.
*/
private fun findHidInterface(usbDevice: UsbDevice): UsbInterface? {
val interfaceCount: Int = usbDevice.interfaceCount
for (interfaceIndex in 0 until interfaceCount) {
val usbInterface = usbDevice.getInterface(interfaceIndex)
if (usbInterface.interfaceClass == UsbConstants.USB_CLASS_HID) {
return usbInterface
}
}
return null
}
/**
* Find the HID endpoints.
*
* @return
* Return the HID endpoints if found, otherwise null.
*/
private fun findHidIO(usbInterface: UsbInterface): Pair<UsbEndpoint?, UsbEndpoint?> {
val endpointCount: Int = usbInterface.endpointCount
var usbEndpointIn: UsbEndpoint? = null
var usbEndpointOut: UsbEndpoint? = null
for (endpointIndex in 0 until endpointCount) {
val usbEndpoint = usbInterface.getEndpoint(endpointIndex)
if (usbEndpoint.type == UsbConstants.USB_ENDPOINT_XFER_INT) {
if (usbEndpoint.direction == UsbConstants.USB_DIR_OUT) {
usbEndpointOut = usbEndpoint
} else {
usbEndpointIn = usbEndpoint
}
}
}
return Pair(usbEndpointIn, usbEndpointOut)
}
}
}

View File

@@ -0,0 +1,254 @@
package dev.slimevr.android.tracking.trackers.hid
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import androidx.core.content.ContextCompat
import dev.slimevr.tracking.trackers.Device
import dev.slimevr.tracking.trackers.Tracker
import dev.slimevr.tracking.trackers.TrackerStatus
import dev.slimevr.tracking.trackers.hid.HIDCommon
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_PID
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.HID_TRACKER_RECEIVER_VID
import dev.slimevr.tracking.trackers.hid.HIDCommon.Companion.PACKET_SIZE
import dev.slimevr.tracking.trackers.hid.HIDDevice
import io.eiren.util.logging.LogManager
import java.nio.ByteBuffer
import java.util.function.Consumer
const val ACTION_USB_PERMISSION = "dev.slimevr.USB_PERMISSION"
/**
* Handles Android USB Host HID dongles and receives tracker data from them.
*/
class AndroidHIDManager(
name: String,
private val trackersConsumer: Consumer<Tracker>,
private val context: Context,
) : Thread(name) {
private val devices: MutableList<HIDDevice> = mutableListOf()
private val devicesBySerial: MutableMap<String, MutableList<Int>> = HashMap()
private val devicesByHID: MutableMap<UsbDevice, MutableList<Int>> = HashMap()
private val connByHID: MutableMap<UsbDevice, AndroidHIDDevice> = HashMap()
private val lastDataByHID: MutableMap<UsbDevice, Int> = HashMap()
private val usbManager: UsbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager
val usbReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
UsbManager.ACTION_USB_DEVICE_ATTACHED -> {
(intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) as UsbDevice?)?.let {
checkConfigureDevice(it, requestPermission = true)
}
}
UsbManager.ACTION_USB_DEVICE_DETACHED -> {
(intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) as UsbDevice?)?.let {
removeDevice(it)
}
}
ACTION_USB_PERMISSION -> {
deviceEnumerate(false)
}
}
}
}
private fun proceedWithDeviceConfiguration(hidDevice: UsbDevice) {
// This is the original logic from checkConfigureDevice after permission is confirmed
LogManager.info("[TrackerServer] USB Permission granted for ${hidDevice.deviceName}. Proceeding with configuration.")
// Close any existing connection (do we still have one?)
this.connByHID[hidDevice]?.close()
// Open new HID connection with USB device
this.connByHID[hidDevice] = AndroidHIDDevice(hidDevice, usbManager)
val serial = hidDevice.serialNumber ?: "Unknown USB Device ${hidDevice.deviceId}"
this.devicesBySerial[serial]?.let {
this.devicesByHID[hidDevice] = it
synchronized(this.devices) {
for (id in it) {
val device = this.devices[id]
for (value in device.trackers.values) {
if (value.status == TrackerStatus.DISCONNECTED) value.status = TrackerStatus.OK
}
}
}
LogManager.info("[TrackerServer] Linked HID device reattached: $serial")
return
}
val list: MutableList<Int> = mutableListOf()
this.devicesBySerial[serial] = list
this.devicesByHID[hidDevice] = list
this.lastDataByHID[hidDevice] = 0 // initialize last data received
LogManager.info("[TrackerServer] (Probably) Compatible HID device detected: $serial")
}
fun checkConfigureDevice(usbDevice: UsbDevice, requestPermission: Boolean = false) {
if (usbDevice.vendorId == HID_TRACKER_RECEIVER_VID && usbDevice.productId == HID_TRACKER_RECEIVER_PID) {
if (usbManager.hasPermission(usbDevice)) {
LogManager.info("[TrackerServer] Already have permission for ${usbDevice.deviceName}")
proceedWithDeviceConfiguration(usbDevice)
} else if (requestPermission) {
LogManager.info("[TrackerServer] Requesting permission for ${usbDevice.deviceName}")
val permissionIntent = PendingIntent.getBroadcast(
context,
0,
Intent(ACTION_USB_PERMISSION).apply { setPackage(context.packageName) }, // Explicitly set package
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
)
usbManager.requestPermission(usbDevice, permissionIntent)
}
}
}
private fun removeDevice(hidDevice: UsbDevice) {
this.devicesByHID[hidDevice]?.let {
synchronized(this.devices) {
for (id in it) {
val device = this.devices[id]
for (value in device.trackers.values) {
if (value.status == TrackerStatus.OK) {
value.status =
TrackerStatus.DISCONNECTED
}
}
}
}
this.devicesByHID.remove(hidDevice)
val oldConn = this.connByHID.remove(hidDevice)
val serial = oldConn?.serialNumber ?: "Unknown"
oldConn?.close()
LogManager.info("[TrackerServer] Linked HID device removed: $serial")
}
}
private fun dataRead() {
synchronized(devicesByHID) {
var devicesPresent = false
var devicesDataReceived = false
val q = intArrayOf(0, 0, 0, 0)
val a = intArrayOf(0, 0, 0)
val m = intArrayOf(0, 0, 0)
for ((hidDevice, deviceList) in devicesByHID) {
val dataReceived = ByteArray(64)
val conn = connByHID[hidDevice]!!
val dataRead = conn.deviceConnection.bulkTransfer(conn.endpointIn, dataReceived, dataReceived.size, 0)
// LogManager.info("[TrackerServer] HID data read ($dataRead bytes): ${dataReceived.contentToString()}")
devicesPresent = true // Even if the device has no data
if (dataRead > 0) {
// Process data
// The data is always received as 64 bytes, this check no longer works
if (dataRead % PACKET_SIZE != 0) {
LogManager.info("[TrackerServer] Malformed HID packet, ignoring")
continue // Don't continue with this data
}
devicesDataReceived = true // Data is received and is valid (not malformed)
lastDataByHID[hidDevice] = 0 // reset last data received
val packetCount = dataRead / PACKET_SIZE
var i = 0
while (i < packetCount * PACKET_SIZE) {
// Common packet data
val packetType = dataReceived[i].toUByte().toInt()
val id = dataReceived[i + 1].toUByte().toInt()
val deviceId = id
// Register device
if (packetType == 255) { // device register packet from receiver
val buffer = ByteBuffer.wrap(dataReceived, i + 2, 8)
buffer.order(java.nio.ByteOrder.LITTLE_ENDIAN)
val addr = buffer.getLong() and 0xFFFFFFFFFFFF
val deviceName = String.format("%012X", addr)
HIDCommon.deviceIdLookup(devices, hidDevice.serialNumber, deviceId, deviceName, deviceList) // register device
// server wants tracker to be unique, so use combination of hid serial and full id
i += PACKET_SIZE
continue
}
val device: HIDDevice? = HIDCommon.deviceIdLookup(devices, hidDevice.serialNumber, deviceId, null, deviceList)
if (device == null) { // not registered yet
i += PACKET_SIZE
continue
}
HIDCommon.processPacket(dataReceived, i, packetType, device, q, a, m, trackersConsumer)
i += PACKET_SIZE
}
// LogManager.info("[TrackerServer] HID received $packetCount tracker packets")
} else {
lastDataByHID[hidDevice] = lastDataByHID[hidDevice]!! + 1 // increment last data received
}
}
if (!devicesPresent) {
sleep(10) // No hid device, "empty loop" so sleep to save the poor cpu
} else if (!devicesDataReceived) {
sleep(1) // read has no timeout, no data also causes an "empty loop"
}
}
}
private fun deviceEnumerate(requestPermission: Boolean = false) {
val hidDeviceList: MutableList<UsbDevice> = usbManager.deviceList.values.filter {
it.vendorId == HID_TRACKER_RECEIVER_VID && it.productId == HID_TRACKER_RECEIVER_PID
}.toMutableList()
synchronized(devicesByHID) {
// Work on devicesByHid and add/remove as necessary
val removeList: MutableList<UsbDevice> = devicesByHID.keys.toMutableList()
removeList.removeAll(hidDeviceList)
for (device in removeList) {
removeDevice(device)
}
hidDeviceList.removeAll(devicesByHID.keys) // addList
for (device in hidDeviceList) {
// This will handle permission check/request
checkConfigureDevice(device, requestPermission)
}
}
}
override fun run() {
val intentFilter = IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED)
intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
intentFilter.addAction(ACTION_USB_PERMISSION)
// Listen for USB device attach/detach
ContextCompat.registerReceiver(
context,
usbReceiver,
intentFilter,
ContextCompat.RECEIVER_NOT_EXPORTED,
)
// Enumerate existing devices
deviceEnumerate(true)
// Data read loop
while (true) {
try {
sleep(0) // Possible performance impact
} catch (e: InterruptedException) {
currentThread().interrupt()
break
}
dataRead() // not in try catch?
}
}
fun getDevices(): List<Device> = devices
companion object {
private const val resetSourceName = "TrackerServer"
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="4617" product-id="30352" />
</resources>

View File

@@ -1 +0,0 @@
protoc --proto_path=../SlimeVR-OpenVR-Driver/src/bridge --java_out=./src/main/java ProtobufMessages.proto

View File

@@ -10,6 +10,8 @@ import dev.slimevr.firmware.SerialFlashingHandler
import dev.slimevr.games.vrchat.VRCConfigHandler
import dev.slimevr.games.vrchat.VRCConfigHandlerStub
import dev.slimevr.games.vrchat.VRChatConfigManager
import dev.slimevr.inputs.Input
import dev.slimevr.inputs.TapInputManager
import dev.slimevr.osc.OSCHandler
import dev.slimevr.osc.OSCRouter
import dev.slimevr.osc.VMCHandler
@@ -117,6 +119,9 @@ class VRServer @JvmOverloads constructor(
@JvmField
val handshakeHandler = HandshakeHandler()
val tapInputManager: TapInputManager
val inputs: MutableList<Input> = FastList()
init {
// UwU
configManager = ConfigManager(configPath)
@@ -147,7 +152,9 @@ class VRServer @JvmOverloads constructor(
for (bridge in bridgeProvider(this, computedTrackers) + sequenceOf(WebSocketVRBridge(computedTrackers, this))) {
tasks.add(Runnable { bridge.startBridge() })
bridges.add(bridge)
bridge.addFingerBones(humanPoseManager.shareableFingerBones)
}
tapInputManager = TapInputManager(humanPoseManager.skeleton, this)
// Initialize OSC handlers
vrcOSCHandler = VRCOSCHandler(
@@ -245,11 +252,23 @@ class VRServer @JvmOverloads constructor(
tracker.tick(fpsTimer.timePerFrame)
}
humanPoseManager.update()
// Check for tap inputs
tapInputManager.update()
// Update bridges
for (bridge in bridges) {
// Send inputs to each bridge
for (input in inputs) {
bridge.sendInput(input)
}
bridge.dataWrite()
}
// Don't forget to clear inputs so that we don't send the same ones again
inputs.clear()
vrcOSCHandler.update()
vMCHandler.update()
// final long time = System.currentTimeMillis() - start;
try {
sleep(1) // 1000Hz

View File

@@ -1,5 +1,7 @@
package dev.slimevr.bridge
import dev.slimevr.inputs.Input
import dev.slimevr.tracking.processor.ShareableBone
import dev.slimevr.tracking.trackers.Tracker
import dev.slimevr.tracking.trackers.TrackerRole
import dev.slimevr.util.ann.VRServerThread
@@ -38,6 +40,24 @@ interface Bridge {
@VRServerThread
fun removeSharedTracker(tracker: Tracker?)
/**
* Adds a list of finger bones to the bridge. This should only be set
* once the skeleton has initialized. Bridge will send finger data for
* the hand trackers if it serves them.
*
* @param bones
*/
@VRServerThread
fun addFingerBones(bones: List<ShareableBone>)
/**
* Reports a virtual controller input to the driver.
*
* @param input the object containing the data about the input
*/
@VRServerThread
fun sendInput(input: Input)
@VRServerThread
fun startBridge()

View File

@@ -0,0 +1,8 @@
package dev.slimevr.inputs
class Input(val rightHand: Boolean, val type: InputType)
enum class InputType {
DOUBLE_TAP,
TRIPLE_TAP,
}

View File

@@ -0,0 +1,98 @@
package dev.slimevr.inputs
import dev.slimevr.VRServer
import dev.slimevr.tracking.processor.skeleton.HumanSkeleton
import dev.slimevr.tracking.processor.skeleton.TapDetection
import dev.slimevr.tracking.trackers.Tracker
import dev.slimevr.util.ann.VRServerThread
/**
* Handles tap detection for SteamVR virtual controller input
*/
class TapInputManager(
private val skeleton: HumanSkeleton,
private val vrServer: VRServer,
) {
// tap detectors
private lateinit var leftDoubleDetector: TapDetection
private lateinit var leftTripleDetector: TapDetection
private lateinit var rightDoubleDetector: TapDetection
private lateinit var rightTripleDetector: TapDetection
val NS_CONVERTER: Float = 1.0e9f
private var doubleTapDelay = 0.25f * NS_CONVERTER
init {
reinit()
}
fun reinit() {
// Create tap detectors for both hands
leftDoubleDetector = TapDetection(skeleton, trackerToWatchLeftHand)
leftDoubleDetector.enabled = true
leftDoubleDetector.setMaxTaps(2)
leftTripleDetector = TapDetection(skeleton, trackerToWatchLeftHand)
leftTripleDetector.enabled = true
leftTripleDetector.setMaxTaps(3)
rightDoubleDetector = TapDetection(skeleton, trackerToWatchRightHand)
rightDoubleDetector.enabled = true
rightDoubleDetector.setMaxTaps(2)
rightTripleDetector = TapDetection(skeleton, trackerToWatchRightHand)
rightTripleDetector.enabled = true
rightTripleDetector.setMaxTaps(3)
}
@VRServerThread
fun update() {
// update the tap detectors
leftDoubleDetector.update()
leftTripleDetector.update()
rightDoubleDetector.update()
rightTripleDetector.update()
// check if any tap detectors have detected taps
if (3 <= leftTripleDetector.taps) {
leftTripleDetector.resetDetector()
leftDoubleDetector.resetDetector()
vrServer.inputs.add(Input(false, InputType.TRIPLE_TAP))
} else if (2 <= leftDoubleDetector.taps && System.nanoTime() - rightDoubleDetector.detectionTime > doubleTapDelay) {
leftTripleDetector.resetDetector()
leftDoubleDetector.resetDetector()
vrServer.inputs.add(Input(false, InputType.DOUBLE_TAP))
}
if (3 <= rightTripleDetector.taps) {
rightTripleDetector.resetDetector()
rightDoubleDetector.resetDetector()
vrServer.inputs.add(Input(true, InputType.TRIPLE_TAP))
} else if (2 <= rightDoubleDetector.taps && System.nanoTime() - rightDoubleDetector.detectionTime > doubleTapDelay) {
rightTripleDetector.resetDetector()
rightDoubleDetector.resetDetector()
vrServer.inputs.add(Input(true, InputType.DOUBLE_TAP))
}
}
private val trackerToWatchLeftHand: Tracker?
get() = listOfNotNull(
skeleton.leftHandTracker,
skeleton.leftLowerArmTracker,
skeleton.leftUpperArmTracker,
skeleton.leftThumbDistalTracker,
skeleton.leftThumbProximalTracker,
skeleton.leftThumbMetacarpalTracker,
).firstOrNull()
private val trackerToWatchRightHand: Tracker?
get() = listOfNotNull(
skeleton.rightHandTracker,
skeleton.rightLowerArmTracker,
skeleton.rightUpperArmTracker,
skeleton.rightThumbDistalTracker,
skeleton.rightThumbProximalTracker,
skeleton.rightThumbMetacarpalTracker,
).firstOrNull()
}

View File

@@ -34,7 +34,6 @@ import solarxr_protocol.rpc.*
import kotlin.io.path.Path
class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeader>() {
private var currTransactionId: Long = 0
private val mainScope = CoroutineScope(SupervisorJob())
init {
@@ -238,7 +237,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val localIp = getLocalIp() ?: return
val response = ServerInfosResponse
.createServerInfosResponse(fbb, fbb.createString(localIp))
val outbound = this.createRPCMessage(fbb, RpcMessage.ServerInfosResponse, response)
val outbound = this.createRPCMessage(fbb, RpcMessage.ServerInfosResponse, response, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -251,7 +250,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val config = api.server.configManager.vrConfig.overlay
val response = OverlayDisplayModeResponse
.createOverlayDisplayModeResponse(fbb, config.isVisible, config.isMirrored)
val outbound = this.createRPCMessage(fbb, RpcMessage.OverlayDisplayModeResponse, response)
val outbound = this.createRPCMessage(fbb, RpcMessage.OverlayDisplayModeResponse, response, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -283,7 +282,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
// might not be a good idea maybe let the client ask again
val fbb = FlatBufferBuilder(300)
val config = createSkeletonConfig(fbb, api.server.humanPoseManager)
val outbound = this.createRPCMessage(fbb, RpcMessage.SkeletonConfigResponse, config)
val outbound = this.createRPCMessage(fbb, RpcMessage.SkeletonConfigResponse, config, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -297,7 +296,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val fbb = FlatBufferBuilder(300)
val config = createSkeletonConfig(fbb, api.server.humanPoseManager)
val outbound = this.createRPCMessage(fbb, RpcMessage.SkeletonConfigResponse, config)
val outbound = this.createRPCMessage(fbb, RpcMessage.SkeletonConfigResponse, config, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -332,7 +331,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val fbb = FlatBufferBuilder(40)
val status = RecordBVHStatus
.createRecordBVHStatus(fbb, api.server.bvhRecorder.isRecording)
val outbound = this.createRPCMessage(fbb, RpcMessage.RecordBVHStatus, status)
val outbound = this.createRPCMessage(fbb, RpcMessage.RecordBVHStatus, status, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -343,7 +342,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val fbb = FlatBufferBuilder(40)
val status = RecordBVHStatus
.createRecordBVHStatus(fbb, api.server.bvhRecorder.isRecording)
val outbound = this.createRPCMessage(fbb, RpcMessage.RecordBVHStatus, status)
val outbound = this.createRPCMessage(fbb, RpcMessage.RecordBVHStatus, status, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -495,13 +494,16 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
}
}
fun createRPCMessage(fbb: FlatBufferBuilder, messageType: Byte, messageOffset: Int): Int {
@JvmOverloads
fun createRPCMessage(fbb: FlatBufferBuilder, messageType: Byte, messageOffset: Int, respondTo: RpcMessageHeader? = null): Int {
val data = IntArray(1)
RpcMessageHeader.startRpcMessageHeader(fbb)
RpcMessageHeader.addMessage(fbb, messageOffset)
RpcMessageHeader.addMessageType(fbb, messageType)
RpcMessageHeader.addTxId(fbb, TransactionId.createTransactionId(fbb, currTransactionId++))
respondTo?.txId()?.let { txId ->
RpcMessageHeader.addTxId(fbb, TransactionId.createTransactionId(fbb, txId.id()))
}
data[0] = RpcMessageHeader.endRpcMessageHeader(fbb)
val messages = MessageBundle.createRpcMsgsVector(fbb, data)
@@ -525,7 +527,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
val response = StatusSystemResponseT()
response.currentStatuses = statuses
val offset = StatusSystemResponse.pack(fbb, response)
val outbound = this.createRPCMessage(fbb, RpcMessage.StatusSystemResponse, offset)
val outbound = this.createRPCMessage(fbb, RpcMessage.StatusSystemResponse, offset, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}
@@ -557,7 +559,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
0f,
)
}
fbb.finish(createRPCMessage(fbb, RpcMessage.HeightResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.HeightResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
}
@@ -572,7 +574,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
0,
api.server.configManager.vrConfig.server.useMagnetometerOnAllTrackers,
)
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
return
}
@@ -584,7 +586,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
trackerId,
tracker.config.shouldHaveMagEnabled == true,
)
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
}
@@ -604,7 +606,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
0,
api.server.configManager.vrConfig.server.useMagnetometerOnAllTrackers,
)
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
}
return
@@ -623,7 +625,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
trackerId,
state,
)
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
return
}
@@ -640,7 +642,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
trackerId,
state,
)
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response))
fbb.finish(createRPCMessage(fbb, RpcMessage.MagToggleResponse, response, messageHeader))
conn.send(fbb.dataBuffer())
}
}
@@ -660,7 +662,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
configManager.saveConfig()
sendSettingsChangedResponse(conn)
sendSettingsChangedResponse(conn, messageHeader)
}
private fun onDetectStayAlignedRelaxedPoseRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
@@ -692,7 +694,7 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
LogManager.info("[detectStayAlignedRelaxedPose] pose=$pose $relaxedPose")
sendSettingsChangedResponse(conn)
sendSettingsChangedResponse(conn, messageHeader)
}
private fun onResetStayAlignedRelaxedPoseRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
@@ -720,13 +722,13 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
LogManager.info("[resetStayAlignedRelaxedPose] pose=$pose")
sendSettingsChangedResponse(conn)
sendSettingsChangedResponse(conn, messageHeader)
}
fun sendSettingsChangedResponse(conn: GenericConnection) {
fun sendSettingsChangedResponse(conn: GenericConnection, messageHeader: RpcMessageHeader?) {
val fbb = FlatBufferBuilder(32)
val settings = RPCSettingsBuilder.createSettingsResponse(fbb, api.server)
val outbound = createRPCMessage(fbb, RpcMessage.SettingsResponse, settings)
val outbound = createRPCMessage(fbb, RpcMessage.SettingsResponse, settings, messageHeader)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())
}

View File

@@ -61,8 +61,8 @@ public class RPCSettingsBuilder {
&& config.getOSCTrackerRole(TrackerRole.RIGHT_FOOT, false),
config.getOSCTrackerRole(TrackerRole.LEFT_ELBOW, false)
&& config.getOSCTrackerRole(TrackerRole.RIGHT_ELBOW, false),
config.getOSCTrackerRole(TrackerRole.LEFT_HAND, false)
&& config.getOSCTrackerRole(TrackerRole.RIGHT_HAND, false)
config.getOSCTrackerRole(TrackerRole.LEFT_CONTROLLER, false)
&& config.getOSCTrackerRole(TrackerRole.RIGHT_CONTROLLER, false)
);
VRCOSCSettings.startVRCOSCSettings(fbb);
VRCOSCSettings.addOscSettings(fbb, generalSettingOffset);
@@ -164,8 +164,8 @@ public class RPCSettingsBuilder {
bridge.getShareSetting(TrackerRole.RIGHT_KNEE),
bridge.getShareSetting(TrackerRole.LEFT_ELBOW),
bridge.getShareSetting(TrackerRole.RIGHT_ELBOW),
bridge.getShareSetting(TrackerRole.LEFT_HAND),
bridge.getShareSetting(TrackerRole.RIGHT_HAND)
bridge.getShareSetting(TrackerRole.LEFT_CONTROLLER),
bridge.getShareSetting(TrackerRole.RIGHT_CONTROLLER)
);
}
return steamvrTrackerSettings;

View File

@@ -39,7 +39,7 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
}
fun onSettingsRequest(conn: GenericConnection, messageHeader: RpcMessageHeader?) {
rpcHandler.sendSettingsChangedResponse(conn)
rpcHandler.sendSettingsChangedResponse(conn, messageHeader)
}
fun onChangeSettingsRequest(conn: GenericConnection?, messageHeader: RpcMessageHeader) {
@@ -59,8 +59,8 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
bridge.changeShareSettings(TrackerRole.RIGHT_KNEE, req.steamVrTrackers().rightKnee())
bridge.changeShareSettings(TrackerRole.LEFT_ELBOW, req.steamVrTrackers().leftElbow())
bridge.changeShareSettings(TrackerRole.RIGHT_ELBOW, req.steamVrTrackers().rightElbow())
bridge.changeShareSettings(TrackerRole.LEFT_HAND, req.steamVrTrackers().leftHand())
bridge.changeShareSettings(TrackerRole.RIGHT_HAND, req.steamVrTrackers().rightHand())
bridge.changeShareSettings(TrackerRole.LEFT_CONTROLLER, req.steamVrTrackers().leftHand())
bridge.changeShareSettings(TrackerRole.RIGHT_CONTROLLER, req.steamVrTrackers().rightHand())
bridge.setAutomaticSharedTrackers(req.steamVrTrackers().automaticTrackerToggle())
}
}
@@ -128,8 +128,8 @@ class RPCSettingsHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
vrcOSCConfig.setOSCTrackerRole(TrackerRole.RIGHT_FOOT, trackers.feet())
vrcOSCConfig.setOSCTrackerRole(TrackerRole.LEFT_ELBOW, trackers.elbows())
vrcOSCConfig.setOSCTrackerRole(TrackerRole.RIGHT_ELBOW, trackers.elbows())
vrcOSCConfig.setOSCTrackerRole(TrackerRole.LEFT_HAND, trackers.hands())
vrcOSCConfig.setOSCTrackerRole(TrackerRole.RIGHT_HAND, trackers.hands())
vrcOSCConfig.setOSCTrackerRole(TrackerRole.LEFT_CONTROLLER, trackers.hands())
vrcOSCConfig.setOSCTrackerRole(TrackerRole.RIGHT_CONTROLLER, trackers.hands())
}
vrcOSCConfig.oscqueryEnabled = req.vrcOsc().oscqueryEnabled()

View File

@@ -28,16 +28,16 @@ class RPCTrackingPause(private val rpcHandler: RPCHandler, private val api: Prot
}
}
private fun getPauseStateResponse(trackingPaused: Boolean): ByteBuffer {
private fun getPauseStateResponse(trackingPaused: Boolean, messageHeader: RpcMessageHeader? = null): ByteBuffer {
val fbb = FlatBufferBuilder(32)
val state = TrackingPauseStateResponse.createTrackingPauseStateResponse(fbb, trackingPaused)
val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.TrackingPauseStateResponse, state)
val outbound = rpcHandler.createRPCMessage(fbb, RpcMessage.TrackingPauseStateResponse, state, messageHeader)
fbb.finish(outbound)
return fbb.dataBuffer()
}
private fun onTrackingPauseStateRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
conn.send(getPauseStateResponse(currentPauseState))
conn.send(getPauseStateResponse(currentPauseState, messageHeader))
}
override fun onTrackingPause(trackingPaused: Boolean) {

View File

@@ -1,14 +1,58 @@
package dev.slimevr.tracking.processor;
@file:Suppress("ktlint:standard:no-wildcard-imports")
import solarxr_protocol.datatypes.BodyPart;
package dev.slimevr.tracking.processor
import dev.slimevr.tracking.processor.BoneType.*
import solarxr_protocol.datatypes.BodyPart
fun BoneType?.isLeftFinger(): Boolean {
this?.let {
return it == LEFT_THUMB_METACARPAL ||
it == LEFT_THUMB_PROXIMAL ||
it == LEFT_THUMB_DISTAL ||
it == LEFT_INDEX_PROXIMAL ||
it == LEFT_INDEX_INTERMEDIATE ||
it == LEFT_INDEX_DISTAL ||
it == LEFT_MIDDLE_PROXIMAL ||
it == LEFT_MIDDLE_INTERMEDIATE ||
it == LEFT_MIDDLE_DISTAL ||
it == LEFT_RING_PROXIMAL ||
it == LEFT_RING_INTERMEDIATE ||
it == LEFT_RING_DISTAL ||
it == LEFT_LITTLE_PROXIMAL ||
it == LEFT_LITTLE_INTERMEDIATE ||
it == LEFT_LITTLE_DISTAL
}
return false
}
fun BoneType?.isRightFinger(): Boolean {
this?.let {
return it == RIGHT_THUMB_METACARPAL ||
it == RIGHT_THUMB_PROXIMAL ||
it == RIGHT_THUMB_DISTAL ||
it == RIGHT_INDEX_PROXIMAL ||
it == RIGHT_INDEX_INTERMEDIATE ||
it == RIGHT_INDEX_DISTAL ||
it == RIGHT_MIDDLE_PROXIMAL ||
it == RIGHT_MIDDLE_INTERMEDIATE ||
it == RIGHT_MIDDLE_DISTAL ||
it == RIGHT_RING_PROXIMAL ||
it == RIGHT_RING_INTERMEDIATE ||
it == RIGHT_RING_DISTAL ||
it == RIGHT_LITTLE_PROXIMAL ||
it == RIGHT_LITTLE_INTERMEDIATE ||
it == RIGHT_LITTLE_DISTAL
}
return false
}
/**
* Keys for all the bones in the skeleton.
*/
public enum BoneType {
enum class BoneType {
HEAD(BodyPart.HEAD),
HEAD_TRACKER(),
HEAD_TRACKER,
NECK(BodyPart.NECK),
UPPER_CHEST(BodyPart.UPPER_CHEST),
CHEST_TRACKER,
@@ -71,17 +115,21 @@ public enum BoneType {
RIGHT_RING_DISTAL(BodyPart.RIGHT_RING_DISTAL),
RIGHT_LITTLE_PROXIMAL(BodyPart.RIGHT_LITTLE_PROXIMAL),
RIGHT_LITTLE_INTERMEDIATE(BodyPart.RIGHT_LITTLE_INTERMEDIATE),
RIGHT_LITTLE_DISTAL(BodyPart.RIGHT_LITTLE_DISTAL);
RIGHT_LITTLE_DISTAL(BodyPart.RIGHT_LITTLE_DISTAL),
;
public static final BoneType[] values = values();
@JvmField
val bodyPart: Int
public final int bodyPart;
BoneType() {
this.bodyPart = BodyPart.NONE;
constructor() {
this.bodyPart = BodyPart.NONE
}
BoneType(int associatedBodyPart) {
this.bodyPart = associatedBodyPart;
constructor(associatedBodyPart: Int) {
this.bodyPart = associatedBodyPart
}
companion object {
val values: Array<BoneType> = entries.toTypedArray()
}
}

View File

@@ -52,11 +52,13 @@ class Constraint(
if (constraintType == ConstraintType.COMPLETE) return thisBone.getGlobalRotation()
// If there is no parent and this is not a complete constraint accept the rotation as is.
if (thisBone.parent == null) return rotation
// TODO: This was changed due to a race condition with the RPC thread, see
// https://github.com/SlimeVR/SlimeVR-Server/issues/1534 for more information.
val parent = thisBone.parent ?: return rotation
val localRotation = getLocalRotation(rotation, thisBone)
val localRotation = getLocalRotation(rotation, thisBone, parent)
val constrainedRotation = constraintFunction(localRotation, swingRad, twistRad, allowedDeviationRad)
return getWorldRotationFromLocal(constrainedRotation, thisBone)
return getWorldRotationFromLocal(constrainedRotation, thisBone, parent)
}
/**
@@ -90,14 +92,12 @@ class Constraint(
ConstraintType.LOOSE_HINGE -> looseHingeConstraint
}
private fun getLocalRotation(rotation: Quaternion, thisBone: Bone): Quaternion {
val parent = thisBone.parent!!
private fun getLocalRotation(rotation: Quaternion, thisBone: Bone, parent: Bone): Quaternion {
val localRotationOffset = parent.rotationOffset.inv() * thisBone.rotationOffset
return (parent.getGlobalRotation() * localRotationOffset).inv() * rotation
}
private fun getWorldRotationFromLocal(rotation: Quaternion, thisBone: Bone): Quaternion {
val parent = thisBone.parent!!
private fun getWorldRotationFromLocal(rotation: Quaternion, thisBone: Bone, parent: Bone): Quaternion {
val localRotationOffset = parent.rotationOffset.inv() * thisBone.rotationOffset
return (parent.getGlobalRotation() * localRotationOffset * rotation).unit()
}

Some files were not shown because too many files have changed in this diff Show More