From d3d8a04dcdb032afbd7fb12f948241a90f38880b Mon Sep 17 00:00:00 2001 From: Mike Mulshine Date: Fri, 11 Oct 2024 18:03:55 -0700 Subject: [PATCH 01/13] Added code. --- dist/Gyro.d.ts | 69 +++++ dist/Gyro.js | 120 +++++++++ dist/gyroCk.d.ts | 3 + dist/gyroCk.js | 94 +++++++ dist/index.d.ts | 3 +- dist/index.js | 3 +- package-lock.json | 7 +- src/Gyro.ts | 143 ++++++++++ src/gyroCk.ts | 96 +++++++ src/index.ts | 3 +- src/wc-bundle.js | 197 +++++++++++++- test/chuckTest.js | 595 +++++++++++++++++++++-------------------- test/testFiles/gyro.ck | 37 +++ 13 files changed, 1072 insertions(+), 298 deletions(-) create mode 100644 dist/Gyro.d.ts create mode 100644 dist/Gyro.js create mode 100644 dist/gyroCk.d.ts create mode 100644 dist/gyroCk.js create mode 100644 src/Gyro.ts create mode 100644 src/gyroCk.ts create mode 100644 test/testFiles/gyro.ck diff --git a/dist/Gyro.d.ts b/dist/Gyro.d.ts new file mode 100644 index 0000000..cca8df8 --- /dev/null +++ b/dist/Gyro.d.ts @@ -0,0 +1,69 @@ +import Chuck from "./Chuck"; +/** + * Introducing HID (Human Interface Device) support for WebChucK. HID wraps + * JavaScript mouse/keyboard event listeners enabling mouse and keyboard + * communication with the native {@link https://chuck.stanford.edu/doc/reference/io.html#Hid | HID} + * class in ChucK. + * + * To get started with HID: + * @example + * ```ts + * import { Chuck, HID } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + */ +export default class Gyro { + private theChuck; + private _gyroActive; + private boundHandleOrientation; + /** @internal */ + constructor(theChuck: Chuck); + /** + * Initialize HID functionality in your WebChucK instance. + * This adds a `Hid` and `HidMsg` class to the ChucK Virtual Machine (VM). + * Mouse and keyboard event listeners are added if `enableMouse` and `enableKeyboard` are true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck, false, true); // Initialize HID, no mouse, only keyboard + * ``` + * @param theChuck WebChucK instance + * @param enableMouse boolean to enable mouse HID + * @param enableKeyboard boolean to enable keyboard HID + */ + static init(theChuck: Chuck, enableGyro?: boolean): Promise; + /** + * @internal + * Check if gyro is active + */ + gyroActive(): Promise; + /** + * Enable Mouse HID Javascript event listeners for HID. + * Adds a mousemove, mousedown, mouseup, and wheel listener to the document. + * This will also disable the context menu on right click. + * @example + * ```ts + * // If mouse HID is not yet enabled + * hid.enableMouse(); + * ``` + */ + enableGyro(): void; + /** + * Disable Mouse HID Javascript event listeners + * @example + * ```ts + * // If mouse HID is enabled + * hid.disableMouse(); + * ``` + */ + disableGyro(): void; + /** @internal */ + private handleOrientation; +} diff --git a/dist/Gyro.js b/dist/Gyro.js new file mode 100644 index 0000000..6eec40a --- /dev/null +++ b/dist/Gyro.js @@ -0,0 +1,120 @@ +import { Gyro_ck, GyroMsg_ck } from "./gyroCk"; +//TODO: Update the latest mouse.ck and kb.ck files +/** + * Introducing HID (Human Interface Device) support for WebChucK. HID wraps + * JavaScript mouse/keyboard event listeners enabling mouse and keyboard + * communication with the native {@link https://chuck.stanford.edu/doc/reference/io.html#Hid | HID} + * class in ChucK. + * + * To get started with HID: + * @example + * ```ts + * import { Chuck, HID } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + */ +export default class Gyro { + /** @internal */ + constructor(theChuck) { + this._gyroActive = false; + // Initialize members + this.theChuck = theChuck; + this.boundHandleOrientation = this.handleOrientation.bind(this); + } + /** + * Initialize HID functionality in your WebChucK instance. + * This adds a `Hid` and `HidMsg` class to the ChucK Virtual Machine (VM). + * Mouse and keyboard event listeners are added if `enableMouse` and `enableKeyboard` are true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck, false, true); // Initialize HID, no mouse, only keyboard + * ``` + * @param theChuck WebChucK instance + * @param enableMouse boolean to enable mouse HID + * @param enableKeyboard boolean to enable keyboard HID + */ + static async init(theChuck, enableGyro = true) { + const gyro = new Gyro(theChuck); + // Add Gyro and GyroMsg classes to ChucK VM + await gyro.theChuck.runCode(GyroMsg_ck); + await gyro.theChuck.runCode(Gyro_ck); + // Enable mouse and keyboard + if (enableGyro) { + if (typeof DeviceOrientationEvent.requestPermission === 'function') { + DeviceOrientationEvent.requestPermission(); + gyro.enableGyro(); + } + else { + console.log("No gyroscope available."); + } + } + return gyro; + } + /** + * @internal + * Check if gyro is active + */ + async gyroActive() { + const x = await this.theChuck.getInt("_gyroActive"); + this._gyroActive = x == 1; + } + /** + * Enable Mouse HID Javascript event listeners for HID. + * Adds a mousemove, mousedown, mouseup, and wheel listener to the document. + * This will also disable the context menu on right click. + * @example + * ```ts + * // If mouse HID is not yet enabled + * hid.enableMouse(); + * ``` + */ + enableGyro() { + //document.addEventListener("reading", this.boundHandleGyroReading); + window.addEventListener("deviceorientation", this.boundHandleOrientation); + } + /** + * Disable Mouse HID Javascript event listeners + * @example + * ```ts + * // If mouse HID is enabled + * hid.disableMouse(); + * ``` + */ + disableGyro() { + window.removeEventListener("deviceorientation", this.boundHandleOrientation); + } + //----------------------------------------- + // JAVASCRIPT HID EVENT HANDLERS + //----------------------------------------- + /** @internal */ + handleOrientation(event) { + this.gyroActive(); + if (this._gyroActive) { + this.theChuck.setFloat("_gyroX", event.alpha ? event.alpha : 0.0); + this.theChuck.setFloat("_gyroY", event.beta ? event.beta : 0.0); + this.theChuck.setFloat("_gyroZ", event.gamma ? event.gamma : 0.0); + this.theChuck.broadcastEvent("_gyroReading"); + } + } +} +//----------------------------------------------- +// HELPER FUNCTIONS +//----------------------------------------------- +/** + * Clamp a value between two numbers + * @param val value to clamp + * @param min min value + * @param max max value + * @returns clamped value + */ +function clamp(val, min, max) { + return Math.min(Math.max(val, min), max); +} diff --git a/dist/gyroCk.d.ts b/dist/gyroCk.d.ts new file mode 100644 index 0000000..1642eaf --- /dev/null +++ b/dist/gyroCk.d.ts @@ -0,0 +1,3 @@ +declare const GyroMsg_ck = "\npublic class GyroMsg {\n float gyroX;\n float gyroY;\n float gyroZ;\n\n function float getGyroX() {\n return gyroX;\n }\n\n function float getGyroY() {\n return gyroY;\n }\n\n function float getGyroZ() {\n return gyroZ;\n }\n\n function void _copy(GyroMsg localMsg) {\n localMsg.gyroX => gyroX;\n localMsg.gyroY => gyroY;\n localMsg.gyroZ => gyroZ;\n }\n}\n"; +declare const Gyro_ck = "\nglobal Event _gyroReading;\nglobal int _gyroActive;\n\nglobal float _gyroX;\nglobal float _gyroY;\nglobal float _gyroZ;\n\npublic class Gyro extends Event {\n\n 0 => int isGyroOpen;\n 0 => int active;\n\n string deviceName; \n\n // GyroMsg Queue\n GyroMsg _gyroMsgQueue[0];\n\n function string name() {\n return deviceName;\n }\n\n function int openGyro(int num) {\n if (num < 0) {\n false => active;\n } else {\n \"js gyro\" => deviceName;\n true => active;\n }\n active => isGyroOpen => _gyroActive;\n spork ~ _gyroListener();\n return active;\n }\n\n\n // Pop the first GyroMsg from the queue\n // Write it to msg and return 1\n function int recv(GyroMsg msg) {\n // is empty\n if (_gyroMsgQueue.size() <= 0) {\n return 0;\n }\n\n // pop the first GyroMsg to msg, return true\n _gyroMsgQueue[0] @=> GyroMsg localMsg;\n msg._copy(localMsg); \n _gyroMsgQueue.popFront();\n return 1;\n }\n\n // Gyro Listener\n // Get variables from JS and write to the GyroMsg \n function void _gyroListener() {\n GyroMsg @ msg;\n while(true){\n new GyroMsg @=> msg;\n _gyroReading => now;\n\n _gyroX => msg.gyroX;\n _gyroY => msg.gyroY;\n _gyroZ => msg.gyroZ;\n\n _gyroMsgQueue << msg;\n this.broadcast();\n }\n }\n}\n"; +export { GyroMsg_ck, Gyro_ck }; diff --git a/dist/gyroCk.js b/dist/gyroCk.js new file mode 100644 index 0000000..66b57c3 --- /dev/null +++ b/dist/gyroCk.js @@ -0,0 +1,94 @@ +const GyroMsg_ck = ` +public class GyroMsg { + float gyroX; + float gyroY; + float gyroZ; + + function float getGyroX() { + return gyroX; + } + + function float getGyroY() { + return gyroY; + } + + function float getGyroZ() { + return gyroZ; + } + + function void _copy(GyroMsg localMsg) { + localMsg.gyroX => gyroX; + localMsg.gyroY => gyroY; + localMsg.gyroZ => gyroZ; + } +} +`; +const Gyro_ck = ` +global Event _gyroReading; +global int _gyroActive; + +global float _gyroX; +global float _gyroY; +global float _gyroZ; + +public class Gyro extends Event { + + 0 => int isGyroOpen; + 0 => int active; + + string deviceName; + + // GyroMsg Queue + GyroMsg _gyroMsgQueue[0]; + + function string name() { + return deviceName; + } + + function int openGyro(int num) { + if (num < 0) { + false => active; + } else { + "js gyro" => deviceName; + true => active; + } + active => isGyroOpen => _gyroActive; + spork ~ _gyroListener(); + return active; + } + + + // Pop the first GyroMsg from the queue + // Write it to msg and return 1 + function int recv(GyroMsg msg) { + // is empty + if (_gyroMsgQueue.size() <= 0) { + return 0; + } + + // pop the first GyroMsg to msg, return true + _gyroMsgQueue[0] @=> GyroMsg localMsg; + msg._copy(localMsg); + _gyroMsgQueue.popFront(); + return 1; + } + + // Gyro Listener + // Get variables from JS and write to the GyroMsg + function void _gyroListener() { + GyroMsg @ msg; + while(true){ + new GyroMsg @=> msg; + _gyroReading => now; + + _gyroX => msg.gyroX; + _gyroY => msg.gyroY; + _gyroZ => msg.gyroZ; + + _gyroMsgQueue << msg; + this.broadcast(); + } + } +} +`; +export { GyroMsg_ck, Gyro_ck }; diff --git a/dist/index.d.ts b/dist/index.d.ts index 74dbde6..8276897 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,4 +1,5 @@ import DeferredPromise from "./DeferredPromise"; import Chuck from "./Chuck"; import HID from "./Hid"; -export { Chuck, HID, DeferredPromise }; +import Gyro from "./Gyro"; +export { Chuck, HID, Gyro, DeferredPromise }; diff --git a/dist/index.js b/dist/index.js index 74dbde6..8276897 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,4 +1,5 @@ import DeferredPromise from "./DeferredPromise"; import Chuck from "./Chuck"; import HID from "./Hid"; -export { Chuck, HID, DeferredPromise }; +import Gyro from "./Gyro"; +export { Chuck, HID, Gyro, DeferredPromise }; diff --git a/package-lock.json b/package-lock.json index a1f608f..95c7378 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,10 +112,11 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", "dev": true, + "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, diff --git a/src/Gyro.ts b/src/Gyro.ts new file mode 100644 index 0000000..da7bfde --- /dev/null +++ b/src/Gyro.ts @@ -0,0 +1,143 @@ +import Chuck from "./Chuck"; +import { Gyro_ck, GyroMsg_ck } from "./gyroCk"; + + +//TODO: Update the latest mouse.ck and kb.ck files +/** + * Introducing HID (Human Interface Device) support for WebChucK. HID wraps + * JavaScript mouse/keyboard event listeners enabling mouse and keyboard + * communication with the native {@link https://chuck.stanford.edu/doc/reference/io.html#Hid | HID} + * class in ChucK. + * + * To get started with HID: + * @example + * ```ts + * import { Chuck, HID } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + */ +export default class Gyro { + // Private members + private theChuck: Chuck; + private _gyroActive: boolean = false; + + private boundHandleOrientation; + + + /** @internal */ + constructor(theChuck: Chuck) { + // Initialize members + this.theChuck = theChuck; + + this.boundHandleOrientation = this.handleOrientation.bind(this); + } + + /** + * Initialize HID functionality in your WebChucK instance. + * This adds a `Hid` and `HidMsg` class to the ChucK Virtual Machine (VM). + * Mouse and keyboard event listeners are added if `enableMouse` and `enableKeyboard` are true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck, false, true); // Initialize HID, no mouse, only keyboard + * ``` + * @param theChuck WebChucK instance + * @param enableMouse boolean to enable mouse HID + * @param enableKeyboard boolean to enable keyboard HID + */ + static async init( + theChuck: Chuck, + enableGyro: boolean = true + ): Promise { + const gyro = new Gyro(theChuck); + // Add Gyro and GyroMsg classes to ChucK VM + await gyro.theChuck.runCode(GyroMsg_ck); + await gyro.theChuck.runCode(Gyro_ck); + + // Enable mouse and keyboard + if (enableGyro) { + if (typeof DeviceOrientationEvent.requestPermission === 'function') { + DeviceOrientationEvent.requestPermission(); + gyro.enableGyro(); + } else { + console.log("No gyroscope available.") + } + } + return gyro; + } + + /** + * @internal + * Check if gyro is active + */ + async gyroActive() { + const x = await this.theChuck.getInt("_gyroActive"); + this._gyroActive = x == 1; + } + + /** + * Enable Mouse HID Javascript event listeners for HID. + * Adds a mousemove, mousedown, mouseup, and wheel listener to the document. + * This will also disable the context menu on right click. + * @example + * ```ts + * // If mouse HID is not yet enabled + * hid.enableMouse(); + * ``` + */ + enableGyro() { + //document.addEventListener("reading", this.boundHandleGyroReading); + window.addEventListener("deviceorientation", this.boundHandleOrientation); + + } + + /** + * Disable Mouse HID Javascript event listeners + * @example + * ```ts + * // If mouse HID is enabled + * hid.disableMouse(); + * ``` + */ + disableGyro() { + window.removeEventListener("deviceorientation", this.boundHandleOrientation); + } + + + //----------------------------------------- + // JAVASCRIPT HID EVENT HANDLERS + //----------------------------------------- + + + /** @internal */ + private handleOrientation(event: DeviceOrientationEvent) { + this.gyroActive(); + if (this._gyroActive) { + this.theChuck.setFloat("_gyroX", event.alpha ? event.alpha : 0.0); + this.theChuck.setFloat("_gyroY", event.beta ? event.beta : 0.0); + this.theChuck.setFloat("_gyroZ", event.gamma ? event.gamma : 0.0); + this.theChuck.broadcastEvent("_gyroReading"); + } + } +} + +//----------------------------------------------- +// HELPER FUNCTIONS +//----------------------------------------------- +/** + * Clamp a value between two numbers + * @param val value to clamp + * @param min min value + * @param max max value + * @returns clamped value + */ +function clamp(val: number, min: number, max: number): number { + return Math.min(Math.max(val, min), max); +} diff --git a/src/gyroCk.ts b/src/gyroCk.ts new file mode 100644 index 0000000..ec63dfd --- /dev/null +++ b/src/gyroCk.ts @@ -0,0 +1,96 @@ +const GyroMsg_ck = ` +public class GyroMsg { + float gyroX; + float gyroY; + float gyroZ; + + function float getGyroX() { + return gyroX; + } + + function float getGyroY() { + return gyroY; + } + + function float getGyroZ() { + return gyroZ; + } + + function void _copy(GyroMsg localMsg) { + localMsg.gyroX => gyroX; + localMsg.gyroY => gyroY; + localMsg.gyroZ => gyroZ; + } +} +`; + +const Gyro_ck = ` +global Event _gyroReading; +global int _gyroActive; + +global float _gyroX; +global float _gyroY; +global float _gyroZ; + +public class Gyro extends Event { + + 0 => int isGyroOpen; + 0 => int active; + + string deviceName; + + // GyroMsg Queue + GyroMsg _gyroMsgQueue[0]; + + function string name() { + return deviceName; + } + + function int openGyro(int num) { + if (num < 0) { + false => active; + } else { + "js gyro" => deviceName; + true => active; + } + active => isGyroOpen => _gyroActive; + spork ~ _gyroListener(); + return active; + } + + + // Pop the first GyroMsg from the queue + // Write it to msg and return 1 + function int recv(GyroMsg msg) { + // is empty + if (_gyroMsgQueue.size() <= 0) { + return 0; + } + + // pop the first GyroMsg to msg, return true + _gyroMsgQueue[0] @=> GyroMsg localMsg; + msg._copy(localMsg); + _gyroMsgQueue.popFront(); + return 1; + } + + // Gyro Listener + // Get variables from JS and write to the GyroMsg + function void _gyroListener() { + GyroMsg @ msg; + while(true){ + new GyroMsg @=> msg; + _gyroReading => now; + + _gyroX => msg.gyroX; + _gyroY => msg.gyroY; + _gyroZ => msg.gyroZ; + + _gyroMsgQueue << msg; + this.broadcast(); + } + } +} +`; + +export { GyroMsg_ck, Gyro_ck }; diff --git a/src/index.ts b/src/index.ts index 1b31dd3..892a3c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ import DeferredPromise from "./DeferredPromise"; import Chuck from "./Chuck"; import HID from "./Hid"; +import Gyro from "./Gyro"; -export { Chuck, HID, DeferredPromise }; +export { Chuck, HID, Gyro, DeferredPromise }; diff --git a/src/wc-bundle.js b/src/wc-bundle.js index 0ab0a33..c6aea41 100644 --- a/src/wc-bundle.js +++ b/src/wc-bundle.js @@ -1516,4 +1516,199 @@ function clamp(val, min, max) { return Math.min(Math.max(val, min), max); } -export { Chuck, DeferredPromise, HID }; +const GyroMsg_ck = ` +public class GyroMsg { + float gyroX; + float gyroY; + float gyroZ; + + function float getGyroX() { + return gyroX; + } + + function float getGyroY() { + return gyroY; + } + + function float getGyroZ() { + return gyroZ; + } + + function void _copy(GyroMsg localMsg) { + localMsg.gyroX => gyroX; + localMsg.gyroY => gyroY; + localMsg.gyroZ => gyroZ; + } +} +`; +const Gyro_ck = ` +global Event _gyroReading; +global int _gyroActive; + +global float _gyroX; +global float _gyroY; +global float _gyroZ; + +public class Gyro extends Event { + + 0 => int isGyroOpen; + 0 => int active; + + string deviceName; + + // GyroMsg Queue + GyroMsg _gyroMsgQueue[0]; + + function string name() { + return deviceName; + } + + function int openGyro(int num) { + if (num < 0) { + false => active; + } else { + "js gyro" => deviceName; + true => active; + } + active => isGyroOpen => _gyroActive; + spork ~ _gyroListener(); + return active; + } + + + // Pop the first GyroMsg from the queue + // Write it to msg and return 1 + function int recv(GyroMsg msg) { + // is empty + if (_gyroMsgQueue.size() <= 0) { + return 0; + } + + // pop the first GyroMsg to msg, return true + _gyroMsgQueue[0] @=> GyroMsg localMsg; + msg._copy(localMsg); + _gyroMsgQueue.popFront(); + return 1; + } + + // Gyro Listener + // Get variables from JS and write to the GyroMsg + function void _gyroListener() { + GyroMsg @ msg; + while(true){ + new GyroMsg @=> msg; + _gyroReading => now; + + _gyroX => msg.gyroX; + _gyroY => msg.gyroY; + _gyroZ => msg.gyroZ; + + _gyroMsgQueue << msg; + this.broadcast(); + } + } +} +`; + +//TODO: Update the latest mouse.ck and kb.ck files +/** + * Introducing HID (Human Interface Device) support for WebChucK. HID wraps + * JavaScript mouse/keyboard event listeners enabling mouse and keyboard + * communication with the native {@link https://chuck.stanford.edu/doc/reference/io.html#Hid | HID} + * class in ChucK. + * + * To get started with HID: + * @example + * ```ts + * import { Chuck, HID } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + */ +class Gyro { + /** @internal */ + constructor(theChuck) { + this._gyroActive = false; + // Initialize members + this.theChuck = theChuck; + this.boundHandleOrientation = this.handleOrientation.bind(this); + } + /** + * Initialize HID functionality in your WebChucK instance. + * This adds a `Hid` and `HidMsg` class to the ChucK Virtual Machine (VM). + * Mouse and keyboard event listeners are added if `enableMouse` and `enableKeyboard` are true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck, false, true); // Initialize HID, no mouse, only keyboard + * ``` + * @param theChuck WebChucK instance + * @param enableMouse boolean to enable mouse HID + * @param enableKeyboard boolean to enable keyboard HID + */ + static async init(theChuck, enableGyro = true) { + const gyro = new Gyro(theChuck); + // Add Gyro and GyroMsg classes to ChucK VM + await gyro.theChuck.runCode(GyroMsg_ck); + await gyro.theChuck.runCode(Gyro_ck); + // Enable mouse and keyboard + if (enableGyro) { + gyro.enableGyro(); + } + return gyro; + } + /** + * @internal + * Check if gyro is active + */ + async gyroActive() { + const x = await this.theChuck.getInt("_gyroActive"); + this._gyroActive = x == 1; + } + /** + * Enable Mouse HID Javascript event listeners for HID. + * Adds a mousemove, mousedown, mouseup, and wheel listener to the document. + * This will also disable the context menu on right click. + * @example + * ```ts + * // If mouse HID is not yet enabled + * hid.enableMouse(); + * ``` + */ + enableGyro() { + //document.addEventListener("reading", this.boundHandleGyroReading); + window.addEventListener("deviceorientation", this.boundHandleOrientation); + } + /** + * Disable Mouse HID Javascript event listeners + * @example + * ```ts + * // If mouse HID is enabled + * hid.disableMouse(); + * ``` + */ + disableGyro() { + window.removeEventListener("deviceorientation", this.boundHandleOrientation); + } + //----------------------------------------- + // JAVASCRIPT HID EVENT HANDLERS + //----------------------------------------- + /** @internal */ + handleOrientation(event) { + this.gyroActive(); + if (this._gyroActive) { + this.theChuck.setFloat("_gyroX", event.alpha ? event.alpha : 0.0); + this.theChuck.setFloat("_gyroY", event.beta ? event.beta : 0.0); + this.theChuck.setFloat("_gyroZ", event.gamma ? event.gamma : 0.0); + this.theChuck.broadcastEvent("_gyroReading"); + } + } +} + +export { Chuck, DeferredPromise, Gyro, HID }; diff --git a/test/chuckTest.js b/test/chuckTest.js index 5d3b20f..0facd5a 100644 --- a/test/chuckTest.js +++ b/test/chuckTest.js @@ -1,7 +1,7 @@ //======================================================================= // WebChucK Test Suite //======================================================================= -import { Chuck, HID } from '../src/wc-bundle.js'; +import { Chuck, HID, Gyro} from '../src/wc-bundle.js'; /** WebChucK Test Class */ class Test { @@ -64,308 +64,321 @@ class Test { //============================================================= const testSuite = [ - new Test(1, "[sound] Define a ChucK and runCode 220hz 0.5 second", async () => { + // new Test(1, "[sound] Define a ChucK and runCode 220hz 0.5 second", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 1); + // aChuck.chuckPrint = (output) => { + // outputBox.innerText = output; + // } + // aChuck.runCode(` + // SinOsc osc => dac; 0.5::second => now; <<< "PASSED", "" >>>; + // `); + // await new Promise(resolve => setTimeout(resolve, 750)); // wait 1.5x time + // return outputBox.innerText == "PASSED"; + // }), + + // new Test(2, "[sound] Preload a server chuck file, runFile, 440Hz 1 second", async () => { + // const aChuck = await Chuck.init([{ serverFilename: "./testFiles/test2.ck", virtualFilename: "test2.ck" }], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 2); + // aChuck.chuckPrint = (output) => { + // outputBox.innerText = output; + // } + // aChuck.runFile("test2.ck"); + // await new Promise(resolve => setTimeout(resolve, 1500)); + // return outputBox.innerText == "PASSED"; + // }), + + // new Test(3, "[sound] Dynamic load in a chuck file from URL, 880Hz 1 second", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 3); + // aChuck.chuckPrint = (output) => { + // outputBox.innerText = output; + // } + // await aChuck.loadFile("./testFiles/test3.ck"); + // aChuck.runFile("test3.ck"); + // await new Promise(resolve => setTimeout(resolve, 1500)); + // return outputBox.innerText == "PASSED"; + // }), + + // new Test(4, "[sound] Dynamic load in a kick wav file from URL", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 4); + // aChuck.chuckPrint = (output) => { + // outputBox.innerText = output; + // } + // await aChuck.loadFile("./testFiles/kick.wav"); + // aChuck.runCode(` + // SndBuf buf => dac; + // buf.read("kick.wav"); + // 1::second => now; + // <<< "KICKED", "" >>>; + // `); + // await new Promise(resolve => setTimeout(resolve, 1500)); + // return outputBox.innerText.includes("KICKED"); + // }), + + // new Test(5, "Sync test set/get int and float, webchuck and js", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 5); + // aChuck.runCode(` + // 1 => global int GLOBAL_INT; + // 1.0 => global float GLOBAL_FLOAT; + // `); + // await new Promise(resolve => setTimeout(resolve, 100)); + // let test1 = await aChuck.getInt("GLOBAL_INT") == 1 && await aChuck.getFloat("GLOBAL_FLOAT") == 1.0; + // outputBox.innerHTML += "Get OK " + test1 + "
"; + // aChuck.setInt("GLOBAL_INT", 2); + // aChuck.setFloat("GLOBAL_FLOAT", 2.0); + // await new Promise(resolve => setTimeout(resolve, 300)); + // let test2 = await aChuck.getInt("GLOBAL_INT") == 2 && await aChuck.getFloat("GLOBAL_FLOAT") == 2.0; + // outputBox.innerHTML += "Set OK " + test2 + "
"; + // return test1 && test2; + // }), + + // new Test(6, "Test shred management, add, remove, replace", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 6); + // aChuck.chuckPrint = (output) => { + // outputBox.innerHTML += output + "
"; + // } + // aChuck.runCode(` + // <<< "Running Shred 1", "" >>>; + // 1::week => now; + // `) + // aChuck.runCode(` + // <<< "Running Shred 2", "" >>>; + // 1::week => now; + // `) + // await new Promise(resolve => setTimeout(resolve, 100)); + // let removed = await aChuck.removeLastCode() == 2; + // await new Promise(resolve => setTimeout(resolve, 100)); + // let replaced = (await aChuck.replaceCode(`<<< "REPLACED Shred 1 with a new program\n", "" >>>; 1::second => now;`)).newShred == 1; + // await new Promise(resolve => setTimeout(resolve, 100)); + // let active = await aChuck.isShredActive(1); + // await new Promise(resolve => setTimeout(resolve, 100)); + // let removed2 = await aChuck.removeShred(1) == 1; + // await new Promise(resolve => setTimeout(resolve, 100)); + // return removed == true && + // replaced == true && + // active == true && + // removed2 == true; + // }), + + // new Test(7, "Test RunFileWithArgs", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 7); + // aChuck.chuckPrint = (output) => { + // outputBox.innerHTML += output + "
"; + // } + + // outputBox.innerHTML += "Passing in arguments: 1 2 foo" + "
"; + + // await aChuck.loadFile("./testFiles/test7.ck"); + // await aChuck.runFileWithArgs("test7.ck", "1:2:foo"); + // await new Promise(resolve => setTimeout(resolve, 200)); + + // return outputBox.innerText.includes("number of arguments: 3"); + // }), + + // new Test(8, "Test isShredActive", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 8); + // aChuck.chuckPrint = (output) => { + // outputBox.innerHTML += output + "
"; + // } + + // outputBox.innerHTML += "Starting shred 1 for 1 second" + "
"; + + // aChuck.runCode(`1::second => now;`); + // let test1; let test2; + // await new Promise(resolve => setTimeout(resolve, 100)); + // await aChuck.isShredActive(1).then((active) => { + // test1 = (active == 1); + // }); + // await new Promise(resolve => setTimeout(resolve, 1000)); + // await aChuck.isShredActive(1).then((active) => { + // test2 = (active == 0); + // }); + + // return test1 && test2; + // }), + + // new Test(9, "Machine.add/replace/remove/clear", async () => { + // const aChuck = await Chuck.init([ + // { serverFilename: "./testFiles/test9.ck", virtualFilename: "test9.ck" }, + // ], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 9); + // aChuck.chuckPrint = (output) => { + // outputBox.innerHTML += output + "
"; + // } + + // aChuck.runCode(` + // Machine.add("test9.ck") => int id; + // 200::ms => now; + // Machine.replace(id, "test9.ck"); + // 400::ms => now; + // Machine.remove(id); + // 200::ms => now; + // Machine.clearVM(); + // `) + // let test1; let test2; let test3; let test4; + // await new Promise(resolve => setTimeout(resolve, 100)); + // await aChuck.isShredActive(2).then((active) => { + // test1 = (active == 1); + // }); + // await new Promise(resolve => setTimeout(resolve, 200)); + // await aChuck.isShredActive(2).then((active) => { + // test2 = (active == 1); + // }); + // await new Promise(resolve => setTimeout(resolve, 400)); + // await aChuck.isShredActive(2).then((active) => { + // test3 = (active == 0); + // }); + // await new Promise(resolve => setTimeout(resolve, 200)); + // await aChuck.isShredActive(1).then((active) => { + // test4 = (active == 0); + // }); + // return test1 && test2 && test3 && test4; + // }), + + // new Test(10, "Chuck get Promise returns", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 10); + // print = (output) => { + // outputBox.innerHTML += output + "
"; + // } + // aChuck.chuckPrint = print; + + // aChuck.runCode(`2::second => now;`); + // aChuck.runCode(`2::second => now;`); + // let test = true; + // await new Promise(resolve => setTimeout(resolve, 50)); + // aChuck.isShredActive(1).then((active) => { + // print("isShredActive(1) returns: " + active); + // test &= active == 1; + // }); + // aChuck.removeShred(1).then((removed) => { + // print("removeShred(1) returns: " + removed); + // test &= removed == 1; + // }); + // aChuck.removeShred(-1).then((removed) => { + // print("removeShred(-1) returns: " + removed); + // test &= removed == 0; + // }).catch((err) => { + // print("removeShred(-1) throws: " + err); + // test &= err === "Remove code failed" + // }); + + // return test; + // }), + + // new Test(11, "[sound] WebChugin Test, ABSaturator", async () => { + // Chuck.loadChugin("./testFiles/ABSaturator.chug.wasm") + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 11); + // aChuck.chuckPrint = (output) => { + // outputBox.innerHTML += output + "
"; + // } + + // aChuck.runCode(` + // SinOsc osc => Delay d => ABSaturator sat => dac; + // 20 => sat.drive; + // 4 => sat.dcOffset; + // 0.5::second => now; + // <<< "PASSED", "" >>>; + // `); + // await new Promise(resolve => setTimeout(resolve, 750)); + + // return outputBox.innerText.includes("PASSED"); + // }), + + // new Test(12, "HID - mouse", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 12); + // aChuck.chuckPrint = (output) => { + // outputBox.innerHTML = output + "
"; + // } + + // let hid = await HID.init(aChuck); + // await aChuck.loadFile("./testFiles/mouse.ck") + // aChuck.runFile("mouse.ck") + + // return true; + // }), + + // new Test(13, "HID - keyboard", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 13); + // aChuck.chuckPrint = (output) => { + // outputBox.innerHTML = output + "
"; + // } + // let hid = await HID.init(aChuck); + // await aChuck.loadFile("./testFiles/kb.ck") + // aChuck.runFile("kb.ck") + + // return true; + // }), + + // new Test(14, "Use Offline Context - 10 seconds", async () => { + // const offlineContext = new OfflineAudioContext(2, 44100 * 10, 44100); + // const aChuck = await Chuck.init([], offlineContext, undefined, "../src/"); + // aChuck.connect(offlineContext.destination); + // const outputBox = document.getElementById("output-" + 14); + // aChuck.chuckPrint = (output) => { + // outputBox.innerHTML = output + "
"; + // } + + // aChuck.runCode(`SinOsc osc => dac; 10::second => now; <<< "PASSED", "" >>>;`); + // await offlineContext.startRendering(); + // return outputBox.innerText.includes("PASSED"); + // }), + + // new Test(15, "Use Local Audio Context, unconnected node", async () => { + // const audioContext = new AudioContext(); + // audioContext.suspend(); + // const aChuck = await Chuck.init([], audioContext, undefined, "../src/"); + // audioContext.resume(); + // const outputBox = document.getElementById("output-" + 15); + // aChuck.chuckPrint = (output) => { + // outputBox.innerHTML = output + "
"; + // } + + // aChuck.runCode(`<<< "PASSED", "" >>>;`); + // await new Promise(resolve => setTimeout(resolve, 750)); + // return outputBox.innerText.includes("PASSED"); + // }), + + new Test(1, "Gyro", async () => { const aChuck = await Chuck.init([], undefined, undefined, "../src/"); const outputBox = document.getElementById("output-" + 1); aChuck.chuckPrint = (output) => { - outputBox.innerText = output; + outputBox.innerHTML = output + "
"; // += for additive } - aChuck.runCode(` - SinOsc osc => dac; 0.5::second => now; <<< "PASSED", "" >>>; - `); - await new Promise(resolve => setTimeout(resolve, 750)); // wait 1.5x time - return outputBox.innerText == "PASSED"; - }), - - new Test(2, "[sound] Preload a server chuck file, runFile, 440Hz 1 second", async () => { - const aChuck = await Chuck.init([{ serverFilename: "./testFiles/test2.ck", virtualFilename: "test2.ck" }], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 2); - aChuck.chuckPrint = (output) => { - outputBox.innerText = output; - } - aChuck.runFile("test2.ck"); - await new Promise(resolve => setTimeout(resolve, 1500)); - return outputBox.innerText == "PASSED"; - }), - - new Test(3, "[sound] Dynamic load in a chuck file from URL, 880Hz 1 second", async () => { - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 3); - aChuck.chuckPrint = (output) => { - outputBox.innerText = output; - } - await aChuck.loadFile("./testFiles/test3.ck"); - aChuck.runFile("test3.ck"); - await new Promise(resolve => setTimeout(resolve, 1500)); - return outputBox.innerText == "PASSED"; - }), - - new Test(4, "[sound] Dynamic load in a kick wav file from URL", async () => { - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 4); - aChuck.chuckPrint = (output) => { - outputBox.innerText = output; - } - await aChuck.loadFile("./testFiles/kick.wav"); - aChuck.runCode(` - SndBuf buf => dac; - buf.read("kick.wav"); - 1::second => now; - <<< "KICKED", "" >>>; - `); - await new Promise(resolve => setTimeout(resolve, 1500)); - return outputBox.innerText.includes("KICKED"); - }), - - new Test(5, "Sync test set/get int and float, webchuck and js", async () => { - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 5); - aChuck.runCode(` - 1 => global int GLOBAL_INT; - 1.0 => global float GLOBAL_FLOAT; - `); - await new Promise(resolve => setTimeout(resolve, 100)); - let test1 = await aChuck.getInt("GLOBAL_INT") == 1 && await aChuck.getFloat("GLOBAL_FLOAT") == 1.0; - outputBox.innerHTML += "Get OK " + test1 + "
"; - aChuck.setInt("GLOBAL_INT", 2); - aChuck.setFloat("GLOBAL_FLOAT", 2.0); - await new Promise(resolve => setTimeout(resolve, 300)); - let test2 = await aChuck.getInt("GLOBAL_INT") == 2 && await aChuck.getFloat("GLOBAL_FLOAT") == 2.0; - outputBox.innerHTML += "Set OK " + test2 + "
"; - return test1 && test2; - }), - - new Test(6, "Test shred management, add, remove, replace", async () => { - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 6); - aChuck.chuckPrint = (output) => { - outputBox.innerHTML += output + "
"; - } - aChuck.runCode(` - <<< "Running Shred 1", "" >>>; - 1::week => now; - `) - aChuck.runCode(` - <<< "Running Shred 2", "" >>>; - 1::week => now; - `) - await new Promise(resolve => setTimeout(resolve, 100)); - let removed = await aChuck.removeLastCode() == 2; - await new Promise(resolve => setTimeout(resolve, 100)); - let replaced = (await aChuck.replaceCode(`<<< "REPLACED Shred 1 with a new program\n", "" >>>; 1::second => now;`)).newShred == 1; - await new Promise(resolve => setTimeout(resolve, 100)); - let active = await aChuck.isShredActive(1); - await new Promise(resolve => setTimeout(resolve, 100)); - let removed2 = await aChuck.removeShred(1) == 1; - await new Promise(resolve => setTimeout(resolve, 100)); - return removed == true && - replaced == true && - active == true && - removed2 == true; - }), - - new Test(7, "Test RunFileWithArgs", async () => { - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 7); - aChuck.chuckPrint = (output) => { - outputBox.innerHTML += output + "
"; - } - - outputBox.innerHTML += "Passing in arguments: 1 2 foo" + "
"; - - await aChuck.loadFile("./testFiles/test7.ck"); - await aChuck.runFileWithArgs("test7.ck", "1:2:foo"); - await new Promise(resolve => setTimeout(resolve, 200)); - - return outputBox.innerText.includes("number of arguments: 3"); - }), - - new Test(8, "Test isShredActive", async () => { - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 8); - aChuck.chuckPrint = (output) => { - outputBox.innerHTML += output + "
"; - } - - outputBox.innerHTML += "Starting shred 1 for 1 second" + "
"; - - aChuck.runCode(`1::second => now;`); - let test1; let test2; - await new Promise(resolve => setTimeout(resolve, 100)); - await aChuck.isShredActive(1).then((active) => { - test1 = (active == 1); - }); - await new Promise(resolve => setTimeout(resolve, 1000)); - await aChuck.isShredActive(1).then((active) => { - test2 = (active == 0); - }); - - return test1 && test2; - }), - - new Test(9, "Machine.add/replace/remove/clear", async () => { - const aChuck = await Chuck.init([ - { serverFilename: "./testFiles/test9.ck", virtualFilename: "test9.ck" }, - ], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 9); - aChuck.chuckPrint = (output) => { - outputBox.innerHTML += output + "
"; - } - - aChuck.runCode(` - Machine.add("test9.ck") => int id; - 200::ms => now; - Machine.replace(id, "test9.ck"); - 400::ms => now; - Machine.remove(id); - 200::ms => now; - Machine.clearVM(); - `) - let test1; let test2; let test3; let test4; - await new Promise(resolve => setTimeout(resolve, 100)); - await aChuck.isShredActive(2).then((active) => { - test1 = (active == 1); - }); - await new Promise(resolve => setTimeout(resolve, 200)); - await aChuck.isShredActive(2).then((active) => { - test2 = (active == 1); - }); - await new Promise(resolve => setTimeout(resolve, 400)); - await aChuck.isShredActive(2).then((active) => { - test3 = (active == 0); - }); - await new Promise(resolve => setTimeout(resolve, 200)); - await aChuck.isShredActive(1).then((active) => { - test4 = (active == 0); - }); - return test1 && test2 && test3 && test4; - }), - - new Test(10, "Chuck get Promise returns", async () => { - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 10); - print = (output) => { - outputBox.innerHTML += output + "
"; - } - aChuck.chuckPrint = print; - - aChuck.runCode(`2::second => now;`); - aChuck.runCode(`2::second => now;`); - let test = true; - await new Promise(resolve => setTimeout(resolve, 50)); - aChuck.isShredActive(1).then((active) => { - print("isShredActive(1) returns: " + active); - test &= active == 1; - }); - aChuck.removeShred(1).then((removed) => { - print("removeShred(1) returns: " + removed); - test &= removed == 1; - }); - aChuck.removeShred(-1).then((removed) => { - print("removeShred(-1) returns: " + removed); - test &= removed == 0; - }).catch((err) => { - print("removeShred(-1) throws: " + err); - test &= err === "Remove code failed" - }); - return test; - }), - - new Test(11, "[sound] WebChugin Test, ABSaturator", async () => { - Chuck.loadChugin("./testFiles/ABSaturator.chug.wasm") - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 11); - aChuck.chuckPrint = (output) => { - outputBox.innerHTML += output + "
"; - } - - aChuck.runCode(` - SinOsc osc => Delay d => ABSaturator sat => dac; - 20 => sat.drive; - 4 => sat.dcOffset; - 0.5::second => now; - <<< "PASSED", "" >>>; - `); - await new Promise(resolve => setTimeout(resolve, 750)); - - return outputBox.innerText.includes("PASSED"); - }), - - new Test(12, "HID - mouse", async () => { - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 12); - aChuck.chuckPrint = (output) => { - outputBox.innerHTML = output + "
"; - } - - let hid = await HID.init(aChuck); - await aChuck.loadFile("./testFiles/mouse.ck") - aChuck.runFile("mouse.ck") + let gyro = await Gyro.init(aChuck); + await aChuck.loadFile("./testFiles/gyro.ck"); + aChuck.runFile("gyro.ck"); return true; }), - new Test(13, "HID - keyboard", async () => { - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 13); - aChuck.chuckPrint = (output) => { - outputBox.innerHTML = output + "
"; - } - - let hid = await HID.init(aChuck); - await aChuck.loadFile("./testFiles/kb.ck") - aChuck.runFile("kb.ck") - - return true; - }), - - new Test(14, "Use Offline Context - 10 seconds", async () => { - const offlineContext = new OfflineAudioContext(2, 44100 * 10, 44100); - const aChuck = await Chuck.init([], offlineContext, undefined, "../src/"); - aChuck.connect(offlineContext.destination); - const outputBox = document.getElementById("output-" + 14); - aChuck.chuckPrint = (output) => { - outputBox.innerHTML = output + "
"; - } - - aChuck.runCode(`SinOsc osc => dac; 10::second => now; <<< "PASSED", "" >>>;`); - await offlineContext.startRendering(); - return outputBox.innerText.includes("PASSED"); - }), - - new Test(15, "Use Local Audio Context, unconnected node", async () => { - const audioContext = new AudioContext(); - audioContext.suspend(); - const aChuck = await Chuck.init([], audioContext, undefined, "../src/"); - audioContext.resume(); - const outputBox = document.getElementById("output-" + 15); - aChuck.chuckPrint = (output) => { - outputBox.innerHTML = output + "
"; - } - aChuck.runCode(`<<< "PASSED", "" >>>;`); - await new Promise(resolve => setTimeout(resolve, 750)); - return outputBox.innerText.includes("PASSED"); - }), + // new Test(99, "Chuck VM operations and parameters", async () => { + // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + // const outputBox = document.getElementById("output-" + 99); + // print = (output) => { + // outputBox.innerHTML += output + "
"; + // } + // aChuck.chuckPrint = print; + // print(await aChuck.getParamString("VERSION")); + // print("sample rate: " + await aChuck.getParamInt("SAMPLE_RATE")); + // print("input channels: " + await aChuck.getParamInt("INPUT_CHANNELS")); + // print("output channels: " + await aChuck.getParamInt("OUTPUT_CHANNELS")); + // print("now: " + await aChuck.now()); - new Test(99, "Chuck VM operations and parameters", async () => { - const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - const outputBox = document.getElementById("output-" + 99); - print = (output) => { - outputBox.innerHTML += output + "
"; - } - aChuck.chuckPrint = print; - - print(await aChuck.getParamString("VERSION")); - print("sample rate: " + await aChuck.getParamInt("SAMPLE_RATE")); - print("input channels: " + await aChuck.getParamInt("INPUT_CHANNELS")); - print("output channels: " + await aChuck.getParamInt("OUTPUT_CHANNELS")); - print("now: " + await aChuck.now()); - - return outputBox.innerText !== ""; - }), + // return outputBox.innerText !== ""; + // }), ] //============================================================= diff --git a/test/testFiles/gyro.ck b/test/testFiles/gyro.ck new file mode 100644 index 0000000..c8d19e4 --- /dev/null +++ b/test/testFiles/gyro.ck @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// name: gyro.ck +// desc: initialize Gyroscope (on web, on mobile) and read x, y, z values +// +// author: Mike Mulshine +//----------------------------------------------------------------------------- + + +Gyro gy; +GyroMsg msg; + +SinOsc osc => dac; +0.1 => osc.gain; + +0 => int device; + +// open gyro +if( !gy.openGyro( device ) ) me.exit(); +<<< "gyro '" + gy.name() + "' ready", "" >>>; + +// infinite event loop +while( true ) +{ + // wait on gyro event + gy => now; + + // get one or more messages + while( gy.recv( msg ) ) + { + // check for action type + ///<<<"yo">>>; + <<< msg.getGyroX() + " " + msg.getGyroY() + " " + msg.getGyroZ() >>>; + (msg.getGyroY() / 360.0) * 1000.0 + 100.0 => osc.freq; + + } +} + From cb8d41daad2a04b86f9f217cbf216eb60d6edafe Mon Sep 17 00:00:00 2001 From: terry feng Date: Fri, 11 Oct 2024 18:08:42 -0700 Subject: [PATCH 02/13] typescript fix gyroscope --- dist/Gyro.js | 11 ++++++++--- src/Gyro.ts | 10 +++++++--- src/wc-bundle.js | 13 ++++++++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/dist/Gyro.js b/dist/Gyro.js index 6eec40a..1bea8ab 100644 --- a/dist/Gyro.js +++ b/dist/Gyro.js @@ -48,9 +48,14 @@ export default class Gyro { await gyro.theChuck.runCode(Gyro_ck); // Enable mouse and keyboard if (enableGyro) { - if (typeof DeviceOrientationEvent.requestPermission === 'function') { - DeviceOrientationEvent.requestPermission(); - gyro.enableGyro(); + if (typeof DeviceOrientationEvent.prototype.requestPermission === 'function') { + const permission = await DeviceOrientationEvent.requestPermission(); + if (permission === 'granted') { + gyro.enableGyro(); + } + else { + console.log("Gyroscope permission denied."); + } } else { console.log("No gyroscope available."); diff --git a/src/Gyro.ts b/src/Gyro.ts index da7bfde..ccae5cb 100644 --- a/src/Gyro.ts +++ b/src/Gyro.ts @@ -63,9 +63,13 @@ export default class Gyro { // Enable mouse and keyboard if (enableGyro) { - if (typeof DeviceOrientationEvent.requestPermission === 'function') { - DeviceOrientationEvent.requestPermission(); - gyro.enableGyro(); + if (typeof (DeviceOrientationEvent as any).prototype.requestPermission === 'function') { + const permission = await (DeviceOrientationEvent as any).requestPermission(); + if (permission === 'granted') { + gyro.enableGyro(); + } else { + console.log("Gyroscope permission denied."); + } } else { console.log("No gyroscope available.") } diff --git a/src/wc-bundle.js b/src/wc-bundle.js index c6aea41..ac50182 100644 --- a/src/wc-bundle.js +++ b/src/wc-bundle.js @@ -1659,7 +1659,18 @@ class Gyro { await gyro.theChuck.runCode(Gyro_ck); // Enable mouse and keyboard if (enableGyro) { - gyro.enableGyro(); + if (typeof DeviceOrientationEvent.prototype.requestPermission === 'function') { + const permission = await DeviceOrientationEvent.requestPermission(); + if (permission === 'granted') { + gyro.enableGyro(); + } + else { + console.log("Gyroscope permission denied."); + } + } + else { + console.log("No gyroscope available."); + } } return gyro; } From 2a935d5fa9149472f51f0fc12707e839dfefa7c6 Mon Sep 17 00:00:00 2001 From: terry feng Date: Fri, 11 Oct 2024 18:25:06 -0700 Subject: [PATCH 03/13] always enable if not ios --- dist/Gyro.js | 3 ++- src/Gyro.ts | 16 ++++++++-------- src/wc-bundle.js | 3 ++- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/dist/Gyro.js b/dist/Gyro.js index 1bea8ab..05252d0 100644 --- a/dist/Gyro.js +++ b/dist/Gyro.js @@ -48,6 +48,7 @@ export default class Gyro { await gyro.theChuck.runCode(Gyro_ck); // Enable mouse and keyboard if (enableGyro) { + // If iOS, request permission if (typeof DeviceOrientationEvent.prototype.requestPermission === 'function') { const permission = await DeviceOrientationEvent.requestPermission(); if (permission === 'granted') { @@ -58,7 +59,7 @@ export default class Gyro { } } else { - console.log("No gyroscope available."); + gyro.enableGyro(); } } return gyro; diff --git a/src/Gyro.ts b/src/Gyro.ts index ccae5cb..1b04f8b 100644 --- a/src/Gyro.ts +++ b/src/Gyro.ts @@ -63,15 +63,16 @@ export default class Gyro { // Enable mouse and keyboard if (enableGyro) { + // If iOS, request permission if (typeof (DeviceOrientationEvent as any).prototype.requestPermission === 'function') { - const permission = await (DeviceOrientationEvent as any).requestPermission(); - if (permission === 'granted') { - gyro.enableGyro(); - } else { - console.log("Gyroscope permission denied."); - } + const permission = await (DeviceOrientationEvent as any).requestPermission(); + if (permission === 'granted') { + gyro.enableGyro(); + } else { + console.log("Gyroscope permission denied."); + } } else { - console.log("No gyroscope available.") + gyro.enableGyro(); } } return gyro; @@ -99,7 +100,6 @@ export default class Gyro { enableGyro() { //document.addEventListener("reading", this.boundHandleGyroReading); window.addEventListener("deviceorientation", this.boundHandleOrientation); - } /** diff --git a/src/wc-bundle.js b/src/wc-bundle.js index ac50182..a75213a 100644 --- a/src/wc-bundle.js +++ b/src/wc-bundle.js @@ -1659,6 +1659,7 @@ class Gyro { await gyro.theChuck.runCode(Gyro_ck); // Enable mouse and keyboard if (enableGyro) { + // If iOS, request permission if (typeof DeviceOrientationEvent.prototype.requestPermission === 'function') { const permission = await DeviceOrientationEvent.requestPermission(); if (permission === 'granted') { @@ -1669,7 +1670,7 @@ class Gyro { } } else { - console.log("No gyroscope available."); + gyro.enableGyro(); } } return gyro; From a96115fdba59acbbd7d5eb8fe855b79ff8ef313f Mon Sep 17 00:00:00 2001 From: terry feng Date: Fri, 11 Oct 2024 18:57:48 -0700 Subject: [PATCH 04/13] attempt 2 request permission --- src/Gyro.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Gyro.ts b/src/Gyro.ts index 1b04f8b..0854688 100644 --- a/src/Gyro.ts +++ b/src/Gyro.ts @@ -64,7 +64,7 @@ export default class Gyro { // Enable mouse and keyboard if (enableGyro) { // If iOS, request permission - if (typeof (DeviceOrientationEvent as any).prototype.requestPermission === 'function') { + if (typeof (DeviceOrientationEvent as any).requestPermission === 'function') { const permission = await (DeviceOrientationEvent as any).requestPermission(); if (permission === 'granted') { gyro.enableGyro(); @@ -72,6 +72,7 @@ export default class Gyro { console.log("Gyroscope permission denied."); } } else { + // just try to enable gyro.enableGyro(); } } From 8496580ebca6374267f7dd97311e32ab812be693 Mon Sep 17 00:00:00 2001 From: terry feng Date: Fri, 11 Oct 2024 18:57:56 -0700 Subject: [PATCH 05/13] attempt 2 request permission --- dist/Gyro.js | 3 ++- src/wc-bundle.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dist/Gyro.js b/dist/Gyro.js index 05252d0..ec13eed 100644 --- a/dist/Gyro.js +++ b/dist/Gyro.js @@ -49,7 +49,7 @@ export default class Gyro { // Enable mouse and keyboard if (enableGyro) { // If iOS, request permission - if (typeof DeviceOrientationEvent.prototype.requestPermission === 'function') { + if (typeof DeviceOrientationEvent.requestPermission === 'function') { const permission = await DeviceOrientationEvent.requestPermission(); if (permission === 'granted') { gyro.enableGyro(); @@ -59,6 +59,7 @@ export default class Gyro { } } else { + // just try to enable gyro.enableGyro(); } } diff --git a/src/wc-bundle.js b/src/wc-bundle.js index a75213a..b9d932d 100644 --- a/src/wc-bundle.js +++ b/src/wc-bundle.js @@ -1660,7 +1660,7 @@ class Gyro { // Enable mouse and keyboard if (enableGyro) { // If iOS, request permission - if (typeof DeviceOrientationEvent.prototype.requestPermission === 'function') { + if (typeof DeviceOrientationEvent.requestPermission === 'function') { const permission = await DeviceOrientationEvent.requestPermission(); if (permission === 'granted') { gyro.enableGyro(); @@ -1670,6 +1670,7 @@ class Gyro { } } else { + // just try to enable gyro.enableGyro(); } } From c6735f3fc4e6a6a6c4ff5b65c3e87a172b9c430b Mon Sep 17 00:00:00 2001 From: Mike Mulshine Date: Fri, 11 Oct 2024 20:49:23 -0700 Subject: [PATCH 06/13] revert to deviceorientation permission request on button press for run tests. --- dist/Gyro.js | 27 ++++++++++++++------------- src/Gyro.ts | 5 +++++ src/wc-bundle.js | 27 ++++++++++++++------------- test/chuckTest.js | 4 ++++ 4 files changed, 37 insertions(+), 26 deletions(-) diff --git a/dist/Gyro.js b/dist/Gyro.js index ec13eed..0dc7379 100644 --- a/dist/Gyro.js +++ b/dist/Gyro.js @@ -47,22 +47,23 @@ export default class Gyro { await gyro.theChuck.runCode(GyroMsg_ck); await gyro.theChuck.runCode(Gyro_ck); // Enable mouse and keyboard + /* if (enableGyro) { - // If iOS, request permission - if (typeof DeviceOrientationEvent.requestPermission === 'function') { - const permission = await DeviceOrientationEvent.requestPermission(); - if (permission === 'granted') { - gyro.enableGyro(); - } - else { - console.log("Gyroscope permission denied."); - } - } - else { - // just try to enable - gyro.enableGyro(); + // If iOS, request permission + if (typeof (DeviceOrientationEvent as any).requestPermission === 'function') { + const permission = await (DeviceOrientationEvent as any).requestPermission(); + if (permission === 'granted') { + gyro.enableGyro(); + } else { + console.log("Gyroscope permission denied."); } + } else { + // just try to enable + gyro.enableGyro(); + } } + */ + gyro.enableGyro(); return gyro; } /** diff --git a/src/Gyro.ts b/src/Gyro.ts index 0854688..506420c 100644 --- a/src/Gyro.ts +++ b/src/Gyro.ts @@ -62,6 +62,7 @@ export default class Gyro { await gyro.theChuck.runCode(Gyro_ck); // Enable mouse and keyboard + /* if (enableGyro) { // If iOS, request permission if (typeof (DeviceOrientationEvent as any).requestPermission === 'function') { @@ -76,9 +77,13 @@ export default class Gyro { gyro.enableGyro(); } } + */ + gyro.enableGyro(); return gyro; } + + /** * @internal * Check if gyro is active diff --git a/src/wc-bundle.js b/src/wc-bundle.js index b9d932d..94777cd 100644 --- a/src/wc-bundle.js +++ b/src/wc-bundle.js @@ -1658,22 +1658,23 @@ class Gyro { await gyro.theChuck.runCode(GyroMsg_ck); await gyro.theChuck.runCode(Gyro_ck); // Enable mouse and keyboard + /* if (enableGyro) { - // If iOS, request permission - if (typeof DeviceOrientationEvent.requestPermission === 'function') { - const permission = await DeviceOrientationEvent.requestPermission(); - if (permission === 'granted') { - gyro.enableGyro(); - } - else { - console.log("Gyroscope permission denied."); - } - } - else { - // just try to enable - gyro.enableGyro(); + // If iOS, request permission + if (typeof (DeviceOrientationEvent as any).requestPermission === 'function') { + const permission = await (DeviceOrientationEvent as any).requestPermission(); + if (permission === 'granted') { + gyro.enableGyro(); + } else { + console.log("Gyroscope permission denied."); } + } else { + // just try to enable + gyro.enableGyro(); + } } + */ + gyro.enableGyro(); return gyro; } /** diff --git a/test/chuckTest.js b/test/chuckTest.js index 0facd5a..741e823 100644 --- a/test/chuckTest.js +++ b/test/chuckTest.js @@ -500,6 +500,10 @@ filterButton.addEventListener("click", (e) => { }); runButton.addEventListener("click", () => { + + if (typeof DeviceOrientationEvent.requestPermission === 'function') { + DeviceOrientationEvent.requestPermission(); + } audioContext.resume(); runTestSuite() }); From 130be169a14e8ad17d2fdc57c6fe5150f12c9290 Mon Sep 17 00:00:00 2001 From: Mike Mulshine Date: Fri, 11 Oct 2024 21:06:41 -0700 Subject: [PATCH 07/13] pre MOtion --- test/chuckTest.js | 602 +++++++++++++++++++++-------------------- test/testFiles/gyro.ck | 16 +- 2 files changed, 308 insertions(+), 310 deletions(-) diff --git a/test/chuckTest.js b/test/chuckTest.js index 741e823..28ceaab 100644 --- a/test/chuckTest.js +++ b/test/chuckTest.js @@ -64,293 +64,293 @@ class Test { //============================================================= const testSuite = [ - // new Test(1, "[sound] Define a ChucK and runCode 220hz 0.5 second", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 1); - // aChuck.chuckPrint = (output) => { - // outputBox.innerText = output; - // } - // aChuck.runCode(` - // SinOsc osc => dac; 0.5::second => now; <<< "PASSED", "" >>>; - // `); - // await new Promise(resolve => setTimeout(resolve, 750)); // wait 1.5x time - // return outputBox.innerText == "PASSED"; - // }), - - // new Test(2, "[sound] Preload a server chuck file, runFile, 440Hz 1 second", async () => { - // const aChuck = await Chuck.init([{ serverFilename: "./testFiles/test2.ck", virtualFilename: "test2.ck" }], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 2); - // aChuck.chuckPrint = (output) => { - // outputBox.innerText = output; - // } - // aChuck.runFile("test2.ck"); - // await new Promise(resolve => setTimeout(resolve, 1500)); - // return outputBox.innerText == "PASSED"; - // }), - - // new Test(3, "[sound] Dynamic load in a chuck file from URL, 880Hz 1 second", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 3); - // aChuck.chuckPrint = (output) => { - // outputBox.innerText = output; - // } - // await aChuck.loadFile("./testFiles/test3.ck"); - // aChuck.runFile("test3.ck"); - // await new Promise(resolve => setTimeout(resolve, 1500)); - // return outputBox.innerText == "PASSED"; - // }), - - // new Test(4, "[sound] Dynamic load in a kick wav file from URL", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 4); - // aChuck.chuckPrint = (output) => { - // outputBox.innerText = output; - // } - // await aChuck.loadFile("./testFiles/kick.wav"); - // aChuck.runCode(` - // SndBuf buf => dac; - // buf.read("kick.wav"); - // 1::second => now; - // <<< "KICKED", "" >>>; - // `); - // await new Promise(resolve => setTimeout(resolve, 1500)); - // return outputBox.innerText.includes("KICKED"); - // }), - - // new Test(5, "Sync test set/get int and float, webchuck and js", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 5); - // aChuck.runCode(` - // 1 => global int GLOBAL_INT; - // 1.0 => global float GLOBAL_FLOAT; - // `); - // await new Promise(resolve => setTimeout(resolve, 100)); - // let test1 = await aChuck.getInt("GLOBAL_INT") == 1 && await aChuck.getFloat("GLOBAL_FLOAT") == 1.0; - // outputBox.innerHTML += "Get OK " + test1 + "
"; - // aChuck.setInt("GLOBAL_INT", 2); - // aChuck.setFloat("GLOBAL_FLOAT", 2.0); - // await new Promise(resolve => setTimeout(resolve, 300)); - // let test2 = await aChuck.getInt("GLOBAL_INT") == 2 && await aChuck.getFloat("GLOBAL_FLOAT") == 2.0; - // outputBox.innerHTML += "Set OK " + test2 + "
"; - // return test1 && test2; - // }), - - // new Test(6, "Test shred management, add, remove, replace", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 6); - // aChuck.chuckPrint = (output) => { - // outputBox.innerHTML += output + "
"; - // } - // aChuck.runCode(` - // <<< "Running Shred 1", "" >>>; - // 1::week => now; - // `) - // aChuck.runCode(` - // <<< "Running Shred 2", "" >>>; - // 1::week => now; - // `) - // await new Promise(resolve => setTimeout(resolve, 100)); - // let removed = await aChuck.removeLastCode() == 2; - // await new Promise(resolve => setTimeout(resolve, 100)); - // let replaced = (await aChuck.replaceCode(`<<< "REPLACED Shred 1 with a new program\n", "" >>>; 1::second => now;`)).newShred == 1; - // await new Promise(resolve => setTimeout(resolve, 100)); - // let active = await aChuck.isShredActive(1); - // await new Promise(resolve => setTimeout(resolve, 100)); - // let removed2 = await aChuck.removeShred(1) == 1; - // await new Promise(resolve => setTimeout(resolve, 100)); - // return removed == true && - // replaced == true && - // active == true && - // removed2 == true; - // }), - - // new Test(7, "Test RunFileWithArgs", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 7); - // aChuck.chuckPrint = (output) => { - // outputBox.innerHTML += output + "
"; - // } - - // outputBox.innerHTML += "Passing in arguments: 1 2 foo" + "
"; - - // await aChuck.loadFile("./testFiles/test7.ck"); - // await aChuck.runFileWithArgs("test7.ck", "1:2:foo"); - // await new Promise(resolve => setTimeout(resolve, 200)); - - // return outputBox.innerText.includes("number of arguments: 3"); - // }), - - // new Test(8, "Test isShredActive", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 8); - // aChuck.chuckPrint = (output) => { - // outputBox.innerHTML += output + "
"; - // } - - // outputBox.innerHTML += "Starting shred 1 for 1 second" + "
"; - - // aChuck.runCode(`1::second => now;`); - // let test1; let test2; - // await new Promise(resolve => setTimeout(resolve, 100)); - // await aChuck.isShredActive(1).then((active) => { - // test1 = (active == 1); - // }); - // await new Promise(resolve => setTimeout(resolve, 1000)); - // await aChuck.isShredActive(1).then((active) => { - // test2 = (active == 0); - // }); - - // return test1 && test2; - // }), - - // new Test(9, "Machine.add/replace/remove/clear", async () => { - // const aChuck = await Chuck.init([ - // { serverFilename: "./testFiles/test9.ck", virtualFilename: "test9.ck" }, - // ], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 9); - // aChuck.chuckPrint = (output) => { - // outputBox.innerHTML += output + "
"; - // } - - // aChuck.runCode(` - // Machine.add("test9.ck") => int id; - // 200::ms => now; - // Machine.replace(id, "test9.ck"); - // 400::ms => now; - // Machine.remove(id); - // 200::ms => now; - // Machine.clearVM(); - // `) - // let test1; let test2; let test3; let test4; - // await new Promise(resolve => setTimeout(resolve, 100)); - // await aChuck.isShredActive(2).then((active) => { - // test1 = (active == 1); - // }); - // await new Promise(resolve => setTimeout(resolve, 200)); - // await aChuck.isShredActive(2).then((active) => { - // test2 = (active == 1); - // }); - // await new Promise(resolve => setTimeout(resolve, 400)); - // await aChuck.isShredActive(2).then((active) => { - // test3 = (active == 0); - // }); - // await new Promise(resolve => setTimeout(resolve, 200)); - // await aChuck.isShredActive(1).then((active) => { - // test4 = (active == 0); - // }); - // return test1 && test2 && test3 && test4; - // }), - - // new Test(10, "Chuck get Promise returns", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 10); - // print = (output) => { - // outputBox.innerHTML += output + "
"; - // } - // aChuck.chuckPrint = print; - - // aChuck.runCode(`2::second => now;`); - // aChuck.runCode(`2::second => now;`); - // let test = true; - // await new Promise(resolve => setTimeout(resolve, 50)); - // aChuck.isShredActive(1).then((active) => { - // print("isShredActive(1) returns: " + active); - // test &= active == 1; - // }); - // aChuck.removeShred(1).then((removed) => { - // print("removeShred(1) returns: " + removed); - // test &= removed == 1; - // }); - // aChuck.removeShred(-1).then((removed) => { - // print("removeShred(-1) returns: " + removed); - // test &= removed == 0; - // }).catch((err) => { - // print("removeShred(-1) throws: " + err); - // test &= err === "Remove code failed" - // }); - - // return test; - // }), - - // new Test(11, "[sound] WebChugin Test, ABSaturator", async () => { - // Chuck.loadChugin("./testFiles/ABSaturator.chug.wasm") - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 11); - // aChuck.chuckPrint = (output) => { - // outputBox.innerHTML += output + "
"; - // } - - // aChuck.runCode(` - // SinOsc osc => Delay d => ABSaturator sat => dac; - // 20 => sat.drive; - // 4 => sat.dcOffset; - // 0.5::second => now; - // <<< "PASSED", "" >>>; - // `); - // await new Promise(resolve => setTimeout(resolve, 750)); - - // return outputBox.innerText.includes("PASSED"); - // }), - - // new Test(12, "HID - mouse", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 12); - // aChuck.chuckPrint = (output) => { - // outputBox.innerHTML = output + "
"; - // } - - // let hid = await HID.init(aChuck); - // await aChuck.loadFile("./testFiles/mouse.ck") - // aChuck.runFile("mouse.ck") - - // return true; - // }), - - // new Test(13, "HID - keyboard", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 13); - // aChuck.chuckPrint = (output) => { - // outputBox.innerHTML = output + "
"; - // } - // let hid = await HID.init(aChuck); - // await aChuck.loadFile("./testFiles/kb.ck") - // aChuck.runFile("kb.ck") - - // return true; - // }), - - // new Test(14, "Use Offline Context - 10 seconds", async () => { - // const offlineContext = new OfflineAudioContext(2, 44100 * 10, 44100); - // const aChuck = await Chuck.init([], offlineContext, undefined, "../src/"); - // aChuck.connect(offlineContext.destination); - // const outputBox = document.getElementById("output-" + 14); - // aChuck.chuckPrint = (output) => { - // outputBox.innerHTML = output + "
"; - // } - - // aChuck.runCode(`SinOsc osc => dac; 10::second => now; <<< "PASSED", "" >>>;`); - // await offlineContext.startRendering(); - // return outputBox.innerText.includes("PASSED"); - // }), - - // new Test(15, "Use Local Audio Context, unconnected node", async () => { - // const audioContext = new AudioContext(); - // audioContext.suspend(); - // const aChuck = await Chuck.init([], audioContext, undefined, "../src/"); - // audioContext.resume(); - // const outputBox = document.getElementById("output-" + 15); - // aChuck.chuckPrint = (output) => { - // outputBox.innerHTML = output + "
"; - // } - - // aChuck.runCode(`<<< "PASSED", "" >>>;`); - // await new Promise(resolve => setTimeout(resolve, 750)); - // return outputBox.innerText.includes("PASSED"); - // }), - - new Test(1, "Gyro", async () => { + new Test(1, "[sound] Define a ChucK and runCode 220hz 0.5 second", async () => { const aChuck = await Chuck.init([], undefined, undefined, "../src/"); const outputBox = document.getElementById("output-" + 1); + aChuck.chuckPrint = (output) => { + outputBox.innerText = output; + } + aChuck.runCode(` + SinOsc osc => dac; 0.5::second => now; <<< "PASSED", "" >>>; + `); + await new Promise(resolve => setTimeout(resolve, 750)); // wait 1.5x time + return outputBox.innerText == "PASSED"; + }), + + new Test(2, "[sound] Preload a server chuck file, runFile, 440Hz 1 second", async () => { + const aChuck = await Chuck.init([{ serverFilename: "./testFiles/test2.ck", virtualFilename: "test2.ck" }], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 2); + aChuck.chuckPrint = (output) => { + outputBox.innerText = output; + } + aChuck.runFile("test2.ck"); + await new Promise(resolve => setTimeout(resolve, 1500)); + return outputBox.innerText == "PASSED"; + }), + + new Test(3, "[sound] Dynamic load in a chuck file from URL, 880Hz 1 second", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 3); + aChuck.chuckPrint = (output) => { + outputBox.innerText = output; + } + await aChuck.loadFile("./testFiles/test3.ck"); + aChuck.runFile("test3.ck"); + await new Promise(resolve => setTimeout(resolve, 1500)); + return outputBox.innerText == "PASSED"; + }), + + new Test(4, "[sound] Dynamic load in a kick wav file from URL", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 4); + aChuck.chuckPrint = (output) => { + outputBox.innerText = output; + } + await aChuck.loadFile("./testFiles/kick.wav"); + aChuck.runCode(` + SndBuf buf => dac; + buf.read("kick.wav"); + 1::second => now; + <<< "KICKED", "" >>>; + `); + await new Promise(resolve => setTimeout(resolve, 1500)); + return outputBox.innerText.includes("KICKED"); + }), + + new Test(5, "Sync test set/get int and float, webchuck and js", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 5); + aChuck.runCode(` + 1 => global int GLOBAL_INT; + 1.0 => global float GLOBAL_FLOAT; + `); + await new Promise(resolve => setTimeout(resolve, 100)); + let test1 = await aChuck.getInt("GLOBAL_INT") == 1 && await aChuck.getFloat("GLOBAL_FLOAT") == 1.0; + outputBox.innerHTML += "Get OK " + test1 + "
"; + aChuck.setInt("GLOBAL_INT", 2); + aChuck.setFloat("GLOBAL_FLOAT", 2.0); + await new Promise(resolve => setTimeout(resolve, 300)); + let test2 = await aChuck.getInt("GLOBAL_INT") == 2 && await aChuck.getFloat("GLOBAL_FLOAT") == 2.0; + outputBox.innerHTML += "Set OK " + test2 + "
"; + return test1 && test2; + }), + + new Test(6, "Test shred management, add, remove, replace", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 6); + aChuck.chuckPrint = (output) => { + outputBox.innerHTML += output + "
"; + } + aChuck.runCode(` + <<< "Running Shred 1", "" >>>; + 1::week => now; + `) + aChuck.runCode(` + <<< "Running Shred 2", "" >>>; + 1::week => now; + `) + await new Promise(resolve => setTimeout(resolve, 100)); + let removed = await aChuck.removeLastCode() == 2; + await new Promise(resolve => setTimeout(resolve, 100)); + let replaced = (await aChuck.replaceCode(`<<< "REPLACED Shred 1 with a new program\n", "" >>>; 1::second => now;`)).newShred == 1; + await new Promise(resolve => setTimeout(resolve, 100)); + let active = await aChuck.isShredActive(1); + await new Promise(resolve => setTimeout(resolve, 100)); + let removed2 = await aChuck.removeShred(1) == 1; + await new Promise(resolve => setTimeout(resolve, 100)); + return removed == true && + replaced == true && + active == true && + removed2 == true; + }), + + new Test(7, "Test RunFileWithArgs", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 7); + aChuck.chuckPrint = (output) => { + outputBox.innerHTML += output + "
"; + } + + outputBox.innerHTML += "Passing in arguments: 1 2 foo" + "
"; + + await aChuck.loadFile("./testFiles/test7.ck"); + await aChuck.runFileWithArgs("test7.ck", "1:2:foo"); + await new Promise(resolve => setTimeout(resolve, 200)); + + return outputBox.innerText.includes("number of arguments: 3"); + }), + + new Test(8, "Test isShredActive", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 8); + aChuck.chuckPrint = (output) => { + outputBox.innerHTML += output + "
"; + } + + outputBox.innerHTML += "Starting shred 1 for 1 second" + "
"; + + aChuck.runCode(`1::second => now;`); + let test1; let test2; + await new Promise(resolve => setTimeout(resolve, 100)); + await aChuck.isShredActive(1).then((active) => { + test1 = (active == 1); + }); + await new Promise(resolve => setTimeout(resolve, 1000)); + await aChuck.isShredActive(1).then((active) => { + test2 = (active == 0); + }); + + return test1 && test2; + }), + + new Test(9, "Machine.add/replace/remove/clear", async () => { + const aChuck = await Chuck.init([ + { serverFilename: "./testFiles/test9.ck", virtualFilename: "test9.ck" }, + ], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 9); + aChuck.chuckPrint = (output) => { + outputBox.innerHTML += output + "
"; + } + + aChuck.runCode(` + Machine.add("test9.ck") => int id; + 200::ms => now; + Machine.replace(id, "test9.ck"); + 400::ms => now; + Machine.remove(id); + 200::ms => now; + Machine.clearVM(); + `) + let test1; let test2; let test3; let test4; + await new Promise(resolve => setTimeout(resolve, 100)); + await aChuck.isShredActive(2).then((active) => { + test1 = (active == 1); + }); + await new Promise(resolve => setTimeout(resolve, 200)); + await aChuck.isShredActive(2).then((active) => { + test2 = (active == 1); + }); + await new Promise(resolve => setTimeout(resolve, 400)); + await aChuck.isShredActive(2).then((active) => { + test3 = (active == 0); + }); + await new Promise(resolve => setTimeout(resolve, 200)); + await aChuck.isShredActive(1).then((active) => { + test4 = (active == 0); + }); + return test1 && test2 && test3 && test4; + }), + + new Test(10, "Chuck get Promise returns", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 10); + print = (output) => { + outputBox.innerHTML += output + "
"; + } + aChuck.chuckPrint = print; + + aChuck.runCode(`2::second => now;`); + aChuck.runCode(`2::second => now;`); + let test = true; + await new Promise(resolve => setTimeout(resolve, 50)); + aChuck.isShredActive(1).then((active) => { + print("isShredActive(1) returns: " + active); + test &= active == 1; + }); + aChuck.removeShred(1).then((removed) => { + print("removeShred(1) returns: " + removed); + test &= removed == 1; + }); + aChuck.removeShred(-1).then((removed) => { + print("removeShred(-1) returns: " + removed); + test &= removed == 0; + }).catch((err) => { + print("removeShred(-1) throws: " + err); + test &= err === "Remove code failed" + }); + + return test; + }), + + new Test(11, "[sound] WebChugin Test, ABSaturator", async () => { + Chuck.loadChugin("./testFiles/ABSaturator.chug.wasm") + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 11); + aChuck.chuckPrint = (output) => { + outputBox.innerHTML += output + "
"; + } + + aChuck.runCode(` + SinOsc osc => Delay d => ABSaturator sat => dac; + 20 => sat.drive; + 4 => sat.dcOffset; + 0.5::second => now; + <<< "PASSED", "" >>>; + `); + await new Promise(resolve => setTimeout(resolve, 750)); + + return outputBox.innerText.includes("PASSED"); + }), + + new Test(12, "HID - mouse", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 12); + aChuck.chuckPrint = (output) => { + outputBox.innerHTML = output + "
"; + } + + let hid = await HID.init(aChuck); + await aChuck.loadFile("./testFiles/mouse.ck") + aChuck.runFile("mouse.ck") + + return true; + }), + + new Test(13, "HID - keyboard", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 13); + aChuck.chuckPrint = (output) => { + outputBox.innerHTML = output + "
"; + } + let hid = await HID.init(aChuck); + await aChuck.loadFile("./testFiles/kb.ck") + aChuck.runFile("kb.ck") + + return true; + }), + + new Test(14, "Use Offline Context - 10 seconds", async () => { + const offlineContext = new OfflineAudioContext(2, 44100 * 10, 44100); + const aChuck = await Chuck.init([], offlineContext, undefined, "../src/"); + aChuck.connect(offlineContext.destination); + const outputBox = document.getElementById("output-" + 14); + aChuck.chuckPrint = (output) => { + outputBox.innerHTML = output + "
"; + } + + aChuck.runCode(`SinOsc osc => dac; 10::second => now; <<< "PASSED", "" >>>;`); + await offlineContext.startRendering(); + return outputBox.innerText.includes("PASSED"); + }), + + new Test(15, "Use Local Audio Context, unconnected node", async () => { + const audioContext = new AudioContext(); + audioContext.suspend(); + const aChuck = await Chuck.init([], audioContext, undefined, "../src/"); + audioContext.resume(); + const outputBox = document.getElementById("output-" + 15); + aChuck.chuckPrint = (output) => { + outputBox.innerHTML = output + "
"; + } + + aChuck.runCode(`<<< "PASSED", "" >>>;`); + await new Promise(resolve => setTimeout(resolve, 750)); + return outputBox.innerText.includes("PASSED"); + }), + + /*new Test(16, "Gyro", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 16); aChuck.chuckPrint = (output) => { outputBox.innerHTML = output + "
"; // += for additive } @@ -360,25 +360,25 @@ const testSuite = [ aChuck.runFile("gyro.ck"); return true; - }), + }),*/ - // new Test(99, "Chuck VM operations and parameters", async () => { - // const aChuck = await Chuck.init([], undefined, undefined, "../src/"); - // const outputBox = document.getElementById("output-" + 99); - // print = (output) => { - // outputBox.innerHTML += output + "
"; - // } - // aChuck.chuckPrint = print; + new Test(99, "Chuck VM operations and parameters", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 99); + print = (output) => { + outputBox.innerHTML += output + "
"; + } + aChuck.chuckPrint = print; - // print(await aChuck.getParamString("VERSION")); - // print("sample rate: " + await aChuck.getParamInt("SAMPLE_RATE")); - // print("input channels: " + await aChuck.getParamInt("INPUT_CHANNELS")); - // print("output channels: " + await aChuck.getParamInt("OUTPUT_CHANNELS")); - // print("now: " + await aChuck.now()); + print(await aChuck.getParamString("VERSION")); + print("sample rate: " + await aChuck.getParamInt("SAMPLE_RATE")); + print("input channels: " + await aChuck.getParamInt("INPUT_CHANNELS")); + print("output channels: " + await aChuck.getParamInt("OUTPUT_CHANNELS")); + print("now: " + await aChuck.now()); - // return outputBox.innerText !== ""; - // }), + return outputBox.innerText !== ""; + }), ] //============================================================= @@ -501,9 +501,11 @@ filterButton.addEventListener("click", (e) => { runButton.addEventListener("click", () => { + // Added to request device orientation event permissions (Mike). Needs to be on button press. if (typeof DeviceOrientationEvent.requestPermission === 'function') { DeviceOrientationEvent.requestPermission(); } + audioContext.resume(); runTestSuite() }); diff --git a/test/testFiles/gyro.ck b/test/testFiles/gyro.ck index c8d19e4..951a9ef 100644 --- a/test/testFiles/gyro.ck +++ b/test/testFiles/gyro.ck @@ -1,17 +1,13 @@ //----------------------------------------------------------------------------- // name: gyro.ck -// desc: initialize Gyroscope (on web, on mobile) and read x, y, z values +// desc: interface with "deviceorientation" JS events (on web, on mobile) and read x, y, z values // // author: Mike Mulshine //----------------------------------------------------------------------------- - Gyro gy; GyroMsg msg; -SinOsc osc => dac; -0.1 => osc.gain; - 0 => int device; // open gyro @@ -19,19 +15,19 @@ if( !gy.openGyro( device ) ) me.exit(); <<< "gyro '" + gy.name() + "' ready", "" >>>; // infinite event loop -while( true ) +while( now < end ) { // wait on gyro event + + <<< "only on mobile" >>>; + gy => now; // get one or more messages while( gy.recv( msg ) ) { - // check for action type - ///<<<"yo">>>; + // print gyro values <<< msg.getGyroX() + " " + msg.getGyroY() + " " + msg.getGyroZ() >>>; - (msg.getGyroY() / 360.0) * 1000.0 + 100.0 => osc.freq; - } } From 883489b2be8fb5aea6f264a178ad58a026758eeb Mon Sep 17 00:00:00 2001 From: Mike Mulshine Date: Fri, 18 Oct 2024 14:57:00 -0700 Subject: [PATCH 08/13] Accel/gyro. --- dist/Accel.d.ts | 57 +++++++++ dist/Accel.js | 105 ++++++++++++++++ dist/Gyro.d.ts | 52 ++++---- dist/Gyro.js | 69 ++++------- dist/accelCk.d.ts | 3 + dist/accelCk.js | 94 +++++++++++++++ dist/gyroCk.d.ts | 2 +- dist/gyroCk.js | 2 +- dist/index.d.ts | 3 +- dist/index.js | 3 +- src/Accel.ts | 129 ++++++++++++++++++++ src/Gyro.ts | 64 +++------- src/accelCk.ts | 96 +++++++++++++++ src/gyroCk.ts | 2 +- src/index.ts | 3 +- src/wc-bundle.js | 259 ++++++++++++++++++++++++++++++++++------ test/chuckTest.js | 31 +++-- test/testFiles/accel.ck | 32 +++++ test/testFiles/gyro.ck | 7 +- 19 files changed, 836 insertions(+), 177 deletions(-) create mode 100644 dist/Accel.d.ts create mode 100644 dist/Accel.js create mode 100644 dist/accelCk.d.ts create mode 100644 dist/accelCk.js create mode 100644 src/Accel.ts create mode 100644 src/accelCk.ts create mode 100644 test/testFiles/accel.ck diff --git a/dist/Accel.d.ts b/dist/Accel.d.ts new file mode 100644 index 0000000..45ba902 --- /dev/null +++ b/dist/Accel.d.ts @@ -0,0 +1,57 @@ +import Chuck from "./Chuck"; +/** + * Introducing Accel (accelerometer, on mobile) support for WebChucK. Accel wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device accelerometers + * in WebChucK code. + * + * To get started with Accel: + * @example + * ```ts + * import { Chuck, Accel } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const accel = await Accel.init(theChuck); // Initialize Accel + * ``` + */ +export default class Accel { + private theChuck; + private _accelActive; + private boundHandleMotion; + /** @internal */ + constructor(theChuck: Chuck); + /** + * Initialize Accel functionality in your WebChucK instance. + * This adds a `Accel` and `AccelMsg` class to the ChucK Virtual Machine (VM). + * Accelerometer event (DeviceMotionEvent) listeners are added if `enableAccel` is true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * accel = await Accel.init(theChuck); // Initialize Accel + */ + static init(theChuck: Chuck, enableAccel?: boolean): Promise; + /** + * @internal + * Check if accel is active + */ + accelActive(): Promise; + /** + * Enable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is not yet enabled + * accel.enableAccel(); + * ``` + */ + enableAccel(): void; + /** + * Disable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is enabled + * accel.disableAccel(); + * ``` + */ + disableAccel(): void; + /** @internal */ + private handleMotion; +} diff --git a/dist/Accel.js b/dist/Accel.js new file mode 100644 index 0000000..136d780 --- /dev/null +++ b/dist/Accel.js @@ -0,0 +1,105 @@ +import { Accel_ck, AccelMsg_ck } from "./accelCk"; +/** + * Introducing Accel (accelerometer, on mobile) support for WebChucK. Accel wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device accelerometers + * in WebChucK code. + * + * To get started with Accel: + * @example + * ```ts + * import { Chuck, Accel } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const accel = await Accel.init(theChuck); // Initialize Accel + * ``` + */ +export default class Accel { + /** @internal */ + constructor(theChuck) { + this._accelActive = false; + // Initialize members + this.theChuck = theChuck; + this.boundHandleMotion = this.handleMotion.bind(this); + } + /** + * Initialize Accel functionality in your WebChucK instance. + * This adds a `Accel` and `AccelMsg` class to the ChucK Virtual Machine (VM). + * Accelerometer event (DeviceMotionEvent) listeners are added if `enableAccel` is true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * accel = await Accel.init(theChuck); // Initialize Accel + */ + static async init(theChuck, enableAccel = true) { + const accel = new Accel(theChuck); + // Add Accel and AccelMsg classes to ChucK VM + await accel.theChuck.runCode(AccelMsg_ck); + await accel.theChuck.runCode(Accel_ck); + // Enable mouse and keyboard + /* + if (enableAccel) { + // If iOS, request permission + if (typeof (DeviceOrientationEvent as any).requestPermission === 'function') { + const permission = await (DeviceOrientationEvent as any).requestPermission(); + if (permission === 'granted') { + accel.enableAccel(); + } else { + console.log("Accelscope permission denied."); + } + } else { + // just try to enable + accel.enableAccel(); + } + } + */ + accel.enableAccel(); + return accel; + } + /** + * @internal + * Check if accel is active + */ + async accelActive() { + const x = await this.theChuck.getInt("_accelActive"); + this._accelActive = x == 1; + } + /** + * Enable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is not yet enabled + * accel.enableAccel(); + * ``` + */ + enableAccel() { + // consider using "deviceorientationabsolute" + // https://developer.mozilla.org/en-US/docs/Web/API/Window/deviceorientationabsolute_event + window.addEventListener("devicemotion", this.boundHandleMotion); + } + /** + * Disable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is enabled + * accel.disableAccel(); + * ``` + */ + disableAccel() { + window.removeEventListener("devicemotion", this.boundHandleMotion); + } + //----------------------------------------- + // JAVASCRIPT HID EVENT HANDLERS + //----------------------------------------- + /** @internal */ + handleMotion(event) { + this.accelActive(); + if (this._accelActive) { + if (event.acceleration != null) { + this.theChuck.setFloat("_accelX", event.acceleration.x ? event.acceleration.x : 0.0); + this.theChuck.setFloat("_accelY", event.acceleration.y ? event.acceleration.y : 0.0); + this.theChuck.setFloat("_accelZ", event.acceleration.z ? event.acceleration.z : 0.0); + this.theChuck.broadcastEvent("_accelReading"); + } + } + } +} diff --git a/dist/Gyro.d.ts b/dist/Gyro.d.ts index cca8df8..f5f083e 100644 --- a/dist/Gyro.d.ts +++ b/dist/Gyro.d.ts @@ -1,17 +1,16 @@ import Chuck from "./Chuck"; /** - * Introducing HID (Human Interface Device) support for WebChucK. HID wraps - * JavaScript mouse/keyboard event listeners enabling mouse and keyboard - * communication with the native {@link https://chuck.stanford.edu/doc/reference/io.html#Hid | HID} - * class in ChucK. + * Introducing Gyro (gyroerometer, on mobile) support for WebChucK. Gyro wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device gyroerometers + * in WebChucK code. * - * To get started with HID: + * To get started with Gyro: * @example * ```ts - * import { Chuck, HID } from "webchuck"; + * import { Chuck, Gyro } from "webchuck"; * * const theChuck = await Chuck.init([]); - * const hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * const gyro = await Gyro.init(theChuck); // Initialize Gyro * ``` */ export default class Gyro { @@ -21,22 +20,13 @@ export default class Gyro { /** @internal */ constructor(theChuck: Chuck); /** - * Initialize HID functionality in your WebChucK instance. - * This adds a `Hid` and `HidMsg` class to the ChucK Virtual Machine (VM). - * Mouse and keyboard event listeners are added if `enableMouse` and `enableKeyboard` are true (default). + * Initialize Gyro functionality in your WebChucK instance. + * This adds a `Gyro` and `GyroMsg` class to the ChucK Virtual Machine (VM). + * Gyroerometer event (DeviceMotionEvent) listeners are added if `enableGyro` is true (default). * @example * ```ts * theChuck = await Chuck.init([]); - * hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard - * ``` - * @example - * ```ts - * theChuck = await Chuck.init([]); - * hid = await HID.init(theChuck, false, true); // Initialize HID, no mouse, only keyboard - * ``` - * @param theChuck WebChucK instance - * @param enableMouse boolean to enable mouse HID - * @param enableKeyboard boolean to enable keyboard HID + * gyro = await Gyro.init(theChuck); // Initialize Gyro */ static init(theChuck: Chuck, enableGyro?: boolean): Promise; /** @@ -45,24 +35,22 @@ export default class Gyro { */ gyroActive(): Promise; /** - * Enable Mouse HID Javascript event listeners for HID. - * Adds a mousemove, mousedown, mouseup, and wheel listener to the document. - * This will also disable the context menu on right click. + * Enable Javascript event (DeviceMotionEvent) listeners for Gyro * @example * ```ts - * // If mouse HID is not yet enabled - * hid.enableMouse(); + * // If gyro is not yet enabled + * gyro.enableGyro(); * ``` */ enableGyro(): void; /** - * Disable Mouse HID Javascript event listeners - * @example - * ```ts - * // If mouse HID is enabled - * hid.disableMouse(); - * ``` - */ + * Disable Javascript event (DeviceMotionEvent) listeners for Gyro + * @example + * ```ts + * // If gyro is enabled + * gyro.disableGyro(); + * ``` + */ disableGyro(): void; /** @internal */ private handleOrientation; diff --git a/dist/Gyro.js b/dist/Gyro.js index 0dc7379..6cb0051 100644 --- a/dist/Gyro.js +++ b/dist/Gyro.js @@ -1,18 +1,16 @@ import { Gyro_ck, GyroMsg_ck } from "./gyroCk"; -//TODO: Update the latest mouse.ck and kb.ck files /** - * Introducing HID (Human Interface Device) support for WebChucK. HID wraps - * JavaScript mouse/keyboard event listeners enabling mouse and keyboard - * communication with the native {@link https://chuck.stanford.edu/doc/reference/io.html#Hid | HID} - * class in ChucK. + * Introducing Gyro (gyroerometer, on mobile) support for WebChucK. Gyro wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device gyroerometers + * in WebChucK code. * - * To get started with HID: + * To get started with Gyro: * @example * ```ts - * import { Chuck, HID } from "webchuck"; + * import { Chuck, Gyro } from "webchuck"; * * const theChuck = await Chuck.init([]); - * const hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * const gyro = await Gyro.init(theChuck); // Initialize Gyro * ``` */ export default class Gyro { @@ -24,22 +22,13 @@ export default class Gyro { this.boundHandleOrientation = this.handleOrientation.bind(this); } /** - * Initialize HID functionality in your WebChucK instance. - * This adds a `Hid` and `HidMsg` class to the ChucK Virtual Machine (VM). - * Mouse and keyboard event listeners are added if `enableMouse` and `enableKeyboard` are true (default). + * Initialize Gyro functionality in your WebChucK instance. + * This adds a `Gyro` and `GyroMsg` class to the ChucK Virtual Machine (VM). + * Gyroerometer event (DeviceMotionEvent) listeners are added if `enableGyro` is true (default). * @example * ```ts * theChuck = await Chuck.init([]); - * hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard - * ``` - * @example - * ```ts - * theChuck = await Chuck.init([]); - * hid = await HID.init(theChuck, false, true); // Initialize HID, no mouse, only keyboard - * ``` - * @param theChuck WebChucK instance - * @param enableMouse boolean to enable mouse HID - * @param enableKeyboard boolean to enable keyboard HID + * gyro = await Gyro.init(theChuck); // Initialize Gyro */ static async init(theChuck, enableGyro = true) { const gyro = new Gyro(theChuck); @@ -75,27 +64,26 @@ export default class Gyro { this._gyroActive = x == 1; } /** - * Enable Mouse HID Javascript event listeners for HID. - * Adds a mousemove, mousedown, mouseup, and wheel listener to the document. - * This will also disable the context menu on right click. + * Enable Javascript event (DeviceMotionEvent) listeners for Gyro * @example * ```ts - * // If mouse HID is not yet enabled - * hid.enableMouse(); + * // If gyro is not yet enabled + * gyro.enableGyro(); * ``` */ enableGyro() { - //document.addEventListener("reading", this.boundHandleGyroReading); + // consider using "deviceorientationabsolute" + // https://developer.mozilla.org/en-US/docs/Web/API/Window/deviceorientationabsolute_event window.addEventListener("deviceorientation", this.boundHandleOrientation); } /** - * Disable Mouse HID Javascript event listeners - * @example - * ```ts - * // If mouse HID is enabled - * hid.disableMouse(); - * ``` - */ + * Disable Javascript event (DeviceMotionEvent) listeners for Gyro + * @example + * ```ts + * // If gyro is enabled + * gyro.disableGyro(); + * ``` + */ disableGyro() { window.removeEventListener("deviceorientation", this.boundHandleOrientation); } @@ -113,16 +101,3 @@ export default class Gyro { } } } -//----------------------------------------------- -// HELPER FUNCTIONS -//----------------------------------------------- -/** - * Clamp a value between two numbers - * @param val value to clamp - * @param min min value - * @param max max value - * @returns clamped value - */ -function clamp(val, min, max) { - return Math.min(Math.max(val, min), max); -} diff --git a/dist/accelCk.d.ts b/dist/accelCk.d.ts new file mode 100644 index 0000000..9e086fa --- /dev/null +++ b/dist/accelCk.d.ts @@ -0,0 +1,3 @@ +declare const AccelMsg_ck = "\npublic class AccelMsg {\n float accelX;\n float accelY;\n float accelZ;\n\n function float getAccelX() {\n return accelX;\n }\n\n function float getAccelY() {\n return accelY;\n }\n\n function float getAccelZ() {\n return accelZ;\n }\n\n function void _copy(AccelMsg localMsg) {\n localMsg.accelX => accelX;\n localMsg.accelY => accelY;\n localMsg.accelZ => accelZ;\n }\n}\n"; +declare const Accel_ck = "\nglobal Event _accelReading;\nglobal int _accelActive;\n\nglobal float _accelX;\nglobal float _accelY;\nglobal float _accelZ;\n\npublic class Accel extends Event {\n\n 0 => int isAccelOpen;\n 0 => int active;\n\n string deviceName; \n\n // AccelMsg Queue\n AccelMsg _accelMsgQueue[0];\n\n function string name() {\n return deviceName;\n }\n\n function int openAccel(int num) {\n if (num < 0) {\n false => active;\n } else {\n \"js DeviceMotionEvent\" => deviceName;\n true => active;\n }\n active => isAccelOpen => _accelActive;\n spork ~ _accelListener();\n return active;\n }\n\n\n // Pop the first AccelMsg from the queue\n // Write it to msg and return 1\n function int recv(AccelMsg msg) {\n // is empty\n if (_accelMsgQueue.size() <= 0) {\n return 0;\n }\n\n // pop the first AccelMsg to msg, return true\n _accelMsgQueue[0] @=> AccelMsg localMsg;\n msg._copy(localMsg); \n _accelMsgQueue.popFront();\n return 1;\n }\n\n // Accel Listener\n // Get variables from JS and write to the AccelMsg \n function void _accelListener() {\n AccelMsg @ msg;\n while(true){\n new AccelMsg @=> msg;\n _accelReading => now;\n\n _accelX => msg.accelX;\n _accelY => msg.accelY;\n _accelZ => msg.accelZ;\n\n _accelMsgQueue << msg;\n this.broadcast();\n }\n }\n}\n"; +export { AccelMsg_ck, Accel_ck }; diff --git a/dist/accelCk.js b/dist/accelCk.js new file mode 100644 index 0000000..fd68455 --- /dev/null +++ b/dist/accelCk.js @@ -0,0 +1,94 @@ +const AccelMsg_ck = ` +public class AccelMsg { + float accelX; + float accelY; + float accelZ; + + function float getAccelX() { + return accelX; + } + + function float getAccelY() { + return accelY; + } + + function float getAccelZ() { + return accelZ; + } + + function void _copy(AccelMsg localMsg) { + localMsg.accelX => accelX; + localMsg.accelY => accelY; + localMsg.accelZ => accelZ; + } +} +`; +const Accel_ck = ` +global Event _accelReading; +global int _accelActive; + +global float _accelX; +global float _accelY; +global float _accelZ; + +public class Accel extends Event { + + 0 => int isAccelOpen; + 0 => int active; + + string deviceName; + + // AccelMsg Queue + AccelMsg _accelMsgQueue[0]; + + function string name() { + return deviceName; + } + + function int openAccel(int num) { + if (num < 0) { + false => active; + } else { + "js DeviceMotionEvent" => deviceName; + true => active; + } + active => isAccelOpen => _accelActive; + spork ~ _accelListener(); + return active; + } + + + // Pop the first AccelMsg from the queue + // Write it to msg and return 1 + function int recv(AccelMsg msg) { + // is empty + if (_accelMsgQueue.size() <= 0) { + return 0; + } + + // pop the first AccelMsg to msg, return true + _accelMsgQueue[0] @=> AccelMsg localMsg; + msg._copy(localMsg); + _accelMsgQueue.popFront(); + return 1; + } + + // Accel Listener + // Get variables from JS and write to the AccelMsg + function void _accelListener() { + AccelMsg @ msg; + while(true){ + new AccelMsg @=> msg; + _accelReading => now; + + _accelX => msg.accelX; + _accelY => msg.accelY; + _accelZ => msg.accelZ; + + _accelMsgQueue << msg; + this.broadcast(); + } + } +} +`; +export { AccelMsg_ck, Accel_ck }; diff --git a/dist/gyroCk.d.ts b/dist/gyroCk.d.ts index 1642eaf..42eb772 100644 --- a/dist/gyroCk.d.ts +++ b/dist/gyroCk.d.ts @@ -1,3 +1,3 @@ declare const GyroMsg_ck = "\npublic class GyroMsg {\n float gyroX;\n float gyroY;\n float gyroZ;\n\n function float getGyroX() {\n return gyroX;\n }\n\n function float getGyroY() {\n return gyroY;\n }\n\n function float getGyroZ() {\n return gyroZ;\n }\n\n function void _copy(GyroMsg localMsg) {\n localMsg.gyroX => gyroX;\n localMsg.gyroY => gyroY;\n localMsg.gyroZ => gyroZ;\n }\n}\n"; -declare const Gyro_ck = "\nglobal Event _gyroReading;\nglobal int _gyroActive;\n\nglobal float _gyroX;\nglobal float _gyroY;\nglobal float _gyroZ;\n\npublic class Gyro extends Event {\n\n 0 => int isGyroOpen;\n 0 => int active;\n\n string deviceName; \n\n // GyroMsg Queue\n GyroMsg _gyroMsgQueue[0];\n\n function string name() {\n return deviceName;\n }\n\n function int openGyro(int num) {\n if (num < 0) {\n false => active;\n } else {\n \"js gyro\" => deviceName;\n true => active;\n }\n active => isGyroOpen => _gyroActive;\n spork ~ _gyroListener();\n return active;\n }\n\n\n // Pop the first GyroMsg from the queue\n // Write it to msg and return 1\n function int recv(GyroMsg msg) {\n // is empty\n if (_gyroMsgQueue.size() <= 0) {\n return 0;\n }\n\n // pop the first GyroMsg to msg, return true\n _gyroMsgQueue[0] @=> GyroMsg localMsg;\n msg._copy(localMsg); \n _gyroMsgQueue.popFront();\n return 1;\n }\n\n // Gyro Listener\n // Get variables from JS and write to the GyroMsg \n function void _gyroListener() {\n GyroMsg @ msg;\n while(true){\n new GyroMsg @=> msg;\n _gyroReading => now;\n\n _gyroX => msg.gyroX;\n _gyroY => msg.gyroY;\n _gyroZ => msg.gyroZ;\n\n _gyroMsgQueue << msg;\n this.broadcast();\n }\n }\n}\n"; +declare const Gyro_ck = "\nglobal Event _gyroReading;\nglobal int _gyroActive;\n\nglobal float _gyroX;\nglobal float _gyroY;\nglobal float _gyroZ;\n\npublic class Gyro extends Event {\n\n 0 => int isGyroOpen;\n 0 => int active;\n\n string deviceName; \n\n // GyroMsg Queue\n GyroMsg _gyroMsgQueue[0];\n\n function string name() {\n return deviceName;\n }\n\n function int openGyro(int num) {\n if (num < 0) {\n false => active;\n } else {\n \"js DeviceOrientationEvent\" => deviceName;\n true => active;\n }\n active => isGyroOpen => _gyroActive;\n spork ~ _gyroListener();\n return active;\n }\n\n\n // Pop the first GyroMsg from the queue\n // Write it to msg and return 1\n function int recv(GyroMsg msg) {\n // is empty\n if (_gyroMsgQueue.size() <= 0) {\n return 0;\n }\n\n // pop the first GyroMsg to msg, return true\n _gyroMsgQueue[0] @=> GyroMsg localMsg;\n msg._copy(localMsg); \n _gyroMsgQueue.popFront();\n return 1;\n }\n\n // Gyro Listener\n // Get variables from JS and write to the GyroMsg \n function void _gyroListener() {\n GyroMsg @ msg;\n while(true){\n new GyroMsg @=> msg;\n _gyroReading => now;\n\n _gyroX => msg.gyroX;\n _gyroY => msg.gyroY;\n _gyroZ => msg.gyroZ;\n\n _gyroMsgQueue << msg;\n this.broadcast();\n }\n }\n}\n"; export { GyroMsg_ck, Gyro_ck }; diff --git a/dist/gyroCk.js b/dist/gyroCk.js index 66b57c3..6171fa2 100644 --- a/dist/gyroCk.js +++ b/dist/gyroCk.js @@ -49,7 +49,7 @@ public class Gyro extends Event { if (num < 0) { false => active; } else { - "js gyro" => deviceName; + "js DeviceOrientationEvent" => deviceName; true => active; } active => isGyroOpen => _gyroActive; diff --git a/dist/index.d.ts b/dist/index.d.ts index 8276897..66855c3 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -2,4 +2,5 @@ import DeferredPromise from "./DeferredPromise"; import Chuck from "./Chuck"; import HID from "./Hid"; import Gyro from "./Gyro"; -export { Chuck, HID, Gyro, DeferredPromise }; +import Accel from "./Accel"; +export { Chuck, HID, Gyro, Accel, DeferredPromise }; diff --git a/dist/index.js b/dist/index.js index 8276897..66855c3 100644 --- a/dist/index.js +++ b/dist/index.js @@ -2,4 +2,5 @@ import DeferredPromise from "./DeferredPromise"; import Chuck from "./Chuck"; import HID from "./Hid"; import Gyro from "./Gyro"; -export { Chuck, HID, Gyro, DeferredPromise }; +import Accel from "./Accel"; +export { Chuck, HID, Gyro, Accel, DeferredPromise }; diff --git a/src/Accel.ts b/src/Accel.ts new file mode 100644 index 0000000..f66ce8e --- /dev/null +++ b/src/Accel.ts @@ -0,0 +1,129 @@ +import Chuck from "./Chuck"; +import { Accel_ck, AccelMsg_ck } from "./accelCk"; + + +/** + * Introducing Accel (accelerometer, on mobile) support for WebChucK. Accel wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device accelerometers + * in WebChucK code. + * + * To get started with Accel: + * @example + * ```ts + * import { Chuck, Accel } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const accel = await Accel.init(theChuck); // Initialize Accel + * ``` + */ +export default class Accel { + // Private members + private theChuck: Chuck; + private _accelActive: boolean = false; + + private boundHandleMotion; + + + /** @internal */ + constructor(theChuck: Chuck) { + // Initialize members + this.theChuck = theChuck; + + this.boundHandleMotion = this.handleMotion.bind(this); + } + + /** + * Initialize Accel functionality in your WebChucK instance. + * This adds a `Accel` and `AccelMsg` class to the ChucK Virtual Machine (VM). + * Accelerometer event (DeviceMotionEvent) listeners are added if `enableAccel` is true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * accel = await Accel.init(theChuck); // Initialize Accel + */ + static async init( + theChuck: Chuck, + enableAccel: boolean = true + ): Promise { + const accel = new Accel(theChuck); + // Add Accel and AccelMsg classes to ChucK VM + await accel.theChuck.runCode(AccelMsg_ck); + await accel.theChuck.runCode(Accel_ck); + + // Enable mouse and keyboard + /* + if (enableAccel) { + // If iOS, request permission + if (typeof (DeviceOrientationEvent as any).requestPermission === 'function') { + const permission = await (DeviceOrientationEvent as any).requestPermission(); + if (permission === 'granted') { + accel.enableAccel(); + } else { + console.log("Accelscope permission denied."); + } + } else { + // just try to enable + accel.enableAccel(); + } + } + */ + accel.enableAccel(); + return accel; + } + + + + /** + * @internal + * Check if accel is active + */ + async accelActive() { + const x = await this.theChuck.getInt("_accelActive"); + this._accelActive = x == 1; + } + + /** + * Enable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is not yet enabled + * accel.enableAccel(); + * ``` + */ + enableAccel() { + // consider using "deviceorientationabsolute" + // https://developer.mozilla.org/en-US/docs/Web/API/Window/deviceorientationabsolute_event + window.addEventListener("devicemotion", this.boundHandleMotion); + } + + /** + * Disable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is enabled + * accel.disableAccel(); + * ``` + */ + disableAccel() { + window.removeEventListener("devicemotion", this.boundHandleMotion); + } + + + //----------------------------------------- + // JAVASCRIPT HID EVENT HANDLERS + //----------------------------------------- + + + /** @internal */ + private handleMotion(event: DeviceMotionEvent) { + this.accelActive(); + if (this._accelActive) { + if (event.acceleration != null) { + this.theChuck.setFloat("_accelX", event.acceleration.x ? event.acceleration.x : 0.0); + this.theChuck.setFloat("_accelY", event.acceleration.y ? event.acceleration.y : 0.0); + this.theChuck.setFloat("_accelZ", event.acceleration.z ? event.acceleration.z : 0.0); + this.theChuck.broadcastEvent("_accelReading"); + } + } + } +} diff --git a/src/Gyro.ts b/src/Gyro.ts index 506420c..add5222 100644 --- a/src/Gyro.ts +++ b/src/Gyro.ts @@ -2,20 +2,18 @@ import Chuck from "./Chuck"; import { Gyro_ck, GyroMsg_ck } from "./gyroCk"; -//TODO: Update the latest mouse.ck and kb.ck files /** - * Introducing HID (Human Interface Device) support for WebChucK. HID wraps - * JavaScript mouse/keyboard event listeners enabling mouse and keyboard - * communication with the native {@link https://chuck.stanford.edu/doc/reference/io.html#Hid | HID} - * class in ChucK. + * Introducing Gyro (gyroerometer, on mobile) support for WebChucK. Gyro wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device gyroerometers + * in WebChucK code. * - * To get started with HID: + * To get started with Gyro: * @example * ```ts - * import { Chuck, HID } from "webchuck"; + * import { Chuck, Gyro } from "webchuck"; * * const theChuck = await Chuck.init([]); - * const hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * const gyro = await Gyro.init(theChuck); // Initialize Gyro * ``` */ export default class Gyro { @@ -35,22 +33,13 @@ export default class Gyro { } /** - * Initialize HID functionality in your WebChucK instance. - * This adds a `Hid` and `HidMsg` class to the ChucK Virtual Machine (VM). - * Mouse and keyboard event listeners are added if `enableMouse` and `enableKeyboard` are true (default). + * Initialize Gyro functionality in your WebChucK instance. + * This adds a `Gyro` and `GyroMsg` class to the ChucK Virtual Machine (VM). + * Gyroerometer event (DeviceMotionEvent) listeners are added if `enableGyro` is true (default). * @example * ```ts * theChuck = await Chuck.init([]); - * hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard - * ``` - * @example - * ```ts - * theChuck = await Chuck.init([]); - * hid = await HID.init(theChuck, false, true); // Initialize HID, no mouse, only keyboard - * ``` - * @param theChuck WebChucK instance - * @param enableMouse boolean to enable mouse HID - * @param enableKeyboard boolean to enable keyboard HID + * gyro = await Gyro.init(theChuck); // Initialize Gyro */ static async init( theChuck: Chuck, @@ -94,26 +83,25 @@ export default class Gyro { } /** - * Enable Mouse HID Javascript event listeners for HID. - * Adds a mousemove, mousedown, mouseup, and wheel listener to the document. - * This will also disable the context menu on right click. + * Enable Javascript event (DeviceMotionEvent) listeners for Gyro * @example * ```ts - * // If mouse HID is not yet enabled - * hid.enableMouse(); + * // If gyro is not yet enabled + * gyro.enableGyro(); * ``` */ enableGyro() { - //document.addEventListener("reading", this.boundHandleGyroReading); + // consider using "deviceorientationabsolute" + // https://developer.mozilla.org/en-US/docs/Web/API/Window/deviceorientationabsolute_event window.addEventListener("deviceorientation", this.boundHandleOrientation); } - /** - * Disable Mouse HID Javascript event listeners + /** + * Disable Javascript event (DeviceMotionEvent) listeners for Gyro * @example * ```ts - * // If mouse HID is enabled - * hid.disableMouse(); + * // If gyro is enabled + * gyro.disableGyro(); * ``` */ disableGyro() { @@ -137,17 +125,3 @@ export default class Gyro { } } } - -//----------------------------------------------- -// HELPER FUNCTIONS -//----------------------------------------------- -/** - * Clamp a value between two numbers - * @param val value to clamp - * @param min min value - * @param max max value - * @returns clamped value - */ -function clamp(val: number, min: number, max: number): number { - return Math.min(Math.max(val, min), max); -} diff --git a/src/accelCk.ts b/src/accelCk.ts new file mode 100644 index 0000000..019cc63 --- /dev/null +++ b/src/accelCk.ts @@ -0,0 +1,96 @@ +const AccelMsg_ck = ` +public class AccelMsg { + float accelX; + float accelY; + float accelZ; + + function float getAccelX() { + return accelX; + } + + function float getAccelY() { + return accelY; + } + + function float getAccelZ() { + return accelZ; + } + + function void _copy(AccelMsg localMsg) { + localMsg.accelX => accelX; + localMsg.accelY => accelY; + localMsg.accelZ => accelZ; + } +} +`; + +const Accel_ck = ` +global Event _accelReading; +global int _accelActive; + +global float _accelX; +global float _accelY; +global float _accelZ; + +public class Accel extends Event { + + 0 => int isAccelOpen; + 0 => int active; + + string deviceName; + + // AccelMsg Queue + AccelMsg _accelMsgQueue[0]; + + function string name() { + return deviceName; + } + + function int openAccel(int num) { + if (num < 0) { + false => active; + } else { + "js DeviceMotionEvent" => deviceName; + true => active; + } + active => isAccelOpen => _accelActive; + spork ~ _accelListener(); + return active; + } + + + // Pop the first AccelMsg from the queue + // Write it to msg and return 1 + function int recv(AccelMsg msg) { + // is empty + if (_accelMsgQueue.size() <= 0) { + return 0; + } + + // pop the first AccelMsg to msg, return true + _accelMsgQueue[0] @=> AccelMsg localMsg; + msg._copy(localMsg); + _accelMsgQueue.popFront(); + return 1; + } + + // Accel Listener + // Get variables from JS and write to the AccelMsg + function void _accelListener() { + AccelMsg @ msg; + while(true){ + new AccelMsg @=> msg; + _accelReading => now; + + _accelX => msg.accelX; + _accelY => msg.accelY; + _accelZ => msg.accelZ; + + _accelMsgQueue << msg; + this.broadcast(); + } + } +} +`; + +export { AccelMsg_ck, Accel_ck }; diff --git a/src/gyroCk.ts b/src/gyroCk.ts index ec63dfd..979040a 100644 --- a/src/gyroCk.ts +++ b/src/gyroCk.ts @@ -50,7 +50,7 @@ public class Gyro extends Event { if (num < 0) { false => active; } else { - "js gyro" => deviceName; + "js DeviceOrientationEvent" => deviceName; true => active; } active => isGyroOpen => _gyroActive; diff --git a/src/index.ts b/src/index.ts index 892a3c1..7736d0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,5 +2,6 @@ import DeferredPromise from "./DeferredPromise"; import Chuck from "./Chuck"; import HID from "./Hid"; import Gyro from "./Gyro"; +import Accel from "./Accel"; -export { Chuck, HID, Gyro, DeferredPromise }; +export { Chuck, HID, Gyro, Accel, DeferredPromise }; diff --git a/src/wc-bundle.js b/src/wc-bundle.js index 94777cd..89b10bf 100644 --- a/src/wc-bundle.js +++ b/src/wc-bundle.js @@ -1567,7 +1567,7 @@ public class Gyro extends Event { if (num < 0) { false => active; } else { - "js gyro" => deviceName; + "js DeviceOrientationEvent" => deviceName; true => active; } active => isGyroOpen => _gyroActive; @@ -1610,20 +1610,18 @@ public class Gyro extends Event { } `; -//TODO: Update the latest mouse.ck and kb.ck files /** - * Introducing HID (Human Interface Device) support for WebChucK. HID wraps - * JavaScript mouse/keyboard event listeners enabling mouse and keyboard - * communication with the native {@link https://chuck.stanford.edu/doc/reference/io.html#Hid | HID} - * class in ChucK. + * Introducing Gyro (gyroerometer, on mobile) support for WebChucK. Gyro wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device gyroerometers + * in WebChucK code. * - * To get started with HID: + * To get started with Gyro: * @example * ```ts - * import { Chuck, HID } from "webchuck"; + * import { Chuck, Gyro } from "webchuck"; * * const theChuck = await Chuck.init([]); - * const hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * const gyro = await Gyro.init(theChuck); // Initialize Gyro * ``` */ class Gyro { @@ -1635,22 +1633,13 @@ class Gyro { this.boundHandleOrientation = this.handleOrientation.bind(this); } /** - * Initialize HID functionality in your WebChucK instance. - * This adds a `Hid` and `HidMsg` class to the ChucK Virtual Machine (VM). - * Mouse and keyboard event listeners are added if `enableMouse` and `enableKeyboard` are true (default). - * @example - * ```ts - * theChuck = await Chuck.init([]); - * hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard - * ``` + * Initialize Gyro functionality in your WebChucK instance. + * This adds a `Gyro` and `GyroMsg` class to the ChucK Virtual Machine (VM). + * Gyroerometer event (DeviceMotionEvent) listeners are added if `enableGyro` is true (default). * @example * ```ts * theChuck = await Chuck.init([]); - * hid = await HID.init(theChuck, false, true); // Initialize HID, no mouse, only keyboard - * ``` - * @param theChuck WebChucK instance - * @param enableMouse boolean to enable mouse HID - * @param enableKeyboard boolean to enable keyboard HID + * gyro = await Gyro.init(theChuck); // Initialize Gyro */ static async init(theChuck, enableGyro = true) { const gyro = new Gyro(theChuck); @@ -1686,27 +1675,26 @@ class Gyro { this._gyroActive = x == 1; } /** - * Enable Mouse HID Javascript event listeners for HID. - * Adds a mousemove, mousedown, mouseup, and wheel listener to the document. - * This will also disable the context menu on right click. + * Enable Javascript event (DeviceMotionEvent) listeners for Gyro * @example * ```ts - * // If mouse HID is not yet enabled - * hid.enableMouse(); + * // If gyro is not yet enabled + * gyro.enableGyro(); * ``` */ enableGyro() { - //document.addEventListener("reading", this.boundHandleGyroReading); + // consider using "deviceorientationabsolute" + // https://developer.mozilla.org/en-US/docs/Web/API/Window/deviceorientationabsolute_event window.addEventListener("deviceorientation", this.boundHandleOrientation); } /** - * Disable Mouse HID Javascript event listeners - * @example - * ```ts - * // If mouse HID is enabled - * hid.disableMouse(); - * ``` - */ + * Disable Javascript event (DeviceMotionEvent) listeners for Gyro + * @example + * ```ts + * // If gyro is enabled + * gyro.disableGyro(); + * ``` + */ disableGyro() { window.removeEventListener("deviceorientation", this.boundHandleOrientation); } @@ -1725,4 +1713,203 @@ class Gyro { } } -export { Chuck, DeferredPromise, Gyro, HID }; +const AccelMsg_ck = ` +public class AccelMsg { + float accelX; + float accelY; + float accelZ; + + function float getAccelX() { + return accelX; + } + + function float getAccelY() { + return accelY; + } + + function float getAccelZ() { + return accelZ; + } + + function void _copy(AccelMsg localMsg) { + localMsg.accelX => accelX; + localMsg.accelY => accelY; + localMsg.accelZ => accelZ; + } +} +`; +const Accel_ck = ` +global Event _accelReading; +global int _accelActive; + +global float _accelX; +global float _accelY; +global float _accelZ; + +public class Accel extends Event { + + 0 => int isAccelOpen; + 0 => int active; + + string deviceName; + + // AccelMsg Queue + AccelMsg _accelMsgQueue[0]; + + function string name() { + return deviceName; + } + + function int openAccel(int num) { + if (num < 0) { + false => active; + } else { + "js DeviceMotionEvent" => deviceName; + true => active; + } + active => isAccelOpen => _accelActive; + spork ~ _accelListener(); + return active; + } + + + // Pop the first AccelMsg from the queue + // Write it to msg and return 1 + function int recv(AccelMsg msg) { + // is empty + if (_accelMsgQueue.size() <= 0) { + return 0; + } + + // pop the first AccelMsg to msg, return true + _accelMsgQueue[0] @=> AccelMsg localMsg; + msg._copy(localMsg); + _accelMsgQueue.popFront(); + return 1; + } + + // Accel Listener + // Get variables from JS and write to the AccelMsg + function void _accelListener() { + AccelMsg @ msg; + while(true){ + new AccelMsg @=> msg; + _accelReading => now; + + _accelX => msg.accelX; + _accelY => msg.accelY; + _accelZ => msg.accelZ; + + _accelMsgQueue << msg; + this.broadcast(); + } + } +} +`; + +/** + * Introducing Accel (accelerometer, on mobile) support for WebChucK. Accel wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device accelerometers + * in WebChucK code. + * + * To get started with Accel: + * @example + * ```ts + * import { Chuck, Accel } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const accel = await Accel.init(theChuck); // Initialize Accel + * ``` + */ +class Accel { + /** @internal */ + constructor(theChuck) { + this._accelActive = false; + // Initialize members + this.theChuck = theChuck; + this.boundHandleMotion = this.handleMotion.bind(this); + } + /** + * Initialize Accel functionality in your WebChucK instance. + * This adds a `Accel` and `AccelMsg` class to the ChucK Virtual Machine (VM). + * Accelerometer event (DeviceMotionEvent) listeners are added if `enableAccel` is true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * accel = await Accel.init(theChuck); // Initialize Accel + */ + static async init(theChuck, enableAccel = true) { + const accel = new Accel(theChuck); + // Add Accel and AccelMsg classes to ChucK VM + await accel.theChuck.runCode(AccelMsg_ck); + await accel.theChuck.runCode(Accel_ck); + // Enable mouse and keyboard + /* + if (enableAccel) { + // If iOS, request permission + if (typeof (DeviceOrientationEvent as any).requestPermission === 'function') { + const permission = await (DeviceOrientationEvent as any).requestPermission(); + if (permission === 'granted') { + accel.enableAccel(); + } else { + console.log("Accelscope permission denied."); + } + } else { + // just try to enable + accel.enableAccel(); + } + } + */ + accel.enableAccel(); + return accel; + } + /** + * @internal + * Check if accel is active + */ + async accelActive() { + const x = await this.theChuck.getInt("_accelActive"); + this._accelActive = x == 1; + } + /** + * Enable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is not yet enabled + * accel.enableAccel(); + * ``` + */ + enableAccel() { + // consider using "deviceorientationabsolute" + // https://developer.mozilla.org/en-US/docs/Web/API/Window/deviceorientationabsolute_event + window.addEventListener("devicemotion", this.boundHandleMotion); + } + /** + * Disable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is enabled + * accel.disableAccel(); + * ``` + */ + disableAccel() { + window.removeEventListener("devicemotion", this.boundHandleMotion); + } + //----------------------------------------- + // JAVASCRIPT HID EVENT HANDLERS + //----------------------------------------- + /** @internal */ + handleMotion(event) { + this.accelActive(); + if (this._accelActive) { + if (event.acceleration != null) { + this.theChuck.setFloat("_accelX", event.acceleration.x ? event.acceleration.x : 0.0); + this.theChuck.setFloat("_accelY", event.acceleration.y ? event.acceleration.y : 0.0); + this.theChuck.setFloat("_accelZ", event.acceleration.z ? event.acceleration.z : 0.0); + this.theChuck.broadcastEvent("_accelReading"); + } + } + } +} + +export { Accel, Chuck, DeferredPromise, Gyro, HID }; diff --git a/test/chuckTest.js b/test/chuckTest.js index 28ceaab..1a6372b 100644 --- a/test/chuckTest.js +++ b/test/chuckTest.js @@ -1,7 +1,7 @@ //======================================================================= // WebChucK Test Suite //======================================================================= -import { Chuck, HID, Gyro} from '../src/wc-bundle.js'; +import { Chuck, HID, Gyro, Accel} from '../src/wc-bundle.js'; /** WebChucK Test Class */ class Test { @@ -348,7 +348,7 @@ const testSuite = [ return outputBox.innerText.includes("PASSED"); }), - /*new Test(16, "Gyro", async () => { + new Test(16, "Gyro", async () => { const aChuck = await Chuck.init([], undefined, undefined, "../src/"); const outputBox = document.getElementById("output-" + 16); aChuck.chuckPrint = (output) => { @@ -360,7 +360,21 @@ const testSuite = [ aChuck.runFile("gyro.ck"); return true; - }),*/ + }), + + new Test(17, "Accel", async () => { + const aChuck = await Chuck.init([], undefined, undefined, "../src/"); + const outputBox = document.getElementById("output-" + 17); + aChuck.chuckPrint = (output) => { + outputBox.innerHTML = output + "
"; // += for additive + } + + let accel = await Accel.init(aChuck); + await aChuck.loadFile("./testFiles/accel.ck"); + aChuck.runFile("accel.ck"); + + return true; + }), new Test(99, "Chuck VM operations and parameters", async () => { @@ -499,12 +513,15 @@ filterButton.addEventListener("click", (e) => { filterTests() }); -runButton.addEventListener("click", () => { +runButton.addEventListener("click", async () => { + + if (typeof DeviceMotionEvent.requestPermission === 'function') { + await DeviceMotionEvent.requestPermission(); + } - // Added to request device orientation event permissions (Mike). Needs to be on button press. if (typeof DeviceOrientationEvent.requestPermission === 'function') { - DeviceOrientationEvent.requestPermission(); - } + await DeviceOrientationEvent.requestPermission(); + } audioContext.resume(); runTestSuite() diff --git a/test/testFiles/accel.ck b/test/testFiles/accel.ck new file mode 100644 index 0000000..ef3d1f5 --- /dev/null +++ b/test/testFiles/accel.ck @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// name: accel.ck +// desc: interface with "deviceorientation" JS events (on web, on mobile) and read x, y, z values +// +// author: Mike Mulshine +//----------------------------------------------------------------------------- + +Accel ac; +AccelMsg msg; + +0 => int device; + +// open accel +if( !ac.openAccel( device ) ) me.exit(); +<<< "accel '" + ac.name() + "' ready", "" >>>; + +<<< "only on mobile" >>>; + +// infinite event loop +while( true ) +{ + // wait on accel event + ac => now; + + // get one or more messages + while( ac.recv( msg ) ) + { + // print accel values + <<< msg.getAccelX() + " " + msg.getAccelY() + " " + msg.getAccelZ() >>>; + } +} + diff --git a/test/testFiles/gyro.ck b/test/testFiles/gyro.ck index 951a9ef..5cb5310 100644 --- a/test/testFiles/gyro.ck +++ b/test/testFiles/gyro.ck @@ -14,13 +14,12 @@ GyroMsg msg; if( !gy.openGyro( device ) ) me.exit(); <<< "gyro '" + gy.name() + "' ready", "" >>>; +<<< "only on mobile" >>>; + // infinite event loop -while( now < end ) +while( true ) { // wait on gyro event - - <<< "only on mobile" >>>; - gy => now; // get one or more messages From 489dde8684e15aa0005e14d268f5dfea1f6e3562 Mon Sep 17 00:00:00 2001 From: Mike Mulshine Date: Fri, 18 Oct 2024 15:04:11 -0700 Subject: [PATCH 09/13] docs --- docs/assets/navigation.js | 2 +- docs/assets/search.js | 2 +- docs/classes/Chuck.html | 118 +++++++++++++++++++------------------- docs/classes/HID.html | 36 ++++++------ docs/index.html | 4 +- docs/modules.html | 6 +- 6 files changed, 89 insertions(+), 79 deletions(-) diff --git a/docs/assets/navigation.js b/docs/assets/navigation.js index a5ee8f0..a5c48ef 100644 --- a/docs/assets/navigation.js +++ b/docs/assets/navigation.js @@ -1 +1 @@ -window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE4uuVipJrShRslJyzihNzlbSUSpILMlQslJKzkksLk4t1gcL62WU5OYo6ShlZ+alKFkZGlnU6sD1eXi6YOry8HTB1BMLAI3BsQVtAAAA" \ No newline at end of file +window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE4uuVipJrShRslJyzihNzlbSUSpILMkAcpNzEouLU4v1wcJ6GSW5OUC57My8FCUrQyOLWh24Pg9PF0xdQEF8etwri/IxNYFE8elyTE5OzcHUBhbG1BcLAJpuLdjbAAAA" \ No newline at end of file diff --git a/docs/assets/search.js b/docs/assets/search.js index 46254e6..33b00e5 100644 --- a/docs/assets/search.js +++ b/docs/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAE72bXW+jyBKG/wt7y2TTX5DkbjRzZifaXZ3RWc3uhRWNiN2xUTBYgJONovz3o26MqTLVDYZRrqIZV9XbVU9/0TSvQVk8V8HN4jV4TPNVcMP4VRjkyVYHN8GnzX75GITBvsyCm2CZJVWlq1/t/15s6m0WhO1/BjdB8Ba2Mfil7IKkeVp7Yvxy+B0ECoNdUuq8PrbAETkrktWnzX6d5r74yGqcimL8KLIs8qou98u6KH0q2KyTqavVh7T6sCvTp6TWPlF2yeVRdaUfdFnq1bey2KYmkkeasP3Z+p+KfV5rbwGcHrPbop90Xn9Ksuw+WT56K9Gz/LnaI6rgsJ/djrT6n05WL95xdDSZrba0w8Vb6s5kihoaxrn+t/5ses/tZ58iNputuix1Uusvaaa9aUKrCVNU4/85LbWZGbz8+qYTp8ShnIDNRAV9mFC9PeTUcIJWuc8/FStvMp3JlPh6lyVLPaiBzCbpbIsn/UdS1cNSJ5bTqjbUBTqT6fH/SevNx3Lt7QN90+mUBnNCZvN0RuVGmk/uHX9tSr0a7hqt2QSdtLLOH5d1+uSt5KnhBK0qXedJ9h+zGvqUsNkEnfuySFbLpKoHpXqWU+a+tKp1/qUobYz/5kv/PEtZT6llnZT1HzZYmq/beN6yujwmqRe7M8Vphynaur4dEGstJkRfD0Zfz4he6fpLViRDrW9tprV/UGE9S6HS9V91mebrgSSORtOyGNZYz9NoesnHsky8OzBsNrlHDeqs5+qAhv6dZHvvLETYzstsUJGwnZbjx6oqlmliVqFz0vW4Tct8Sjv8bjPmkzG9GBnOmFnG9OS5Wqi5Y/D2redmOAbmT1HFnfO8tL2Os/v1ecX42W2pdP0tKZPt8GIPzKblPEZnPVenbeiYDQAynJHTmK3AXK22uaM2BdhyRmajtgez1fLi2Xv6ZH+ecgKU6aS0Frd5VScDzwyk9VTV37LiPsn8x3jYboqS+fFbmfqHFLIapwJfRHwljga/3n6e+RKijeB/BWG0Heek9UbTr0iOkYHFmBNLj9ajftkmO7fS8feZOj+2xb7SjkOCo9qJ1VzNx3vXsUSnCG1m6t0X+3z1NclXmf7T5PFn4VN2WJ/fhkgpEXVJ/6hfdmfKXhydnH31gmzuyEJ8Lp7779KcLTpYv3chjOyhEB/YWaWwDR5Ziu+esUbavncZvu/aIvCzivB9N7YE/2y0zsY3qDV/70JY3bYW4qxaNE0eLsfv+mX00Ohs37EQB9G2CnJsFdrGjirByCHRWr5v+t1wUGckjwYD2jHoPLk/9BJ3E7DRmP0D0lil1QiRE6uzVZpG/q5f7ouk7L9iOEkG2E3NZ1iqb+jRuguDNF/pf4Ob1+BJl1Va5MFNwC/ExXUQBg+pzlbmAkvTiDBYFtut8b47/Pa3fZ9qLBqTXy+DcHEZiuuLOIru7sJF62F/sP9hzVgQLlgo5EWkYmTGkBkPwgWnonFkJoJwISgzgcxkEC4kZSaRmQrChaLMFDKLgnARUWYRMouDcBFTZjEyuwrCxRVldoXMroNwcU2ZXePymmozkgM7AWFJMNISs2Cm5oykwTAOZsrOSCAME2Gm8oxkwjAUZorPSCwMc2Gm/owkwzAaZhAwEg7DdJihwEg+DANiBgQjETHMiBsQnGTEMSNuQHCSET8ZL3bA0CMGM+IGBCcZccyIGxCcZMQxI25AcJIRx4y4AcFJRhwz4gYEJxlxzIgbEJxkxDEjbkBwkhHHjIQBIUhGAjMSBoQgGQnMSBgQgmQkTqY1O6/RExtmJAwIQTISmJEwIATJSGBGwoAQJCOBGQkDQpCMBGYkDAhBMhKYkTAgBMlIYEbSgJAkI4kZSQNCkowkZiQNCEkykpiRNCAkyUierD52+aHXH8xIGhCSZCQxI2lASJKRxIykASFJRhIzkgaEJBlJzEgaEJJkJDEjZUAokpHCjJQBoUhGCjNSBoQiGSnMSBkQimSkMCNlQCiSkTrZJNhdAr1NwIyUAaFIRgozUgaEIhkpzEhdufZPCiNShoMiYSqMKLKISJgRRhQZDhEJM8KIIsMhImFGGFFkOEQkzAgjigyHSIRcXbBLji0xoshwiEiY0clWLnLHxIgiwyEisUcYUXTljokZRQZERG8lMaP40hkzxoxiy4jed2JGMXfHxIxiy4jsSzFmFLsZxZhRbBmRvS7GjGIDIiZ7XXyy47ZbbrLXxZhRbEDEZK+LG0b28ehJl7Ve3TaPSYvF8dn5NfhxeHaKRPuY9hpEMrh5fQuDKDr8vWr+xpeHv/zw19q9dc9U5l+mCeYwNjkcxgIJBiS4y9WeHBPOl8CZOZztw/vGPrw/6peVPWYBIeIuRDwuxH6HAlyBAGI4gE2l1woJElEjg2yLk2pwEGRsS06SUSBEPDLEc3OGB6JEIMq1K0p7t043d706dw4awV3FWDZvSDov0BUufT675oVS56hAa1Xkdm2uJHd+152bK0f7psyqpsc3c0AYQFeuUWNDrNuXbcAZFEk5iwQ/MOl8wah2dZPmYvmqu1jeeTPQzZhrxDb+D/ZeLXAFg525Blv7Uciu+Shk2X4QAXIHqY8LgkoHyu6q+uGs6fF41tR5xyD9+Mrvv22O34Az6KWxq681Z2qkNqhf7BqejXtPOgL9NXaVzY7F5eFTFKLycLIcEwLVHc4KDue1rpPunsiDuXWQmHsiT809EcAQBJPjoqW5KxaAIl1zx1rXD80lCDCOQFcQrpHQOibNdScgC7zlKO9+y2FXdvXlta5PZjwBEhaehNuKIV9QeOEpvLPaAqyXwjV+1rremcsZvZIr0HTlabr1PklbAmU5pFwdroUAacBLeXj1PQXAJFyYNika6goMNeUaa80dBDC/gum12Y0pV55pVTZffnXeoDxur8rc8u/vxDhYU7hrUWmvG4AswaQUuXA2d+IfitJOLcXJKspBh+SuDmm+K1oevucErsDT46hXxOrPAFPmYmq8e2sgqBRzVcp8t2aXMNwpGOj9zFUue+sIlBjoKZde851IllT10n5CBCRBP2Sufti4V81nJqC+sMCuCh8+ienpAqzMhfXge1piDsrEXWUCvs9pvUnspzggBhhL3LVVKfd5r91gc8Jcy2y5z3vdAow+5hp+Bz+qvQyMJObaj1ajl1eQhXRlUY1bXgVomfC0rL+8AgTChaByLq/AW47y7lcBDB3pGjpVb3nlIGHuSZhcXkHhhafw7mqD8Spc47VyLa8SNF16mk4ur0BZDikTyyvgpTy8iOUVPs84MdmvxvrPmmAa565p3H4albWfJ7ULEQoDcufO3Oti548C5gDumgPqje49+yrgqEhsd2GwS3c6S3Md3Czu3t7+DwG96PHEQQAA"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAE72c7W+jRhPA/xffV1+afQOcb6e759pTW/XUqu2HKDoRmzgotrGAJI2i+9+f3QXMDMwuGE75FMWel535zewuC/h1kWfPxeLq+nXxkB42iyvGo+XiEO+TxdXi4/3j+mGxXDzmO/3fehcXRVL8ZD+9uC/3O/1V/aH+evF92djgl7I1kh7S0mPjXf09MLRcHOM8OZSnETgs77J4owW26cFnH0mN86IYPzlZZ4eizB/XZZb7vGCx1k1ZbN6nxftjnj7FZeJzyi65PHndJHdJniebr3m2T40lj2tC9kf7/5g9HsrEmwCnxuyxJE9a6GO8293G6wdvJnqSP9b3iCw45GePIy3+TOLNi7ePTiKzva1tu3hT3YpM8Yba+JD8V34y1fPlk88jFpvtdZ0nWvxzuku8YUKpCVNUpf8pzRMzM3j59UUnTolDMQGZiR6SekL1VkhXcIKv/PHwMdt4g2lFpthPjrt4nQz6QGKT/Oyzp+S3uCiHXXUkp2VtqARaken2/03L+w/51lsDfdHplAZjQmLz/IyKjRSfXB1/3evFc7g0GrEJftLCKn9Yl+mTN5NdwQm+inR7iHf/M6uhzxMWm+DnNtezzFq3y6CrnuSUuS8tyuTwOcutjT8Oa/88S0lPyWUZ5+Vv1lh62Db2vGl1aUzynh3PdE4rTPGdlF8GnDUSE6xvB61vZ1jXI/usl8Ch0Tcy08Y/6GE7y4Me319lrjEOBHESmhbFsI/tPB9VlXzI89i7A8Nikytq0M92rh8w0H/i3aN3FiJk50U26JGQnRbjh6LI1mlsVqFzwvWoTYt8yjj8ajPmkzFVjARnzCxjKnmuLzTcMXj70nMjHAPzh3jFxXle2F7F2XV9XjJ+9Fh0eF/jPN4PL/ZAbFrMY/xs5/ppBjpmA4AEZ8Q0Zisw11cz3FGbAiw5I7JR24PZ3g7Zs89F9fWUE6BdEudW4stB78sHrhlI6alef95lt/HOf4yH5aZ4Ml9+1Xn3Fh+SGucF3oj4hTga1J/NvAnRWPDfgjC+Heek5X1C3yI5WQYSY04sPb4ekpd9fHR7On0/08+3ffZYJI5DgpO3jtRcnw+3rmOJ1iOUmenvNns8bH6JD5td8ruJ4/fM59khff4YAqVE0Ab9rXw5nun24qTkrNULcrgjE/Epe+7fS3OOqJZ+60QYt3Ui3rOzUmEHPDIVf3t6jZR96zT8fWySwM9Kgh7syBT8e58ku/EDasTfOhHWb5MLcVYuqiEPp+PX5GV0a7Syb5iI2mmTBTk2C81gR6VgZEs0km8bftsO6ozgUTOgHUNyiG/rKnEPAQuN2T8gH5u0GOGkI3W2l2qQOtjbLM77txg6wQC5qfEMu+oLjtp7gR3hzy951rNvPpy5JzyZ8G8Krftzd4Wt7TO3hT5v37b6S8fmqXWIpeb6BD30R55q+bhMs/7s2Lp3KkwYyahJYsj10EbK6jtG7W0zf12+Q0KjSotqsAEnWGpcAYPW+rBeE8u+/XRmc7U2/N1VjeDc9gLWz+wvr79vsfnW0WHAZ0dutl+0VyH7CzinhKeMYFRz+fwONVal2x+ut6mGKvIdFhtXVFRjDTrqyHk93Sx1F2yS/xZXr4unJC9MmFcLfiEuVlr6Lk12G/PgZTUEbSvb743+Tf3dP/Y5ICNRifx0uVheXy4lu1DR6uZmed1o2C/sB1ZMXxJds6W4vAguBRJjSExfNFxzyhpHYno/fS0oMYHE9IbzWlJiEonprdm1osQUEgv0fwElFiCxUP8XUmIhEov0fxElFiExDeV6RYmtcHpNthnJgXVAWBKMlMQsmMk5I2kwjIOZtDMSCMNEmMk8I5kwDIWZ5DMSC8NcmMk/I8kwjIYZBIyEwzAdZigwkg/DgJgBwUhEDDPiBgQnGXHMiBsQnGTEO/1iG4buGMyIGxCcZMQxI25AcJIRx4y4AcFJRhwz4gYEJxlxzIgbEJxkxDEjbkBwkhHHjLgBwUlGHDMSBoQgGQnMSBgQgmQkMCNhQAiSkehMa3Zeoyc2zEgYEIJkJDAjYUAIkpHAjIQBIUhGAjMSBoQgGQnMSBgQgmQkMCNhQAiSkcCMpAEhSUYSM5IGhCQZScxIGhCSZCQxI2lASJKR7Kw+dvmh1x/MSBoQkmQkMSNpQEiSkcSMpAEhSUYSM5IGhCQZScxIGhCSZCQxI2VAKJKRwoyUAaFIRgozUgaEIhkpzEgZEIpkpDAjZUAokpHqbBLsLoHeJmBGyoBQJCOFGSkDQpGMFGakItf+SWFEynBQ0VKIC8YCLIkRBRYRCTPAiALDISBhBhhRYDgEJMwAIwoMh4CEGWBEgeEQiCWXF1zi2AOMKDAcAhJm0NnKBW6bGFFgOAQk9gAjCiK3TcwoMCACeiuJGYWXTpshZhRaRvS+EzMKudsmZhRaRuTEEGJGoZtRiBmFlhFZdSFmFBoQIVl1YWfHbbfcZNWFmFFoQIRk1YWYUWhAhOQUEmJG0aWrNSOMKGLO1owwoshwCMlCjjCiyHAIyfKMMKLIjSjCiCLDISTLM8KIIouILLoII4osIvp6p3Nd5JzpIkwocs90ESa0MhxC+ioKI1oZDhFZcyuMaOXuohVGtDIcIrI6VxjRynCIyOpcVYjsxbu+ai+TzZfqIl5fhjdnG6+Lb/WVvR5XfYTwutBz5NXr9+VCz2vV36j6q+eP6i+v/9ZyUf13ZT//3p4AmP/MkKoTpbg+UWqd6gSfnK4uXcrmxJfQla1u5HT8cLvpq+rVpo3VqWofDCCUL4EycyjH1bEKGG0ERhs6tOxh0r09THpIXjb23htwHLYmQpdjbOLxiAyAMejJeNDAvj5/ay1EIPbViCHYFPbiAOD0GjvOyD7rUODAyKhYtJFOOhQwMQKJNfFc3RoGVgJgZTVsJYN3DUB9gMFEzniaNz+S6k2EVp0Dde7K6bo6SG61AE1X81mdY/W4U6uoQNAqcKtWL8y1erDdXVrmOS7rNT09NwYcg9pR0mdi2zwKBpRBkpQzSfD151YXzI4uOtVrj5v2tcdWm4FqZa4Jp9K/s299AVUwVzFXyzWvLB+rV5bXzeu6IHYQ+jgjKHUg7a6s1wfKvZkvArGvBpS39kYP6Aow5UWuOqt1H063YIE+8B1Gfv19dVcaKIP2CF3Oq/P6ftCA2spVMJVuL2bQW5GLVaVKhgw8h65ZrVLvRRyA/gydrs3cs65fDCcqDa5SY0ygOoOTqUN5m5Rx+9T2nXkGODZPbT9VT22DmgXG5Dhreppz2AK1IF1zpbZ1Vz2SDOYNUIHC1fmNYly9fADcAm05Srs/cti6rvbTJjozvAABC0/ATcaQLki88CTemW0BNirC1bbawNE8Kt1LuQJDV56hW+1O2BJ4lkOei/ohbeAa8FIeXn1NATAJJ6buVAFWpdC1oN6naH5QoD+Vq0Gre9pgEQJr0MJu9VV9SRDVlwSRK1lpkVc/5gAmRzDBObUK8+Juf/fNQcjcNbE2TxCDqMHMFrhqonrN9S7L7fyUdbYeHFQ1d1W1+amAdf0TLUAVaHoUkw2xZWKgMJirMIx2b+MAMsVcmTI/RWHXfVwkDLQQc6XLvkgAUgz8KZe/6tXvnd7Cru2vAgCXoC6Zqy4r9aJ6cxzkFybYleH6LfeeX4CVubDWut0Uc5Am7koT0H1Oy/vYvl0PbIDe4q79Xf546I0b7OiYa63Wer2yAN3HXO1X61HjZaCTmGvOKUav0SAK6YqiGLdGCzAy4RlZf40GCIQLQeFco4G2HKXdzwJoHelqnaK3RnMQMPcETK7RIPHCk3h3tkG/Cle/Fq41WoKhS8/QyTUaeJZDnok1GvBSHl7EGg0vAp2Y7A9B9C/QwTTOXdO4/bWDXfOLA81ChMyA2Lkz9jI7+q2AOYC75oDyPukdGCigqFb1yl/vCCIS481ycUyPyS49aKXrm+/f/w/Z0aacp00AAA=="; \ No newline at end of file diff --git a/docs/classes/Chuck.html b/docs/classes/Chuck.html index 34559ff..0dd9f3a 100644 --- a/docs/classes/Chuck.html +++ b/docs/classes/Chuck.html @@ -28,7 +28,7 @@

Hierarchy

  • Chuck
+
  • Defined in src/Chuck.ts:32
  • @@ -151,39 +151,39 @@

    Returns

    +
  • Defined in src/Chuck.ts:54
  • Properties

    deferredPromises: DeferredPromisesMap = {}
    +
  • Defined in src/Chuck.ts:33
  • deferredPromiseCounter: number = 0
    +
  • Defined in src/Chuck.ts:34
  • eventCallbacks: EventCallbacksMap = {}
    +
  • Defined in src/Chuck.ts:35
  • eventCallbackCounter: number = 0
    +
  • Defined in src/Chuck.ts:36
  • isReady: default<void> = ...
    +
  • Defined in src/Chuck.ts:37
  • chugins: string[] = []
    +
  • Defined in src/Chuck.ts:43
  • onprocessorerror: null | ((this, ev) => any)
    @@ -305,7 +305,7 @@

    Example

    // Initialize
     
    +
  • Defined in src/Chuck.ts:117
  • +
  • Defined in src/Chuck.ts:235
  • +
  • Defined in src/Chuck.ts:159
  • +
  • Defined in src/Chuck.ts:174
  • +
  • Defined in src/Chuck.ts:186
  • +
  • Defined in src/Chuck.ts:201
  • +
  • Defined in src/Chuck.ts:245
  • +
  • Defined in src/Chuck.ts:255
  • +
  • Defined in src/Chuck.ts:285
  • +
  • Defined in src/Chuck.ts:324
  • +
  • Defined in src/Chuck.ts:344
  • +
  • Defined in src/Chuck.ts:379
  • +
  • Defined in src/Chuck.ts:419
  • +
  • Defined in src/Chuck.ts:462
  • +
  • Defined in src/Chuck.ts:511
  • +
  • Defined in src/Chuck.ts:525
  • +
  • Defined in src/Chuck.ts:539
  • +
  • Defined in src/Chuck.ts:547
  • +
  • Defined in src/Chuck.ts:558
  • +
  • Defined in src/Chuck.ts:576
  • +
  • Defined in src/Chuck.ts:592
  • +
  • Defined in src/Chuck.ts:606
  • +
  • Defined in src/Chuck.ts:616
  • +
  • Defined in src/Chuck.ts:630
  • +
  • Defined in src/Chuck.ts:639
  • +
  • Defined in src/Chuck.ts:653
  • +
  • Defined in src/Chuck.ts:662
  • +
  • Defined in src/Chuck.ts:677
  • +
  • Defined in src/Chuck.ts:686
  • +
  • Defined in src/Chuck.ts:701
  • +
  • Defined in src/Chuck.ts:715
  • +
  • Defined in src/Chuck.ts:733
  • +
  • Defined in src/Chuck.ts:748
  • +
  • Defined in src/Chuck.ts:764
  • +
  • Defined in src/Chuck.ts:774
  • +
  • Defined in src/Chuck.ts:789
  • +
  • Defined in src/Chuck.ts:804
  • +
  • Defined in src/Chuck.ts:822
  • +
  • Defined in src/Chuck.ts:837
  • +
  • Defined in src/Chuck.ts:854
  • +
  • Defined in src/Chuck.ts:863
  • +
  • Defined in src/Chuck.ts:877
  • +
  • Defined in src/Chuck.ts:885
  • +
  • Defined in src/Chuck.ts:899
  • +
  • Defined in src/Chuck.ts:908
  • +
  • Defined in src/Chuck.ts:922
  • +
  • Defined in src/Chuck.ts:932
  • +
  • Defined in src/Chuck.ts:939
  • +
  • Defined in src/Chuck.ts:960
  • \ No newline at end of file diff --git a/docs/classes/HID.html b/docs/classes/HID.html index 07ba628..e457388 100644 --- a/docs/classes/HID.html +++ b/docs/classes/HID.html @@ -31,7 +31,7 @@

    Hierarchy

    • HID
    +
  • Defined in src/Hid.ts:27
  • @@ -64,22 +64,22 @@

    Properties

    theChuck: Chuck
    +
  • Defined in src/Hid.ts:29
  • keymap: boolean[]
    +
  • Defined in src/Hid.ts:30
  • _mouseActive: boolean = false
    +
  • Defined in src/Hid.ts:31
  • _kbdActive: boolean = false
    +
  • Defined in src/Hid.ts:32
  • boundHandleMouseMove: {}
    @@ -89,7 +89,7 @@

    Type declaration

    • +
    • Defined in src/Hid.ts:35
    • boundHandleMouseDown: {}
      @@ -99,7 +99,7 @@

      Type declaration

      • +
      • Defined in src/Hid.ts:36
      • boundHandleMouseUp: {}
        @@ -109,7 +109,7 @@

        Type declaration

        • +
        • Defined in src/Hid.ts:37
        • boundHandleMouseWheel: {}
          @@ -119,7 +119,7 @@

          Type declaration

          • +
          • Defined in src/Hid.ts:38
          • boundHandleKeyDown: {}
            @@ -129,7 +129,7 @@

            Type declaration

            • +
            • Defined in src/Hid.ts:39
            • boundHandleKeyUp: {}
              @@ -139,7 +139,7 @@

              Type declaration

              • +
              • Defined in src/Hid.ts:40
              • Methods

                @@ -178,7 +178,7 @@

                Example

                theChuck
                +
              • Defined in src/Hid.ts:75
                • @@ -194,7 +194,7 @@

                  Example

                  // If mouse HI
                   
                +
              • Defined in src/Hid.ts:136
                • @@ -208,7 +208,7 @@

                  Example

                  // If mouse HI
                   
                +
              • Defined in src/Hid.ts:152
                • @@ -223,7 +223,7 @@

                  Example

                  // If keyboard
                   
                +
              • Defined in src/Hid.ts:169
                • @@ -237,7 +237,7 @@

                  Example

                  // If keyboard
                   
                +
              • Defined in src/Hid.ts:182
              • +
              • HID
              • +
              • Gyro
              • +
              • Accel
              • \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 7b2ce14..38e381d 100644 --- a/docs/index.html +++ b/docs/index.html @@ -73,5 +73,7 @@

                webchuck +
              • HID
              • +
              • Gyro
              • +
              • Accel
              • \ No newline at end of file diff --git a/docs/modules.html b/docs/modules.html index 1f1c4c5..192d4e1 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -19,6 +19,8 @@

                Index

                Classes

                +
                +
                +
                  +
                • Preparing search index...
                • +
                • The search index is not available
                webchuck
                +
                +
                +
                +
                + +

                Class Accel

                +
                +

                Introducing Accel (accelerometer, on mobile) support for WebChucK. Accel wraps +JavaScript DeviceMotionEvent listeners easing access to mobile device accelerometers +in WebChucK code.

                +

                To get started with Accel:

                +
                +
                +

                Example

                import { Chuck, Accel } from "webchuck";

                const theChuck = await Chuck.init([]);
                const accel = await Accel.init(theChuck); // Initialize Accel +
                +
                +
                +

                Hierarchy

                +
                  +
                • Accel
                +
                +
                +
                + +
                +
                +

                Properties

                +
                + +
                theChuck: Chuck
                +
                + +
                _accelActive: boolean = false
                +
                + +
                boundHandleMotion: {}
                +
                +

                Type declaration

                +
                  +
                • +
                  +
                  +

                  Methods

                  +
                  + +
                    + +
                  • +

                    Initialize Accel functionality in your WebChucK instance. +This adds a Accel and AccelMsg class to the ChucK Virtual Machine (VM). +Accelerometer event (DeviceMotionEvent) listeners are added if enableAccel is true (default).

                    +
                    +
                    +

                    Parameters

                    +
                      +
                    • +
                      theChuck: Chuck
                    • +
                    • +
                      enableAccel: boolean = true
                    +

                    Returns Promise<Accel>

                    +
                    +

                    Example

                    theChuck = await Chuck.init([]);
                    accel = await Accel.init(theChuck); // Initialize Accel +
                    +
                  +
                  + +
                    + +
                  • +

                    Enable Javascript event (DeviceMotionEvent) listeners for Accel

                    +
                    +

                    Returns void

                    +
                    +

                    Example

                    // If accel is not yet enabled
                    accel.enableAccel(); +
                    +
                  +
                  + +
                    + +
                  • +

                    Disable Javascript event (DeviceMotionEvent) listeners for Accel

                    +
                    +

                    Returns void

                    +
                    +

                    Example

                    // If accel is enabled
                    accel.disableAccel(); +
                    +
                  +
                  + +
                  +
                  \ No newline at end of file diff --git a/docs/classes/Gyro.html b/docs/classes/Gyro.html new file mode 100644 index 0000000..586ec74 --- /dev/null +++ b/docs/classes/Gyro.html @@ -0,0 +1,156 @@ +Gyro | webchuck
                  +
                  + +
                  +
                  +
                  +
                  + +

                  Class Gyro

                  +
                  +

                  Introducing Gyro (gyroerometer, on mobile) support for WebChucK. Gyro wraps +JavaScript DeviceMotionEvent listeners easing access to mobile device gyroerometers +in WebChucK code.

                  +

                  To get started with Gyro:

                  +
                  +
                  +

                  Example

                  import { Chuck, Gyro } from "webchuck";

                  const theChuck = await Chuck.init([]);
                  const gyro = await Gyro.init(theChuck); // Initialize Gyro +
                  +
                  +
                  +

                  Hierarchy

                  +
                    +
                  • Gyro
                  +
                  +
                  +
                  + +
                  +
                  +

                  Properties

                  +
                  + +
                  theChuck: Chuck
                  +
                  + +
                  _gyroActive: boolean = false
                  +
                  + +
                  boundHandleOrientation: {}
                  +
                  +

                  Type declaration

                  +
                    +
                  • +
                    +
                    +

                    Methods

                    +
                    + +
                      + +
                    • +

                      Initialize Gyro functionality in your WebChucK instance. +This adds a Gyro and GyroMsg class to the ChucK Virtual Machine (VM). +Gyroerometer event (DeviceMotionEvent) listeners are added if enableGyro is true (default).

                      +
                      +
                      +

                      Parameters

                      +
                        +
                      • +
                        theChuck: Chuck
                      • +
                      • +
                        enableGyro: boolean = true
                      +

                      Returns Promise<Gyro>

                      +
                      +

                      Example

                      theChuck = await Chuck.init([]);
                      gyro = await Gyro.init(theChuck); // Initialize Gyro +
                      +
                    +
                    + +
                      + +
                    • +

                      Enable Javascript event (DeviceMotionEvent) listeners for Gyro

                      +
                      +

                      Returns void

                      +
                      +

                      Example

                      // If gyro is not yet enabled
                      gyro.enableGyro(); +
                      +
                    +
                    + +
                      + +
                    • +

                      Disable Javascript event (DeviceMotionEvent) listeners for Gyro

                      +
                      +

                      Returns void

                      +
                      +

                      Example

                      // If gyro is enabled
                      gyro.disableGyro(); +
                      +
                    +
                    + +
                    +
                    \ No newline at end of file From 556bc52fbbe0b2579c839291cc51b0472a7be1b7 Mon Sep 17 00:00:00 2001 From: Mike Mulshine Date: Fri, 18 Oct 2024 16:38:51 -0700 Subject: [PATCH 11/13] Added accel, gyro, hid to subfolders for organization, refactored to reflect path changes. --- dist/accel/Accel.d.ts | 57 +++++++++ dist/accel/Accel.js | 105 ++++++++++++++++ dist/accel/accelCk.d.ts | 3 + dist/accel/accelCk.js | 94 ++++++++++++++ dist/gyro/Gyro.d.ts | 57 +++++++++ dist/gyro/Gyro.js | 103 +++++++++++++++ dist/gyro/gyroCk.d.ts | 3 + dist/gyro/gyroCk.js | 94 ++++++++++++++ dist/hid/Hid.d.ts | 129 +++++++++++++++++++ dist/hid/Hid.js | 252 +++++++++++++++++++++++++++++++++++++ dist/hid/hidCk.d.ts | 3 + dist/hid/hidCk.js | 168 +++++++++++++++++++++++++ dist/index.d.ts | 6 +- dist/index.js | 6 +- src/{ => accel}/Accel.ts | 2 +- src/{ => accel}/accelCk.ts | 0 src/{ => gyro}/Gyro.ts | 2 +- src/{ => gyro}/gyroCk.ts | 0 src/{ => hid}/Hid.ts | 2 +- src/{ => hid}/hidCk.ts | 0 src/index.ts | 6 +- 21 files changed, 1080 insertions(+), 12 deletions(-) create mode 100644 dist/accel/Accel.d.ts create mode 100644 dist/accel/Accel.js create mode 100644 dist/accel/accelCk.d.ts create mode 100644 dist/accel/accelCk.js create mode 100644 dist/gyro/Gyro.d.ts create mode 100644 dist/gyro/Gyro.js create mode 100644 dist/gyro/gyroCk.d.ts create mode 100644 dist/gyro/gyroCk.js create mode 100644 dist/hid/Hid.d.ts create mode 100644 dist/hid/Hid.js create mode 100644 dist/hid/hidCk.d.ts create mode 100644 dist/hid/hidCk.js rename src/{ => accel}/Accel.ts (99%) rename src/{ => accel}/accelCk.ts (100%) rename src/{ => gyro}/Gyro.ts (99%) rename src/{ => gyro}/gyroCk.ts (100%) rename src/{ => hid}/Hid.ts (99%) rename src/{ => hid}/hidCk.ts (100%) diff --git a/dist/accel/Accel.d.ts b/dist/accel/Accel.d.ts new file mode 100644 index 0000000..0b9ede4 --- /dev/null +++ b/dist/accel/Accel.d.ts @@ -0,0 +1,57 @@ +import Chuck from "../Chuck"; +/** + * Introducing Accel (accelerometer, on mobile) support for WebChucK. Accel wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device accelerometers + * in WebChucK code. + * + * To get started with Accel: + * @example + * ```ts + * import { Chuck, Accel } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const accel = await Accel.init(theChuck); // Initialize Accel + * ``` + */ +export default class Accel { + private theChuck; + private _accelActive; + private boundHandleMotion; + /** @internal */ + constructor(theChuck: Chuck); + /** + * Initialize Accel functionality in your WebChucK instance. + * This adds a `Accel` and `AccelMsg` class to the ChucK Virtual Machine (VM). + * Accelerometer event (DeviceMotionEvent) listeners are added if `enableAccel` is true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * accel = await Accel.init(theChuck); // Initialize Accel + */ + static init(theChuck: Chuck, enableAccel?: boolean): Promise; + /** + * @internal + * Check if accel is active + */ + accelActive(): Promise; + /** + * Enable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is not yet enabled + * accel.enableAccel(); + * ``` + */ + enableAccel(): void; + /** + * Disable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is enabled + * accel.disableAccel(); + * ``` + */ + disableAccel(): void; + /** @internal */ + private handleMotion; +} diff --git a/dist/accel/Accel.js b/dist/accel/Accel.js new file mode 100644 index 0000000..136d780 --- /dev/null +++ b/dist/accel/Accel.js @@ -0,0 +1,105 @@ +import { Accel_ck, AccelMsg_ck } from "./accelCk"; +/** + * Introducing Accel (accelerometer, on mobile) support for WebChucK. Accel wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device accelerometers + * in WebChucK code. + * + * To get started with Accel: + * @example + * ```ts + * import { Chuck, Accel } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const accel = await Accel.init(theChuck); // Initialize Accel + * ``` + */ +export default class Accel { + /** @internal */ + constructor(theChuck) { + this._accelActive = false; + // Initialize members + this.theChuck = theChuck; + this.boundHandleMotion = this.handleMotion.bind(this); + } + /** + * Initialize Accel functionality in your WebChucK instance. + * This adds a `Accel` and `AccelMsg` class to the ChucK Virtual Machine (VM). + * Accelerometer event (DeviceMotionEvent) listeners are added if `enableAccel` is true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * accel = await Accel.init(theChuck); // Initialize Accel + */ + static async init(theChuck, enableAccel = true) { + const accel = new Accel(theChuck); + // Add Accel and AccelMsg classes to ChucK VM + await accel.theChuck.runCode(AccelMsg_ck); + await accel.theChuck.runCode(Accel_ck); + // Enable mouse and keyboard + /* + if (enableAccel) { + // If iOS, request permission + if (typeof (DeviceOrientationEvent as any).requestPermission === 'function') { + const permission = await (DeviceOrientationEvent as any).requestPermission(); + if (permission === 'granted') { + accel.enableAccel(); + } else { + console.log("Accelscope permission denied."); + } + } else { + // just try to enable + accel.enableAccel(); + } + } + */ + accel.enableAccel(); + return accel; + } + /** + * @internal + * Check if accel is active + */ + async accelActive() { + const x = await this.theChuck.getInt("_accelActive"); + this._accelActive = x == 1; + } + /** + * Enable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is not yet enabled + * accel.enableAccel(); + * ``` + */ + enableAccel() { + // consider using "deviceorientationabsolute" + // https://developer.mozilla.org/en-US/docs/Web/API/Window/deviceorientationabsolute_event + window.addEventListener("devicemotion", this.boundHandleMotion); + } + /** + * Disable Javascript event (DeviceMotionEvent) listeners for Accel + * @example + * ```ts + * // If accel is enabled + * accel.disableAccel(); + * ``` + */ + disableAccel() { + window.removeEventListener("devicemotion", this.boundHandleMotion); + } + //----------------------------------------- + // JAVASCRIPT HID EVENT HANDLERS + //----------------------------------------- + /** @internal */ + handleMotion(event) { + this.accelActive(); + if (this._accelActive) { + if (event.acceleration != null) { + this.theChuck.setFloat("_accelX", event.acceleration.x ? event.acceleration.x : 0.0); + this.theChuck.setFloat("_accelY", event.acceleration.y ? event.acceleration.y : 0.0); + this.theChuck.setFloat("_accelZ", event.acceleration.z ? event.acceleration.z : 0.0); + this.theChuck.broadcastEvent("_accelReading"); + } + } + } +} diff --git a/dist/accel/accelCk.d.ts b/dist/accel/accelCk.d.ts new file mode 100644 index 0000000..9e086fa --- /dev/null +++ b/dist/accel/accelCk.d.ts @@ -0,0 +1,3 @@ +declare const AccelMsg_ck = "\npublic class AccelMsg {\n float accelX;\n float accelY;\n float accelZ;\n\n function float getAccelX() {\n return accelX;\n }\n\n function float getAccelY() {\n return accelY;\n }\n\n function float getAccelZ() {\n return accelZ;\n }\n\n function void _copy(AccelMsg localMsg) {\n localMsg.accelX => accelX;\n localMsg.accelY => accelY;\n localMsg.accelZ => accelZ;\n }\n}\n"; +declare const Accel_ck = "\nglobal Event _accelReading;\nglobal int _accelActive;\n\nglobal float _accelX;\nglobal float _accelY;\nglobal float _accelZ;\n\npublic class Accel extends Event {\n\n 0 => int isAccelOpen;\n 0 => int active;\n\n string deviceName; \n\n // AccelMsg Queue\n AccelMsg _accelMsgQueue[0];\n\n function string name() {\n return deviceName;\n }\n\n function int openAccel(int num) {\n if (num < 0) {\n false => active;\n } else {\n \"js DeviceMotionEvent\" => deviceName;\n true => active;\n }\n active => isAccelOpen => _accelActive;\n spork ~ _accelListener();\n return active;\n }\n\n\n // Pop the first AccelMsg from the queue\n // Write it to msg and return 1\n function int recv(AccelMsg msg) {\n // is empty\n if (_accelMsgQueue.size() <= 0) {\n return 0;\n }\n\n // pop the first AccelMsg to msg, return true\n _accelMsgQueue[0] @=> AccelMsg localMsg;\n msg._copy(localMsg); \n _accelMsgQueue.popFront();\n return 1;\n }\n\n // Accel Listener\n // Get variables from JS and write to the AccelMsg \n function void _accelListener() {\n AccelMsg @ msg;\n while(true){\n new AccelMsg @=> msg;\n _accelReading => now;\n\n _accelX => msg.accelX;\n _accelY => msg.accelY;\n _accelZ => msg.accelZ;\n\n _accelMsgQueue << msg;\n this.broadcast();\n }\n }\n}\n"; +export { AccelMsg_ck, Accel_ck }; diff --git a/dist/accel/accelCk.js b/dist/accel/accelCk.js new file mode 100644 index 0000000..fd68455 --- /dev/null +++ b/dist/accel/accelCk.js @@ -0,0 +1,94 @@ +const AccelMsg_ck = ` +public class AccelMsg { + float accelX; + float accelY; + float accelZ; + + function float getAccelX() { + return accelX; + } + + function float getAccelY() { + return accelY; + } + + function float getAccelZ() { + return accelZ; + } + + function void _copy(AccelMsg localMsg) { + localMsg.accelX => accelX; + localMsg.accelY => accelY; + localMsg.accelZ => accelZ; + } +} +`; +const Accel_ck = ` +global Event _accelReading; +global int _accelActive; + +global float _accelX; +global float _accelY; +global float _accelZ; + +public class Accel extends Event { + + 0 => int isAccelOpen; + 0 => int active; + + string deviceName; + + // AccelMsg Queue + AccelMsg _accelMsgQueue[0]; + + function string name() { + return deviceName; + } + + function int openAccel(int num) { + if (num < 0) { + false => active; + } else { + "js DeviceMotionEvent" => deviceName; + true => active; + } + active => isAccelOpen => _accelActive; + spork ~ _accelListener(); + return active; + } + + + // Pop the first AccelMsg from the queue + // Write it to msg and return 1 + function int recv(AccelMsg msg) { + // is empty + if (_accelMsgQueue.size() <= 0) { + return 0; + } + + // pop the first AccelMsg to msg, return true + _accelMsgQueue[0] @=> AccelMsg localMsg; + msg._copy(localMsg); + _accelMsgQueue.popFront(); + return 1; + } + + // Accel Listener + // Get variables from JS and write to the AccelMsg + function void _accelListener() { + AccelMsg @ msg; + while(true){ + new AccelMsg @=> msg; + _accelReading => now; + + _accelX => msg.accelX; + _accelY => msg.accelY; + _accelZ => msg.accelZ; + + _accelMsgQueue << msg; + this.broadcast(); + } + } +} +`; +export { AccelMsg_ck, Accel_ck }; diff --git a/dist/gyro/Gyro.d.ts b/dist/gyro/Gyro.d.ts new file mode 100644 index 0000000..3fd547a --- /dev/null +++ b/dist/gyro/Gyro.d.ts @@ -0,0 +1,57 @@ +import Chuck from "../Chuck"; +/** + * Introducing Gyro (gyroerometer, on mobile) support for WebChucK. Gyro wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device gyroerometers + * in WebChucK code. + * + * To get started with Gyro: + * @example + * ```ts + * import { Chuck, Gyro } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const gyro = await Gyro.init(theChuck); // Initialize Gyro + * ``` + */ +export default class Gyro { + private theChuck; + private _gyroActive; + private boundHandleOrientation; + /** @internal */ + constructor(theChuck: Chuck); + /** + * Initialize Gyro functionality in your WebChucK instance. + * This adds a `Gyro` and `GyroMsg` class to the ChucK Virtual Machine (VM). + * Gyroerometer event (DeviceMotionEvent) listeners are added if `enableGyro` is true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * gyro = await Gyro.init(theChuck); // Initialize Gyro + */ + static init(theChuck: Chuck, enableGyro?: boolean): Promise; + /** + * @internal + * Check if gyro is active + */ + gyroActive(): Promise; + /** + * Enable Javascript event (DeviceMotionEvent) listeners for Gyro + * @example + * ```ts + * // If gyro is not yet enabled + * gyro.enableGyro(); + * ``` + */ + enableGyro(): void; + /** + * Disable Javascript event (DeviceMotionEvent) listeners for Gyro + * @example + * ```ts + * // If gyro is enabled + * gyro.disableGyro(); + * ``` + */ + disableGyro(): void; + /** @internal */ + private handleOrientation; +} diff --git a/dist/gyro/Gyro.js b/dist/gyro/Gyro.js new file mode 100644 index 0000000..6cb0051 --- /dev/null +++ b/dist/gyro/Gyro.js @@ -0,0 +1,103 @@ +import { Gyro_ck, GyroMsg_ck } from "./gyroCk"; +/** + * Introducing Gyro (gyroerometer, on mobile) support for WebChucK. Gyro wraps + * JavaScript DeviceMotionEvent listeners easing access to mobile device gyroerometers + * in WebChucK code. + * + * To get started with Gyro: + * @example + * ```ts + * import { Chuck, Gyro } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const gyro = await Gyro.init(theChuck); // Initialize Gyro + * ``` + */ +export default class Gyro { + /** @internal */ + constructor(theChuck) { + this._gyroActive = false; + // Initialize members + this.theChuck = theChuck; + this.boundHandleOrientation = this.handleOrientation.bind(this); + } + /** + * Initialize Gyro functionality in your WebChucK instance. + * This adds a `Gyro` and `GyroMsg` class to the ChucK Virtual Machine (VM). + * Gyroerometer event (DeviceMotionEvent) listeners are added if `enableGyro` is true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * gyro = await Gyro.init(theChuck); // Initialize Gyro + */ + static async init(theChuck, enableGyro = true) { + const gyro = new Gyro(theChuck); + // Add Gyro and GyroMsg classes to ChucK VM + await gyro.theChuck.runCode(GyroMsg_ck); + await gyro.theChuck.runCode(Gyro_ck); + // Enable mouse and keyboard + /* + if (enableGyro) { + // If iOS, request permission + if (typeof (DeviceOrientationEvent as any).requestPermission === 'function') { + const permission = await (DeviceOrientationEvent as any).requestPermission(); + if (permission === 'granted') { + gyro.enableGyro(); + } else { + console.log("Gyroscope permission denied."); + } + } else { + // just try to enable + gyro.enableGyro(); + } + } + */ + gyro.enableGyro(); + return gyro; + } + /** + * @internal + * Check if gyro is active + */ + async gyroActive() { + const x = await this.theChuck.getInt("_gyroActive"); + this._gyroActive = x == 1; + } + /** + * Enable Javascript event (DeviceMotionEvent) listeners for Gyro + * @example + * ```ts + * // If gyro is not yet enabled + * gyro.enableGyro(); + * ``` + */ + enableGyro() { + // consider using "deviceorientationabsolute" + // https://developer.mozilla.org/en-US/docs/Web/API/Window/deviceorientationabsolute_event + window.addEventListener("deviceorientation", this.boundHandleOrientation); + } + /** + * Disable Javascript event (DeviceMotionEvent) listeners for Gyro + * @example + * ```ts + * // If gyro is enabled + * gyro.disableGyro(); + * ``` + */ + disableGyro() { + window.removeEventListener("deviceorientation", this.boundHandleOrientation); + } + //----------------------------------------- + // JAVASCRIPT HID EVENT HANDLERS + //----------------------------------------- + /** @internal */ + handleOrientation(event) { + this.gyroActive(); + if (this._gyroActive) { + this.theChuck.setFloat("_gyroX", event.alpha ? event.alpha : 0.0); + this.theChuck.setFloat("_gyroY", event.beta ? event.beta : 0.0); + this.theChuck.setFloat("_gyroZ", event.gamma ? event.gamma : 0.0); + this.theChuck.broadcastEvent("_gyroReading"); + } + } +} diff --git a/dist/gyro/gyroCk.d.ts b/dist/gyro/gyroCk.d.ts new file mode 100644 index 0000000..42eb772 --- /dev/null +++ b/dist/gyro/gyroCk.d.ts @@ -0,0 +1,3 @@ +declare const GyroMsg_ck = "\npublic class GyroMsg {\n float gyroX;\n float gyroY;\n float gyroZ;\n\n function float getGyroX() {\n return gyroX;\n }\n\n function float getGyroY() {\n return gyroY;\n }\n\n function float getGyroZ() {\n return gyroZ;\n }\n\n function void _copy(GyroMsg localMsg) {\n localMsg.gyroX => gyroX;\n localMsg.gyroY => gyroY;\n localMsg.gyroZ => gyroZ;\n }\n}\n"; +declare const Gyro_ck = "\nglobal Event _gyroReading;\nglobal int _gyroActive;\n\nglobal float _gyroX;\nglobal float _gyroY;\nglobal float _gyroZ;\n\npublic class Gyro extends Event {\n\n 0 => int isGyroOpen;\n 0 => int active;\n\n string deviceName; \n\n // GyroMsg Queue\n GyroMsg _gyroMsgQueue[0];\n\n function string name() {\n return deviceName;\n }\n\n function int openGyro(int num) {\n if (num < 0) {\n false => active;\n } else {\n \"js DeviceOrientationEvent\" => deviceName;\n true => active;\n }\n active => isGyroOpen => _gyroActive;\n spork ~ _gyroListener();\n return active;\n }\n\n\n // Pop the first GyroMsg from the queue\n // Write it to msg and return 1\n function int recv(GyroMsg msg) {\n // is empty\n if (_gyroMsgQueue.size() <= 0) {\n return 0;\n }\n\n // pop the first GyroMsg to msg, return true\n _gyroMsgQueue[0] @=> GyroMsg localMsg;\n msg._copy(localMsg); \n _gyroMsgQueue.popFront();\n return 1;\n }\n\n // Gyro Listener\n // Get variables from JS and write to the GyroMsg \n function void _gyroListener() {\n GyroMsg @ msg;\n while(true){\n new GyroMsg @=> msg;\n _gyroReading => now;\n\n _gyroX => msg.gyroX;\n _gyroY => msg.gyroY;\n _gyroZ => msg.gyroZ;\n\n _gyroMsgQueue << msg;\n this.broadcast();\n }\n }\n}\n"; +export { GyroMsg_ck, Gyro_ck }; diff --git a/dist/gyro/gyroCk.js b/dist/gyro/gyroCk.js new file mode 100644 index 0000000..6171fa2 --- /dev/null +++ b/dist/gyro/gyroCk.js @@ -0,0 +1,94 @@ +const GyroMsg_ck = ` +public class GyroMsg { + float gyroX; + float gyroY; + float gyroZ; + + function float getGyroX() { + return gyroX; + } + + function float getGyroY() { + return gyroY; + } + + function float getGyroZ() { + return gyroZ; + } + + function void _copy(GyroMsg localMsg) { + localMsg.gyroX => gyroX; + localMsg.gyroY => gyroY; + localMsg.gyroZ => gyroZ; + } +} +`; +const Gyro_ck = ` +global Event _gyroReading; +global int _gyroActive; + +global float _gyroX; +global float _gyroY; +global float _gyroZ; + +public class Gyro extends Event { + + 0 => int isGyroOpen; + 0 => int active; + + string deviceName; + + // GyroMsg Queue + GyroMsg _gyroMsgQueue[0]; + + function string name() { + return deviceName; + } + + function int openGyro(int num) { + if (num < 0) { + false => active; + } else { + "js DeviceOrientationEvent" => deviceName; + true => active; + } + active => isGyroOpen => _gyroActive; + spork ~ _gyroListener(); + return active; + } + + + // Pop the first GyroMsg from the queue + // Write it to msg and return 1 + function int recv(GyroMsg msg) { + // is empty + if (_gyroMsgQueue.size() <= 0) { + return 0; + } + + // pop the first GyroMsg to msg, return true + _gyroMsgQueue[0] @=> GyroMsg localMsg; + msg._copy(localMsg); + _gyroMsgQueue.popFront(); + return 1; + } + + // Gyro Listener + // Get variables from JS and write to the GyroMsg + function void _gyroListener() { + GyroMsg @ msg; + while(true){ + new GyroMsg @=> msg; + _gyroReading => now; + + _gyroX => msg.gyroX; + _gyroY => msg.gyroY; + _gyroZ => msg.gyroZ; + + _gyroMsgQueue << msg; + this.broadcast(); + } + } +} +`; +export { GyroMsg_ck, Gyro_ck }; diff --git a/dist/hid/Hid.d.ts b/dist/hid/Hid.d.ts new file mode 100644 index 0000000..3304915 --- /dev/null +++ b/dist/hid/Hid.d.ts @@ -0,0 +1,129 @@ +import Chuck from "../Chuck"; +/** + * HID (Human Interface Device) support for WebChucK. HID wraps + * JavaScript mouse/keyboard event listeners enabling mouse and keyboard + * input via the {@link https://chuck.stanford.edu/doc/reference/io.html#Hid | HID} + * class in ChucK. + * + * To get started with HID: + * @example + * ```ts + * import { Chuck, HID } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + */ +export default class HID { + private theChuck; + private keymap; + private _mouseActive; + private _kbdActive; + private boundHandleMouseMove; + private boundHandleMouseDown; + private boundHandleMouseUp; + private boundHandleMouseWheel; + private boundHandleKeyDown; + private boundHandleKeyUp; + /** @internal */ + constructor(theChuck: Chuck); + /** + * Initialize HID functionality in your WebChucK instance. + * This adds a `Hid` and `HidMsg` class to the ChucK Virtual Machine (VM). + * Mouse and keyboard event listeners are added if `enableMouse` and `enableKeyboard` are true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck, false, true); // Initialize HID, no mouse, only keyboard + * ``` + * @param theChuck WebChucK instance + * @param enableMouse boolean to enable mouse HID + * @param enableKeyboard boolean to enable keyboard HID + */ + static init(theChuck: Chuck, enableMouse?: boolean, enableKeyboard?: boolean): Promise; + /** + * @internal + * Check if keyboard is active + */ + kbdActive(): Promise; + /** + * @internal + * Check if mouse is active + */ + mouseActive(): Promise; + /** + * @internal + * Get mouse position from the MouseEvent + * @param mouseEvent Mouse event + * @returns mouse position + */ + getMousePos(mouseEvent: MouseEvent): { + x: number; + y: number; + }; + /** + * Enable Mouse HID Javascript event listeners for HID. + * Adds a mousemove, mousedown, mouseup, and wheel listener to the document. + * This will also disable the context menu on right click. + * @example + * ```ts + * // If mouse HID is not yet enabled + * hid.enableMouse(); + * ``` + */ + enableMouse(): void; + /** + * Disable Mouse HID Javascript event listeners + * @example + * ```ts + * // If mouse HID is enabled + * hid.disableMouse(); + * ``` + */ + disableMouse(): void; + /** + * Enable keyboard HID Javascript event listeners for HID. + * Adds a keydown and keyup listener to the document. + * @example + * ```ts + * // If keyboard HID is not yet enabled + * hid.enableKeyboard(); + * ``` + */ + enableKeyboard(): void; + /** + * Disable keyboard HID javascript event listeners + * @example + * ```ts + * // If keyboard HID is enabled + * hid.disableKeyboard(); + * ``` + */ + disableKeyboard(): void; + /** @internal */ + private handleMouseMove; + /** @internal */ + private handleMouseDown; + /** @internal */ + private handleMouseUp; + /** @internal */ + private handleMouseWheel; + /** @internal */ + private static handleContextMenu; + /** @internal */ + private handleKeyDown; + /** @internal */ + private handleKeyUp; + /** + * @internal + * Handle keyboard presses to send to chuck + * @param e Keyboard event + * @param isDown Is key down + */ + private keyPressManager; +} diff --git a/dist/hid/Hid.js b/dist/hid/Hid.js new file mode 100644 index 0000000..89b57d2 --- /dev/null +++ b/dist/hid/Hid.js @@ -0,0 +1,252 @@ +import { Hid_ck, HidMsg_ck } from "./hidCk"; +var HidMsgType; +(function (HidMsgType) { + HidMsgType[HidMsgType["BUTTON_DOWN"] = 1] = "BUTTON_DOWN"; + HidMsgType[HidMsgType["BUTTON_UP"] = 2] = "BUTTON_UP"; + HidMsgType[HidMsgType["MOUSE_MOTION"] = 5] = "MOUSE_MOTION"; + HidMsgType[HidMsgType["WHEEL_MOTION"] = 6] = "WHEEL_MOTION"; +})(HidMsgType || (HidMsgType = {})); +//TODO: Update the latest mouse.ck and kb.ck files +/** + * HID (Human Interface Device) support for WebChucK. HID wraps + * JavaScript mouse/keyboard event listeners enabling mouse and keyboard + * input via the {@link https://chuck.stanford.edu/doc/reference/io.html#Hid | HID} + * class in ChucK. + * + * To get started with HID: + * @example + * ```ts + * import { Chuck, HID } from "webchuck"; + * + * const theChuck = await Chuck.init([]); + * const hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + */ +export default class HID { + /** @internal */ + constructor(theChuck) { + this._mouseActive = false; + this._kbdActive = false; + // Initialize members + this.theChuck = theChuck; + this.keymap = new Array(256).fill(false); + // Bind handlers + this.boundHandleMouseMove = this.handleMouseMove.bind(this); + this.boundHandleMouseDown = this.handleMouseDown.bind(this); + this.boundHandleMouseUp = this.handleMouseUp.bind(this); + this.boundHandleMouseWheel = this.handleMouseWheel.bind(this); + this.boundHandleKeyDown = this.handleKeyDown.bind(this); + this.boundHandleKeyUp = this.handleKeyUp.bind(this); + } + /** + * Initialize HID functionality in your WebChucK instance. + * This adds a `Hid` and `HidMsg` class to the ChucK Virtual Machine (VM). + * Mouse and keyboard event listeners are added if `enableMouse` and `enableKeyboard` are true (default). + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck); // Initialize HID with mouse and keyboard + * ``` + * @example + * ```ts + * theChuck = await Chuck.init([]); + * hid = await HID.init(theChuck, false, true); // Initialize HID, no mouse, only keyboard + * ``` + * @param theChuck WebChucK instance + * @param enableMouse boolean to enable mouse HID + * @param enableKeyboard boolean to enable keyboard HID + */ + static async init(theChuck, enableMouse = true, enableKeyboard = true) { + const hid = new HID(theChuck); + // Add HID and HIDMsg classes to ChucK VM + await hid.theChuck.runCode(HidMsg_ck); + await hid.theChuck.runCode(Hid_ck); + // Enable mouse and keyboard + if (enableMouse) { + hid.enableMouse(); + } + if (enableKeyboard) { + hid.enableKeyboard(); + } + return hid; + } + /** + * @internal + * Check if keyboard is active + */ + async kbdActive() { + const x = await this.theChuck.getInt("_kbdActive"); + this._kbdActive = x == 1; + } + /** + * @internal + * Check if mouse is active + */ + async mouseActive() { + const x = await this.theChuck.getInt("_mouseActive"); + this._mouseActive = x == 1; + } + /** + * @internal + * Get mouse position from the MouseEvent + * @param mouseEvent Mouse event + * @returns mouse position + */ + getMousePos(mouseEvent) { + return { + x: mouseEvent.clientX, + y: mouseEvent.clientY, + }; + } + /** + * Enable Mouse HID Javascript event listeners for HID. + * Adds a mousemove, mousedown, mouseup, and wheel listener to the document. + * This will also disable the context menu on right click. + * @example + * ```ts + * // If mouse HID is not yet enabled + * hid.enableMouse(); + * ``` + */ + enableMouse() { + document.addEventListener("mousemove", this.boundHandleMouseMove); + document.addEventListener("mousedown", this.boundHandleMouseDown); + document.addEventListener("mouseup", this.boundHandleMouseUp); + document.addEventListener("wheel", this.boundHandleMouseWheel); + document.addEventListener("contextmenu", HID.handleContextMenu); + } + /** + * Disable Mouse HID Javascript event listeners + * @example + * ```ts + * // If mouse HID is enabled + * hid.disableMouse(); + * ``` + */ + disableMouse() { + document.removeEventListener("mousemove", this.boundHandleMouseMove); + document.removeEventListener("mousedown", this.boundHandleMouseDown); + document.removeEventListener("mouseup", this.boundHandleMouseUp); + document.removeEventListener("wheel", this.boundHandleMouseWheel); + document.removeEventListener("contextmenu", HID.handleContextMenu); + } + /** + * Enable keyboard HID Javascript event listeners for HID. + * Adds a keydown and keyup listener to the document. + * @example + * ```ts + * // If keyboard HID is not yet enabled + * hid.enableKeyboard(); + * ``` + */ + enableKeyboard() { + document.addEventListener("keydown", this.boundHandleKeyDown); + document.addEventListener("keyup", this.boundHandleKeyUp); + } + /** + * Disable keyboard HID javascript event listeners + * @example + * ```ts + * // If keyboard HID is enabled + * hid.disableKeyboard(); + * ``` + */ + disableKeyboard() { + document.removeEventListener("keydown", this.boundHandleKeyDown); + document.removeEventListener("keyup", this.boundHandleKeyUp); + } + //----------------------------------------- + // JAVASCRIPT HID EVENT HANDLERS + //----------------------------------------- + //----------- MOUSE --------- // + /** @internal */ + handleMouseMove(e) { + this.mouseActive(); + if (this._mouseActive) { + const mousePos = this.getMousePos(e); + this.theChuck.setInt("_cursorX", mousePos.x); + this.theChuck.setInt("_cursorY", mousePos.y); + this.theChuck.setFloat("_deltaX", e.movementX); + this.theChuck.setFloat("_deltaY", e.movementY); + this.theChuck.setFloat("_scaledCursorX", mousePos.x / document.documentElement.clientWidth); + this.theChuck.setFloat("_scaledCursorY", mousePos.y / document.documentElement.clientHeight); + this.theChuck.setInt("_type", HidMsgType.MOUSE_MOTION); + this.theChuck.broadcastEvent("_mouseHid"); + } + } + /** @internal */ + handleMouseDown(e) { + this.mouseActive(); + if (this._mouseActive) { + this.theChuck.setInt("_which", e.which); + this.theChuck.setInt("_type", HidMsgType.BUTTON_DOWN); + this.theChuck.broadcastEvent("_mouseHid"); + } + } + /** @internal */ + handleMouseUp(e) { + this.mouseActive(); + if (this._mouseActive) { + this.theChuck.setInt("_which", e.which); + this.theChuck.setInt("_type", HidMsgType.BUTTON_UP); + this.theChuck.broadcastEvent("_mouseHid"); + } + } + /** @internal */ + handleMouseWheel(e) { + this.mouseActive(); + if (this._mouseActive) { + this.theChuck.setFloat("_deltaX", clamp(e.deltaX, -1, 1)); + this.theChuck.setFloat("_deltaY", clamp(e.deltaY, -1, 1)); + this.theChuck.setInt("_type", HidMsgType.WHEEL_MOTION); + this.theChuck.broadcastEvent("_mouseHid"); + } + } + /** @internal */ + static handleContextMenu(e) { + e.preventDefault(); + } + //----------- KEYBOARD --------- // + /** @internal */ + handleKeyDown(e) { + this.kbdActive(); + if (this._kbdActive && !this.keymap[e.keyCode]) { + this.keymap[e.keyCode] = true; + this.keyPressManager(e, true); + } + } + /** @internal */ + handleKeyUp(e) { + this.kbdActive(); + if (this._kbdActive) { + this.keymap[e.keyCode] = false; + this.keyPressManager(e, false); + } + } + /** + * @internal + * Handle keyboard presses to send to chuck + * @param e Keyboard event + * @param isDown Is key down + */ + keyPressManager(e, isDown) { + this.theChuck.setString("_key", e.key); + this.theChuck.setInt("_which", e.which); + this.theChuck.setInt("_ascii", e.keyCode); + this.theChuck.setInt("_type", isDown ? HidMsgType.BUTTON_DOWN : HidMsgType.BUTTON_UP); + this.theChuck.broadcastEvent("_kbHid"); + } +} +//----------------------------------------------- +// HELPER FUNCTIONS +//----------------------------------------------- +/** + * Clamp a value between two numbers + * @param val value to clamp + * @param min min value + * @param max max value + * @returns clamped value + */ +function clamp(val, min, max) { + return Math.min(Math.max(val, min), max); +} diff --git a/dist/hid/hidCk.d.ts b/dist/hid/hidCk.d.ts new file mode 100644 index 0000000..f2bb945 --- /dev/null +++ b/dist/hid/hidCk.d.ts @@ -0,0 +1,3 @@ +declare const HidMsg_ck = "\npublic class HidMsg {\n int type;\n int deviceType;\n int cursorX;\n int cursorY;\n float deltaX;\n float deltaY;\n float scaledCursorX;\n float scaledCursorY;\n int which;\n int ascii;\n string key;\n\n // type 1 message\n function int isButtonDown() {\n return type == 1;\n }\n\n // type 2 message\n function int isButtonUp() {\n return type == 2;\n }\n\n // type 5 message\n function int isMouseMotion(){\n return type == 5;\n }\n\n // type 6 message\n function int isWheelMotion(){\n return type == 6;\n }\n\n function void _copy(HidMsg localMsg) {\n localMsg.type => type;\n localMsg.deviceType => deviceType;\n localMsg.cursorX => cursorX;\n localMsg.cursorY => cursorY;\n localMsg.deltaX => deltaX;\n localMsg.deltaY => deltaY;\n localMsg.scaledCursorX => scaledCursorX;\n localMsg.scaledCursorY => scaledCursorY;\n localMsg.which => which;\n localMsg.ascii => ascii;\n localMsg.key => key;\n }\n}\n"; +declare const Hid_ck = "\nglobal Event _kbHid;\nglobal Event _mouseHid;\nglobal int _type;\nglobal int _mouseActive;\nglobal int _kbdActive;\n\nglobal int _cursorX;\nglobal int _cursorY;\nglobal float _deltaX;\nglobal float _deltaY;\nglobal float _scaledCursorX;\nglobal float _scaledCursorY;\n\nglobal int _ascii;\nglobal int _which;\nglobal string _key;\n\npublic class Hid extends Event {\n\n 0 => int isMouseOpen;\n 0 => int isKBDOpen;\n 0 => int active;\n\n string deviceName; \n int deviceType; // mouse = 2, keyboard = 3\n\n // HidMsg Queue\n HidMsg _hidMsgQueue[0];\n\n function string name() {\n return deviceName;\n }\n\n function int openMouse(int num) {\n if (num < 0) {\n false => active;\n } else {\n \"virtualJS mouse/trackpad\" => deviceName;\n 2 => deviceType;\n true => active;\n }\n active => isMouseOpen => _mouseActive;\n spork ~ _mouseListener();\n return active;\n }\n\n function int openKeyboard(int num) {\n if (num < 0) {\n false => active;\n } else {\n \"virtualJS keyboard\" => deviceName;\n 3 => deviceType;\n true => active;\n }\n active => isKBDOpen => _kbdActive;\n spork ~ _keyboardListener();\n return active;\n }\n\n // Pop the first HidMsg from the queue\n // Write it to msg and return 1\n function int recv(HidMsg msg) {\n // is empty\n if (_hidMsgQueue.size() <= 0) {\n return 0;\n }\n\n // pop the first HidMsg to msg, return true\n _hidMsgQueue[0] @=> HidMsg localMsg;\n msg._copy(localMsg); \n _hidMsgQueue.popFront();\n return 1;\n }\n\n // Keyboard Hid Listener\n // Get variables from JS and write to the HidMsg \n function void _keyboardListener() {\n HidMsg @ msg;\n while(true){\n new HidMsg @=> msg;\n deviceType => msg.deviceType;\n _kbHid => now;\n\n _type => msg.type;\n _which => msg.which;\n _ascii => msg.ascii;\n _key => msg.key;\n\n _hidMsgQueue << msg;\n this.broadcast();\n }\n }\n\n // Mouse Hid Listener\n // Get variables from JS and write to the HidMsg \n function void _mouseListener() {\n HidMsg @ msg;\n while(true){\n new HidMsg @=> msg;\n deviceType => msg.deviceType;\n _mouseHid => now;\n\n _type => msg.type;\n _cursorX => msg.cursorX;\n _cursorY => msg.cursorY;\n _deltaX => msg.deltaX;\n _deltaY => msg.deltaY;\n _scaledCursorX => msg.scaledCursorX;\n _scaledCursorY => msg.scaledCursorY;\n _which => msg.which;\n\n _hidMsgQueue << msg;\n this.broadcast();\n }\n }\n}\n"; +export { HidMsg_ck, Hid_ck }; diff --git a/dist/hid/hidCk.js b/dist/hid/hidCk.js new file mode 100644 index 0000000..77f1464 --- /dev/null +++ b/dist/hid/hidCk.js @@ -0,0 +1,168 @@ +const HidMsg_ck = ` +public class HidMsg { + int type; + int deviceType; + int cursorX; + int cursorY; + float deltaX; + float deltaY; + float scaledCursorX; + float scaledCursorY; + int which; + int ascii; + string key; + + // type 1 message + function int isButtonDown() { + return type == 1; + } + + // type 2 message + function int isButtonUp() { + return type == 2; + } + + // type 5 message + function int isMouseMotion(){ + return type == 5; + } + + // type 6 message + function int isWheelMotion(){ + return type == 6; + } + + function void _copy(HidMsg localMsg) { + localMsg.type => type; + localMsg.deviceType => deviceType; + localMsg.cursorX => cursorX; + localMsg.cursorY => cursorY; + localMsg.deltaX => deltaX; + localMsg.deltaY => deltaY; + localMsg.scaledCursorX => scaledCursorX; + localMsg.scaledCursorY => scaledCursorY; + localMsg.which => which; + localMsg.ascii => ascii; + localMsg.key => key; + } +} +`; +const Hid_ck = ` +global Event _kbHid; +global Event _mouseHid; +global int _type; +global int _mouseActive; +global int _kbdActive; + +global int _cursorX; +global int _cursorY; +global float _deltaX; +global float _deltaY; +global float _scaledCursorX; +global float _scaledCursorY; + +global int _ascii; +global int _which; +global string _key; + +public class Hid extends Event { + + 0 => int isMouseOpen; + 0 => int isKBDOpen; + 0 => int active; + + string deviceName; + int deviceType; // mouse = 2, keyboard = 3 + + // HidMsg Queue + HidMsg _hidMsgQueue[0]; + + function string name() { + return deviceName; + } + + function int openMouse(int num) { + if (num < 0) { + false => active; + } else { + "virtualJS mouse/trackpad" => deviceName; + 2 => deviceType; + true => active; + } + active => isMouseOpen => _mouseActive; + spork ~ _mouseListener(); + return active; + } + + function int openKeyboard(int num) { + if (num < 0) { + false => active; + } else { + "virtualJS keyboard" => deviceName; + 3 => deviceType; + true => active; + } + active => isKBDOpen => _kbdActive; + spork ~ _keyboardListener(); + return active; + } + + // Pop the first HidMsg from the queue + // Write it to msg and return 1 + function int recv(HidMsg msg) { + // is empty + if (_hidMsgQueue.size() <= 0) { + return 0; + } + + // pop the first HidMsg to msg, return true + _hidMsgQueue[0] @=> HidMsg localMsg; + msg._copy(localMsg); + _hidMsgQueue.popFront(); + return 1; + } + + // Keyboard Hid Listener + // Get variables from JS and write to the HidMsg + function void _keyboardListener() { + HidMsg @ msg; + while(true){ + new HidMsg @=> msg; + deviceType => msg.deviceType; + _kbHid => now; + + _type => msg.type; + _which => msg.which; + _ascii => msg.ascii; + _key => msg.key; + + _hidMsgQueue << msg; + this.broadcast(); + } + } + + // Mouse Hid Listener + // Get variables from JS and write to the HidMsg + function void _mouseListener() { + HidMsg @ msg; + while(true){ + new HidMsg @=> msg; + deviceType => msg.deviceType; + _mouseHid => now; + + _type => msg.type; + _cursorX => msg.cursorX; + _cursorY => msg.cursorY; + _deltaX => msg.deltaX; + _deltaY => msg.deltaY; + _scaledCursorX => msg.scaledCursorX; + _scaledCursorY => msg.scaledCursorY; + _which => msg.which; + + _hidMsgQueue << msg; + this.broadcast(); + } + } +} +`; +export { HidMsg_ck, Hid_ck }; diff --git a/dist/index.d.ts b/dist/index.d.ts index 66855c3..fcbeec2 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,6 +1,6 @@ import DeferredPromise from "./DeferredPromise"; import Chuck from "./Chuck"; -import HID from "./Hid"; -import Gyro from "./Gyro"; -import Accel from "./Accel"; +import HID from "./hid/Hid"; +import Gyro from "./gyro/Gyro"; +import Accel from "./accel/Accel"; export { Chuck, HID, Gyro, Accel, DeferredPromise }; diff --git a/dist/index.js b/dist/index.js index 66855c3..fcbeec2 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,6 +1,6 @@ import DeferredPromise from "./DeferredPromise"; import Chuck from "./Chuck"; -import HID from "./Hid"; -import Gyro from "./Gyro"; -import Accel from "./Accel"; +import HID from "./hid/Hid"; +import Gyro from "./gyro/Gyro"; +import Accel from "./accel/Accel"; export { Chuck, HID, Gyro, Accel, DeferredPromise }; diff --git a/src/Accel.ts b/src/accel/Accel.ts similarity index 99% rename from src/Accel.ts rename to src/accel/Accel.ts index f66ce8e..df2a8fd 100644 --- a/src/Accel.ts +++ b/src/accel/Accel.ts @@ -1,4 +1,4 @@ -import Chuck from "./Chuck"; +import Chuck from "../Chuck"; import { Accel_ck, AccelMsg_ck } from "./accelCk"; diff --git a/src/accelCk.ts b/src/accel/accelCk.ts similarity index 100% rename from src/accelCk.ts rename to src/accel/accelCk.ts diff --git a/src/Gyro.ts b/src/gyro/Gyro.ts similarity index 99% rename from src/Gyro.ts rename to src/gyro/Gyro.ts index add5222..e5f5e93 100644 --- a/src/Gyro.ts +++ b/src/gyro/Gyro.ts @@ -1,4 +1,4 @@ -import Chuck from "./Chuck"; +import Chuck from "../Chuck"; import { Gyro_ck, GyroMsg_ck } from "./gyroCk"; diff --git a/src/gyroCk.ts b/src/gyro/gyroCk.ts similarity index 100% rename from src/gyroCk.ts rename to src/gyro/gyroCk.ts diff --git a/src/Hid.ts b/src/hid/Hid.ts similarity index 99% rename from src/Hid.ts rename to src/hid/Hid.ts index d3248f2..e3315d0 100644 --- a/src/Hid.ts +++ b/src/hid/Hid.ts @@ -1,4 +1,4 @@ -import Chuck from "./Chuck"; +import Chuck from "../Chuck"; import { Hid_ck, HidMsg_ck } from "./hidCk"; enum HidMsgType { diff --git a/src/hidCk.ts b/src/hid/hidCk.ts similarity index 100% rename from src/hidCk.ts rename to src/hid/hidCk.ts diff --git a/src/index.ts b/src/index.ts index 7736d0e..0a75cd9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import DeferredPromise from "./DeferredPromise"; import Chuck from "./Chuck"; -import HID from "./Hid"; -import Gyro from "./Gyro"; -import Accel from "./Accel"; +import HID from "./hid/Hid"; +import Gyro from "./gyro/Gyro"; +import Accel from "./accel/Accel"; export { Chuck, HID, Gyro, Accel, DeferredPromise }; From 889cca4e0d7086a69eaf6d501ffaba69ee83dc5d Mon Sep 17 00:00:00 2001 From: Mike Mulshine Date: Fri, 18 Oct 2024 17:06:43 -0700 Subject: [PATCH 12/13] Update to documentation, removal of faff. --- docs/classes/Accel.html | 16 +++--- docs/classes/Chuck.html | 114 ++++++++++++++++++++-------------------- docs/classes/Gyro.html | 16 +++--- docs/classes/HID.html | 32 +++++------ src/accel/Accel.ts | 20 ++----- src/gyro/Gyro.ts | 20 ++----- 6 files changed, 99 insertions(+), 119 deletions(-) diff --git a/docs/classes/Accel.html b/docs/classes/Accel.html index 03b0da3..f2546c3 100644 --- a/docs/classes/Accel.html +++ b/docs/classes/Accel.html @@ -24,13 +24,15 @@

                    Class Accel

                    Example

                    import { Chuck, Accel } from "webchuck";

                    const theChuck = await Chuck.init([]);
                    const accel = await Accel.init(theChuck); // Initialize Accel
                    +

                    The "devicemotion" event gives acceleration of the device on the three axes: x, y, and z. Acceleration is expressed in m/s². +More on the "devicemotion" event can be found online here.

                    Hierarchy

                    • Accel
                    +
                  • Defined in src/accel/Accel.ts:23
                  • @@ -54,12 +56,12 @@

                    Properties

                    theChuck: Chuck
                    +
                  • Defined in src/accel/Accel.ts:25
                  • _accelActive: boolean = false
                    +
                  • Defined in src/accel/Accel.ts:26
                  • boundHandleMotion: {}
                    @@ -69,7 +71,7 @@

                    Type declaration

                    • +
                    • Defined in src/accel/Accel.ts:28
                    • Methods

                      @@ -94,7 +96,7 @@

                      Example

                      theChuck
                      +
                    • Defined in src/accel/Accel.ts:48
                      • @@ -108,7 +110,7 @@

                        Example

                        // If accel is
                         
                      +
                    • Defined in src/accel/Accel.ts:81
                    • +
                    • Defined in src/accel/Accel.ts:95
                    • +
                    • Defined in src/Chuck.ts:117
                      • @@ -329,7 +329,7 @@

                        Example

                        Chuck
                      +
                    • Defined in src/Chuck.ts:235
                    • +
                    • Defined in src/Chuck.ts:159
                    • +
                    • Defined in src/Chuck.ts:174
                    • +
                    • Defined in src/Chuck.ts:186
                    • +
                    • Defined in src/Chuck.ts:201
                    • +
                    • Defined in src/Chuck.ts:245
                    • +
                    • Defined in src/Chuck.ts:255
                    • +
                    • Defined in src/Chuck.ts:285
                    • +
                    • Defined in src/Chuck.ts:324
                    • +
                    • Defined in src/Chuck.ts:344
                    • +
                    • Defined in src/Chuck.ts:379
                    • +
                    • Defined in src/Chuck.ts:419
                    • +
                    • Defined in src/Chuck.ts:462
                    • +
                    • Defined in src/Chuck.ts:511
                    • +
                    • Defined in src/Chuck.ts:525
                    • +
                    • Defined in src/Chuck.ts:539
                    • +
                    • Defined in src/Chuck.ts:547
                    • +
                    • Defined in src/Chuck.ts:558
                    • +
                    • Defined in src/Chuck.ts:576
                    • +
                    • Defined in src/Chuck.ts:592
                    • +
                    • Defined in src/Chuck.ts:606
                    • +
                    • Defined in src/Chuck.ts:616
                    • +
                    • Defined in src/Chuck.ts:630
                    • +
                    • Defined in src/Chuck.ts:639
                    • +
                    • Defined in src/Chuck.ts:653
                    • +
                    • Defined in src/Chuck.ts:662
                    • +
                    • Defined in src/Chuck.ts:677
                    • +
                    • Defined in src/Chuck.ts:686
                    • +
                    • Defined in src/Chuck.ts:701
                    • +
                    • Defined in src/Chuck.ts:715
                    • +
                    • Defined in src/Chuck.ts:733
                    • +
                    • Defined in src/Chuck.ts:748
                    • +
                    • Defined in src/Chuck.ts:764
                    • +
                    • Defined in src/Chuck.ts:774
                    • +
                    • Defined in src/Chuck.ts:789
                    • +
                    • Defined in src/Chuck.ts:804
                    • +
                    • Defined in src/Chuck.ts:822
                    • +
                    • Defined in src/Chuck.ts:837
                    • +
                    • Defined in src/Chuck.ts:854
                    • +
                    • Defined in src/Chuck.ts:863
                    • +
                    • Defined in src/Chuck.ts:877
                    • +
                    • Defined in src/Chuck.ts:885
                    • +
                    • Defined in src/Chuck.ts:899
                    • +
                    • Defined in src/Chuck.ts:908
                    • +
                    • Defined in src/Chuck.ts:922
                    • +
                    • Defined in src/Chuck.ts:932
                    • +
                    • Defined in src/Chuck.ts:939
                    • +
                    • Defined in src/Chuck.ts:960
                      • diff --git a/docs/classes/Gyro.html b/docs/classes/Gyro.html index 586ec74..92849c1 100644 --- a/docs/classes/Gyro.html +++ b/docs/classes/Gyro.html @@ -24,13 +24,15 @@

                        Class Gyro

                        Example

                        import { Chuck, Gyro } from "webchuck";

                        const theChuck = await Chuck.init([]);
                        const gyro = await Gyro.init(theChuck); // Initialize Gyro
                        +

                        The "deviceorientation" event gives motion of the device around the three axes (x, y, and z) represented as degrees from 0 to 360. +More on the "deviceorientation" event can be found online here.

                      Hierarchy

                      • Gyro
                      +
                    • Defined in src/gyro/Gyro.ts:23
                    • @@ -54,12 +56,12 @@

                      Properties

                      theChuck: Chuck
                      +
                    • Defined in src/gyro/Gyro.ts:25
                    • _gyroActive: boolean = false
                      +
                    • Defined in src/gyro/Gyro.ts:26
                    • boundHandleOrientation: {}
                      @@ -69,7 +71,7 @@

                      Type declaration

                      • +
                      • Defined in src/gyro/Gyro.ts:28
                      • Methods

                        @@ -94,7 +96,7 @@

                        Example

                        theChuck
                        +
                      • Defined in src/gyro/Gyro.ts:48
                        • @@ -108,7 +110,7 @@

                          Example

                          // If gyro is
                           
                        +
                      • Defined in src/gyro/Gyro.ts:81
                      • +
                      • Defined in src/gyro/Gyro.ts:95
                      • +
                      • Defined in src/hid/Hid.ts:36
                      • boundHandleMouseUp: {}
                        @@ -109,7 +109,7 @@

                        Type declaration

                        • +
                        • Defined in src/hid/Hid.ts:37
                        • boundHandleMouseWheel: {}
                          @@ -119,7 +119,7 @@

                          Type declaration

                          • +
                          • Defined in src/hid/Hid.ts:38
                          • boundHandleKeyDown: {}
                            @@ -129,7 +129,7 @@

                            Type declaration

                            • +
                            • Defined in src/hid/Hid.ts:39
                            • boundHandleKeyUp: {}
                              @@ -139,7 +139,7 @@

                              Type declaration

                              • +
                              • Defined in src/hid/Hid.ts:40
                              • Methods

                                @@ -178,7 +178,7 @@

                                Example

                                theChuck
                                +
                              • Defined in src/hid/Hid.ts:75
                                • @@ -194,7 +194,7 @@

                                  Example

                                  // If mouse HI
                                   
                                +
                              • Defined in src/hid/Hid.ts:136
                                • @@ -208,7 +208,7 @@

                                  Example

                                  // If mouse HI
                                   
                                +
                              • Defined in src/hid/Hid.ts:152
                                • @@ -223,7 +223,7 @@

                                  Example

                                  // If keyboard
                                   
                                +
                              • Defined in src/hid/Hid.ts:169
                                • @@ -237,7 +237,7 @@

                                  Example

                                  // If keyboard
                                   
                                +
                              • Defined in src/hid/Hid.ts:182