Skip to content

Commit 98081d6

Browse files
LevevTibixDev
andauthored
Add Dynamic USB Passthrough (#60)
* feat: Start work on implementing dynamic USB passthrough * chore: Minor refactor, and actually call the guest listener setup in usbmanager * Carry progress over from static passthrough implementation (#31) * feat: start work on usb passthrough, fix z-index issue with x-menus * feat: usb passthrough screen functional, implemented parsing to/from docker compose file * feat: start work on communicating with qemu through qmp, refactor current usb logic * feat: Finish QMPManager class * feat: Finished QMP communications There were also some logical problems like not checking whether the docker was running and calling a function multiple times this commit fixes * feat: Get rid of edge-cases where the QMP connection init blocked the event loop * feat: Fix USB hotplugging * feat: Handle Docker Compose mismatch co-authored by: @TibixDev * feat: Prefer passing USB's to QEMU by hostdevice instead of VID/PID --------- Co-authored-by: TibixDev <tibix@fhs.sh> Co-authored-by: Tibix <fuloptibi03@gmail.com>
1 parent b14dee8 commit 98081d6

13 files changed

Lines changed: 1167 additions & 71 deletions

File tree

.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"tabWidth": 4,
3+
"bracketSameLine": true,
4+
"printWidth": 160
5+
}

package-lock.json

Lines changed: 53 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"node-fetch": "2",
4646
"path-browserify": "^1.0.1",
4747
"turndown": "^7.2.0",
48+
"usb": "^2.16.0",
4849
"vue": "^3.5.13",
4950
"vue-router": "^4.5.0",
5051
"vue3-apexcharts": "^1.8.0",

src/renderer/App.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
<!-- Updater -->
2020
<dialog ref="updateDialog">
2121
<Icon class="text-indigo-400 size-12" icon="mdi:cloud-upload"></Icon>
22-
2322
<template v-if="manualUpdateRequired">
2423
<h3 class="mt-2">Manual Guest Server Update Required</h3>
2524
<div class="max-w-[60vw]">

src/renderer/lib/config.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ const fs: typeof import("fs") = require("fs");
22
const path: typeof import("path") = require("path");
33
import { type WinApp } from "../../types";
44
import { WINBOAT_DIR } from "./constants";
5+
import { type PTSerializableDeviceInfo } from "./usbmanager";
56

67
export type WinboatConfigObj = {
78
scale: number;
89
smartcardEnabled: boolean
910
rdpMonitoringEnabled: boolean
11+
passedThroughDevices: PTSerializableDeviceInfo[];
1012
customApps: WinApp[]
1113
experimentalFeatures: boolean
1214
};
@@ -15,21 +17,21 @@ const defaultConfig: WinboatConfigObj = {
1517
scale: 100,
1618
smartcardEnabled: false,
1719
rdpMonitoringEnabled: false,
20+
passedThroughDevices: [],
1821
customApps: [],
1922
experimentalFeatures: false
2023
};
2124

22-
let instance: WinboatConfig | null = null;
23-
24-
export class WinboatConfig {
25+
export class WinboatConfig {
26+
private static instance: WinboatConfig;
2527
#configPath: string = path.join(WINBOAT_DIR, "winboat.config.json");
2628
#configData: WinboatConfigObj = { ...defaultConfig };
2729

2830
constructor() {
29-
if (instance) return instance;
31+
if (WinboatConfig.instance) return WinboatConfig.instance;
3032
this.#configData = this.readConfig();
3133
console.log("Reading current config", this.#configData);
32-
instance = this;
34+
WinboatConfig.instance = this;
3335
}
3436

3537
get config(): WinboatConfigObj {
@@ -53,6 +55,7 @@ export class WinboatConfig {
5355
}
5456

5557
writeConfig(): void {
58+
console.log("writing data: ", this.#configData);
5659
fs.writeFileSync(this.#configPath, JSON.stringify(this.#configData, null, 4), "utf-8");
5760
}
5861

src/renderer/lib/constants.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,14 @@ export const WINDOWS_LANGUAGES = {
5555
"🇺🇦 Ukrainian": "Ukrainian"
5656
}
5757

58+
// Ports
5859
export const RDP_PORT = 3389;
5960
export const PORT_MAX = 65535;
61+
62+
// USB
63+
export const USB_CLASS_IMAGING = 6;
64+
export const USB_INTERFACE_MTP = 5;
65+
export const USB_VID_BLACKLIST = [
66+
// Linux Foundation VID
67+
"1d6b:"
68+
];

src/renderer/lib/install.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const execAsync = promisify(exec);
1414
const logger = createLogger(path.join(WINBOAT_DIR, 'install.log'));
1515

1616
const composeFilePath = path.join(WINBOAT_DIR, 'docker-compose.yml');
17-
const defaultCompose: ComposeConfig = {
17+
export const DefaultCompose: ComposeConfig = {
1818
"name": "winboat",
1919
"volumes": {
2020
"data": null
@@ -32,7 +32,8 @@ const defaultCompose: ComposeConfig = {
3232
"PASSWORD": "MyWindowsPassword",
3333
"HOME": "${HOME}",
3434
"LANGUAGE": "English",
35-
"ARGUMENTS": "-cpu host,arch_capabilities=off"
35+
"HOST_PORTS": "7149",
36+
"ARGUMENTS": "-cpu host,arch_capabilities=off \n-qmp tcp:0.0.0.0:7149,server,wait=off\n"
3637
},
3738
"cap_add": [
3839
"NET_ADMIN"
@@ -41,6 +42,7 @@ const defaultCompose: ComposeConfig = {
4142
"ports": [
4243
"8006:8006", // VNC Web Interface
4344
"7148:7148", // Winboat Guest Server API
45+
"7149:7149", // QEMU QMP Port
4446
"3389:3389/tcp", // RDP
4547
"3389:3389/udp" // RDP
4648
],
@@ -49,10 +51,11 @@ const defaultCompose: ComposeConfig = {
4951
"volumes": [
5052
"data:/storage",
5153
"${HOME}:/shared",
52-
"./oem:/oem"
54+
"/dev/bus/usb:/dev/bus/usb", // QEMU Synamic USB Passthrough
55+
"./oem:/oem",
5356
],
5457
"devices": [
55-
"/dev/kvm"
58+
"/dev/kvm",
5659
]
5760
}
5861
}
@@ -115,7 +118,7 @@ export class InstallManager {
115118
}
116119

117120
// Configure the compose file
118-
const composeContent = { ...defaultCompose }
121+
const composeContent = { ...DefaultCompose }
119122

120123
composeContent.services.windows.environment.RAM_SIZE = `${this.conf.ramGB}G`;
121124
composeContent.services.windows.environment.CPU_CORES = `${this.conf.cpuThreads}`;

0 commit comments

Comments
 (0)