diff --git a/playground/playground.js b/playground/playground.js index 0de1f9afb..2367d539c 100644 --- a/playground/playground.js +++ b/playground/playground.js @@ -88,6 +88,24 @@ window.onload = function() { workspace.reportValue(data.id, data.value); }); + // Feed mouse events as VM I/O events. + document.addEventListener('mousemove', function (e) { + var rect = canvas.getBoundingClientRect(); + var coordinates = { + x: e.clientX - rect.left - rect.width / 2, + y: e.clientY - rect.top - rect.height / 2 + }; + window.vm.postIOData('mouse', coordinates); + }); + canvas.addEventListener('mousedown', function (e) { + window.vm.postIOData('mouse', {isDown: true}); + e.preventDefault(); + }); + canvas.addEventListener('mouseup', function (e) { + window.vm.postIOData('mouse', {isDown: false}); + e.preventDefault(); + }); + // Run threads vm.start(); diff --git a/src/blocks/scratch3_sensing.js b/src/blocks/scratch3_sensing.js new file mode 100644 index 000000000..ee75cc43a --- /dev/null +++ b/src/blocks/scratch3_sensing.js @@ -0,0 +1,43 @@ +function Scratch3SensingBlocks(runtime) { + /** + * The runtime instantiating this block package. + * @type {Runtime} + */ + this.runtime = runtime; +} + +/** + * Retrieve the block primitives implemented by this package. + * @return {Object.} Mapping of opcode to Function. + */ +Scratch3SensingBlocks.prototype.getPrimitives = function() { + return { + 'sensing_timer': this.getTimer, + 'sensing_resettimer': this.resetTimer, + 'sensing_mousex': this.getMouseX, + 'sensing_mousey': this.getMouseY, + 'sensing_mousedown': this.getMouseDown + }; +}; + +Scratch3SensingBlocks.prototype.getTimer = function (args, util) { + return util.ioQuery('clock', 'projectTimer'); +}; + +Scratch3SensingBlocks.prototype.resetTimer = function (args, util) { + util.ioQuery('clock', 'resetProjectTimer'); +}; + +Scratch3SensingBlocks.prototype.getMouseX = function (args, util) { + return util.ioQuery('mouse', 'getX'); +}; + +Scratch3SensingBlocks.prototype.getMouseY = function (args, util) { + return util.ioQuery('mouse', 'getY'); +}; + +Scratch3SensingBlocks.prototype.getMouseDown = function (args, util) { + return util.ioQuery('mouse', 'getIsDown'); +}; + +module.exports = Scratch3SensingBlocks; diff --git a/src/engine/execute.js b/src/engine/execute.js index 73b724965..d885a6c58 100644 --- a/src/engine/execute.js +++ b/src/engine/execute.js @@ -77,7 +77,14 @@ var execute = function (sequencer, thread) { startBranch: function (branchNum) { sequencer.stepToBranch(thread, branchNum); }, - target: target + target: target, + ioQuery: function (device, func, args) { + // Find the I/O device and execute the query/function call. + if (runtime.ioDevices[device] && runtime.ioDevices[device][func]) { + var devObject = runtime.ioDevices[device]; + return devObject[func].call(devObject, args); + } + } }); // Deal with any reported value. diff --git a/src/engine/runtime.js b/src/engine/runtime.js index c32a72115..d244544aa 100644 --- a/src/engine/runtime.js +++ b/src/engine/runtime.js @@ -3,12 +3,17 @@ var Sequencer = require('./sequencer'); var Thread = require('./thread'); var util = require('util'); +// Virtual I/O devices. +var Clock = require('../io/clock'); +var Mouse = require('../io/mouse'); + var defaultBlockPackages = { 'scratch3_control': require('../blocks/scratch3_control'), 'scratch3_event': require('../blocks/scratch3_event'), 'scratch3_looks': require('../blocks/scratch3_looks'), 'scratch3_motion': require('../blocks/scratch3_motion'), - 'scratch3_operators': require('../blocks/scratch3_operators') + 'scratch3_operators': require('../blocks/scratch3_operators'), + 'scratch3_sensing': require('../blocks/scratch3_sensing') }; /** @@ -43,6 +48,11 @@ function Runtime (targets) { */ this._primitives = {}; this._registerBlockPackages(); + + this.ioDevices = { + 'clock': new Clock(), + 'mouse': new Mouse() + }; } /** diff --git a/src/index.js b/src/index.js index db99f1d15..8807634a6 100644 --- a/src/index.js +++ b/src/index.js @@ -101,6 +101,17 @@ VirtualMachine.prototype.animationFrame = function () { this.runtime.animationFrame(); }; +/** + * Post I/O data to the virtual devices. + * @param {?string} device Name of virtual I/O device. + * @param {Object} data Any data object to post to the I/O device. + */ +VirtualMachine.prototype.postIOData = function (device, data) { + if (this.runtime.ioDevices[device]) { + this.runtime.ioDevices[device].postData(data); + } +}; + /* * Worker handlers: for all public methods available above, * we must also provide a message handler in case the VM is run @@ -140,6 +151,9 @@ if (ENV_WORKER) { case 'animationFrame': self.vmInstance.animationFrame(); break; + case 'postIOData': + self.vmInstance.postIOData(messageData.device, messageData.data); + break; default: if (e.data.id == 'RendererConnected') { //initRenderWorker(); diff --git a/src/io/clock.js b/src/io/clock.js new file mode 100644 index 000000000..b3bf1e0be --- /dev/null +++ b/src/io/clock.js @@ -0,0 +1,16 @@ +var Timer = require('../util/timer'); + +function Clock () { + this._projectTimer = new Timer(); + this._projectTimer.start(); +} + +Clock.prototype.projectTimer = function () { + return this._projectTimer.timeElapsed() / 1000; +}; + +Clock.prototype.resetProjectTimer = function () { + this._projectTimer.start(); +}; + +module.exports = Clock; diff --git a/src/io/mouse.js b/src/io/mouse.js new file mode 100644 index 000000000..7432c4358 --- /dev/null +++ b/src/io/mouse.js @@ -0,0 +1,33 @@ +var MathUtil = require('../util/math-util'); + +function Mouse () { + this._x = 0; + this._y = 0; + this._isDown = false; +} + +Mouse.prototype.postData = function(data) { + if (data.x) { + this._x = data.x; + } + if (data.y) { + this._y = data.y; + } + if (typeof data.isDown !== 'undefined') { + this._isDown = data.isDown; + } +}; + +Mouse.prototype.getX = function () { + return MathUtil.clamp(this._x, -240, 240); +}; + +Mouse.prototype.getY = function () { + return MathUtil.clamp(-this._y, -180, 180); +}; + +Mouse.prototype.getIsDown = function () { + return this._isDown; +}; + +module.exports = Mouse; diff --git a/src/worker.js b/src/worker.js index 70e6da5cb..248894537 100644 --- a/src/worker.js +++ b/src/worker.js @@ -51,6 +51,14 @@ VirtualMachine.prototype.getPlaygroundData = function () { this.vmWorker.postMessage({method: 'getPlaygroundData'}); }; +VirtualMachine.prototype.postIOData = function (device, data) { + this.vmWorker.postMessage({ + method: 'postIOData', + device: device, + data: data + }); +}; + VirtualMachine.prototype.start = function () { this.vmWorker.postMessage({method: 'start'}); };