mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-05 18:01:56 +02:00
move bridges to desktop
This commit is contained in:
@@ -45,7 +45,7 @@ fun main(activity: AppCompatActivity) {
|
||||
e1.printStackTrace()
|
||||
}
|
||||
try {
|
||||
vrServer = VRServer(File(activity.filesDir, "vrconfig.yml").absolutePath)
|
||||
vrServer = VRServer(configPath = File(activity.filesDir, "vrconfig.yml").absolutePath)
|
||||
vrServer.start()
|
||||
Keybinding(vrServer)
|
||||
vrServer.join()
|
||||
|
||||
64
server/build.gradle.kts
Normal file
64
server/build.gradle.kts
Normal file
@@ -0,0 +1,64 @@
|
||||
plugins {
|
||||
id("com.diffplug.spotless")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
configure<com.diffplug.gradle.spotless.SpotlessExtension> {
|
||||
// optional: limit format enforcement to just the files changed by this feature branch
|
||||
// ratchetFrom "origin/main"
|
||||
|
||||
format("misc") {
|
||||
// define the files to apply `misc` to
|
||||
target("*.gradle", "*.md", ".gitignore")
|
||||
|
||||
// define the steps to apply to those files
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
indentWithTabs()
|
||||
}
|
||||
// format "yaml", {
|
||||
// target "*.yml", "*.yaml",
|
||||
|
||||
// trimTrailingWhitespace()
|
||||
// endWithNewline()
|
||||
// indentWithSpaces(2) // YAML cannot contain tabs: https://yaml.org/faq.html
|
||||
// }
|
||||
|
||||
// .editorconfig doesn't work so, manual override
|
||||
// https://github.com/diffplug/spotless/issues/142
|
||||
val editorConfig =
|
||||
mapOf(
|
||||
"indent_size" to 4,
|
||||
"indent_style" to "tab",
|
||||
// "max_line_length" to 88,
|
||||
"ktlint_experimental" to "enabled",
|
||||
"ij_kotlin_packages_to_use_import_on_demand" to
|
||||
"java.util.*,kotlin.math.*,dev.slimevr.autobone.errors.*,io.github.axisangles.ktmath.*,kotlinx.atomicfu.*",
|
||||
"ij_kotlin_allow_trailing_comma" to true
|
||||
)
|
||||
val ktlintVersion = "0.47.1"
|
||||
kotlinGradle {
|
||||
target("**/*.gradle.kts") // default target for kotlinGradle
|
||||
ktlint(ktlintVersion)
|
||||
.setUseExperimental(true)
|
||||
.editorConfigOverride(editorConfig)
|
||||
}
|
||||
kotlin {
|
||||
target("**/*.kt")
|
||||
targetExclude("**/build/**/**.kt")
|
||||
ktlint(ktlintVersion)
|
||||
.setUseExperimental(true)
|
||||
.editorConfigOverride(editorConfig)
|
||||
}
|
||||
java {
|
||||
target("**/*.java")
|
||||
targetExclude("**/BuildConfig.java")
|
||||
|
||||
removeUnusedImports()
|
||||
// Use eclipse JDT formatter
|
||||
eclipse().configFile("spotless.xml")
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ plugins {
|
||||
kotlin("jvm")
|
||||
kotlin("plugin.serialization")
|
||||
`java-library`
|
||||
id("com.diffplug.spotless")
|
||||
}
|
||||
|
||||
// FIXME: Please replace these to Java 11 as that's what they actually are
|
||||
@@ -74,11 +73,9 @@ dependencies {
|
||||
implementation("org.apache.commons:commons-lang3:3.12.0")
|
||||
implementation("org.apache.commons:commons-collections4:4.4")
|
||||
|
||||
implementation("net.java.dev.jna:jna:5.+")
|
||||
implementation("net.java.dev.jna:jna-platform:5.+")
|
||||
implementation("com.illposed.osc:javaosc-core:0.8")
|
||||
implementation("com.fazecast:jSerialComm:2.+")
|
||||
implementation("com.google.protobuf:protobuf-java:3.21.12")
|
||||
api("com.google.protobuf:protobuf-java:3.21.12")
|
||||
implementation("org.java-websocket:Java-WebSocket:1.+")
|
||||
implementation("com.melloware:jintellitype:1.+")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||
@@ -103,58 +100,3 @@ fun String.runCommand(currentWorkingDir: File = file("./")): String {
|
||||
}
|
||||
return String(byteOut.toByteArray()).trim()
|
||||
}
|
||||
|
||||
configure<com.diffplug.gradle.spotless.SpotlessExtension> {
|
||||
// optional: limit format enforcement to just the files changed by this feature branch
|
||||
// ratchetFrom "origin/main"
|
||||
|
||||
format("misc") {
|
||||
// define the files to apply `misc` to
|
||||
target("*.gradle", "*.md", ".gitignore")
|
||||
|
||||
// define the steps to apply to those files
|
||||
trimTrailingWhitespace()
|
||||
endWithNewline()
|
||||
indentWithTabs()
|
||||
}
|
||||
// format "yaml", {
|
||||
// target "*.yml", "*.yaml",
|
||||
|
||||
// trimTrailingWhitespace()
|
||||
// endWithNewline()
|
||||
// indentWithSpaces(2) // YAML cannot contain tabs: https://yaml.org/faq.html
|
||||
// }
|
||||
|
||||
// .editorconfig doesn't work so, manual override
|
||||
// https://github.com/diffplug/spotless/issues/142
|
||||
val editorConfig =
|
||||
mapOf(
|
||||
"indent_size" to 4,
|
||||
"indent_style" to "tab",
|
||||
// "max_line_length" to 88,
|
||||
"ktlint_experimental" to "enabled",
|
||||
"ij_kotlin_packages_to_use_import_on_demand" to
|
||||
"java.util.*,kotlin.math.*,dev.slimevr.autobone.errors.*,io.github.axisangles.ktmath.*,kotlinx.atomicfu.*",
|
||||
"ij_kotlin_allow_trailing_comma" to true
|
||||
)
|
||||
val ktlintVersion = "0.47.1"
|
||||
kotlinGradle {
|
||||
target("*.gradle.kts") // default target for kotlinGradle
|
||||
ktlint(ktlintVersion)
|
||||
.setUseExperimental(true)
|
||||
.editorConfigOverride(editorConfig)
|
||||
}
|
||||
kotlin {
|
||||
targetExclude("build/**/**.kt")
|
||||
ktlint(ktlintVersion)
|
||||
.setUseExperimental(true)
|
||||
.editorConfigOverride(editorConfig)
|
||||
}
|
||||
java {
|
||||
targetExclude("**/BuildConfig.java")
|
||||
|
||||
removeUnusedImports()
|
||||
// Use eclipse JDT formatter
|
||||
eclipse().configFile("spotless.xml")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,6 @@ import dev.slimevr.osc.OSCRouter
|
||||
import dev.slimevr.osc.VMCHandler
|
||||
import dev.slimevr.osc.VRCOSCHandler
|
||||
import dev.slimevr.platform.SteamVRBridge
|
||||
import dev.slimevr.platform.linux.UnixSocketBridge
|
||||
import dev.slimevr.platform.windows.WindowsNamedPipeBridge
|
||||
import dev.slimevr.posestreamer.BVHRecorder
|
||||
import dev.slimevr.protocol.ProtocolAPI
|
||||
import dev.slimevr.reset.ResetHandler
|
||||
@@ -26,19 +24,27 @@ import dev.slimevr.tracking.trackers.TrackerPosition
|
||||
import dev.slimevr.tracking.trackers.udp.TrackersUDPServer
|
||||
import dev.slimevr.util.ann.VRServerThread
|
||||
import dev.slimevr.websocketapi.WebSocketVRBridge
|
||||
import io.eiren.util.OperatingSystem
|
||||
import io.eiren.util.ann.ThreadSafe
|
||||
import io.eiren.util.ann.ThreadSecure
|
||||
import io.eiren.util.collections.FastList
|
||||
import io.eiren.util.logging.LogManager
|
||||
import solarxr_protocol.datatypes.TrackerIdT
|
||||
import java.nio.file.Paths
|
||||
import java.util.*
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.function.Consumer
|
||||
|
||||
class VRServer @JvmOverloads constructor(configPath: String? = "vrconfig.yml") : Thread("VRServer") {
|
||||
typealias SteamBridgeProvider = (
|
||||
server: VRServer,
|
||||
hmdTracker: Tracker,
|
||||
computedTrackers: List<Tracker>,
|
||||
) -> SteamVRBridge?
|
||||
|
||||
class VRServer constructor(
|
||||
driverBridgeProvider: SteamBridgeProvider = { _: VRServer, _: Tracker, _: List<Tracker> -> null },
|
||||
feederBridgeProvider: (VRServer) -> SteamVRBridge? = { _: VRServer -> null },
|
||||
configPath: String,
|
||||
) : Thread("VRServer") {
|
||||
@JvmField
|
||||
val configManager: ConfigManager
|
||||
|
||||
@@ -107,12 +113,9 @@ class VRServer @JvmOverloads constructor(configPath: String? = "vrconfig.yml") :
|
||||
"HMD",
|
||||
TrackerPosition.HEAD,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
hasPosition = true,
|
||||
hasRotation = true,
|
||||
isComputed = true
|
||||
)
|
||||
humanPoseManager = HumanPoseManager(this)
|
||||
val computedTrackers = humanPoseManager.computedTrackers
|
||||
@@ -124,81 +127,19 @@ class VRServer @JvmOverloads constructor(configPath: String? = "vrconfig.yml") :
|
||||
trackerPort,
|
||||
"Sensors UDP server"
|
||||
) { tracker: Tracker -> registerTracker(tracker) }
|
||||
val driverBridge: SteamVRBridge?
|
||||
if (OperatingSystem.getCurrentPlatform() == OperatingSystem.WINDOWS) {
|
||||
|
||||
// Create named pipe bridge for SteamVR driver
|
||||
driverBridge = WindowsNamedPipeBridge(
|
||||
this,
|
||||
hmdTracker,
|
||||
"steamvr",
|
||||
"SteamVR Driver Bridge",
|
||||
"\\\\.\\pipe\\SlimeVRDriver",
|
||||
computedTrackers
|
||||
)
|
||||
// Start bridges for SteamVR and Feeder
|
||||
val driverBridge = driverBridgeProvider(this, hmdTracker, computedTrackers)
|
||||
if (driverBridge != null) {
|
||||
tasks.add(Runnable { driverBridge.startBridge() })
|
||||
bridges.add(driverBridge)
|
||||
|
||||
// Create named pipe bridge for SteamVR input
|
||||
// TODO: how do we want to handle HMD input from the feeder app?
|
||||
val feederBridge = WindowsNamedPipeBridge(
|
||||
this,
|
||||
null,
|
||||
"steamvr_feeder",
|
||||
"SteamVR Feeder Bridge",
|
||||
"\\\\.\\pipe\\SlimeVRInput",
|
||||
FastList()
|
||||
)
|
||||
}
|
||||
val feederBridge = feederBridgeProvider(this)
|
||||
if (feederBridge != null) {
|
||||
tasks.add(Runnable { feederBridge.startBridge() })
|
||||
bridges.add(feederBridge)
|
||||
} else if (OperatingSystem.getCurrentPlatform() == OperatingSystem.LINUX) {
|
||||
var linuxBridge: SteamVRBridge? = null
|
||||
try {
|
||||
linuxBridge = UnixSocketBridge(
|
||||
this,
|
||||
hmdTracker,
|
||||
"steamvr",
|
||||
"SteamVR Driver Bridge",
|
||||
Paths.get(OperatingSystem.getTempDirectory(), "SlimeVRDriver").toString(),
|
||||
computedTrackers
|
||||
)
|
||||
} catch (ex: Exception) {
|
||||
LogManager.severe("Failed to initiate Unix socket, disabling driver bridge...", ex)
|
||||
}
|
||||
driverBridge = linuxBridge
|
||||
if (driverBridge != null) {
|
||||
tasks.add(Runnable { driverBridge.startBridge() })
|
||||
bridges.add(driverBridge)
|
||||
}
|
||||
try {
|
||||
val feederBridge: SteamVRBridge = UnixSocketBridge(
|
||||
this,
|
||||
null,
|
||||
"steamvr_feeder",
|
||||
"SteamVR Feeder Bridge",
|
||||
Paths.get(OperatingSystem.getTempDirectory(), "SlimeVRInput").toString(),
|
||||
FastList()
|
||||
)
|
||||
tasks.add(Runnable { feederBridge.startBridge() })
|
||||
bridges.add(feederBridge)
|
||||
} catch (ex: Exception) {
|
||||
LogManager.severe("Failed to initiate Unix socket, disabling feeder bridge...", ex)
|
||||
}
|
||||
} else {
|
||||
driverBridge = null
|
||||
}
|
||||
|
||||
// Add shutdown hook
|
||||
Runtime.getRuntime().addShutdownHook(
|
||||
Thread {
|
||||
try {
|
||||
(driverBridge as? UnixSocketBridge)?.close()
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Create WebSocket server
|
||||
val wsBridge = WebSocketVRBridge(hmdTracker, computedTrackers, this)
|
||||
tasks.add(Runnable { wsBridge.startBridge() })
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package dev.slimevr.hardware.magentometer;
|
||||
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.Native;
|
||||
|
||||
|
||||
public interface Magneto extends Library {
|
||||
|
||||
Magneto INSTANCE = Native.load("MagnetoLib", Magneto.class);
|
||||
|
||||
void calculate(double[] data, int nlines, double nxsrej, double hm, double[] B, double[] A_1);
|
||||
|
||||
double calculateHnorm(double[] data, int nlines);
|
||||
}
|
||||
@@ -64,9 +64,10 @@ dependencies {
|
||||
|
||||
implementation("commons-cli:commons-cli:1.5.0")
|
||||
implementation("org.apache.commons:commons-lang3:3.12.0")
|
||||
implementation("net.java.dev.jna:jna:5.+")
|
||||
implementation("net.java.dev.jna:jna-platform:5.+")
|
||||
}
|
||||
|
||||
|
||||
tasks.shadowJar {
|
||||
minimize {
|
||||
exclude(dependency("com.fazecast:jSerialComm:.*"))
|
||||
@@ -97,13 +98,15 @@ buildConfig {
|
||||
useKotlinOutput { topLevelConstants = true }
|
||||
packageName("dev.slimevr.desktop")
|
||||
|
||||
val gitVersionTag = grgit.describe(mapOf(
|
||||
"tags" to true,
|
||||
"abbrev" to 0,
|
||||
))
|
||||
val gitVersionTag = grgit.describe(
|
||||
mapOf(
|
||||
"tags" to true,
|
||||
"abbrev" to 0
|
||||
)
|
||||
)
|
||||
val latestCommitTag =
|
||||
grgit.tag.list().find { it.name == gitVersionTag }!!.commit.abbreviatedId == grgit.head().abbreviatedId
|
||||
val gitLatestVersionTag = if (latestCommitTag) { gitVersionTag} else { "" }
|
||||
val gitLatestVersionTag = if (latestCommitTag) { gitVersionTag } else { "" }
|
||||
buildConfigField("String", "GIT_COMMIT_HASH", "\"${grgit.head().abbreviatedId}\"")
|
||||
buildConfigField("String", "GIT_VERSION_TAG", "\"${gitLatestVersionTag}\"")
|
||||
buildConfigField("boolean", "GIT_CLEAN", grgit.status().isClean.toString())
|
||||
|
||||
@@ -4,6 +4,12 @@ package dev.slimevr.desktop
|
||||
|
||||
import dev.slimevr.Keybinding
|
||||
import dev.slimevr.VRServer
|
||||
import dev.slimevr.desktop.platform.linux.UnixSocketBridge
|
||||
import dev.slimevr.desktop.platform.windows.WindowsNamedPipeBridge
|
||||
import dev.slimevr.platform.SteamVRBridge
|
||||
import dev.slimevr.tracking.trackers.Tracker
|
||||
import io.eiren.util.OperatingSystem
|
||||
import io.eiren.util.collections.FastList
|
||||
import io.eiren.util.logging.LogManager
|
||||
import org.apache.commons.cli.CommandLine
|
||||
import org.apache.commons.cli.CommandLineParser
|
||||
@@ -15,6 +21,7 @@ import java.io.File
|
||||
import java.io.IOException
|
||||
import java.lang.System
|
||||
import java.net.ServerSocket
|
||||
import java.nio.file.Paths
|
||||
import javax.swing.JOptionPane
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.system.exitProcess
|
||||
@@ -99,7 +106,7 @@ fun main(args: Array<String>) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
val vrServer = VRServer()
|
||||
val vrServer = VRServer(::provideSteamVRBridge, ::provideFeederBridge, "vrconfig.yml")
|
||||
vrServer.start()
|
||||
Keybinding(vrServer)
|
||||
val scanner = thread {
|
||||
@@ -119,3 +126,89 @@ fun main(args: Array<String>) {
|
||||
exitProcess(1)
|
||||
}
|
||||
}
|
||||
|
||||
fun provideSteamVRBridge(
|
||||
server: VRServer,
|
||||
hmdTracker: Tracker,
|
||||
computedTrackers: List<Tracker>,
|
||||
): SteamVRBridge? {
|
||||
val driverBridge: SteamVRBridge?
|
||||
if (OperatingSystem.getCurrentPlatform() == OperatingSystem.WINDOWS) {
|
||||
// Create named pipe bridge for SteamVR driver
|
||||
driverBridge = WindowsNamedPipeBridge(
|
||||
server,
|
||||
hmdTracker,
|
||||
"steamvr",
|
||||
"SteamVR Driver Bridge",
|
||||
"""\\.\pipe\SlimeVRDriver""",
|
||||
computedTrackers
|
||||
)
|
||||
} else if (OperatingSystem.getCurrentPlatform() == OperatingSystem.LINUX) {
|
||||
var linuxBridge: SteamVRBridge? = null
|
||||
try {
|
||||
linuxBridge = UnixSocketBridge(
|
||||
server,
|
||||
hmdTracker,
|
||||
"steamvr",
|
||||
"SteamVR Driver Bridge",
|
||||
Paths.get(OperatingSystem.getTempDirectory(), "SlimeVRDriver")
|
||||
.toString(),
|
||||
computedTrackers
|
||||
)
|
||||
} catch (ex: Exception) {
|
||||
LogManager.severe(
|
||||
"Failed to initiate Unix socket, disabling driver bridge...",
|
||||
ex
|
||||
)
|
||||
}
|
||||
driverBridge = linuxBridge
|
||||
if (driverBridge != null) {
|
||||
// Close the named socket on shutdown, or otherwise it's not going to get removed
|
||||
Runtime.getRuntime().addShutdownHook(
|
||||
Thread {
|
||||
try {
|
||||
(driverBridge as? UnixSocketBridge)?.close()
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
driverBridge = null
|
||||
}
|
||||
|
||||
return driverBridge
|
||||
}
|
||||
|
||||
fun provideFeederBridge(
|
||||
server: VRServer,
|
||||
): SteamVRBridge? {
|
||||
val feederBridge: SteamVRBridge?
|
||||
if (OperatingSystem.getCurrentPlatform() == OperatingSystem.WINDOWS) {
|
||||
// Create named pipe bridge for SteamVR input
|
||||
// TODO: how do we want to handle HMD input from the feeder app?
|
||||
feederBridge = WindowsNamedPipeBridge(
|
||||
server,
|
||||
null,
|
||||
"steamvr_feeder",
|
||||
"SteamVR Feeder Bridge",
|
||||
"""\\.\pipe\SlimeVRInput""",
|
||||
FastList()
|
||||
)
|
||||
} else if (OperatingSystem.getCurrentPlatform() == OperatingSystem.LINUX) {
|
||||
feederBridge = UnixSocketBridge(
|
||||
server,
|
||||
null,
|
||||
"steamvr_feeder",
|
||||
"SteamVR Feeder Bridge",
|
||||
Paths.get(OperatingSystem.getTempDirectory(), "SlimeVRInput")
|
||||
.toString(),
|
||||
FastList()
|
||||
)
|
||||
} else {
|
||||
feederBridge = null
|
||||
}
|
||||
|
||||
return feederBridge
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package dev.slimevr.platform.linux;
|
||||
package dev.slimevr.desktop.platform.linux;
|
||||
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import dev.slimevr.VRServer;
|
||||
@@ -9,7 +9,10 @@ import dev.slimevr.tracking.trackers.Tracker;
|
||||
import io.eiren.util.ann.ThreadSafe;
|
||||
import io.eiren.util.logging.LogManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.StandardProtocolFamily;
|
||||
import java.net.UnixDomainSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
@@ -21,6 +24,7 @@ import java.util.List;
|
||||
|
||||
public class UnixSocketBridge extends SteamVRBridge implements AutoCloseable {
|
||||
public final String socketPath;
|
||||
public final UnixDomainSocketAddress socketAddress;
|
||||
private final ByteBuffer dst = ByteBuffer.allocate(2048);
|
||||
private final ByteBuffer src = ByteBuffer.allocate(2048).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
@@ -39,8 +43,13 @@ public class UnixSocketBridge extends SteamVRBridge implements AutoCloseable {
|
||||
) {
|
||||
super(server, hmd, "Named socket thread", bridgeName, bridgeSettingsKey, shareableTrackers);
|
||||
this.socketPath = socketPath;
|
||||
this.socketAddress = UnixDomainSocketAddress.of(socketPath);
|
||||
|
||||
throw new RuntimeException("Unix socket cannot be run on Android.");
|
||||
File socketFile = new File(socketPath);
|
||||
if (socketFile.exists()) {
|
||||
throw new RuntimeException(socketPath + " socket already exists.");
|
||||
}
|
||||
socketFile.deleteOnExit();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,7 +227,10 @@ public class UnixSocketBridge extends SteamVRBridge implements AutoCloseable {
|
||||
}
|
||||
|
||||
private ServerSocketChannel createSocket() throws IOException {
|
||||
return null;
|
||||
ServerSocketChannel server = ServerSocketChannel.open(StandardProtocolFamily.UNIX);
|
||||
server.bind(this.socketAddress);
|
||||
LogManager.info("[" + bridgeName + "] Socket " + this.socketPath + " created");
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1,4 +1,4 @@
|
||||
package dev.slimevr.platform.windows;
|
||||
package dev.slimevr.desktop.platform.windows;
|
||||
|
||||
import com.google.protobuf.CodedOutputStream;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
@@ -1,4 +1,4 @@
|
||||
package dev.slimevr.platform.windows;
|
||||
package dev.slimevr.desktop.platform.windows;
|
||||
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.WinBase;
|
||||
@@ -1,4 +1,4 @@
|
||||
package dev.slimevr.platform.windows;
|
||||
package dev.slimevr.desktop.platform.windows;
|
||||
|
||||
import com.sun.jna.platform.win32.Kernel32;
|
||||
import com.sun.jna.platform.win32.WinNT.HANDLE;
|
||||
Reference in New Issue
Block a user