+4
-1
.gitignore
+4
-1
.gitignore
-3
.vscode/extensions.json
-3
.vscode/extensions.json
+1
package.json
+1
package.json
+10
pnpm-lock.yaml
+10
pnpm-lock.yaml
···
20
20
'@tauri-apps/plugin-opener':
21
21
specifier: ^2.5.2
22
22
version: 2.5.2
23
+
'@tauri-apps/plugin-os':
24
+
specifier: ~2
25
+
version: 2.3.2
23
26
animejs:
24
27
specifier: ^4.2.2
25
28
version: 4.2.2
···
490
493
491
494
'@tauri-apps/plugin-opener@2.5.2':
492
495
resolution: {integrity: sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew==}
496
+
497
+
'@tauri-apps/plugin-os@2.3.2':
498
+
resolution: {integrity: sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A==}
493
499
494
500
'@types/animejs@3.1.13':
495
501
resolution: {integrity: sha512-yWg9l1z7CAv/TKpty4/vupEh24jDGUZXv4r26StRkpUPQm04ztJaftgpto8vwdFs8SiTq6XfaPKCSI+wjzNMvQ==}
···
1080
1086
'@tauri-apps/api': 2.9.0
1081
1087
1082
1088
'@tauri-apps/plugin-opener@2.5.2':
1089
+
dependencies:
1090
+
'@tauri-apps/api': 2.9.0
1091
+
1092
+
'@tauri-apps/plugin-os@2.3.2':
1083
1093
dependencies:
1084
1094
'@tauri-apps/api': 2.9.0
1085
1095
+3
-1
src/App.tsx
+3
-1
src/App.tsx
···
15
15
import * as keybinds from './keybinds';
16
16
import { listen } from "@tauri-apps/api/event";
17
17
18
+
// TODO: Only allow one node to input on non-flow inputs
19
+
18
20
let App = () => {
19
21
let [ selectedNodes, setSelectedNodes ] = createSignal<Node[]>([], { equals: false });
20
22
let [ mousePos, setMousePos ] = createSignal<[ number, number ]>([ 0, 0 ]);
···
402
404
isMouseDown = false;
403
405
}
404
406
405
-
keybinds.load(mousePos, selectedNodes, setSelectedNodes);
407
+
keybinds.load(canvas, mousePos, selectedNodes, setSelectedNodes);
406
408
requestAnimationFrame(update);
407
409
408
410
let unlisten_0 = await listen('hide-window', () => {
+35
-2
src/Mangers/NodeManager.tsx
+35
-2
src/Mangers/NodeManager.tsx
···
7
7
import { NodesByID } from "../Nodes/Nodes";
8
8
import { save } from "@tauri-apps/plugin-dialog";
9
9
import { ConfirmationManager } from "./ConfirmationManager";
10
+
import { platform } from "@tauri-apps/plugin-os";
10
11
11
12
export interface TabHashMap {
12
13
[details: string] : Tab;
···
26
27
setInterval(() => {
27
28
let tabs = Object.values(this._tabs).filter(x => x.needSync);
28
29
for(let tab of tabs){
29
-
invoke('sync_tab', { graph: this._generateTabGraph(tab.id)[0], id: tab.id, name: tab.name, location: tab.saveLocation });
30
+
invoke('sync_tab', {
31
+
graph: this._generateTabGraph(tab.id)[0],
32
+
id: tab.id,
33
+
name: tab.name,
34
+
location: tab.saveLocation,
35
+
saveState: tab.needsSave()
36
+
});
37
+
30
38
tab.needSync = false;
31
39
}
32
40
}, 1000);
···
39
47
let version = await getVersion();
40
48
41
49
for(let tab of Object.entries<any>(tabs)){
42
-
await this._loadFromConfig(tab[1][2], tab[0], JSON.stringify({
50
+
let loaded_tab = await this._loadFromConfig(tab[1][2], tab[0], JSON.stringify({
43
51
tab_name: tab[1][1],
44
52
version,
45
53
graph: tab[1][0]
46
54
}));
55
+
56
+
if(loaded_tab)
57
+
loaded_tab.setNeedsSave(tab[1][3]);
47
58
};
59
+
60
+
this.UpdateConfig();
48
61
});
49
62
50
63
listen('prompt_to_close', async _ => {
···
333
346
334
347
tab.needSync = false;
335
348
if(!id)this.UpdateConfig(false);
349
+
350
+
return tab;
336
351
}
337
352
338
353
private _generateTabGraph( tabId: string | null ): [ any, Tab | null ]{
···
348
363
let node = tab.nodes[i];
349
364
350
365
let nodeOutputs = [];
366
+
let nodeInputs = [];
351
367
352
368
for (let j = 0; j < node.outputs.length; j++) {
353
369
let output = node.outputs[j];
···
364
380
})
365
381
}
366
382
383
+
for (let j = 0; j < node.inputs.length; j++) {
384
+
let input = node.inputs[j];
385
+
386
+
nodeInputs.push({
387
+
name: input.name,
388
+
type: input.type,
389
+
connections: input.connections.map(x => { return {
390
+
name: x.name,
391
+
node: x.parent.id,
392
+
index: x.index,
393
+
type: x.type
394
+
}})
395
+
})
396
+
}
397
+
367
398
nodesToSave.push({
368
399
name: node.name,
369
400
id: node.id,
370
401
typeId: node.typeId,
371
402
pos: [ node.x, node.y ],
372
403
outputs: nodeOutputs,
404
+
inputs: nodeInputs,
373
405
statics: node.statics
374
406
})
375
407
}
···
384
416
invoke('save_graph', { graph: JSON.stringify({
385
417
tab_name: tab.name,
386
418
version: await getVersion(),
419
+
platform: platform(),
387
420
graph: nodesToSave
388
421
}), path });
389
422
}
+2
-1
src/Nodes/Conditional/IfEqual.tsx
+2
-1
src/Nodes/Conditional/IfEqual.tsx
+2
-1
src/Nodes/Conditional/IfFalse.tsx
+2
-1
src/Nodes/Conditional/IfFalse.tsx
+2
-1
src/Nodes/Conditional/IfTrue.tsx
+2
-1
src/Nodes/Conditional/IfTrue.tsx
+2
src/Nodes/Conditional.tsx
+2
src/Nodes/Conditional.tsx
+2
-1
src/Nodes/Debug.tsx
+2
-1
src/Nodes/Debug.tsx
+24
-3
src/Nodes/Nodes.tsx
+24
-3
src/Nodes/Nodes.tsx
···
1
1
import { Node, NodeStatic, NodeType } from "../structs/node";
2
+
import { platform } from '@tauri-apps/plugin-os';
2
3
3
4
import { NodeConditional } from "./Conditional";
4
5
import { NodeDebug } from "./Debug";
5
6
import { NodeOSCActions } from "./OSCActions";
6
7
import { NodeOSCTrigger } from "./OSCTrigger";
8
+
import { NodePressKey } from "./PressKey";
7
9
import { NodeStatics } from "./Statics";
10
+
import { NodeShellCommand } from "./Shell";
8
11
9
12
export interface NodeDefinition{
13
+
os: string,
10
14
isSingle: boolean,
11
15
name: string,
12
16
typeId?: string,
13
17
onStaticsUpdate?: ( node: Node ) => Promise<void>,
14
18
// build?: ( pos: [ number, number ], onStaticsUpdate: ( node: Node ) => void ) => Promise<Node>,
15
19
w?: number,
16
-
h?: number,
17
20
statics?: NodeStatic[],
18
21
inputs?: { name: string, type: NodeType }[],
19
22
outputs?: { name: string, type: NodeType }[],
···
25
28
[details: string] : NodeDefinition;
26
29
}
27
30
28
-
export let Nodes: NodeDefinition[] = [
31
+
// TODO: (Node Additions) Pressing keyboard keys (like to do linux, but has extra steps)
32
+
// TODO: (Node Additions) Getting media state from os
33
+
// TODO: (Node Additions) Sending custom OSC messages
34
+
// TODO: (Node Additions) Sending HTTP requests?
35
+
// TODO: (Node Additions) Voicemeeter integrations (win only)
36
+
// TODO: (Node Additions) Voicemod integrations (win only)
37
+
// TODO: (Node Additions) Executing shell commands? (probably need some kinda popup warning when these are imported about dangerous usage)
38
+
39
+
export let Nodes: NodeDefinition[] = [];
40
+
let nodes = [
29
41
NodeOSCTrigger,
30
42
NodeConditional,
31
43
NodeStatics,
32
44
NodeOSCActions,
33
-
NodeDebug
45
+
NodeDebug,
46
+
NodePressKey,
47
+
NodeShellCommand
34
48
]
49
+
50
+
let os = platform();
51
+
52
+
for (let i = 0; i < nodes.length; i++) {
53
+
let node = nodes[i];
54
+
if(node.os === 'any' || node.os === os)Nodes.push(node);
55
+
}
35
56
36
57
export let NodesByID: NodeDefinitionHashMap = {}
37
58
+2
-1
src/Nodes/OSCActions/Send Chatbox.tsx
+2
-1
src/Nodes/OSCActions/Send Chatbox.tsx
+2
src/Nodes/OSCActions.tsx
+2
src/Nodes/OSCActions.tsx
+27
-10
src/Nodes/OSCTrigger.tsx
+27
-10
src/Nodes/OSCTrigger.tsx
···
5
5
import { NodeDefinition } from "./Nodes";
6
6
7
7
export let NodeOSCTrigger: NodeDefinition = {
8
+
os: 'any',
9
+
8
10
isSingle: true,
9
11
name: 'OSC Trigger',
10
12
typeId: 'osctrigger',
11
13
12
14
w: 200,
13
-
h: 50,
14
15
15
16
statics: [
16
17
{
···
39
40
}
40
41
}
41
42
42
-
node.outputs.map(output => {
43
-
output.connections.map(partner => {
44
-
partner.connections = partner.connections.filter(x => x != output);
45
-
})
46
-
})
47
-
node.outputs = [];
43
+
let tempOutputs = [];
48
44
49
-
node.outputs.push({
45
+
tempOutputs.push({
50
46
name: 'Flow',
51
47
type: NodeType.Flow,
52
48
connections: [],
···
73
69
}
74
70
75
71
if(type){
76
-
node.outputs.push({
72
+
tempOutputs.push({
77
73
name: dat.desc === '' ? dat.type : dat.desc,
78
74
type: type,
79
75
connections: [],
···
83
79
}
84
80
});
85
81
86
-
node.h = 60 + (parameters.length + 1) * 30;
82
+
let hasChanged = false;
83
+
84
+
for(let i in tempOutputs){
85
+
if(
86
+
node.outputs[i] === undefined ||
87
+
tempOutputs[i].type != node.outputs[i].type
88
+
){
89
+
hasChanged = true;
90
+
}
91
+
}
92
+
93
+
if(hasChanged){
94
+
node.outputs.map(output => {
95
+
output.connections.map(partner => {
96
+
partner.connections = partner.connections.filter(x => x != output);
97
+
})
98
+
})
99
+
100
+
node.outputs = tempOutputs;
101
+
node.updateSize();
102
+
}
103
+
87
104
NodeManager.Instance.UpdateConfig();
88
105
}
89
106
};
+29
src/Nodes/PressKey.tsx
+29
src/Nodes/PressKey.tsx
···
1
+
import { Node, NodeType } from "../structs/node";
2
+
import { NodeDefinition } from "./Nodes";
3
+
4
+
export let NodePressKey: NodeDefinition = {
5
+
os: 'windows',
6
+
7
+
isSingle: true,
8
+
name: 'Press Key',
9
+
typeId: 'presskey',
10
+
11
+
w: 200,
12
+
13
+
statics: [
14
+
{
15
+
name: "Key",
16
+
type: NodeType.String,
17
+
value: ""
18
+
}
19
+
],
20
+
21
+
inputs: [
22
+
{
23
+
name: "Flow",
24
+
type: NodeType.Flow,
25
+
}
26
+
],
27
+
28
+
onStaticsUpdate: async ( _node: Node ) => {}
29
+
};
+38
src/Nodes/Shell.tsx
+38
src/Nodes/Shell.tsx
···
1
+
import { Node, NodeType } from "../structs/node";
2
+
import { NodeDefinition } from "./Nodes";
3
+
4
+
export let NodeShellCommand: NodeDefinition = {
5
+
os: 'any',
6
+
7
+
isSingle: true,
8
+
name: 'Shell Command',
9
+
typeId: 'shellcommand',
10
+
11
+
w: 200,
12
+
13
+
statics: [],
14
+
15
+
inputs: [
16
+
{
17
+
name: "Flow",
18
+
type: NodeType.Flow,
19
+
},
20
+
{
21
+
name: "Command",
22
+
type: NodeType.String,
23
+
},
24
+
],
25
+
26
+
outputs: [
27
+
{
28
+
name: "Flow",
29
+
type: NodeType.Flow,
30
+
},
31
+
{
32
+
name: "Output",
33
+
type: NodeType.String,
34
+
},
35
+
],
36
+
37
+
onStaticsUpdate: async ( _node: Node ) => {}
38
+
};
+2
-1
src/Nodes/Statics/Float.tsx
+2
-1
src/Nodes/Statics/Float.tsx
+2
-1
src/Nodes/Statics/Int.tsx
+2
-1
src/Nodes/Statics/Int.tsx
+2
-1
src/Nodes/Statics/String.tsx
+2
-1
src/Nodes/Statics/String.tsx
+2
src/Nodes/Statics.tsx
+2
src/Nodes/Statics.tsx
+8
-8
src/components/SettingsMenu.css
+8
-8
src/components/SettingsMenu.css
···
1
1
.settings-menu{
2
-
position: fixed;
3
-
z-index: 110;
4
-
top: 0;
5
-
left: 0;
6
-
width: 100vw;
7
-
height: 100vh;
8
-
background: rgba(0, 0, 0, 0.75);
9
-
}
2
+
position: fixed;
3
+
z-index: 110;
4
+
top: 0;
5
+
left: 0;
6
+
width: 100vw;
7
+
height: 100vh;
8
+
background: rgba(0, 0, 0, 0.75);
9
+
}
10
10
11
11
.settings-menu-inner{
12
12
position: fixed;
+3
src/components/SettingsMenu.tsx
+3
src/components/SettingsMenu.tsx
+19
-4
src/keybinds.ts
+19
-4
src/keybinds.ts
···
6
6
7
7
let isKeyDown: any = {};
8
8
9
-
export let load = ( mousePos: Accessor<[ number, number ]>, selectedNode: Accessor<Node[]>, setSelectedNode: Setter<Node[]> ) => {
9
+
export let load = ( canvas: HTMLCanvasElement, mousePos: Accessor<[ number, number ]>, selectedNode: Accessor<Node[]>, setSelectedNode: Setter<Node[]> ) => {
10
10
// TODO: Add undo / redo -ing
11
11
12
-
window.onkeydown = async ( e ) => {
13
-
isKeyDown[e.key] = true;
14
-
12
+
canvas.onkeydown = async ( e ) => {
15
13
switch(e.key){
16
14
case 'Delete':
17
15
let nodes = selectedNode();
···
33
31
34
32
setSelectedNode([]);
35
33
break;
34
+
}
35
+
}
36
+
37
+
window.onkeydown = async ( e ) => {
38
+
isKeyDown[e.key] = true;
39
+
40
+
switch(e.key){
36
41
case 's':
37
42
if(e.ctrlKey){
38
43
let currentTab = NodeManager.Instance.CurrentTab();
···
68
73
NodeManager.Instance.AddNode(node);
69
74
70
75
setSelectedNode(nodes);
76
+
}
77
+
break;
78
+
case 'z':
79
+
if(e.ctrlKey){
80
+
console.log('undo');
81
+
}
82
+
break;
83
+
case 'y':
84
+
if(e.ctrlKey){
85
+
console.log('redo');
71
86
}
72
87
break;
73
88
}
+7
-2
src/structs/node.ts
+7
-2
src/structs/node.ts
···
20
20
this.typeId = node.typeId!;
21
21
this.x = pos[0];
22
22
this.y = pos[1];
23
-
this.w = node.w!;
24
-
this.h = node.h!;
25
23
26
24
this.inputs = node.inputs ? node.inputs.map(( x, indx ) => {
27
25
return {
···
43
41
}
44
42
}) : [];
45
43
44
+
this.w = node.w || 200;
45
+
this.h = 50 + Math.max(this.outputs.length, this.inputs.length) * 30;
46
+
46
47
this.selected = false;
47
48
this.statics = node.statics!,
48
49
this.onStaticsUpdate = node.onStaticsUpdate!;
50
+
}
51
+
52
+
updateSize(){
53
+
this.h = 50 + Math.max(this.outputs.length, this.inputs.length) * 30;
49
54
}
50
55
}
51
56
+40
-1
src/utils/clipboard.ts
+40
-1
src/utils/clipboard.ts
···
7
7
8
8
for(let node of selectedNodes){
9
9
arr.push({
10
+
id: node.id,
10
11
type_id: node.typeId,
11
12
statics: node.statics,
12
13
x: node.x - mousePos[0],
13
-
y: node.y - mousePos[1]
14
+
y: node.y - mousePos[1],
15
+
outputs: node.outputs.map(x => {
16
+
return x.connections.map(x => {
17
+
return { node: x.parent.id, index: x.index } }) })
14
18
})
15
19
}
16
20
21
+
for(let node of arr){
22
+
for(let output of node.outputs){
23
+
for(let i in output){
24
+
let indx = arr.findIndex(x => x.id === output[i].node);
25
+
if(indx === -1)
26
+
delete output[i];
27
+
else
28
+
output[i].node = indx;
29
+
}
30
+
}
31
+
}
32
+
33
+
for(let node of arr)delete node.id;
34
+
35
+
console.log(arr);
17
36
return 'VRCMACRO' + btoa(JSON.stringify(arr));
18
37
}
19
38
···
35
54
await n.onStaticsUpdate(n);
36
55
37
56
nodes.push(n);
57
+
}
58
+
59
+
for(let i in nodes){
60
+
let outputs: { node: number, index: number }[][] = json[i].outputs;
61
+
let node = nodes[i];
62
+
63
+
for(let j in outputs){
64
+
let output = node.outputs[j];
65
+
66
+
for(let k in outputs[j]){
67
+
let connection = outputs[j][k];
68
+
if(!connection)continue;
69
+
70
+
let peerNode = nodes[connection.node];
71
+
let input = peerNode.inputs[connection.index];
72
+
73
+
output.connections.push(input);
74
+
input.connections.push(output);
75
+
}
76
+
}
38
77
}
39
78
40
79
return nodes;
+150
-3
src-tauri/Cargo.lock
+150
-3
src-tauri/Cargo.lock
···
7
7
version = "0.1.0"
8
8
dependencies = [
9
9
"anyhow",
10
+
"chrono",
10
11
"crossbeam-channel",
11
12
"dirs",
13
+
"enigo",
12
14
"flate2",
13
15
"serde",
14
16
"serde_json",
···
17
19
"tauri-plugin-clipboard-manager",
18
20
"tauri-plugin-dialog",
19
21
"tauri-plugin-opener",
22
+
"tauri-plugin-os",
20
23
"tokio",
21
24
]
22
25
···
517
520
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
518
521
dependencies = [
519
522
"iana-time-zone",
523
+
"js-sys",
520
524
"num-traits",
521
525
"serde",
526
+
"wasm-bindgen",
522
527
"windows-link 0.2.1",
523
528
]
524
529
···
587
592
version = "0.24.0"
588
593
source = "registry+https://github.com/rust-lang/crates.io-index"
589
594
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
595
+
dependencies = [
596
+
"bitflags 2.10.0",
597
+
"core-foundation",
598
+
"core-graphics-types",
599
+
"foreign-types",
600
+
"libc",
601
+
]
602
+
603
+
[[package]]
604
+
name = "core-graphics"
605
+
version = "0.25.0"
606
+
source = "registry+https://github.com/rust-lang/crates.io-index"
607
+
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
590
608
dependencies = [
591
609
"bitflags 2.10.0",
592
610
"core-foundation",
···
909
927
version = "1.1.0"
910
928
source = "registry+https://github.com/rust-lang/crates.io-index"
911
929
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
930
+
931
+
[[package]]
932
+
name = "enigo"
933
+
version = "0.6.1"
934
+
source = "registry+https://github.com/rust-lang/crates.io-index"
935
+
checksum = "71c6c56e50f7acae2906a0dcbb34529ca647e40421119ad5d12e7f8ba6e50010"
936
+
dependencies = [
937
+
"core-foundation",
938
+
"core-graphics 0.25.0",
939
+
"foreign-types-shared",
940
+
"libc",
941
+
"log",
942
+
"nom 8.0.0",
943
+
"objc2 0.6.3",
944
+
"objc2-app-kit",
945
+
"objc2-foundation 0.3.2",
946
+
"windows",
947
+
"x11rb",
948
+
"xkbcommon",
949
+
"xkeysym",
950
+
]
912
951
913
952
[[package]]
914
953
name = "enumflags2"
···
2130
2169
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
2131
2170
2132
2171
[[package]]
2172
+
name = "memmap2"
2173
+
version = "0.9.9"
2174
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2175
+
checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490"
2176
+
dependencies = [
2177
+
"libc",
2178
+
]
2179
+
2180
+
[[package]]
2133
2181
name = "memoffset"
2134
2182
version = "0.9.1"
2135
2183
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2268
2316
]
2269
2317
2270
2318
[[package]]
2319
+
name = "nom"
2320
+
version = "8.0.0"
2321
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2322
+
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
2323
+
dependencies = [
2324
+
"memchr",
2325
+
]
2326
+
2327
+
[[package]]
2271
2328
name = "num-conv"
2272
2329
version = "0.1.0"
2273
2330
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2408
2465
]
2409
2466
2410
2467
[[package]]
2468
+
name = "objc2-core-location"
2469
+
version = "0.3.2"
2470
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2471
+
checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009"
2472
+
dependencies = [
2473
+
"objc2 0.6.3",
2474
+
"objc2-foundation 0.3.2",
2475
+
]
2476
+
2477
+
[[package]]
2411
2478
name = "objc2-core-text"
2412
2479
version = "0.3.2"
2413
2480
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2526
2593
dependencies = [
2527
2594
"bitflags 2.10.0",
2528
2595
"objc2 0.6.3",
2596
+
"objc2-core-foundation",
2529
2597
"objc2-foundation 0.3.2",
2530
2598
]
2531
2599
···
2547
2615
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
2548
2616
dependencies = [
2549
2617
"bitflags 2.10.0",
2618
+
"block2 0.6.2",
2550
2619
"objc2 0.6.3",
2620
+
"objc2-cloud-kit",
2621
+
"objc2-core-data",
2551
2622
"objc2-core-foundation",
2623
+
"objc2-core-graphics",
2624
+
"objc2-core-image",
2625
+
"objc2-core-location",
2626
+
"objc2-core-text",
2627
+
"objc2-foundation 0.3.2",
2628
+
"objc2-quartz-core 0.3.2",
2629
+
"objc2-user-notifications",
2630
+
]
2631
+
2632
+
[[package]]
2633
+
name = "objc2-user-notifications"
2634
+
version = "0.3.2"
2635
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2636
+
checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
2637
+
dependencies = [
2638
+
"objc2 0.6.3",
2552
2639
"objc2-foundation 0.3.2",
2553
2640
]
2554
2641
···
2600
2687
dependencies = [
2601
2688
"futures-core",
2602
2689
"pin-project-lite",
2690
+
]
2691
+
2692
+
[[package]]
2693
+
name = "os_info"
2694
+
version = "3.13.0"
2695
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2696
+
checksum = "7c39b5918402d564846d5aba164c09a66cc88d232179dfd3e3c619a25a268392"
2697
+
dependencies = [
2698
+
"android_system_properties",
2699
+
"log",
2700
+
"nix",
2701
+
"objc2 0.6.3",
2702
+
"objc2-foundation 0.3.2",
2703
+
"objc2-ui-kit",
2704
+
"serde",
2705
+
"windows-sys 0.61.2",
2603
2706
]
2604
2707
2605
2708
[[package]]
···
3687
3790
dependencies = [
3688
3791
"bytemuck",
3689
3792
"cfg_aliases",
3690
-
"core-graphics",
3793
+
"core-graphics 0.24.0",
3691
3794
"foreign-types",
3692
3795
"js-sys",
3693
3796
"log",
···
3824
3927
]
3825
3928
3826
3929
[[package]]
3930
+
name = "sys-locale"
3931
+
version = "0.3.2"
3932
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3933
+
checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
3934
+
dependencies = [
3935
+
"libc",
3936
+
]
3937
+
3938
+
[[package]]
3827
3939
name = "system-deps"
3828
3940
version = "6.2.2"
3829
3941
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3845
3957
"bitflags 2.10.0",
3846
3958
"block2 0.6.2",
3847
3959
"core-foundation",
3848
-
"core-graphics",
3960
+
"core-graphics 0.24.0",
3849
3961
"crossbeam-channel",
3850
3962
"dispatch",
3851
3963
"dlopen2",
···
4100
4212
"url",
4101
4213
"windows",
4102
4214
"zbus",
4215
+
]
4216
+
4217
+
[[package]]
4218
+
name = "tauri-plugin-os"
4219
+
version = "2.3.2"
4220
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4221
+
checksum = "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997"
4222
+
dependencies = [
4223
+
"gethostname",
4224
+
"log",
4225
+
"os_info",
4226
+
"serde",
4227
+
"serde_json",
4228
+
"serialize-to-javascript",
4229
+
"sys-locale",
4230
+
"tauri",
4231
+
"tauri-plugin",
4232
+
"thiserror 2.0.17",
4103
4233
]
4104
4234
4105
4235
[[package]]
···
4563
4693
checksum = "52fac5f7d176f7f7f7e827299ead28ef98de642c5d93a97e0cd0816d17557f19"
4564
4694
dependencies = [
4565
4695
"memchr",
4566
-
"nom",
4696
+
"nom 7.1.3",
4567
4697
"petgraph",
4568
4698
]
4569
4699
···
5575
5705
version = "0.13.2"
5576
5706
source = "registry+https://github.com/rust-lang/crates.io-index"
5577
5707
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
5708
+
5709
+
[[package]]
5710
+
name = "xkbcommon"
5711
+
version = "0.9.0"
5712
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5713
+
checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d"
5714
+
dependencies = [
5715
+
"libc",
5716
+
"memmap2",
5717
+
"xkeysym",
5718
+
]
5719
+
5720
+
[[package]]
5721
+
name = "xkeysym"
5722
+
version = "0.2.1"
5723
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5724
+
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
5578
5725
5579
5726
[[package]]
5580
5727
name = "yoke"
+5
src-tauri/Cargo.toml
+5
src-tauri/Cargo.toml
+2
-1
src-tauri/capabilities/default.json
+2
-1
src-tauri/capabilities/default.json
+1
-1
src-tauri/src/frontend_calls/load_previous_tabs.rs
+1
-1
src-tauri/src/frontend_calls/load_previous_tabs.rs
···
8
8
pub fn load_previous_tabs(
9
9
window: Window,
10
10
conf: State<Config>,
11
-
) -> HashMap<String, (Vec<Node>, String, Option<String>)> {
11
+
) -> HashMap<String, (Vec<Node>, String, Option<String>, bool)> {
12
12
let config = conf.store.lock().unwrap();
13
13
14
14
if !config.hide_editor_on_start {
+5
-1
src-tauri/src/frontend_calls/sync_tab.rs
+5
-1
src-tauri/src/frontend_calls/sync_tab.rs
···
1
+
use chrono::Utc;
1
2
use crossbeam_channel::Sender;
2
3
3
4
use tauri::State;
···
9
10
graph: Vec<Node>,
10
11
id: String,
11
12
name: String,
13
+
save_state: bool,
12
14
location: Option<String>,
13
15
cmd: State<Sender<RuntimeCommand>>,
14
16
conf: State<Config>,
···
18
20
.unwrap();
19
21
20
22
let mut config = conf.store.lock().unwrap();
21
-
config.loaded_tabs.insert(id, (graph, name, location)); // TODO: When loading a tab into config, store the save state of it too
23
+
config.loaded_tabs.insert(id, (graph, name, location, save_state));
24
+
25
+
conf.save_prelocked(config);
22
26
}
23
27
24
28
#[tauri::command]
+6
-3
src-tauri/src/lib.rs
+6
-3
src-tauri/src/lib.rs
···
3
3
use crossbeam_channel::bounded;
4
4
use frontend_calls::*;
5
5
6
-
use crate::{osc::OSCMessage, setup::setup, utils::config::Config};
6
+
use crate::{osc::OSCMessage, setup::setup, utils::{config::Config, vrchat_builtin_parameters}};
7
7
8
8
mod frontend_calls;
9
9
mod osc;
···
11
11
mod setup;
12
12
mod structs;
13
13
mod utils;
14
-
15
-
// TODO: Add built-in OSC endpoints
16
14
17
15
#[cfg_attr(mobile, tauri::mobile_entry_point)]
18
16
#[tokio::main]
···
35
33
36
34
static ADDRESSES: Mutex<Vec<OSCMessage>> = Mutex::new(Vec::new());
37
35
36
+
let mut addresses = ADDRESSES.lock().unwrap();
37
+
addresses.append(&mut vrchat_builtin_parameters::get_read_addresses());
38
+
drop(addresses);
39
+
38
40
let (runtime_sender, runtime_receiver) = bounded(1024);
39
41
40
42
tauri::Builder::default()
43
+
.plugin(tauri_plugin_os::init())
41
44
.plugin(tauri_plugin_clipboard_manager::init())
42
45
.plugin(tauri_plugin_dialog::init())
43
46
.plugin(tauri_plugin_opener::init())
+3
-1
src-tauri/src/main.rs
+3
-1
src-tauri/src/main.rs
+21
-38
src-tauri/src/runtime/nodes/conditional/ifequal.rs
+21
-38
src-tauri/src/runtime/nodes/conditional/ifequal.rs
···
5
5
6
6
pub struct ConditionalIfEqual {
7
7
outputs: Vec<Vec<(String, isize, isize)>>,
8
-
value1: ParameterType,
9
-
value2: ParameterType,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
10
9
}
11
10
12
11
impl ConditionalIfEqual {
13
12
pub fn new(node: Node) -> Box<Self> {
14
13
Box::new(Self {
15
-
outputs: node
16
-
.outputs
17
-
.iter()
18
-
.map(|x| {
19
-
x.connections
20
-
.iter()
21
-
.map(|x| (x.node.clone(), x.index, x.value_type))
22
-
.collect()
23
-
})
24
-
.collect(),
25
-
value1: ParameterType::None,
26
-
value2: ParameterType::None,
14
+
outputs: node.outputs.iter().map(|x| {
15
+
x.connections.iter()
16
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
17
+
18
+
inputs: node.inputs.iter().map(|x| {
19
+
let y = x.connections.get(0);
20
+
if let Some(y) = y{
21
+
Some((y.node.clone(), y.index, y.value_type))
22
+
} else{
23
+
None
24
+
}
25
+
}).collect(),
27
26
})
28
27
}
29
28
}
···
31
30
impl RuntimeNode for ConditionalIfEqual {
32
31
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
33
32
self.outputs.clone()
34
-
}
35
-
fn execute_dry(&mut self, _: &Vec<ParameterType>) -> Option<Vec<ParameterType>> {
36
-
Some(vec![])
37
33
}
38
34
39
-
fn execute(&mut self) -> Option<Vec<ParameterType>> {
40
-
if self.value1 == ParameterType::None && self.value2 == ParameterType::None {
41
-
None
42
-
} else {
43
-
let equal = self.value1 == self.value2;
44
-
Some(vec![
45
-
ParameterType::Flow(equal),
46
-
ParameterType::Flow(!equal),
47
-
])
48
-
}
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
49
37
}
50
38
51
-
fn update_arg(&mut self, index: usize, arg: ParameterType) -> bool {
52
-
match index {
53
-
1 => {
54
-
self.value1 = arg;
55
-
}
56
-
2 => {
57
-
self.value2 = arg;
58
-
}
59
-
_ => {}
60
-
}
39
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
40
+
let is_equal = args[1] == args[2];
61
41
62
-
false
42
+
vec![
43
+
ParameterType::Flow(is_equal),
44
+
ParameterType::Flow(!is_equal),
45
+
]
63
46
}
64
47
65
48
fn is_entrypoint(&self) -> bool {
+22
-28
src-tauri/src/runtime/nodes/conditional/iffalse.rs
+22
-28
src-tauri/src/runtime/nodes/conditional/iffalse.rs
···
5
5
6
6
pub struct ConditionalIfFalse {
7
7
outputs: Vec<Vec<(String, isize, isize)>>,
8
-
runtime_active: bool,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
9
}
10
10
11
11
impl ConditionalIfFalse {
12
12
pub fn new(node: Node) -> Box<Self> {
13
13
Box::new(Self {
14
-
outputs: node
15
-
.outputs
16
-
.iter()
17
-
.map(|x| {
18
-
x.connections
19
-
.iter()
20
-
.map(|x| (x.node.clone(), x.index, x.value_type))
21
-
.collect()
22
-
})
23
-
.collect(),
24
-
runtime_active: false,
14
+
outputs: node.outputs.iter().map(|x| {
15
+
x.connections.iter()
16
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
17
+
18
+
inputs: node.inputs.iter().map(|x| {
19
+
let y = x.connections.get(0);
20
+
if let Some(y) = y{
21
+
Some((y.node.clone(), y.index, y.value_type))
22
+
} else{
23
+
None
24
+
}
25
+
}).collect(),
25
26
})
26
27
}
27
28
}
···
30
31
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
31
32
self.outputs.clone()
32
33
}
33
-
fn execute_dry(&mut self, _: &Vec<ParameterType>) -> Option<Vec<ParameterType>> {
34
-
Some(vec![])
34
+
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
35
37
}
36
38
37
-
fn execute(&mut self) -> Option<Vec<ParameterType>> {
38
-
Some(vec![
39
-
ParameterType::Flow(!self.runtime_active),
40
-
ParameterType::Flow(self.runtime_active),
41
-
])
42
-
}
39
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
40
+
let is_false = !args[1].as_bool().unwrap();
43
41
44
-
fn update_arg(&mut self, _: usize, arg: ParameterType) -> bool {
45
-
if arg.as_bool().unwrap() {
46
-
self.runtime_active = true;
47
-
true
48
-
} else {
49
-
self.runtime_active = false;
50
-
false
51
-
}
42
+
vec![
43
+
ParameterType::Flow(is_false),
44
+
ParameterType::Flow(!is_false),
45
+
]
52
46
}
53
47
54
48
fn is_entrypoint(&self) -> bool {
+22
-28
src-tauri/src/runtime/nodes/conditional/iftrue.rs
+22
-28
src-tauri/src/runtime/nodes/conditional/iftrue.rs
···
5
5
6
6
pub struct ConditionalIfTrue {
7
7
outputs: Vec<Vec<(String, isize, isize)>>,
8
-
runtime_active: bool,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
9
}
10
10
11
11
impl ConditionalIfTrue {
12
12
pub fn new(node: Node) -> Box<Self> {
13
13
Box::new(Self {
14
-
outputs: node
15
-
.outputs
16
-
.iter()
17
-
.map(|x| {
18
-
x.connections
19
-
.iter()
20
-
.map(|x| (x.node.clone(), x.index, x.value_type))
21
-
.collect()
22
-
})
23
-
.collect(),
24
-
runtime_active: false,
14
+
outputs: node.outputs.iter().map(|x| {
15
+
x.connections.iter()
16
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
17
+
18
+
inputs: node.inputs.iter().map(|x| {
19
+
let y = x.connections.get(0);
20
+
if let Some(y) = y{
21
+
Some((y.node.clone(), y.index, y.value_type))
22
+
} else{
23
+
None
24
+
}
25
+
}).collect(),
25
26
})
26
27
}
27
28
}
···
30
31
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
31
32
self.outputs.clone()
32
33
}
33
-
fn execute_dry(&mut self, _: &Vec<ParameterType>) -> Option<Vec<ParameterType>> {
34
-
Some(vec![])
34
+
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
35
37
}
36
38
37
-
fn execute(&mut self) -> Option<Vec<ParameterType>> {
38
-
Some(vec![
39
-
ParameterType::Flow(self.runtime_active),
40
-
ParameterType::Flow(!self.runtime_active),
41
-
])
42
-
}
39
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
40
+
let is_true = args[1].as_bool().unwrap();
43
41
44
-
fn update_arg(&mut self, _: usize, arg: ParameterType) -> bool {
45
-
if arg.as_bool().unwrap() {
46
-
self.runtime_active = true;
47
-
true
48
-
} else {
49
-
self.runtime_active = false;
50
-
false
51
-
}
42
+
vec![
43
+
ParameterType::Flow(is_true),
44
+
ParameterType::Flow(!is_true),
45
+
]
52
46
}
53
47
54
48
fn is_entrypoint(&self) -> bool {
+23
-19
src-tauri/src/runtime/nodes/debug.rs
+23
-19
src-tauri/src/runtime/nodes/debug.rs
···
4
4
};
5
5
6
6
pub struct Debug {
7
-
to_log: Option<ParameterType>,
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>
8
9
}
9
10
10
11
impl Debug {
11
-
pub fn new(_: Node) -> Box<Self> {
12
-
Box::new(Self { to_log: None })
12
+
pub fn new(node: Node) -> Box<Self> {
13
+
Box::new(Self {
14
+
outputs: node.outputs.iter().map(|x| {
15
+
x.connections.iter()
16
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
17
+
18
+
inputs: node.inputs.iter().map(|x| {
19
+
let y = x.connections.get(0);
20
+
if let Some(y) = y{
21
+
Some((y.node.clone(), y.index, y.value_type))
22
+
} else{
23
+
None
24
+
}
25
+
}).collect(),
26
+
})
13
27
}
14
28
}
15
29
16
30
impl RuntimeNode for Debug {
17
31
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
18
-
vec![]
19
-
}
20
-
fn execute_dry(&mut self, _: &Vec<ParameterType>) -> Option<Vec<ParameterType>> {
21
-
Some(vec![])
32
+
self.outputs.clone()
22
33
}
23
34
24
-
fn execute(&mut self) -> Option<Vec<ParameterType>> {
25
-
dbg!(&self.to_log);
26
-
self.to_log = None;
27
-
28
-
None
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
29
37
}
30
38
31
-
fn update_arg(&mut self, index: usize, value: ParameterType) -> bool {
32
-
if index == 1 {
33
-
self.to_log = Some(value);
34
-
true
35
-
} else {
36
-
false
37
-
}
39
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
40
+
dbg!(&args); // TODO: Debug to actual UI instead of console
41
+
vec![]
38
42
}
39
43
40
44
fn is_entrypoint(&self) -> bool {
+33
-25
src-tauri/src/runtime/nodes/oscactions/sendchatbox.rs
+33
-25
src-tauri/src/runtime/nodes/oscactions/sendchatbox.rs
···
7
7
};
8
8
9
9
pub struct OSCActionsSendChatbox {
10
-
to_log: String,
10
+
outputs: Vec<Vec<(String, isize, isize)>>,
11
+
inputs: Vec<Option<(String, isize, isize)>>,
11
12
}
12
13
13
14
impl OSCActionsSendChatbox {
14
-
pub fn new(_: Node) -> Box<Self> {
15
-
Box::new(Self { to_log: "".into() })
15
+
pub fn new(node: Node) -> Box<Self> {
16
+
Box::new(Self {
17
+
outputs: node.outputs.iter().map(|x| {
18
+
x.connections.iter()
19
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
20
+
21
+
inputs: node.inputs.iter().map(|x| {
22
+
let y = x.connections.get(0);
23
+
if let Some(y) = y{
24
+
Some((y.node.clone(), y.index, y.value_type))
25
+
} else{
26
+
None
27
+
}
28
+
}).collect(),
29
+
})
16
30
}
17
31
}
18
32
19
33
impl RuntimeNode for OSCActionsSendChatbox {
20
34
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
21
-
vec![]
22
-
}
23
-
fn execute_dry(&mut self, _: &Vec<ParameterType>) -> Option<Vec<ParameterType>> {
24
-
Some(vec![])
35
+
self.outputs.clone()
25
36
}
26
37
27
-
fn execute(&mut self) -> Option<Vec<ParameterType>> {
28
-
osc::send_message(
29
-
"/chatbox/input",
30
-
vec![
31
-
ParameterType::String(self.to_log.clone()),
32
-
ParameterType::Boolean(true),
33
-
ParameterType::Boolean(false),
34
-
],
35
-
"127.0.0.1:9000",
36
-
);
37
-
38
-
None
38
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
39
+
self.inputs.clone()
39
40
}
40
41
41
-
fn update_arg(&mut self, index: usize, value: ParameterType) -> bool {
42
-
if index == 1 {
43
-
self.to_log = value.as_string().unwrap();
44
-
true
45
-
} else {
46
-
false
42
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
43
+
if let Ok(msg) = args[1].as_string(){
44
+
osc::send_message(
45
+
"/chatbox/input",
46
+
vec![
47
+
ParameterType::String(msg.clone()),
48
+
ParameterType::Boolean(true),
49
+
ParameterType::Boolean(false),
50
+
],
51
+
"127.0.0.1:9000",
52
+
);
47
53
}
54
+
55
+
vec![]
48
56
}
49
57
50
58
fn is_entrypoint(&self) -> bool {
+32
-42
src-tauri/src/runtime/nodes/osctrigger.rs
+32
-42
src-tauri/src/runtime/nodes/osctrigger.rs
···
5
5
6
6
pub struct OSCTrigger {
7
7
outputs: Vec<Vec<(String, isize, isize)>>,
8
-
address: Option<String>,
9
-
runtime_active: bool,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
+
10
+
address: Option<String>
10
11
}
11
12
12
13
impl OSCTrigger {
···
14
15
let value = &node.statics[0].value;
15
16
16
17
Box::new(Self {
18
+
outputs: node.outputs.iter().map(|x| {
19
+
x.connections.iter()
20
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
21
+
22
+
inputs: node.inputs.iter().map(|x| {
23
+
let y = x.connections.get(0);
24
+
if let Some(y) = y{
25
+
Some((y.node.clone(), y.index, y.value_type))
26
+
} else{
27
+
None
28
+
}
29
+
}).collect(),
30
+
17
31
address: if value.is_null() {
18
32
None
19
33
} else {
20
34
Some(value.as_str().unwrap().to_owned())
21
35
},
22
-
outputs: node
23
-
.outputs
24
-
.iter()
25
-
.map(|x| {
26
-
x.connections
27
-
.iter()
28
-
.map(|x| (x.node.clone(), x.index, x.value_type))
29
-
.collect()
30
-
})
31
-
.collect(),
32
-
runtime_active: false,
33
36
})
34
37
}
35
38
}
···
39
42
self.outputs.clone()
40
43
}
41
44
42
-
fn execute_dry(&mut self, msg: &Vec<ParameterType>) -> Option<Vec<ParameterType>> {
43
-
if self.address.is_none() {
44
-
self.runtime_active = false;
45
-
return None;
46
-
}
47
-
48
-
if let ParameterType::String(address) = &msg[0] {
49
-
if *address == *self.address.as_ref().unwrap() {
50
-
self.runtime_active = true;
51
-
Some(msg.clone())
52
-
// The first value is technically the address value,
53
-
// but this value gets ignored as the first output of
54
-
// the osctrigger node is a flow output which gets ignored
55
-
// on dry runs.
56
-
} else {
57
-
self.runtime_active = false;
58
-
None
59
-
}
60
-
} else {
61
-
self.runtime_active = false;
62
-
None
63
-
}
45
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
46
+
self.inputs.clone()
64
47
}
65
48
66
-
fn execute(&mut self) -> Option<Vec<ParameterType>> {
67
-
let execute = self.runtime_active;
68
-
self.runtime_active = false;
49
+
fn execute(&mut self, mut args: Vec<ParameterType>) -> Vec<ParameterType> {
50
+
if args.len() == 0{ return args }
51
+
52
+
let execute = if let Some(internal_address) = &self.address {
53
+
if let Ok(address) = args[0].as_string() {
54
+
address == *internal_address
55
+
} else{
56
+
false
57
+
}
58
+
} else{
59
+
false
60
+
};
69
61
70
-
Some(vec![ParameterType::Flow(execute)])
62
+
args[0] = ParameterType::Flow(execute);
63
+
args
71
64
}
72
65
73
-
fn update_arg(&mut self, _: usize, _: ParameterType) -> bool {
74
-
false
75
-
}
76
66
fn is_entrypoint(&self) -> bool {
77
67
true
78
68
}
+73
src-tauri/src/runtime/nodes/press_key.rs
+73
src-tauri/src/runtime/nodes/press_key.rs
···
1
+
use std::sync::{Arc, Mutex};
2
+
3
+
use enigo::{Direction, Enigo, Key, Keyboard};
4
+
5
+
use crate::{
6
+
runtime::nodes::RuntimeNode,
7
+
structs::{nodes::Node, parameter_types::ParameterType},
8
+
};
9
+
10
+
pub struct PressKey {
11
+
outputs: Vec<Vec<(String, isize, isize)>>,
12
+
inputs: Vec<Option<(String, isize, isize)>>,
13
+
14
+
key: Option<char>,
15
+
enigo: Arc<Mutex<Enigo>>,
16
+
}
17
+
18
+
impl PressKey {
19
+
pub fn new(node: Node, enigo: Arc<Mutex<Enigo>>) -> Box<Self> {
20
+
let value = &node.statics[0].value;
21
+
22
+
Box::new(Self {
23
+
outputs: node.outputs.iter().map(|x| {
24
+
x.connections.iter()
25
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
26
+
27
+
inputs: node.inputs.iter().map(|x| {
28
+
let y = x.connections.get(0);
29
+
if let Some(y) = y{
30
+
Some((y.node.clone(), y.index, y.value_type))
31
+
} else{
32
+
None
33
+
}
34
+
}).collect(),
35
+
36
+
enigo,
37
+
key: if value.is_null() {
38
+
None
39
+
} else {
40
+
let string = value.as_str().unwrap().to_owned();
41
+
42
+
if string.len() == 1 {
43
+
Some(string.chars().nth(0).unwrap())
44
+
} else {
45
+
None
46
+
}
47
+
},
48
+
})
49
+
}
50
+
}
51
+
52
+
impl RuntimeNode for PressKey {
53
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
54
+
self.outputs.clone()
55
+
}
56
+
57
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
58
+
self.inputs.clone()
59
+
}
60
+
61
+
fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> {
62
+
if self.key.is_some() {
63
+
let mut enigo = self.enigo.lock().unwrap();
64
+
enigo.key(Key::MediaPlayPause, Direction::Click).unwrap();
65
+
}
66
+
67
+
vec![]
68
+
}
69
+
70
+
fn is_entrypoint(&self) -> bool {
71
+
false
72
+
}
73
+
}
+70
src-tauri/src/runtime/nodes/shell.rs
+70
src-tauri/src/runtime/nodes/shell.rs
···
1
+
use std::{io::Stdin, process::{Command, Stdio}};
2
+
3
+
use crate::{
4
+
runtime::nodes::RuntimeNode,
5
+
structs::{nodes::Node, parameter_types::ParameterType},
6
+
};
7
+
8
+
pub struct ShellCommand {
9
+
outputs: Vec<Vec<(String, isize, isize)>>,
10
+
inputs: Vec<Option<(String, isize, isize)>>
11
+
}
12
+
13
+
impl ShellCommand {
14
+
pub fn new(node: Node) -> Box<Self> {
15
+
Box::new(Self {
16
+
outputs: node.outputs.iter().map(|x| {
17
+
x.connections.iter()
18
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
19
+
20
+
inputs: node.inputs.iter().map(|x| {
21
+
let y = x.connections.get(0);
22
+
if let Some(y) = y{
23
+
Some((y.node.clone(), y.index, y.value_type))
24
+
} else{
25
+
None
26
+
}
27
+
}).collect()
28
+
})
29
+
}
30
+
}
31
+
32
+
impl RuntimeNode for ShellCommand {
33
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
34
+
self.outputs.clone()
35
+
}
36
+
37
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
38
+
self.inputs.clone()
39
+
}
40
+
41
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
42
+
if let Ok(cmd) = args[1].as_string(){
43
+
if cmd != ""{
44
+
let mut split_cmd = cmd.split(" ");
45
+
46
+
let mut cmd = Command::new(split_cmd.nth(0).unwrap());
47
+
if split_cmd.clone().count() > 0{ cmd.args(split_cmd); }
48
+
49
+
cmd.stdout(Stdio::piped());
50
+
cmd.stderr(Stdio::piped());
51
+
52
+
let child = cmd.spawn().unwrap();
53
+
let output = child.wait_with_output().unwrap();
54
+
55
+
vec![
56
+
ParameterType::Flow(true),
57
+
ParameterType::String(str::from_utf8(&output.stdout).unwrap().to_owned())
58
+
]
59
+
} else{
60
+
vec![ ParameterType::Flow(false) ]
61
+
}
62
+
} else{
63
+
vec![ ParameterType::Flow(false) ]
64
+
}
65
+
}
66
+
67
+
fn is_entrypoint(&self) -> bool {
68
+
false
69
+
}
70
+
}
+24
-22
src-tauri/src/runtime/nodes/statics/float.rs
+24
-22
src-tauri/src/runtime/nodes/statics/float.rs
···
5
5
6
6
pub struct StaticFloat {
7
7
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
+
8
10
value: Option<f32>,
9
11
}
10
12
···
13
15
let value = &node.statics[0].value;
14
16
15
17
Box::new(Self {
18
+
outputs: node.outputs.iter().map(|x| {
19
+
x.connections.iter()
20
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
21
+
22
+
inputs: node.inputs.iter().map(|x| {
23
+
let y = x.connections.get(0);
24
+
if let Some(y) = y{
25
+
Some((y.node.clone(), y.index, y.value_type))
26
+
} else{
27
+
None
28
+
}
29
+
}).collect(),
30
+
16
31
value: if value.is_null() {
17
32
None
18
33
} else {
19
34
Some(value.as_f64().unwrap() as f32)
20
-
},
21
-
outputs: node
22
-
.outputs
23
-
.iter()
24
-
.map(|x| {
25
-
x.connections
26
-
.iter()
27
-
.map(|x| (x.node.clone(), x.index, x.value_type))
28
-
.collect()
29
-
})
30
-
.collect(),
35
+
}
31
36
})
32
37
}
33
38
}
···
37
42
self.outputs.clone()
38
43
}
39
44
40
-
fn execute_dry(&mut self, _: &Vec<ParameterType>) -> Option<Vec<ParameterType>> {
45
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
46
+
self.inputs.clone()
47
+
}
48
+
49
+
fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> {
41
50
if self.value.is_some() {
42
-
Some(vec![ParameterType::Float(self.value.clone().unwrap())])
51
+
vec![ParameterType::Float(self.value.clone().unwrap())]
43
52
} else {
44
-
None
53
+
vec![ParameterType::Float(0.0)]
45
54
}
46
55
}
47
56
48
-
fn execute(&mut self) -> Option<Vec<ParameterType>> {
49
-
None
50
-
}
51
-
52
-
fn update_arg(&mut self, _: usize, _: ParameterType) -> bool {
57
+
fn is_entrypoint(&self) -> bool {
53
58
false
54
-
}
55
-
fn is_entrypoint(&self) -> bool {
56
-
true
57
59
}
58
60
}
+24
-22
src-tauri/src/runtime/nodes/statics/int.rs
+24
-22
src-tauri/src/runtime/nodes/statics/int.rs
···
5
5
6
6
pub struct StaticInt {
7
7
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
+
8
10
value: Option<i32>,
9
11
}
10
12
···
13
15
let value = &node.statics[0].value;
14
16
15
17
Box::new(Self {
18
+
outputs: node.outputs.iter().map(|x| {
19
+
x.connections.iter()
20
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
21
+
22
+
inputs: node.inputs.iter().map(|x| {
23
+
let y = x.connections.get(0);
24
+
if let Some(y) = y{
25
+
Some((y.node.clone(), y.index, y.value_type))
26
+
} else{
27
+
None
28
+
}
29
+
}).collect(),
30
+
16
31
value: if value.is_null() {
17
32
None
18
33
} else {
19
34
Some(value.as_i64().unwrap() as i32)
20
-
},
21
-
outputs: node
22
-
.outputs
23
-
.iter()
24
-
.map(|x| {
25
-
x.connections
26
-
.iter()
27
-
.map(|x| (x.node.clone(), x.index, x.value_type))
28
-
.collect()
29
-
})
30
-
.collect(),
35
+
}
31
36
})
32
37
}
33
38
}
···
37
42
self.outputs.clone()
38
43
}
39
44
40
-
fn execute_dry(&mut self, _: &Vec<ParameterType>) -> Option<Vec<ParameterType>> {
45
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
46
+
self.inputs.clone()
47
+
}
48
+
49
+
fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> {
41
50
if self.value.is_some() {
42
-
Some(vec![ParameterType::Int(self.value.clone().unwrap())])
51
+
vec![ParameterType::Int(self.value.clone().unwrap())]
43
52
} else {
44
-
None
53
+
vec![ParameterType::Int(0)]
45
54
}
46
55
}
47
56
48
-
fn execute(&mut self) -> Option<Vec<ParameterType>> {
49
-
None
50
-
}
51
-
52
-
fn update_arg(&mut self, _: usize, _: ParameterType) -> bool {
57
+
fn is_entrypoint(&self) -> bool {
53
58
false
54
-
}
55
-
fn is_entrypoint(&self) -> bool {
56
-
true
57
59
}
58
60
}
+24
-22
src-tauri/src/runtime/nodes/statics/string.rs
+24
-22
src-tauri/src/runtime/nodes/statics/string.rs
···
5
5
6
6
pub struct StaticString {
7
7
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
+
8
10
value: Option<String>,
9
11
}
10
12
···
13
15
let value = &node.statics[0].value;
14
16
15
17
Box::new(Self {
18
+
outputs: node.outputs.iter().map(|x| {
19
+
x.connections.iter()
20
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
21
+
22
+
inputs: node.inputs.iter().map(|x| {
23
+
let y = x.connections.get(0);
24
+
if let Some(y) = y{
25
+
Some((y.node.clone(), y.index, y.value_type))
26
+
} else{
27
+
None
28
+
}
29
+
}).collect(),
30
+
16
31
value: if value.is_null() {
17
32
None
18
33
} else {
19
34
Some(value.as_str().unwrap().to_owned())
20
-
},
21
-
outputs: node
22
-
.outputs
23
-
.iter()
24
-
.map(|x| {
25
-
x.connections
26
-
.iter()
27
-
.map(|x| (x.node.clone(), x.index, x.value_type))
28
-
.collect()
29
-
})
30
-
.collect(),
35
+
}
31
36
})
32
37
}
33
38
}
···
37
42
self.outputs.clone()
38
43
}
39
44
40
-
fn execute_dry(&mut self, _: &Vec<ParameterType>) -> Option<Vec<ParameterType>> {
45
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
46
+
self.inputs.clone()
47
+
}
48
+
49
+
fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> {
41
50
if self.value.is_some() {
42
-
Some(vec![ParameterType::String(self.value.clone().unwrap())])
51
+
vec![ParameterType::String(self.value.clone().unwrap())]
43
52
} else {
44
-
None
53
+
vec![ParameterType::String("".to_owned())]
45
54
}
46
55
}
47
56
48
-
fn execute(&mut self) -> Option<Vec<ParameterType>> {
49
-
None
50
-
}
51
-
52
-
fn update_arg(&mut self, _: usize, _: ParameterType) -> bool {
57
+
fn is_entrypoint(&self) -> bool {
53
58
false
54
-
}
55
-
fn is_entrypoint(&self) -> bool {
56
-
true
57
59
}
58
60
}
+30
-11
src-tauri/src/runtime/nodes.rs
+30
-11
src-tauri/src/runtime/nodes.rs
···
1
-
use std::collections::HashMap;
1
+
use std::{
2
+
collections::HashMap,
3
+
sync::{Arc, Mutex},
4
+
};
5
+
6
+
// #[cfg(target_os = "windows")]
7
+
use enigo::Enigo;
2
8
3
9
use crate::{
4
10
runtime::nodes::{
5
11
conditional::{
6
12
ifequal::ConditionalIfEqual, iffalse::ConditionalIfFalse, iftrue::ConditionalIfTrue,
7
-
},
8
-
debug::Debug,
9
-
oscactions::sendchatbox::OSCActionsSendChatbox,
10
-
osctrigger::OSCTrigger,
11
-
statics::{float::StaticFloat, int::StaticInt, string::StaticString},
13
+
}, debug::Debug, oscactions::sendchatbox::OSCActionsSendChatbox, osctrigger::OSCTrigger, shell::ShellCommand, statics::{float::StaticFloat, int::StaticInt, string::StaticString}
12
14
},
13
15
structs::{nodes::Node, parameter_types::ParameterType},
14
16
};
15
17
18
+
// #[cfg(target_os = "windows")]
19
+
use crate::runtime::nodes::press_key::PressKey;
20
+
16
21
mod conditional;
17
22
mod debug;
18
23
mod oscactions;
19
24
mod osctrigger;
20
25
mod statics;
26
+
mod shell;
27
+
28
+
// #[cfg(target_os = "windows")]
29
+
mod press_key;
21
30
22
31
pub struct RuntimeNodeTree {
23
32
pub nodes: HashMap<String, Box<dyn RuntimeNode>>,
···
26
35
unsafe impl Send for RuntimeNodeTree {}
27
36
28
37
impl RuntimeNodeTree {
29
-
pub fn from(tree: Vec<Node>) -> Self {
38
+
pub fn from(tree: Vec<Node>, /*#[cfg(target_os = "windows")]*/ enigo: Arc<Mutex<Enigo>>) -> Self {
30
39
let mut runtime_nodes: HashMap<String, Box<dyn RuntimeNode>> = HashMap::new();
31
40
for node in tree {
32
41
match node.type_id.as_str() {
···
61
70
"debug" => {
62
71
runtime_nodes.insert(node.id.clone(), Debug::new(node));
63
72
}
73
+
74
+
// #[cfg(target_os = "windows")]
75
+
"presskey" => {
76
+
runtime_nodes.insert(node.id.clone(), PressKey::new(node, enigo.clone()));
77
+
}
78
+
79
+
"shellcommand" => {
80
+
runtime_nodes.insert(node.id.clone(), ShellCommand::new(node));
81
+
}
82
+
64
83
_ => {}
65
84
}
66
85
}
···
73
92
74
93
pub trait RuntimeNode {
75
94
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>>; // Node ID, input index, output value type
76
-
fn execute_dry(&mut self, msg: &Vec<ParameterType>) -> Option<Vec<ParameterType>>; // Only update values on the first loop through
77
-
fn execute(&mut self) -> Option<Vec<ParameterType>>; // Then call functions on the second loop
78
-
fn update_arg(&mut self, index: usize, value: ParameterType) -> bool;
95
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>>; // Node ID, input index, output value type
96
+
97
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType>; // Then call functions on the second loop
79
98
fn is_entrypoint(&self) -> bool;
80
-
}
99
+
}
+61
-50
src-tauri/src/runtime.rs
+61
-50
src-tauri/src/runtime.rs
···
1
+
use std::collections::HashMap;
2
+
1
3
use anyhow::{bail, Result};
2
4
3
5
use crate::{runtime::nodes::RuntimeNodeTree, structs::parameter_types::ParameterType};
···
5
7
pub mod commands;
6
8
pub mod nodes;
7
9
8
-
// This is horrible. I know, I'm sorry.
10
+
// TODO: Variables
9
11
10
-
pub fn runtime_dry(
11
-
entry: String,
12
-
parameters: &Vec<ParameterType>,
13
-
tab: &mut RuntimeNodeTree,
14
-
) -> Result<()> {
15
-
let node = tab.nodes.get_mut(&entry);
16
-
if node.is_none() {
17
-
bail!("Cannot find node");
18
-
}
12
+
pub fn recurse_runtime(entry: String, tab: &mut RuntimeNodeTree, args: Vec<ParameterType>) -> Result<()>{
13
+
let ( out_args, output_map ) = runtime(entry, tab, args)?;
19
14
20
-
let node = node.unwrap();
15
+
let mut next_node_args: HashMap<String, Vec<ParameterType>> = HashMap::new();
21
16
22
-
let output_map = node.outputs();
23
-
let args = node.execute_dry(parameters);
17
+
for i in 0..out_args.len(){
18
+
if output_map.len() <= i { break; }
19
+
let links = &output_map[i];
24
20
25
-
if args.is_some() {
26
-
let args = args.unwrap();
21
+
for ( id, link_index, _ ) in links{
22
+
let link_index = link_index.clone() as usize;
27
23
28
-
for i in 0..args.len() {
29
-
let arg = &args[i];
24
+
if next_node_args.contains_key(id){
25
+
let args: &mut _ = next_node_args.get_mut(id).unwrap();
26
+
while args.len() < link_index{ args.push(ParameterType::None); }
30
27
31
-
for output in &output_map[i] {
32
-
if output.2 == 5 {
33
-
break;
34
-
} // Ignore flow outputs
28
+
args.push(out_args[i].clone());
29
+
} else{
30
+
let mut args = vec![ParameterType::None; link_index];
31
+
args.push(out_args[i].clone());
35
32
36
-
let next_node = tab.nodes.get_mut(&output.0);
37
-
if next_node.is_none() {
38
-
bail!("Cannot find node {}", output.0)
39
-
}
33
+
next_node_args.insert(id.clone(), args);
34
+
}
35
+
}
36
+
}
40
37
41
-
let next_node = next_node.unwrap();
42
-
let can_update = next_node.update_arg(output.1 as usize, arg.clone());
38
+
for i in 0..out_args.len(){
39
+
if let ParameterType::Flow(next) = out_args[i]{
40
+
if next{
41
+
let links = &output_map[i];
43
42
44
-
if can_update {
45
-
runtime_dry(output.0.clone(), &vec![], tab)?;
43
+
for ( id, _, _ ) in links{
44
+
let args = next_node_args.remove(id).unwrap();
45
+
recurse_runtime(id.clone(), tab, args)?;
46
46
}
47
47
}
48
48
}
···
51
51
Ok(())
52
52
}
53
53
54
-
pub fn runtime(entry: String, tab: &mut RuntimeNodeTree) -> Result<()> {
54
+
pub fn runtime(entry: String, tab: &mut RuntimeNodeTree, mut args: Vec<ParameterType>) -> Result<(Vec<ParameterType>, Vec<Vec<(String, isize, isize)>>)> {
55
55
let node = tab.nodes.get_mut(&entry);
56
-
if node.is_none() {
57
-
bail!("Cannot find node");
58
-
}
56
+
if node.is_none() { bail!("Cannot find node"); }
59
57
60
58
let node = node.unwrap();
59
+
let inputs = node.inputs();
61
60
62
-
let next = node.execute();
63
-
if next.is_some() {
64
-
let next = next.unwrap();
61
+
let mut needed_input_nodes = HashMap::new();
65
62
66
-
let outputs = node.outputs();
63
+
for i in 0..inputs.len(){
64
+
if i >= args.len() || args[i] == ParameterType::None{
65
+
if let Some(input) = &inputs[i]{
66
+
if !needed_input_nodes.contains_key(&input.0){
67
+
needed_input_nodes.insert(input.0.clone(), vec![(input.1.clone(), i.clone())]);
68
+
} else{
69
+
needed_input_nodes.get_mut(&input.0).unwrap().push((input.1.clone(), i.clone()));
70
+
}
71
+
}
72
+
}
73
+
}
74
+
75
+
for ( id, needed ) in needed_input_nodes{
76
+
let (out_args, _) = runtime(id, tab, vec![]).unwrap();
67
77
68
-
for i in 0..next.len() {
69
-
let arg = &next[i];
70
-
if i >= outputs.len() {
71
-
break;
72
-
}
78
+
for ( output, input ) in needed{
79
+
let arg = &out_args[output as usize];
73
80
74
-
for output in &outputs[i] {
75
-
if let ParameterType::Flow(next) = arg {
76
-
if *next {
77
-
// This is a flow output, continue
78
-
runtime(output.0.clone(), tab)?;
79
-
}
80
-
}
81
+
if args.len() >= input{
82
+
while args.len() < input{ args.push(ParameterType::None); }
83
+
args.push(arg.clone());
84
+
} else{
85
+
args[input] = arg.clone();
81
86
}
82
87
}
83
88
}
84
89
85
-
Ok(())
90
+
let node = tab.nodes.get_mut(&entry); // TODO: Find a way to only do this lookup once
91
+
if node.is_none() { bail!("Cannot find node"); }
92
+
93
+
let node = node.unwrap();
94
+
95
+
let output = node.execute(args);
96
+
Ok((output, node.outputs()))
86
97
}
+38
-24
src-tauri/src/setup.rs
+38
-24
src-tauri/src/setup.rs
···
1
1
use crossbeam_channel::{bounded, Receiver};
2
-
use std::{collections::HashMap, fs::File, io::Read, sync::Mutex};
2
+
use std::{
3
+
collections::HashMap,
4
+
fs::File,
5
+
io::Read,
6
+
sync::{Arc, Mutex},
7
+
};
3
8
4
9
use flate2::read::GzDecoder;
5
10
use serde_json::{Map, Value};
6
11
use tauri::{App, Emitter, Listener, Manager, WindowEvent};
7
12
8
13
use crate::{
9
-
osc::{self, OSCMessage},
10
-
runtime::{commands::RuntimeCommand, nodes::RuntimeNodeTree, runtime, runtime_dry},
11
-
structs::parameter_types::ParameterType,
12
-
utils::setup_traymenu::setup_traymenu,
14
+
osc::{self, OSCMessage}, runtime::{commands::RuntimeCommand, nodes::RuntimeNodeTree, recurse_runtime}, structs::parameter_types::ParameterType, utils::{setup_traymenu::setup_traymenu, vrchat_builtin_parameters}
13
15
};
14
16
15
17
pub fn setup(
···
21
23
window.hide().unwrap();
22
24
23
25
let win_handle = window.clone();
26
+
24
27
window.on_window_event(move |event| match event {
25
28
WindowEvent::CloseRequested { api, .. } => {
26
29
api.prevent_close();
27
-
28
-
win_handle.hide().unwrap();
29
-
win_handle.emit("hide-window", ()).unwrap();
30
+
win_handle.emit("prompt_to_close", ()).unwrap();
31
+
}
32
+
WindowEvent::Resized(_) => {
33
+
let minimised = win_handle.is_minimized().unwrap();
34
+
if minimised{
35
+
win_handle.hide().unwrap();
36
+
win_handle.emit("hide-window", ()).unwrap();
37
+
win_handle.unminimize().unwrap();
38
+
}
30
39
}
31
40
_ => {}
32
41
});
···
78
87
let mut addrs = addresses.lock().unwrap();
79
88
if !addrs.contains(&msg) {
80
89
addrs.push(msg);
90
+
}
91
+
92
+
if message.address == "/avatar/change".to_owned(){
93
+
*addrs = vrchat_builtin_parameters::get_read_addresses();
94
+
95
+
// TODO: Read avatar paramaters from file
81
96
}
82
97
83
98
runtime_sender
···
86
101
}
87
102
});
88
103
104
+
// TODO: Run tabs in seperate threads (really not looking forward to this... thanks rust)
105
+
89
106
tokio::spawn(async move {
90
107
let mut tabs: HashMap<String, RuntimeNodeTree> = HashMap::new();
91
108
109
+
// #[cfg(target_os = "windows")]
110
+
let enigo = Arc::new(Mutex::new(enigo::Enigo::new(&enigo::Settings::default()).unwrap()));
111
+
92
112
loop {
93
113
let cmd = runtime_receiver.recv().unwrap();
94
114
···
97
117
for (_, mut tab) in &mut tabs {
98
118
let keys: Vec<String> = tab.nodes.keys().map(|x| x.clone()).collect();
99
119
100
-
for id in keys.clone() {
120
+
for id in keys {
101
121
let entry = tab.nodes[&id].is_entrypoint();
102
122
103
123
if entry {
104
-
let args = vec![
105
-
vec![ParameterType::String(msg.address.clone())],
106
-
msg.values.clone(),
107
-
]
108
-
.concat();
124
+
let mut args = vec![ ParameterType::String(msg.address.clone())];
125
+
let mut values = msg.values.clone();
109
126
110
-
runtime_dry(id.clone(), &args, &mut tab).unwrap();
111
-
}
112
-
}
113
-
114
-
for id in keys {
115
-
let entry = tab.nodes[&id].is_entrypoint();
116
-
117
-
if entry {
118
-
let _ = runtime(id.clone(), &mut tab);
127
+
args.append(&mut values);
128
+
let _ = recurse_runtime(id.clone(), &mut tab, args);
119
129
}
120
130
}
121
131
}
122
132
}
123
133
124
134
RuntimeCommand::AddTab(graph, id) => {
125
-
tabs.insert(id, RuntimeNodeTree::from(graph));
135
+
// #[cfg(target_os = "windows")]
136
+
tabs.insert(id, RuntimeNodeTree::from(graph, enigo.clone()));
137
+
138
+
// #[cfg(target_os = "linux")]
139
+
// tabs.insert(id, RuntimeNodeTree::from(graph));
126
140
}
127
141
RuntimeCommand::RemoveTab(id) => {
128
142
tabs.remove(&id);
+3
-2
src-tauri/src/structs/nodes.rs
+3
-2
src-tauri/src/structs/nodes.rs
···
5
5
pub struct Node {
6
6
pub id: String,
7
7
pub name: String,
8
-
pub outputs: Vec<NodeOutput>,
8
+
pub outputs: Vec<NodeIO>,
9
+
pub inputs: Vec<NodeIO>,
9
10
pub pos: [f32; 2],
10
11
pub statics: Vec<NodeStatic>,
11
12
···
23
24
}
24
25
25
26
#[derive(Serialize, Deserialize, Debug, Clone)]
26
-
pub struct NodeOutput {
27
+
pub struct NodeIO {
27
28
pub name: String,
28
29
29
30
#[serde(rename = "type")]
+16
-6
src-tauri/src/utils/config.rs
+16
-6
src-tauri/src/utils/config.rs
···
3
3
fs::File,
4
4
io::{Read, Write},
5
5
path::PathBuf,
6
-
sync::Mutex,
6
+
sync::{Mutex, MutexGuard},
7
7
};
8
8
9
+
use chrono::Utc;
9
10
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
10
11
use serde::{Deserialize, Serialize};
11
12
···
14
15
#[derive(Clone, Serialize, Deserialize, Debug)]
15
16
pub struct ConfigValues {
16
17
#[serde(default)]
17
-
pub loaded_tabs: HashMap<String, (Vec<Node>, String, Option<String>)>,
18
+
pub loaded_tabs: HashMap<String, (Vec<Node>, String, Option<String>, bool)>,
18
19
19
20
#[serde(default)]
20
21
pub hide_editor_on_start: bool,
···
39
40
40
41
let json: ConfigValues = serde_json::from_str(&json_string).unwrap();
41
42
42
-
dbg!(&json);
43
-
44
43
Config {
45
44
store: Mutex::new(json),
46
45
path: path,
···
48
47
}
49
48
50
49
pub fn save(&self) {
51
-
let dat = serde_json::to_string(&self.store.lock().unwrap().clone()).unwrap();
52
-
dbg!(&dat);
50
+
let mut dat = self.store.lock().unwrap();
51
+
52
+
let dat = serde_json::to_string(&*dat).unwrap();
53
+
54
+
let file = File::create(&self.path).unwrap();
55
+
let mut encoder = GzEncoder::new(file, Compression::default());
56
+
57
+
encoder.write_all(dat.as_bytes()).unwrap();
58
+
encoder.finish().unwrap();
59
+
}
60
+
61
+
pub fn save_prelocked(&self, mut dat: MutexGuard<'_, ConfigValues>) {
62
+
let dat = serde_json::to_string(&*dat).unwrap();
53
63
54
64
let file = File::create(&self.path).unwrap();
55
65
let mut encoder = GzEncoder::new(file, Compression::default());
+1
src-tauri/src/utils/mod.rs
+1
src-tauri/src/utils/mod.rs
+315
src-tauri/src/utils/vrchat_builtin_parameters.rs
+315
src-tauri/src/utils/vrchat_builtin_parameters.rs
···
1
+
use crate::{osc::OSCMessage, structs::parameter_types::ParameterType};
2
+
3
+
pub fn get_read_addresses() -> Vec<OSCMessage>{
4
+
vec![
5
+
// Avatar Parameters
6
+
OSCMessage {
7
+
address: "/avatar/change".into(),
8
+
values: vec![ ParameterType::String("".into()) ]
9
+
},
10
+
11
+
OSCMessage {
12
+
address: "/avatar/parameters/VRCEmote".into(),
13
+
values: vec![ ParameterType::Int(0) ]
14
+
},
15
+
OSCMessage {
16
+
address: "/avatar/parameters/VRCFaceBlendV".into(),
17
+
values: vec![ ParameterType::Float(0.0) ]
18
+
},
19
+
OSCMessage {
20
+
address: "/avatar/parameters/VRCFaceBlendH".into(),
21
+
values: vec![ ParameterType::Float(0.0) ]
22
+
},
23
+
OSCMessage {
24
+
address: "/avatar/parameters/PreviewMode".into(),
25
+
values: vec![ ParameterType::Int(0) ]
26
+
},
27
+
OSCMessage {
28
+
address: "/avatar/parameters/IsAnimatorEnabled".into(),
29
+
values: vec![ ParameterType::Boolean(true) ]
30
+
},
31
+
32
+
OSCMessage {
33
+
address: "/avatar/parameters/GestureRightWeight".into(),
34
+
values: vec![ ParameterType::Float(0.0) ]
35
+
},
36
+
OSCMessage {
37
+
address: "/avatar/parameters/GestureLeftWeight".into(),
38
+
values: vec![ ParameterType::Float(0.0) ]
39
+
},
40
+
OSCMessage {
41
+
address: "/avatar/parameters/GestureRight".into(),
42
+
values: vec![ ParameterType::Int(0) ]
43
+
},
44
+
OSCMessage {
45
+
address: "/avatar/parameters/GestureLeft".into(),
46
+
values: vec![ ParameterType::Int(0) ]
47
+
},
48
+
49
+
OSCMessage {
50
+
address: "/avatar/parameters/ScaleModified".into(),
51
+
values: vec![ ParameterType::Boolean(true) ]
52
+
},
53
+
OSCMessage {
54
+
address: "/avatar/parameters/ScaleFactor".into(),
55
+
values: vec![ ParameterType::Float(1.0) ]
56
+
},
57
+
OSCMessage {
58
+
address: "/avatar/parameters/ScaleFactorInverse".into(),
59
+
values: vec![ ParameterType::Float(1.0) ]
60
+
},
61
+
OSCMessage {
62
+
address: "/avatar/parameters/EyeHeightAsPercent".into(),
63
+
values: vec![ ParameterType::Float(1.0) ]
64
+
},
65
+
66
+
OSCMessage {
67
+
address: "/avatar/parameters/Viseme".into(),
68
+
values: vec![ ParameterType::Int(0) ]
69
+
},
70
+
OSCMessage {
71
+
address: "/avatar/parameters/Voice".into(),
72
+
values: vec![ ParameterType::Float(0.0) ]
73
+
},
74
+
OSCMessage {
75
+
address: "/avatar/parameters/Earmuffs".into(),
76
+
values: vec![ ParameterType::Boolean(true) ]
77
+
},
78
+
OSCMessage {
79
+
address: "/avatar/parameters/MuteSelf".into(),
80
+
values: vec![ ParameterType::Boolean(true) ]
81
+
},
82
+
83
+
OSCMessage {
84
+
address: "/avatar/parameters/AFK".into(),
85
+
values: vec![ ParameterType::Boolean(true) ]
86
+
},
87
+
OSCMessage {
88
+
address: "/avatar/parameters/InStation".into(),
89
+
values: vec![ ParameterType::Boolean(true) ]
90
+
},
91
+
OSCMessage {
92
+
address: "/avatar/parameters/Seated".into(),
93
+
values: vec![ ParameterType::Boolean(true) ]
94
+
},
95
+
OSCMessage {
96
+
address: "/avatar/parameters/VRMode".into(),
97
+
values: vec![ ParameterType::Int(0) ]
98
+
},
99
+
OSCMessage {
100
+
address: "/avatar/parameters/TrackingType".into(),
101
+
values: vec![ ParameterType::Int(0) ]
102
+
},
103
+
104
+
OSCMessage {
105
+
address: "/avatar/parameters/Grounded".into(),
106
+
values: vec![ ParameterType::Boolean(true) ]
107
+
},
108
+
OSCMessage {
109
+
address: "/avatar/parameters/Upright".into(),
110
+
values: vec![ ParameterType::Float(1.0) ]
111
+
},
112
+
OSCMessage {
113
+
address: "/avatar/parameters/AngularY".into(),
114
+
values: vec![ ParameterType::Float(1.0) ]
115
+
},
116
+
OSCMessage {
117
+
address: "/avatar/parameters/VelocityX".into(),
118
+
values: vec![ ParameterType::Float(1.0) ]
119
+
},
120
+
OSCMessage {
121
+
address: "/avatar/parameters/VelocityY".into(),
122
+
values: vec![ ParameterType::Float(1.0) ]
123
+
},
124
+
OSCMessage {
125
+
address: "/avatar/parameters/VelocityZ".into(),
126
+
values: vec![ ParameterType::Float(1.0) ]
127
+
},
128
+
OSCMessage {
129
+
address: "/avatar/parameters/VelocityMagnitude".into(),
130
+
values: vec![ ParameterType::Float(1.0) ]
131
+
},
132
+
133
+
// User Camera
134
+
OSCMessage {
135
+
address: "/usercamera/Mode".into(),
136
+
values: vec![ ParameterType::Int(0) ]
137
+
},
138
+
OSCMessage {
139
+
address: "/usercamera/Pose".into(),
140
+
values: vec![
141
+
ParameterType::Float(0.0),
142
+
ParameterType::Float(0.0),
143
+
ParameterType::Float(0.0),
144
+
145
+
ParameterType::Float(0.0),
146
+
ParameterType::Float(0.0),
147
+
ParameterType::Float(0.0)
148
+
]
149
+
},
150
+
151
+
OSCMessage {
152
+
address: "/usercamera/ShowUIInCamera".into(),
153
+
values: vec![ ParameterType::Boolean(true) ]
154
+
},
155
+
OSCMessage {
156
+
address: "/usercamera/LocalPlayer".into(),
157
+
values: vec![ ParameterType::Boolean(true) ]
158
+
},
159
+
OSCMessage {
160
+
address: "/usercamera/RemotePlayer".into(),
161
+
values: vec![ ParameterType::Boolean(true) ]
162
+
},
163
+
OSCMessage {
164
+
address: "/usercamera/Environment".into(),
165
+
values: vec![ ParameterType::Boolean(true) ]
166
+
},
167
+
OSCMessage {
168
+
address: "/usercamera/GreenScreen".into(),
169
+
values: vec![ ParameterType::Boolean(true) ]
170
+
},
171
+
OSCMessage {
172
+
address: "/usercamera/Lock".into(),
173
+
values: vec![ ParameterType::Boolean(true) ]
174
+
},
175
+
OSCMessage {
176
+
address: "/usercamera/SmoothMovement".into(),
177
+
values: vec![ ParameterType::Boolean(true) ]
178
+
},
179
+
OSCMessage {
180
+
address: "/usercamera/LookAtMe".into(),
181
+
values: vec![ ParameterType::Boolean(true) ]
182
+
},
183
+
OSCMessage {
184
+
address: "/usercamera/AutoLevelRoll".into(),
185
+
values: vec![ ParameterType::Boolean(true) ]
186
+
},
187
+
OSCMessage {
188
+
address: "/usercamera/AutoLevelPitch".into(),
189
+
values: vec![ ParameterType::Boolean(true) ]
190
+
},
191
+
OSCMessage {
192
+
address: "/usercamera/Flying".into(),
193
+
values: vec![ ParameterType::Boolean(true) ]
194
+
},
195
+
OSCMessage {
196
+
address: "/usercamera/TriggerTakesPhotos".into(),
197
+
values: vec![ ParameterType::Boolean(true) ]
198
+
},
199
+
OSCMessage {
200
+
address: "/usercamera/DollyPathsStayVisible".into(),
201
+
values: vec![ ParameterType::Boolean(true) ]
202
+
},
203
+
OSCMessage {
204
+
address: "/usercamera/AudioFromCamera".into(),
205
+
values: vec![ ParameterType::Boolean(true) ]
206
+
},
207
+
OSCMessage {
208
+
address: "/usercamera/ShowFocus".into(),
209
+
values: vec![ ParameterType::Boolean(true) ]
210
+
},
211
+
OSCMessage {
212
+
address: "/usercamera/Streaming".into(),
213
+
values: vec![ ParameterType::Boolean(true) ]
214
+
},
215
+
OSCMessage {
216
+
address: "/usercamera/RollWhileFlying".into(),
217
+
values: vec![ ParameterType::Boolean(true) ]
218
+
},
219
+
OSCMessage {
220
+
address: "/usercamera/OrientationIsLandscape".into(),
221
+
values: vec![ ParameterType::Boolean(true) ]
222
+
},
223
+
224
+
OSCMessage {
225
+
address: "/usercamera/Zoom".into(),
226
+
values: vec![ ParameterType::Float(0.0) ]
227
+
},
228
+
OSCMessage {
229
+
address: "/usercamera/Exposure".into(),
230
+
values: vec![ ParameterType::Float(0.0) ]
231
+
},
232
+
OSCMessage {
233
+
address: "/usercamera/Aperture".into(),
234
+
values: vec![ ParameterType::Float(0.0) ]
235
+
},
236
+
OSCMessage {
237
+
address: "/usercamera/Hue".into(),
238
+
values: vec![ ParameterType::Float(0.0) ]
239
+
},
240
+
OSCMessage {
241
+
address: "/usercamera/Saturation".into(),
242
+
values: vec![ ParameterType::Float(0.0) ]
243
+
},
244
+
OSCMessage {
245
+
address: "/usercamera/Lightness".into(),
246
+
values: vec![ ParameterType::Float(0.0) ]
247
+
},
248
+
OSCMessage {
249
+
address: "/usercamera/LookAtMeXOffset".into(),
250
+
values: vec![ ParameterType::Float(0.0) ]
251
+
},
252
+
OSCMessage {
253
+
address: "/usercamera/LookAtMeYOffset".into(),
254
+
values: vec![ ParameterType::Float(0.0) ]
255
+
},
256
+
OSCMessage {
257
+
address: "/usercamera/FlySpeed".into(),
258
+
values: vec![ ParameterType::Float(0.0) ]
259
+
},
260
+
OSCMessage {
261
+
address: "/usercamera/TurnSpeed".into(),
262
+
values: vec![ ParameterType::Float(0.0) ]
263
+
},
264
+
OSCMessage {
265
+
address: "/usercamera/SmoothStrength".into(),
266
+
values: vec![ ParameterType::Float(0.0) ]
267
+
},
268
+
OSCMessage {
269
+
address: "/usercamera/PhotoRate".into(),
270
+
values: vec![ ParameterType::Float(0.0) ]
271
+
},
272
+
OSCMessage {
273
+
address: "/usercamera/Duration".into(),
274
+
values: vec![ ParameterType::Float(0.0) ]
275
+
},
276
+
277
+
// Trackers
278
+
OSCMessage {
279
+
address: "/tracking/vrsystem/head/pose".into(),
280
+
values: vec![
281
+
ParameterType::Float(0.0),
282
+
ParameterType::Float(0.0),
283
+
ParameterType::Float(0.0),
284
+
285
+
ParameterType::Float(0.0),
286
+
ParameterType::Float(0.0),
287
+
ParameterType::Float(0.0),
288
+
]
289
+
},
290
+
OSCMessage {
291
+
address: "/tracking/vrsystem/rightwrist/pose".into(),
292
+
values: vec![
293
+
ParameterType::Float(0.0),
294
+
ParameterType::Float(0.0),
295
+
ParameterType::Float(0.0),
296
+
297
+
ParameterType::Float(0.0),
298
+
ParameterType::Float(0.0),
299
+
ParameterType::Float(0.0),
300
+
]
301
+
},
302
+
OSCMessage {
303
+
address: "/tracking/vrsystem/leftwrist/pose".into(),
304
+
values: vec![
305
+
ParameterType::Float(0.0),
306
+
ParameterType::Float(0.0),
307
+
ParameterType::Float(0.0),
308
+
309
+
ParameterType::Float(0.0),
310
+
ParameterType::Float(0.0),
311
+
ParameterType::Float(0.0),
312
+
]
313
+
},
314
+
]
315
+
}