js-dos 0.80.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +43 -0
- package/.github/workflows/build.yml +43 -0
- package/.gitmodules +4 -0
- package/README.md +24 -0
- package/index.html +106 -0
- package/package.json +48 -0
- package/postcss.config.cjs +8 -0
- package/src/app.css +14 -0
- package/src/base.css +10 -0
- package/src/components/checkbox.tsx +41 -0
- package/src/components/close-button.tsx +17 -0
- package/src/components/components.css +28 -0
- package/src/components/dos-option-checkbox.tsx +72 -0
- package/src/components/dos-option-select.tsx +94 -0
- package/src/components/dos-option-slider.tsx +35 -0
- package/src/components/error.tsx +16 -0
- package/src/components/loading.tsx +26 -0
- package/src/components/lock.tsx +10 -0
- package/src/components/select.tsx +33 -0
- package/src/components/slider.tsx +69 -0
- package/src/download-file.ts +18 -0
- package/src/frame/account-frame.tsx +155 -0
- package/src/frame/editor/defaults.ts +344 -0
- package/src/frame/editor/editor-conf-frame.tsx +42 -0
- package/src/frame/editor/editor-frame.css +55 -0
- package/src/frame/editor/editor-fs-frame.tsx +315 -0
- package/src/frame/frame.css +127 -0
- package/src/frame/frame.tsx +34 -0
- package/src/frame/host-cache-frame.tsx +124 -0
- package/src/frame/image-rendering-frame.tsx +8 -0
- package/src/frame/network-frame.tsx +157 -0
- package/src/frame/quick-save-frame.tsx +95 -0
- package/src/frame/settings-frame.tsx +14 -0
- package/src/frame/stats-frame.tsx +82 -0
- package/src/host/bundle-storage.ts +91 -0
- package/src/host/fullscreen.ts +36 -0
- package/src/host/lcache.ts +203 -0
- package/src/host/lstorage.ts +156 -0
- package/src/i18n.ts +167 -0
- package/src/index.css +7 -0
- package/src/load.ts +139 -0
- package/src/login/login.css +9 -0
- package/src/login/login.tsx +77 -0
- package/src/main.tsx +195 -0
- package/src/non-serializable-store.ts +35 -0
- package/src/public/types.ts +39 -0
- package/src/sidebar/account-button.tsx +32 -0
- package/src/sidebar/cloud-save-button.tsx +89 -0
- package/src/sidebar/diskette-icon.tsx +14 -0
- package/src/sidebar/fullscreen-button.tsx +44 -0
- package/src/sidebar/network-button.tsx +39 -0
- package/src/sidebar/sidebar-button.tsx +243 -0
- package/src/sidebar/sidebar.css +47 -0
- package/src/sidebar/sidebar.tsx +30 -0
- package/src/sockdrive/LICENSE +674 -0
- package/src/sockdrive/README.md +22 -0
- package/src/sockdrive/SDL2/LRUCache11.hpp +240 -0
- package/src/sockdrive/SDL2/sockdrive.cpp +207 -0
- package/src/sockdrive/include/sockdrive.h +23 -0
- package/src/sockdrive/js/.eslintrc.json +43 -0
- package/src/sockdrive/js/README.md +70 -0
- package/src/sockdrive/js/package.json +29 -0
- package/src/sockdrive/js/src/fatfs/chains.js +318 -0
- package/src/sockdrive/js/src/fatfs/dir.js +385 -0
- package/src/sockdrive/js/src/fatfs/helpers.js +210 -0
- package/src/sockdrive/js/src/fatfs/index.js +606 -0
- package/src/sockdrive/js/src/fatfs/structs.js +255 -0
- package/src/sockdrive/js/src/fatfs/vol.js +166 -0
- package/src/sockdrive/js/src/sockdrive/cache.ts +133 -0
- package/src/sockdrive/js/src/sockdrive/drive.ts +364 -0
- package/src/sockdrive/js/src/sockdrive/lru.ts +221 -0
- package/src/sockdrive/js/src/sockdrive/types.ts +10 -0
- package/src/sockdrive/js/src/sockdrive-fat.d.ts +64 -0
- package/src/sockdrive/js/src/sockdrive-fat.ts +228 -0
- package/src/sockdrive/js/src/sockdrive-native.ts +98 -0
- package/src/sockdrive/js/src/test/test.ts +186 -0
- package/src/sockdrive/js/tsconfig.json +11 -0
- package/src/sockdrive/js/webpack.config.js +68 -0
- package/src/sockdrive/js/yarn.lock +1808 -0
- package/src/sockdrive/lz4/lz4.c +2773 -0
- package/src/sockdrive/lz4/lz4.h +862 -0
- package/src/store/auth.ts +104 -0
- package/src/store/dos.ts +284 -0
- package/src/store/editor.ts +59 -0
- package/src/store/storage.ts +30 -0
- package/src/store/ui.ts +183 -0
- package/src/store.ts +27 -0
- package/src/ui.tsx +103 -0
- package/src/v8/changes.ts +56 -0
- package/src/v8/config.ts +18 -0
- package/src/v8/subscriptions.ts +62 -0
- package/src/vite-env.d.ts +2 -0
- package/src/window/dos/controls/keyboard.ts +32 -0
- package/src/window/dos/controls/keys.ts +331 -0
- package/src/window/dos/controls/mouse/mount.ts +99 -0
- package/src/window/dos/controls/mouse/mouse-locked.ts +51 -0
- package/src/window/dos/controls/mouse/mouse-not-locked.ts +84 -0
- package/src/window/dos/controls/mouse/mouse-swipe.ts +57 -0
- package/src/window/dos/controls/mouse/pointer.ts +85 -0
- package/src/window/dos/controls/mouse.ts +21 -0
- package/src/window/dos/dos-dhry2.tsx +191 -0
- package/src/window/dos/dos-runtime.tsx +185 -0
- package/src/window/dos/dos-window.tsx +82 -0
- package/src/window/dos/render/canvas.ts +65 -0
- package/src/window/dos/render/resize.ts +31 -0
- package/src/window/dos/render/webgl.ts +173 -0
- package/src/window/dos/sound/audio-node.ts +148 -0
- package/src/window/error-window.tsx +9 -0
- package/src/window/loading-window.tsx +54 -0
- package/src/window/prerun-window.tsx +98 -0
- package/src/window/select-window.tsx +118 -0
- package/src/window/window.css +78 -0
- package/src/window/window.tsx +49 -0
- package/src/ws/ws-sockdrive.ts +102 -0
- package/src/ws/ws-transport-layer.ts +457 -0
- package/tailwind.config.cjs +48 -0
- package/tsconfig.json +32 -0
- package/tsconfig.node.json +9 -0
- package/vite.config.ts +22 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TransportLayer, ClientMessage, MessageHandler,
|
|
3
|
+
ServerMessage, DataChunk,
|
|
4
|
+
} from "emulators/dist/types/protocol/protocol";
|
|
5
|
+
|
|
6
|
+
import { createSockdrive } from "./ws-sockdrive";
|
|
7
|
+
|
|
8
|
+
const sockdriveConfirmWrite = false;
|
|
9
|
+
|
|
10
|
+
export interface Hardware {
|
|
11
|
+
readConfig(): string;
|
|
12
|
+
sendMessage(payload: string): void;
|
|
13
|
+
addKey(key: number, pressed: number, timeMs: number): void;
|
|
14
|
+
mouseMove(x: number, y: number, relative: boolean, timeMs: number): void;
|
|
15
|
+
mouseButton(button: number, pressed: number, timeMs: number): void;
|
|
16
|
+
getFramePayload(): string;
|
|
17
|
+
|
|
18
|
+
createFile(path: string): string;
|
|
19
|
+
appendFile(blob: string): string;
|
|
20
|
+
writeFile(path: string): string;
|
|
21
|
+
closeFile(): string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const textDecoder = new TextDecoder();
|
|
25
|
+
const textEncoder = new TextEncoder();
|
|
26
|
+
const fMultiplier = 200000000;
|
|
27
|
+
|
|
28
|
+
// eslint-disable-next-line max-len
|
|
29
|
+
const clientMessageValues: ClientMessage[] = [
|
|
30
|
+
"wc-install", "wc-run", "wc-pack-fs-to-bundle", "wc-add-key", "wc-mouse-move", "wc-mouse-button", "wc-mouse-sync",
|
|
31
|
+
"wc-exit", "wc-sync-sleep", "wc-pause", "wc-resume", "wc-mute", "wc-unmute", "wc-connect", "wc-disconnect",
|
|
32
|
+
"wc-backend-event", "wc-asyncify-stats", "wc-fs-tree", "wc-fs-get-file", "wc-send-data-chunk",
|
|
33
|
+
];
|
|
34
|
+
const clientMessageEnum: { [msg: string]: number } = {};
|
|
35
|
+
clientMessageValues.forEach((v, i) => clientMessageEnum[v] = i);
|
|
36
|
+
|
|
37
|
+
const serverMessageValues: ServerMessage[] = [
|
|
38
|
+
"ws-extract-progress", "ws-ready", "ws-server-ready", "ws-frame-set-size",
|
|
39
|
+
"ws-update-lines", "ws-log", "ws-warn", "ws-err",
|
|
40
|
+
"ws-stdout", "ws-exit", "ws-persist", "ws-sound-init", "ws-sound-push",
|
|
41
|
+
"ws-config", "ws-sync-sleep", "ws-connected", "ws-disconnected",
|
|
42
|
+
"ws-asyncify-stats", "ws-fs-tree", "ws-send-data-chunk",
|
|
43
|
+
];
|
|
44
|
+
const serverMessageEnum: { [num: string]: ServerMessage } = {};
|
|
45
|
+
serverMessageValues.forEach((v, i) => serverMessageEnum[i] = v);
|
|
46
|
+
|
|
47
|
+
export class WsTransportLayer implements TransportLayer {
|
|
48
|
+
socket: WebSocket;
|
|
49
|
+
sessionId: string = Date.now() + "";
|
|
50
|
+
hardware: Hardware;
|
|
51
|
+
|
|
52
|
+
private cycles = 0;
|
|
53
|
+
private sockdrive = createSockdrive(this.onSockdriveOpen.bind(this), this.onSockdriveError.bind(this));
|
|
54
|
+
|
|
55
|
+
private handler: MessageHandler = () => {/**/};
|
|
56
|
+
|
|
57
|
+
private writeUint32(container: Uint8Array, value: number, offset: number) {
|
|
58
|
+
container[offset] = value & 0xFF;
|
|
59
|
+
container[offset + 1] = (value & 0x0000FF00) >> 8;
|
|
60
|
+
container[offset + 2] = (value & 0x00FF0000) >> 16;
|
|
61
|
+
container[offset + 3] = (value & 0xFF000000) >> 24;
|
|
62
|
+
return offset + 4;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private readUint32(container: Uint8Array, offset: number) {
|
|
66
|
+
return (container[offset] & 0x000000FF) |
|
|
67
|
+
((container[offset + 1] << 8) & 0x0000FF00) |
|
|
68
|
+
((container[offset + 2] << 16) & 0x00FF0000) |
|
|
69
|
+
((container[offset + 3] << 24) & 0xFF000000);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private readUint64(container: Uint8Array, offset: number) {
|
|
73
|
+
return this.readUint32(container, offset) + this.readUint32(container, offset + 4) * 2 ** 32;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private sendMessageToSocket(id: ClientMessage | number, ...payload: (Uint8Array | null)[]) {
|
|
77
|
+
let length = 1;
|
|
78
|
+
for (const next of payload) {
|
|
79
|
+
length += 4 + (next?.length ?? 0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const encoded = new Uint8Array(length);
|
|
83
|
+
encoded[0] = typeof id === "string" ? clientMessageEnum[id] : id;
|
|
84
|
+
let position = 1;
|
|
85
|
+
for (const next of payload) {
|
|
86
|
+
this.writeUint32(encoded, next?.length ?? 0, position);
|
|
87
|
+
position += 4;
|
|
88
|
+
if (next) {
|
|
89
|
+
encoded.set(next, position);
|
|
90
|
+
position += next.length;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.socket.send(encoded);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private readMessage(payload: Uint8Array): { id: number, payload: (Uint8Array | null)[] } {
|
|
98
|
+
const out: Uint8Array[] = [];
|
|
99
|
+
let position = 1;
|
|
100
|
+
while (position + 4 <= payload.length) {
|
|
101
|
+
const length = this.readUint32(payload, position);
|
|
102
|
+
position += 4;
|
|
103
|
+
|
|
104
|
+
if (position + length > payload.length) {
|
|
105
|
+
throw new Error("read_message out of bounds");
|
|
106
|
+
} else {
|
|
107
|
+
out.push(payload.slice(position, position + length));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
position += length;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
id: payload[0],
|
|
115
|
+
payload: out,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private onMessage(msg: Uint8Array) {
|
|
120
|
+
const { id, payload } = this.readMessage(msg);
|
|
121
|
+
const message = serverMessageEnum[id];
|
|
122
|
+
switch (message) {
|
|
123
|
+
case "ws-ready":
|
|
124
|
+
case "ws-server-ready":
|
|
125
|
+
case "ws-exit":
|
|
126
|
+
{
|
|
127
|
+
// delay ws-server-ready until ws-sound-init
|
|
128
|
+
if (message !== "ws-server-ready") {
|
|
129
|
+
this.handler(message, {});
|
|
130
|
+
}
|
|
131
|
+
} break;
|
|
132
|
+
case "ws-stdout":
|
|
133
|
+
case "ws-log":
|
|
134
|
+
case "ws-warn":
|
|
135
|
+
case "ws-err": {
|
|
136
|
+
this.handler(message, {
|
|
137
|
+
tag: textDecoder.decode(payload[0]!),
|
|
138
|
+
message: textDecoder.decode(payload[1]!),
|
|
139
|
+
});
|
|
140
|
+
} break;
|
|
141
|
+
case "ws-send-data-chunk": {
|
|
142
|
+
const chunk: DataChunk = {
|
|
143
|
+
type: textDecoder.decode(payload[0]!) as any,
|
|
144
|
+
name: textDecoder.decode(payload[1]!),
|
|
145
|
+
data: payload[2],
|
|
146
|
+
};
|
|
147
|
+
this.handler(message, { chunk });
|
|
148
|
+
} break;
|
|
149
|
+
case "ws-config": {
|
|
150
|
+
this.handler(message, {
|
|
151
|
+
dosboxConf: payload[0]!,
|
|
152
|
+
jsdosConf: textDecoder.decode(payload[1]!),
|
|
153
|
+
});
|
|
154
|
+
} break;
|
|
155
|
+
case "ws-sound-init": {
|
|
156
|
+
this.handler(message, {
|
|
157
|
+
freq: this.readUint32(payload[0]!, 0),
|
|
158
|
+
});
|
|
159
|
+
this.handler("ws-server-ready", {});
|
|
160
|
+
} break;
|
|
161
|
+
case "ws-sound-push": {
|
|
162
|
+
this.handler(message, {
|
|
163
|
+
samples: new Float32Array(payload[0]!.buffer),
|
|
164
|
+
});
|
|
165
|
+
} break;
|
|
166
|
+
case "ws-frame-set-size": {
|
|
167
|
+
this.handler(message, {
|
|
168
|
+
width: this.readUint32(payload[0]!, 0),
|
|
169
|
+
height: this.readUint32(payload[0]!, 4),
|
|
170
|
+
});
|
|
171
|
+
} break;
|
|
172
|
+
case "ws-update-lines": {
|
|
173
|
+
if (payload.length > 0) {
|
|
174
|
+
const lines: { start: number, heapu8: Uint8Array }[] = [];
|
|
175
|
+
for (const next of payload) {
|
|
176
|
+
lines.push({
|
|
177
|
+
start: this.readUint32(next!, 0),
|
|
178
|
+
heapu8: next!.slice(4),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
this.handler(message, { lines });
|
|
182
|
+
}
|
|
183
|
+
} break;
|
|
184
|
+
case "ws-asyncify-stats": {
|
|
185
|
+
this.cycles += this.readUint32(payload[0]!, 0);
|
|
186
|
+
const stats = {
|
|
187
|
+
messageSent: this.readUint32(payload[0]!, 4),
|
|
188
|
+
messageReceived: this.readUint32(payload[0]!, 8),
|
|
189
|
+
messageFrame: this.readUint32(payload[0]!, 12),
|
|
190
|
+
messageSound: this.readUint32(payload[0]!, 16),
|
|
191
|
+
nonSkippableSleepCount: 0,
|
|
192
|
+
sleepCount: 0,
|
|
193
|
+
sleepTime: 0,
|
|
194
|
+
cycles: this.cycles,
|
|
195
|
+
netSent: 0,
|
|
196
|
+
netRecv: 0,
|
|
197
|
+
driveSent: this.sockdrive.stats.write,
|
|
198
|
+
driveRecv: this.sockdrive.stats.read,
|
|
199
|
+
driveRecvTime: this.sockdrive.stats.readTotalTime,
|
|
200
|
+
driveCacheHit: this.sockdrive.stats.cacheHit,
|
|
201
|
+
driveCacheMiss: this.sockdrive.stats.cacheMiss,
|
|
202
|
+
driveCacheUsed: this.sockdrive.stats.cacheUsed,
|
|
203
|
+
};
|
|
204
|
+
this.handler(message, stats);
|
|
205
|
+
} break;
|
|
206
|
+
case "ws-connected": {
|
|
207
|
+
this.handler(message, { networkType: payload[0]![0], address: textDecoder.decode(payload[1]!) });
|
|
208
|
+
} break;
|
|
209
|
+
case "ws-disconnected": {
|
|
210
|
+
this.handler(message, { networkType: payload[0]![0] });
|
|
211
|
+
} break;
|
|
212
|
+
default: {
|
|
213
|
+
if (message === undefined) { // not standard messages
|
|
214
|
+
(async () => {
|
|
215
|
+
switch (id) {
|
|
216
|
+
case 100/* ws-sockdrive-open */: {
|
|
217
|
+
const url = textDecoder.decode(payload[0]!);
|
|
218
|
+
const owner = textDecoder.decode(payload[1]!);
|
|
219
|
+
const name = textDecoder.decode(payload[2]!);
|
|
220
|
+
const token = textDecoder.decode(payload[3]!);
|
|
221
|
+
const aheadRange = this.readUint32(payload[4]!, 0);
|
|
222
|
+
const handle = await this.sockdrive.open(url, owner, name, token, aheadRange);
|
|
223
|
+
const template = this.sockdrive.template(handle);
|
|
224
|
+
const packet = new Uint8Array(4 * 6);
|
|
225
|
+
let offset = 0;
|
|
226
|
+
offset = this.writeUint32(packet, handle, offset);
|
|
227
|
+
offset = this.writeUint32(packet, template.size, offset);
|
|
228
|
+
offset = this.writeUint32(packet, template.heads, offset);
|
|
229
|
+
offset = this.writeUint32(packet, template.cylinders, offset);
|
|
230
|
+
offset = this.writeUint32(packet, template.sectors, offset);
|
|
231
|
+
this.writeUint32(packet, template.sectorSize, offset);
|
|
232
|
+
this.sendMessageToSocket(100, packet);
|
|
233
|
+
} break;
|
|
234
|
+
case 101/* ws-sockdrive-read */: {
|
|
235
|
+
const handle = this.readUint32(payload[0]!, 0);
|
|
236
|
+
const sector = this.readUint32(payload[0]!, 4);
|
|
237
|
+
const response = await this.sockdrive.read(handle, sector);
|
|
238
|
+
const packet = new Uint8Array(4);
|
|
239
|
+
this.writeUint32(packet, response.code, 0);
|
|
240
|
+
this.sendMessageToSocket(101, packet, response.buffer ?? null);
|
|
241
|
+
} break;
|
|
242
|
+
case 102/* ws-sockdrive-write */: {
|
|
243
|
+
const handle = this.readUint32(payload[0]!, 0);
|
|
244
|
+
const sector = this.readUint32(payload[0]!, 4);
|
|
245
|
+
const code = this.sockdrive.write(handle, sector, payload[1]!);
|
|
246
|
+
const packet = new Uint8Array(4);
|
|
247
|
+
this.writeUint32(packet, code, 0);
|
|
248
|
+
if (sockdriveConfirmWrite) {
|
|
249
|
+
this.sendMessageToSocket(102, packet);
|
|
250
|
+
}
|
|
251
|
+
} break;
|
|
252
|
+
case 103/* ws-sockdrive-close */: {
|
|
253
|
+
this.sockdrive.close(this.readUint32(payload[0]!, 0));
|
|
254
|
+
} break;
|
|
255
|
+
default:
|
|
256
|
+
console.log("WARN! Unhandled server non standard message", id, payload);
|
|
257
|
+
}
|
|
258
|
+
})().catch((e) => {
|
|
259
|
+
this.handler("ws-err", {
|
|
260
|
+
tag: "panic",
|
|
261
|
+
message: "sockdrive error: " + (e.message ?? "???"),
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
} else {
|
|
265
|
+
console.warn("WARN! Unhandled server message", message);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
constructor(socket: WebSocket) {
|
|
272
|
+
this.socket = socket;
|
|
273
|
+
this.socket.addEventListener("error", (e) => {
|
|
274
|
+
this.handler("ws-err", { tag: "ws", message: (e as any).message ?? "Unknown transport layer error" });
|
|
275
|
+
this.handler("ws-exit", {});
|
|
276
|
+
this.socket.close();
|
|
277
|
+
});
|
|
278
|
+
this.socket.addEventListener("message", async (ev) => {
|
|
279
|
+
const blob: Blob = ev.data;
|
|
280
|
+
this.onMessage(new Uint8Array(await blob.arrayBuffer()));
|
|
281
|
+
});
|
|
282
|
+
this.sendMessageToSocket("wc-install");
|
|
283
|
+
this.hardware = (null) as any;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async sendMessageToServer(name: ClientMessage, props?: { [key: string]: any; }) {
|
|
287
|
+
if (props === undefined || props?.sessionId !== this.sessionId) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const messageId = clientMessageEnum[name];
|
|
292
|
+
switch (name) {
|
|
293
|
+
case "wc-run": {
|
|
294
|
+
const token = props.token ?? "";
|
|
295
|
+
this.sendMessageToSocket(messageId, textEncoder.encode(token));
|
|
296
|
+
} break;
|
|
297
|
+
// case "wc-run": {
|
|
298
|
+
// let errorMessage = writeFile(this.hardware, "bundle_0.zip", props.bundles[0]);
|
|
299
|
+
|
|
300
|
+
// if (errorMessage.length > 0) {
|
|
301
|
+
// console.error(errorMessage);
|
|
302
|
+
// throw new Error(errorMessage);
|
|
303
|
+
// }
|
|
304
|
+
|
|
305
|
+
// if (props.bundles[1] !== undefined) {
|
|
306
|
+
// errorMessage = writeFile(this.hardware, "bundle_1.zip", props.bundles[1]);
|
|
307
|
+
// if (errorMessage.length > 0) {
|
|
308
|
+
// console.error(errorMessage);
|
|
309
|
+
// throw new Error(errorMessage);
|
|
310
|
+
// }
|
|
311
|
+
// }
|
|
312
|
+
|
|
313
|
+
// const payload = "wc-run\n";
|
|
314
|
+
// this.hardware.sendMessage(payload);
|
|
315
|
+
// } break;
|
|
316
|
+
// case "wc-add-key": {
|
|
317
|
+
// this.hardware.addKey(props.key, props.pressed ? 1 : 0, props.timeMs);
|
|
318
|
+
// } break;
|
|
319
|
+
// case "wc-pause": {
|
|
320
|
+
// this.hardware.sendMessage("wc-pause\n" + this.sessionId + "\n");
|
|
321
|
+
// } break;
|
|
322
|
+
// case "wc-resume": {
|
|
323
|
+
// this.hardware.sendMessage("wc-resume\n" + this.sessionId + "\n");
|
|
324
|
+
// } break;
|
|
325
|
+
// case "wc-mute": {
|
|
326
|
+
// this.hardware.sendMessage("wc-mute\n" + this.sessionId + "\n");
|
|
327
|
+
// } break;
|
|
328
|
+
// case "wc-unmute": {
|
|
329
|
+
// this.hardware.sendMessage("wc-unmute\n" + this.sessionId + "\n");
|
|
330
|
+
// } break;
|
|
331
|
+
// case "wc-exit": {
|
|
332
|
+
// this.alive = false;
|
|
333
|
+
// this.hardware.sendMessage("wc-exit\n" + this.sessionId + "\n");
|
|
334
|
+
// } break;
|
|
335
|
+
// case "wc-mouse-move": {
|
|
336
|
+
// this.hardware.mouseMove(props.x, props.y, props.relative, props.timeMs);
|
|
337
|
+
// } break;
|
|
338
|
+
// case "wc-mouse-button": {
|
|
339
|
+
// this.hardware.mouseButton(props.button, props.pressed ? 1 : 0, props.timeMs);
|
|
340
|
+
// } break;
|
|
341
|
+
// case "wc-pack-fs-to-bundle": {
|
|
342
|
+
// this.hardware.sendMessage("wc-pack-fs-to-bundle\n" + this.sessionId + "\n");
|
|
343
|
+
// } break;
|
|
344
|
+
// case "wc-connect": {
|
|
345
|
+
// this.hardware.sendMessage("wc-connect\n" + this.sessionId + "\n" +
|
|
346
|
+
// props.networkType + "\n" +
|
|
347
|
+
// props.address.replace("ws://", "").replace("wss://", "") + "\n" +
|
|
348
|
+
// (props.port - 1) + "\n");
|
|
349
|
+
// } break;
|
|
350
|
+
// case "wc-disconnect": {
|
|
351
|
+
// this.hardware.sendMessage("wc-disconnect\n" + this.sessionId + "\n" +
|
|
352
|
+
// props.networkType + "\n");
|
|
353
|
+
// } break;
|
|
354
|
+
case "wc-send-data-chunk": {
|
|
355
|
+
const chunk: DataChunk = props.chunk;
|
|
356
|
+
this.sendMessageToSocket(messageId,
|
|
357
|
+
textEncoder.encode(chunk.type),
|
|
358
|
+
textEncoder.encode(chunk.name),
|
|
359
|
+
chunk.data ? new Uint8Array(chunk.data) : null);
|
|
360
|
+
} break;
|
|
361
|
+
case "wc-asyncify-stats": {
|
|
362
|
+
this.sendMessageToSocket(messageId);
|
|
363
|
+
} break;
|
|
364
|
+
case "wc-add-key": {
|
|
365
|
+
const payload = new Uint8Array(3 * 4);
|
|
366
|
+
let offset = 0;
|
|
367
|
+
offset = this.writeUint32(payload, props.key, offset);
|
|
368
|
+
offset = this.writeUint32(payload, props.pressed ? 1 : 0, offset);
|
|
369
|
+
this.writeUint32(payload, props.timeMs, offset);
|
|
370
|
+
this.sendMessageToSocket(messageId, payload);
|
|
371
|
+
} break;
|
|
372
|
+
case "wc-mouse-move": {
|
|
373
|
+
const payload = new Uint8Array(3 * 4 + 3);
|
|
374
|
+
let offset = 0;
|
|
375
|
+
offset = this.writeUint32(payload, Math.abs(props.x) * fMultiplier, offset);
|
|
376
|
+
offset = this.writeUint32(payload, Math.abs(props.y) * fMultiplier, offset);
|
|
377
|
+
offset = this.writeUint32(payload, props.timeMs, offset);
|
|
378
|
+
payload[offset] = props.relative ? 1 : 0;
|
|
379
|
+
payload[offset + 1] = props.x >= 0 ? 0 : 1;
|
|
380
|
+
payload[offset + 2] = props.y >= 0 ? 0 : 1;
|
|
381
|
+
this.sendMessageToSocket(messageId, payload);
|
|
382
|
+
} break;
|
|
383
|
+
case "wc-mouse-button": {
|
|
384
|
+
const payload = new Uint8Array(4 + 2);
|
|
385
|
+
payload[0] = props.button;
|
|
386
|
+
payload[1] = props.pressed ? 1 : 0;
|
|
387
|
+
this.writeUint32(payload, props.timeMs, 2);
|
|
388
|
+
this.sendMessageToSocket(messageId, payload);
|
|
389
|
+
} break;
|
|
390
|
+
case "wc-mouse-sync": {
|
|
391
|
+
const payload = new Uint8Array(4);
|
|
392
|
+
this.writeUint32(payload, props.timeMs, 0);
|
|
393
|
+
this.sendMessageToSocket(messageId, payload);
|
|
394
|
+
} break;
|
|
395
|
+
case "wc-connect": {
|
|
396
|
+
this.sendMessageToSocket(messageId, new Uint8Array([props.networkType]),
|
|
397
|
+
textEncoder.encode(props.address));
|
|
398
|
+
} break;
|
|
399
|
+
default: {
|
|
400
|
+
console.log("Unhandled client message (wc):", name, messageId, props);
|
|
401
|
+
} break;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
initMessageHandler(handler: MessageHandler) {
|
|
406
|
+
this.handler = (message, props) => {
|
|
407
|
+
props.sessionId = this.sessionId;
|
|
408
|
+
handler(message, props);
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
exit() {
|
|
413
|
+
this.sendMessageToSocket("wc-exit");
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
onSockdriveOpen(drive: string, read: boolean, write: boolean) {
|
|
417
|
+
this.handler("ws-log", {
|
|
418
|
+
tag: "worker",
|
|
419
|
+
message: "sockdrive: " + drive + ", read=" + read + ", write=" + write,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
onSockdriveError(e: Error) {
|
|
424
|
+
this.handler("ws-err", {
|
|
425
|
+
tag: "panic",
|
|
426
|
+
message: e.message ?? "unable to open sockdrive",
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function createWsTransportLayer(url: string): Promise<TransportLayer> {
|
|
432
|
+
return new Promise<TransportLayer>((resolve) => {
|
|
433
|
+
let locked = false;
|
|
434
|
+
const inervalId = setInterval(() => {
|
|
435
|
+
if (locked) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
locked = true;
|
|
440
|
+
const ws = new WebSocket(url);
|
|
441
|
+
const onSuccess = () => {
|
|
442
|
+
clearInterval(inervalId);
|
|
443
|
+
console.log("Connected to", url);
|
|
444
|
+
resolve(new WsTransportLayer(ws));
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
ws.addEventListener("error", (error) => {
|
|
448
|
+
console.error("Can't conect to ", url, error);
|
|
449
|
+
ws.removeEventListener("open", onSuccess);
|
|
450
|
+
ws.close();
|
|
451
|
+
locked = false;
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
ws.addEventListener("open", onSuccess);
|
|
455
|
+
}, 1000);
|
|
456
|
+
});
|
|
457
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
content: [
|
|
4
|
+
"./index.html",
|
|
5
|
+
"./src/**/*.{js,ts,jsx,tsx}",
|
|
6
|
+
],
|
|
7
|
+
theme: {
|
|
8
|
+
extend: {},
|
|
9
|
+
},
|
|
10
|
+
plugins: [require("daisyui")],
|
|
11
|
+
safelist: [
|
|
12
|
+
"alert-success",
|
|
13
|
+
"alert-error",
|
|
14
|
+
],
|
|
15
|
+
daisyui: {
|
|
16
|
+
themes: [
|
|
17
|
+
"light",
|
|
18
|
+
"dark",
|
|
19
|
+
"cupcake",
|
|
20
|
+
"bumblebee",
|
|
21
|
+
"emerald",
|
|
22
|
+
"corporate",
|
|
23
|
+
"synthwave",
|
|
24
|
+
"retro",
|
|
25
|
+
"cyberpunk",
|
|
26
|
+
"valentine",
|
|
27
|
+
"halloween",
|
|
28
|
+
"garden",
|
|
29
|
+
"forest",
|
|
30
|
+
"aqua",
|
|
31
|
+
"lofi",
|
|
32
|
+
"pastel",
|
|
33
|
+
"fantasy",
|
|
34
|
+
"wireframe",
|
|
35
|
+
"black",
|
|
36
|
+
"luxury",
|
|
37
|
+
"dracula",
|
|
38
|
+
"cmyk",
|
|
39
|
+
"autumn",
|
|
40
|
+
"business",
|
|
41
|
+
"acid",
|
|
42
|
+
"lemonade",
|
|
43
|
+
"night",
|
|
44
|
+
"coffee",
|
|
45
|
+
"winter",
|
|
46
|
+
],
|
|
47
|
+
}
|
|
48
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"paths": {
|
|
4
|
+
"react": ["./node_modules/preact/compat/"],
|
|
5
|
+
"react-dom": ["./node_modules/preact/compat/"]
|
|
6
|
+
},
|
|
7
|
+
"target": "ESNext",
|
|
8
|
+
"useDefineForClassFields": true,
|
|
9
|
+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
10
|
+
"allowJs": false,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"esModuleInterop": false,
|
|
13
|
+
"allowSyntheticDefaultImports": true,
|
|
14
|
+
"strict": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"module": "ESNext",
|
|
17
|
+
"moduleResolution": "Node",
|
|
18
|
+
"resolveJsonModule": true,
|
|
19
|
+
"isolatedModules": true,
|
|
20
|
+
"noEmit": true,
|
|
21
|
+
"jsx": "react-jsx",
|
|
22
|
+
"jsxImportSource": "preact"
|
|
23
|
+
},
|
|
24
|
+
"include": ["src"],
|
|
25
|
+
"exclude": [
|
|
26
|
+
"src/sockdrive/js/src/test",
|
|
27
|
+
"src/sockdrive/js/src/sockdrive-fat.ts",
|
|
28
|
+
"src/sockdrive/js/src/sockdrive-native.ts",
|
|
29
|
+
"src/sockdrive/js/src/fatfs"
|
|
30
|
+
],
|
|
31
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
32
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import preact from "@preact/preset-vite";
|
|
3
|
+
|
|
4
|
+
// https://vitejs.dev/config/
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [preact()],
|
|
7
|
+
server: {
|
|
8
|
+
port: 3000,
|
|
9
|
+
host: "0.0.0.0",
|
|
10
|
+
cors: true,
|
|
11
|
+
},
|
|
12
|
+
build: {
|
|
13
|
+
rollupOptions: {
|
|
14
|
+
output: {
|
|
15
|
+
entryFileNames: "js-dos.js",
|
|
16
|
+
assetFileNames: (info) => {
|
|
17
|
+
return info.name === "index.css" ? "js-dos.css" : info.name;
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|