Update to form and ui.

This commit is contained in:
HannahPadd
2026-03-25 23:18:37 +01:00
parent ef7e4b1550
commit 9c8cd8517e
18 changed files with 354 additions and 262 deletions

View File

@@ -17,6 +17,7 @@
},
"scripts": {
"start": "vite --force",
"dev:clean": "rm -rf node_modules/.vite && pnpm run gui",
"gui": "electron-vite dev --config electron.vite.config.ts --watch",
"build": "electron-vite build --config electron.vite.config.ts",
"package": "electron-builder",

View File

@@ -605,9 +605,22 @@ settings-stay_aligned-debug-copy-label = Copy settings to clipboard
settings-keybinds = Keybind settings
settings-keybinds-description = Change keybinds for various shortcuts
keybind_config-keybind_name = Keybind
keybind_config-keybind_value = Combination
keybind_config-keybind_delay = Delay before trigger (s)
settings-keybinds_full-reset = Full Reset
settings-keybinds_yaw-reset = Yaw Reset
settings-keybinds_mounting-reset = Mounting Reset
settings-keybinds_feet-mounting-reset = Feet Mounting Reset
settings-keybinds_pause-tracking = Pause Tracking
settings-keybinds_record-keybind = Click to record
settings-keybinds_now-recording = Recording…
settings-keybinds_reset-button = Reset
settings-keybinds_reset-all-button = Reset all
settings-keybinds-wayland-description = You appear to be using wayland, Please change your shortcuts in your system settings.
settings-keybinds-wayland-open-system-settings-button = Open system settings
settings-sidebar-keybinds = Keybinds
settings-sidebar_keybinds_record-keybind = Click to record
settings-sidebar_keybinds_now-recording = Recording…
## FK/Tracking settings
settings-general-fk_settings = Tracking settings

View File

@@ -14,12 +14,24 @@ export const KeybindRecorder = forwardRef<
return (
<div className="relative w-full">
<style>
{`
@keyframes keyslot {
0%, 100% { transform: scale(1); opacity: 0.6; }
50% { transform: scale(1.08); opacity: 1; }
}
.keyslot-animate {
animation: keyslot 1s ease-in-out infinite;
}
`}
</style>
<input
ref={ref}
className="opacity-0 absolute inset-0 cursor-pointer"
onFocus={() => {
setOldKeys(keys);
onKeysChange([]);
onKeysChange(['CTRL', 'ALT']);
setIsRecording(true);
}}
onBlur={() => {
@@ -33,21 +45,29 @@ export const KeybindRecorder = forwardRef<
}
}}
/>
<div className="flex gap-2 min-h-[42px] items-center px-3 py-2 rounded-lg bg-background-80">
<div className="flex flex-grow gap-2 flex-wrap">
{keys.map((key, i) => (
<div
key={i}
className="bg-accent-background-50 px-3 py-1 rounded-md text-sm"
>
{key}
</div>
))}
<div className="flex flex-grow gap-2 min-w-[180px]">
{Array.from({ length: 4 }).map((_, i) => {
const key = keys[i];
const isActive = isRecording && i === keys.length;
return (
<div
key={i}
className={`
px-2 py-1 rounded-md text-sm min-w-[32px] text-center
${key ? 'bg-accent-background-50' : 'bg-accent-background-50/30'}
${isActive ? 'keyslot-animate ring-2 ring-accent' : ''}
`}
>
{key ?? ''}
</div>
);
})}
</div>
<div className="text-accent-background-10 text-right text-sm font-medium">
{keys.length < 4 && isRecording ? l10n.getString('settings-sidebar_keybinds_now-recording') : l10n.getString('settings-sidebar_keybinds_record-keybind') }
<div className="w-40 flex-shrink-0 text-accent-background-10 text-right text-sm font-medium">
{keys.length < 4 && isRecording
? l10n.getString('settings-keybinds_now-recording')
: l10n.getString('settings-keybinds_record-keybind')}
</div>
</div>
</div>

View File

@@ -0,0 +1,16 @@
:root {
--row-h: 80px;
}
.keybind-row {
display: grid;
grid-column: 1 / -1;
grid-template-columns: subgrid;
height: var(--row-h);
align-items: center;
gap: 20px;
}
.number-selector::after {
gap: 0;
}

View File

@@ -3,19 +3,22 @@ import { Button } from './Button';
import { NumberSelector } from './NumberSelector';
import { KeybindRecorder } from './KeybindRecorder';
import { useLocaleConfig } from '@/i18n/config';
import { Typography } from './Typography';
import './KeybindRow.scss';
export function KeybindRow({
label,
id,
control,
resetField,
bindingName,
delayName,
name,
delay,
}: {
label: string;
id?: string;
label?: string;
control: Control<any>;
resetField: UseFormResetField<any>;
bindingName: string;
delayName: string;
name: string;
delay: string;
}) {
const { currentLocales } = useLocaleConfig();
const secondsFormat = new Intl.NumberFormat(currentLocales, {
@@ -24,50 +27,41 @@ export function KeybindRow({
unitDisplay: 'narrow',
maximumFractionDigits: 2,
});
return (
<tr className="border-b border-background-60 h-20">
<td className="px-6 py-4 pr-4">
<label className="text-sm font-medium text-background-10">
{label}
</label>
</td>
<td className="px-4">
<Controller
control={control}
name={bindingName}
render={({ field }) => (
<KeybindRecorder
keys={field.value ?? []}
onKeysChange={field.onChange}
ref={field.ref}
/>
)}
/>
</td>
<td className="px-4">
<div className="keybind-row">
<label className="text-sm font-medium text-background-10">
<Typography id={id} />
</label>
<Controller
control={control}
name={name}
render={({ field }) => (
<KeybindRecorder
keys={field.value}
onKeysChange={field.onChange}
ref={field.ref}
/>
)}
/>
<NumberSelector
control={control}
name={delayName}
name={delay}
valueLabelFormat={(value) => secondsFormat.format(value)}
min={0}
max={10}
step={0.2}
/>
</td>
<td className="px-2">
<div className="flex gap-2 justify-center px-4">
<Button
variant="primary"
onClick={() => {
resetField(bindingName);
resetField(delayName);
}}
>
Reset
</Button>
</div>
</td>
</tr>
<div className='max-w-[45px]'>
<Button
id="settings-keybinds_reset-button"
variant="primary"
onClick={() => {
resetField(name);
resetField(delay);
}}
/>
</div>
</div>
);
}

View File

@@ -8,17 +8,17 @@ import { useWebsocketAPI } from '@/hooks/websocket-api';
import { RpcMessage, InstalledInfoResponseT } from 'solarxr-protocol';
import { useConfig } from '@/hooks/config';
import { useLocalization } from '@fluent/react';
import { useAppContext } from '@/hooks/app';
export function UdevRulesModal() {
const { config, setConfig } = useConfig();
const { useRPCPacket, sendRPCPacket } = useWebsocketAPI();
const electron = useElectron();
const [udevContent, setUdevContent] = useState('');
const [isUdevInstalledResponse, setIsUdevInstalledResponse] = useState(true);
const [showUdevWarning, setShowUdevWarning] = useState(false);
const [dontShowThisSession, setDontShowThisSession] = useState(false);
const [dontShowAgain, setDontShowAgain] = useState(false);
const { l10n } = useLocalization();
const { installInfo } = useAppContext();
const handleUdevContent = async () => {
if (electron.isElectron) {
@@ -38,28 +38,15 @@ export function UdevRulesModal() {
if (!config) throw 'Invalid state!';
if (electron.isElectron) {
const isLinux = electron.data().os.type === 'linux';
const udevMissing = !isUdevInstalledResponse;
const udevMissing = !installInfo?.isUdevInstalled;
const notHiddenGlobally = !config.dontShowUdevModal;
const notHiddenThisSession = !dontShowThisSession;
const shouldShow =
isLinux && udevMissing && notHiddenGlobally && notHiddenThisSession;
setShowUdevWarning(shouldShow);
}
}, [config, isUdevInstalledResponse, dontShowThisSession]);
}, [config, dontShowThisSession]);
useEffect(() => {
sendRPCPacket(
RpcMessage.InstalledInfoRequest,
new InstalledInfoResponseT()
);
}, []);
useRPCPacket(
RpcMessage.InstalledInfoResponse,
({ isUdevInstalled }: InstalledInfoResponseT) => {
setIsUdevInstalledResponse(isUdevInstalled);
}
);
const handleModalClose = () => {
if (!config) throw 'Invalid State!';

View File

@@ -0,0 +1,22 @@
:root {
--keybindrow-w: 1fr;
--top-row-h: 10px;
--keybindrow-h: 10px;
--buttonrow-h: 10px;
}
.keybind-settings {
display: grid;
grid-template:
'n v d e' auto
'k k k k' auto
'b . . .' auto
/ 200px 1fr 120px auto;
grid-template-columns: subgrid;
grid-template-rows: subgrid;
place-items: center;
}

View File

@@ -4,8 +4,9 @@ import {
SettingsPagePaneLayout,
} from '@/components/settings/SettingsPageLayout';
import { Typography } from '@/components/commons/Typography';
import { Localized, useLocalization } from '@fluent/react';
import { useForm } from 'react-hook-form';
import { useLocalization } from '@fluent/react';
import { DefaultValues, useForm } from 'react-hook-form';
import './KeybindSettings.scss';
import { ReactNode, useEffect } from 'react';
import { KeybindRow } from '@/components/commons/KeybindRow';
import { Button } from '@/components/commons/Button';
@@ -17,7 +18,30 @@ import {
KeybindT,
KeybindName,
ChangeKeybindRequestT,
OpenUriRequestT,
} from 'solarxr-protocol';
import { useAppContext } from '@/hooks/app';
function Table({ children }: { children: ReactNode }) {
return (
<table className="min-w-full divide-y divide-background-50">
<thead>
<tr>
<th scope="col" className="px-6 py-3 text-start">
<Typography id="keybind_config-keybind_name" />
</th>
<th scope="col" className="px-6 py-3 text-middle">
<Typography id="keybind_config-keybind_value" />
</th>
<th scope="col" className="px-6 py-3 text-middle">
<Typography id="keybind_config-keybind_delay" />
</th>
</tr>
</thead>
<tbody>{children}</tbody>
</table>
);
}
export type KeybindsForm = {
names: {
@@ -67,39 +91,17 @@ const defaultValues: KeybindsForm = {
},
};
export function useKeybindsForm() {
const {
register,
reset,
handleSubmit,
formState,
control,
getValues,
resetField,
watch,
} = useForm<KeybindsForm>({
defaultValues,
});
return {
control,
register,
reset,
handleSubmit,
formState,
getValues,
resetField,
watch,
};
}
export function KeybindSettings() {
const { l10n } = useLocalization();
const { control, reset, handleSubmit, watch, getValues, resetField } =
useKeybindsForm();
const { control, reset, handleSubmit, watch, resetField, getValues } =
useForm<KeybindsForm>({
defaultValues,
});
const { sendRPCPacket, useRPCPacket } = useWebsocketAPI();
const { installInfo } = useAppContext();
const onSubmit = (values: KeybindsForm) => {
console.log('Onsubmit in KeybindSettings');
const keybinds = new ChangeKeybindRequestT();
const fullResetKeybind = new KeybindT();
@@ -134,7 +136,6 @@ export function KeybindSettings() {
feetResetKeybind.keybindDelay = values.delays.pauseTrackingDelay;
keybinds.keybind.push(feetResetKeybind);
console.log(`Delay ${Number(fullResetKeybind.keybindDelay)}`);
sendRPCPacket(RpcMessage.ChangeKeybindRequest, keybinds);
};
@@ -150,162 +151,151 @@ export function KeybindSettings() {
useRPCPacket(RpcMessage.KeybindResponse, ({ keybind }: KeybindResponseT) => {
if (!keybind) return;
const keybindValues: KeybindsForm = {
names: {
fullResetName: KeybindName.FULL_RESET,
yawResetName: KeybindName.YAW_RESET,
mountingResetName: KeybindName.MOUNTING_RESET,
pauseTrackingName: KeybindName.PAUSE_TRACKING,
feetResetName: KeybindName.FEET_MOUNTING_RESET,
reset(
{
names: {
fullResetName: KeybindName.FULL_RESET,
yawResetName: KeybindName.YAW_RESET,
mountingResetName: KeybindName.MOUNTING_RESET,
pauseTrackingName: KeybindName.PAUSE_TRACKING,
feetResetName: KeybindName.FEET_MOUNTING_RESET,
},
bindings: {
fullResetBinding:
(typeof keybind[KeybindName.FULL_RESET].keybindValue === 'string'
? keybind[KeybindName.FULL_RESET].keybindValue
: ''
).split('+') || defaultValues.bindings.fullResetBinding,
yawResetBinding:
(typeof keybind[KeybindName.YAW_RESET].keybindValue === 'string'
? keybind[KeybindName.YAW_RESET].keybindValue
: ''
).split('+') || defaultValues.bindings.yawResetBinding,
mountingResetBinding:
(typeof keybind[KeybindName.MOUNTING_RESET].keybindValue ===
'string'
? keybind[KeybindName.MOUNTING_RESET].keybindValue
: ''
).split('+') || defaultValues.bindings.mountingResetBinding,
pauseTrackingBinding:
(typeof keybind[KeybindName.PAUSE_TRACKING].keybindValue ===
'string'
? keybind[KeybindName.PAUSE_TRACKING].keybindValue
: ''
).split('+') || defaultValues.bindings.pauseTrackingBinding,
feetResetBinding:
(typeof keybind[KeybindName.FEET_MOUNTING_RESET].keybindValue ===
'string'
? keybind[KeybindName.FEET_MOUNTING_RESET].keybindValue
: ''
).split('+') || defaultValues.bindings.feetResetBinding,
},
delays: {
fullResetDelay:
keybind[KeybindName.FULL_RESET].keybindDelay ||
defaultValues.delays.fullResetDelay,
yawResetDelay:
keybind[KeybindName.YAW_RESET].keybindDelay ||
defaultValues.delays.yawResetDelay,
mountingResetDelay:
keybind[KeybindName.MOUNTING_RESET].keybindDelay ||
defaultValues.delays.mountingResetDelay,
pauseTrackingDelay:
keybind[KeybindName.PAUSE_TRACKING].keybindDelay ||
defaultValues.delays.pauseTrackingDelay,
feetResetDelay:
keybind[KeybindName.FEET_MOUNTING_RESET].keybindDelay ||
defaultValues.delays.feetResetDelay,
},
},
bindings: {
fullResetBinding:
(typeof keybind[KeybindName.FULL_RESET].keybindValue === 'string'
? keybind[KeybindName.FULL_RESET].keybindValue
: ''
).split('+') || defaultValues.bindings.fullResetBinding,
yawResetBinding:
(typeof keybind[KeybindName.YAW_RESET].keybindValue === 'string'
? keybind[KeybindName.YAW_RESET].keybindValue
: ''
).split('+') || defaultValues.bindings.yawResetBinding,
mountingResetBinding:
(typeof keybind[KeybindName.MOUNTING_RESET].keybindValue === 'string'
? keybind[KeybindName.MOUNTING_RESET].keybindValue
: ''
).split('+') || defaultValues.bindings.mountingResetBinding,
pauseTrackingBinding:
(typeof keybind[KeybindName.PAUSE_TRACKING].keybindValue === 'string'
? keybind[KeybindName.PAUSE_TRACKING].keybindValue
: ''
).split('+') || defaultValues.bindings.pauseTrackingBinding,
feetResetBinding:
(typeof keybind[KeybindName.FEET_MOUNTING_RESET].keybindValue ===
'string'
? keybind[KeybindName.FEET_MOUNTING_RESET].keybindValue
: ''
).split('+') || defaultValues.bindings.feetResetBinding,
},
delays: {
fullResetDelay:
keybind[KeybindName.FULL_RESET].keybindDelay ||
defaultValues.delays.fullResetDelay,
yawResetDelay:
keybind[KeybindName.YAW_RESET].keybindDelay ||
defaultValues.delays.yawResetDelay,
mountingResetDelay:
keybind[KeybindName.MOUNTING_RESET].keybindDelay ||
defaultValues.delays.mountingResetDelay,
pauseTrackingDelay:
keybind[KeybindName.PAUSE_TRACKING].keybindDelay ||
defaultValues.delays.mountingResetDelay,
feetResetDelay:
keybind[KeybindName.FEET_MOUNTING_RESET].keybindDelay ||
defaultValues.delays.feetResetDelay,
},
};
reset({ ...getValues(), ...keybindValues });
{
keepDefaultValues: true,
}
);
});
const handleResetButton = () => {
reset(defaultValues);
const handleOpenSystemSettingsButton = () => {
sendRPCPacket(RpcMessage.OpenUriRequest, new OpenUriRequestT());
};
function Table({ children }: { children: ReactNode }) {
return (
<table className="min-w-full divide-y divide-background-50">
<thead>
<tr>
<th scope="col" className="px-6 py-3 text-start">
<Localized id={'keybind_config-keybind_name'}>
<Typography />
</Localized>
Keybind
</th>
<th scope="col" className="px-6 py-3 text-middle">
<Localized id={'keybind_config-keybind_value'}>
<Typography />
</Localized>
Combination
</th>
<th scope="col" className="px-6 py-3 text-middle">
<Localized id={'keybind_config-keybind_delay'}>
<Typography />
</Localized>
Delay before trigger (S)
</th>
</tr>
</thead>
<tbody>{children}</tbody>
</table>
);
}
const handleResetAllButton = () => {
reset(defaultValues);
};
return (
<SettingsPageLayout>
<form className="flex flex-col gap-2 w-full">
<SettingsPagePaneLayout icon={<WrenchIcon />} id="keybinds">
<>
<Typography variant="main-title" id="settings-keybinds" />
<div className="flex flex-col pt-2 pb-4">
{l10n
.getString('settings-keybinds-description')
.split('\n')
.map((line, i) => (
<Typography key={i}>{line}</Typography>
))}
<Typography variant="main-title" id="settings-keybinds" />
<div className="flex flex-col pt-2 pb-4">
{l10n
.getString('settings-keybinds-description')
.split('\n')
.map((line, i) => (
<Typography key={i}>{line}</Typography>
))}
</div>
{!installInfo?.isWayland ? (
<div className="flex flex-col gap-4">
<Typography id="settings-keybinds-wayland-description" />
<div>
<Button
id="settings-keybinds-wayland-open-system-settings-button"
className="flex flex-col"
onClick={handleOpenSystemSettingsButton}
variant="primary"
/>
</div>
</div>
<Table>
<KeybindRow
label="Full Reset"
control={control}
resetField={resetField}
bindingName="bindings.fullResetBinding"
delayName="delays.fullResetDelay"
/>
<KeybindRow
label="Yaw Reset"
control={control}
resetField={resetField}
bindingName="bindings.yawResetBinding"
delayName="delays.yawResetDelay"
/>
<KeybindRow
label="Mounting Reset"
control={control}
resetField={resetField}
bindingName="bindings.mountingResetBinding"
delayName="delays.mountingResetDelay"
/>
<KeybindRow
label="Feet Mounting Reset"
control={control}
resetField={resetField}
bindingName="bindings.feetResetBinding"
delayName="delays.feetResetDelay"
/>
<KeybindRow
label="Pause Tracking"
control={control}
resetField={resetField}
bindingName="bindings.pauseTrackingBinding"
delayName="delays.pauseTrackingDelay"
/>
</Table>
<div className="flex flex-col pt-4" />
<Button
className="flex flex-col"
onClick={handleResetButton}
variant="primary"
>
Reset All
</Button>
</>
) : (
<div className="keybind-settings">
<Typography id="keybind_config-keybind_name" />
<Typography id="keybind_config-keybind_value" />
<Typography id="keybind_config-keybind_delay" />
<div />
<KeybindRow
id="settings-keybinds_full-reset"
control={control}
resetField={resetField}
name="bindings.fullResetBinding"
delay="delays.fullResetDelay"
/>
<KeybindRow
id="settings-keybinds_yaw-reset"
control={control}
resetField={resetField}
name="bindings.yawResetBinding"
delay="delays.yawResetDelay"
/>
<KeybindRow
id="settings-keybinds_mounting-reset"
control={control}
resetField={resetField}
name="bindings.mountingResetBinding"
delay="delays.mountingResetDelay"
/>
<KeybindRow
id="settings-keybinds_feet-mounting-reset"
control={control}
resetField={resetField}
name="bindings.feetResetBinding"
delay="delays.feetResetDelay"
/>
<KeybindRow
id="settings-keybinds_pause-tracking"
control={control}
resetField={resetField}
name="bindings.pauseTrackingBinding"
delay="delays.pauseTrackingDelay"
/>
<Button
id="settings-keybinds_reset-all-button"
className="justify-self-start"
onClick={handleResetAllButton}
variant="primary"
/>
</div>
)}
</SettingsPagePaneLayout>
</form>
</SettingsPageLayout>

View File

@@ -39,8 +39,8 @@ function StaAlignedPoseModal({
<Typography variant="main-title">{l10n.getString(title)}</Typography>
</div>
<div className="flex flex-col gap-1">
{descriptionKeys.map((descriptionKey) => (
<Typography>{l10n.getString(descriptionKey)}</Typography>
{descriptionKeys.map((descriptionKey, i) => (
<Typography key={i}>{l10n.getString(descriptionKey)}</Typography>
))}
</div>
<div className="flex pt-1 items-center fill-background-50 justify-center px-12">

View File

@@ -5,6 +5,7 @@ import {
ResetResponseT,
RpcMessage,
StartDataFeedT,
InstalledInfoResponseT
} from 'solarxr-protocol';
import { handleResetSounds } from '@/sounds/sounds';
import { useConfig } from './config';
@@ -18,10 +19,11 @@ import { DEFAULT_LOCALE, LangContext } from '@/i18n/config';
export interface AppContext {
currentFirmwareRelease: FirmwareRelease | null;
installInfo: InstalledInfoResponseT | null;
}
export function useProvideAppContext(): AppContext {
const { useRPCPacket, sendDataFeedPacket, useDataFeedPacket, isConnected } =
const { useRPCPacket, sendRPCPacket, sendDataFeedPacket, useDataFeedPacket, isConnected } =
useWebsocketAPI();
const { changeLocales } = useContext(LangContext);
const { config } = useConfig();
@@ -34,6 +36,8 @@ export function useProvideAppContext(): AppContext {
const [currentFirmwareRelease, setCurrentFirmwareRelease] =
useState<FirmwareRelease | null>(null);
const [installInfo, setInstallInfo] = useState<InstalledInfoResponseT | null>(null)
useEffect(() => {
if (isConnected) {
const startDataFeed = new StartDataFeedT();
@@ -50,6 +54,24 @@ export function useProvideAppContext(): AppContext {
}
});
useEffect(() => {
sendRPCPacket(
RpcMessage.InstalledInfoRequest,
new InstalledInfoResponseT()
);
}, []);
useRPCPacket(
RpcMessage.InstalledInfoResponse,
({ isUdevInstalled, isWayland }: InstalledInfoResponseT) => {
setInstallInfo(new InstalledInfoResponseT(
isUdevInstalled,
isWayland
));
}
);
useEffect(() => {
updateSentryContext(devices);
}, [devices]);
@@ -85,6 +107,7 @@ export function useProvideAppContext(): AppContext {
return {
currentFirmwareRelease,
installInfo
};
}

View File

@@ -8,7 +8,7 @@
"gui"
],
"scripts": {
"gui": "pnpm run update-solarxr && cd gui && pnpm run gui",
"gui": "pnpm run update-solarxr && cd gui && pnpm run dev:clean",
"lint:fix": "cd gui && pnpm lint:fix",
"skipbundler": "cd gui && pnpm run skipbundler",
"build": "cd gui && pnpm build",

View File

@@ -79,7 +79,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
implementation("com.mayakapps.kache:kache:2.1.1")
implementation("com.github.HannahPadd:DbusGlobalShortcutsWayland:5010e75bd4")
implementation("com.github.HannahPadd:DbusGlobalShortcutsWayland:v0.1.0")
api("com.github.loucass003:EspflashKotlin:v0.11.0")

View File

@@ -3,7 +3,7 @@ package dev.slimevr
import com.melloware.jintellitype.HotkeyListener
import com.melloware.jintellitype.JIntellitype
import dev.hannah.portals.PortalManager
import dev.hannah.portals.Shortcut
import dev.hannah.portals.globalShortcuts.Shortcut
import dev.hannah.portals.globalShortcuts.ShortcutTuple
import dev.slimevr.config.KeybindingsConfig
import dev.slimevr.tracking.trackers.TrackerUtils

View File

@@ -12,6 +12,7 @@ import dev.slimevr.protocol.rpc.firmware.RPCFirmwareUpdateHandler
import dev.slimevr.protocol.rpc.games.vrchat.RPCVRChatHandler
import dev.slimevr.protocol.rpc.installinfo.RPCInstallInfoHandler
import dev.slimevr.protocol.rpc.keybinds.RPCKeybindHandler
import dev.slimevr.protocol.rpc.openuri.RPCOpenUriHandler
import dev.slimevr.protocol.rpc.reset.RPCResetHandler
import dev.slimevr.protocol.rpc.serial.RPCProvisioningHandler
import dev.slimevr.protocol.rpc.serial.RPCSerialHandler
@@ -54,6 +55,8 @@ class RPCHandler(private val api: ProtocolAPI) : ProtocolHandler<RpcMessageHeade
RPCVRChatHandler(this, api)
RPCTrackingChecklistHandler(this, api)
RPCUserHeightCalibration(this, api)
RPCInstallInfoHandler(this, api)
RPCOpenUriHandler(this, api)
try {
RPCKeybindHandler(this, api)
} catch (e: Exception) {

View File

@@ -32,12 +32,13 @@ class RPCInstallInfoHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
LogManager.warning("Server couldn't verify if udev is installed")
return
}
val response = udevResponse.contains("slime")
val isUdevInstalled = udevResponse.contains("slime")
val isWayland = System.getenv("XDG_SESSION_TYPE").lowercase().contains("wayland")
val fbb = FlatBufferBuilder(1024)
val outbound = this.rpcHandler.createRPCMessage(
fbb,
RpcMessage.InstalledInfoResponse,
createInstalledInfoResponse(fbb, response),
createInstalledInfoResponse(fbb, isUdevInstalled, isWayland),
)
fbb.finish(outbound)
conn.send(fbb.dataBuffer())

View File

@@ -39,6 +39,7 @@ class RPCKeybindHandler(
private fun onKeybindRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
println("Received KeybindsRequest")
val fbb = FlatBufferBuilder(32)
val response = buildKeybindResponse(fbb)
val outbound = rpcHandler.createRPCMessage(
@@ -51,6 +52,7 @@ class RPCKeybindHandler(
}
private fun onChangeKeybindRequest(conn: GenericConnection, messageHeader: RpcMessageHeader) {
println("Received Keybinds Change request")
val req = (messageHeader.message(ChangeKeybindRequest()) as ChangeKeybindRequest).unpack()
keybindingConfig.fullResetBinding = req.keybind[KeybindName.FULL_RESET].keybindValue

View File

@@ -0,0 +1,21 @@
package dev.slimevr.protocol.rpc.openuri
import dev.slimevr.protocol.GenericConnection
import dev.slimevr.protocol.ProtocolAPI
import dev.slimevr.protocol.rpc.RPCHandler
import solarxr_protocol.rpc.RpcMessage
import solarxr_protocol.rpc.RpcMessageHeader
import dev.hannah.portals.PortalManager
import dev.slimevr.SLIMEVR_IDENTIFIER
class RPCOpenUriHandler(var rpcHandler: RPCHandler, var api: ProtocolAPI) {
init {
rpcHandler.registerPacketListener(RpcMessage.OpenUriRequest, ::onOpenUriRequest)
}
fun onOpenUriRequest(conn: GenericConnection, messageHeader: RpcMessageHeader?) {
val portalManager = PortalManager(SLIMEVR_IDENTIFIER)
portalManager.openGlobalShortcutsSettings()
}
}

View File

@@ -73,8 +73,7 @@ tasks.shadowJar {
exclude(dependency("com.fazecast:jSerialComm:.*"))
exclude(dependency("net.java.dev.jna:.*:.*"))
exclude(dependency("com.google.flatbuffers:flatbuffers-java:.*"))
exclude(dependency("com.github.HannahPadd:DbusGlobalShortcutsWayland:5010e75bd4"))
exclude(dependency("com.github.HannahPadd:DbusGlobalShortcutsWayland:v0.1.0"))
exclude(project(":solarxr-protocol"))
}
archiveBaseName.set("slimevr")