From 224b783b29a1eb086ca0f4b989283b3e8194781a Mon Sep 17 00:00:00 2001 From: Face Kapow Date: Thu, 18 Aug 2016 19:40:22 -0400 Subject: [PATCH 1/2] Basic mouse support! --- .eslintrc | 1 + js/core/index.js | 2 + js/core/mouse/index.js | 20 +++++ js/core/ps2/index.js | 10 +++ js/driver/ps2/index.js | 1 + js/driver/ps2/mouse.js | 196 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 230 insertions(+) create mode 100644 js/core/mouse/index.js create mode 100644 js/driver/ps2/mouse.js diff --git a/.eslintrc b/.eslintrc index e4336abdc..7025bb960 100644 --- a/.eslintrc +++ b/.eslintrc @@ -41,6 +41,7 @@ rules: - 2 - props: false + no-shadow: 0 parserOptions: ecmaVersion: 6 diff --git a/js/core/index.js b/js/core/index.js index ebddb3600..08010ab2f 100644 --- a/js/core/index.js +++ b/js/core/index.js @@ -19,6 +19,7 @@ require('./polyfill'); const random = require('./random'); const keyboard = require('./keyboard'); +const mouse = require('./mouse'); const ps2 = require('./ps2'); const pci = require('./pci'); const net = require('./net'); @@ -29,6 +30,7 @@ class Runtime { Object.assign(this, { random, keyboard, + mouse, pci, ps2, allocator, diff --git a/js/core/mouse/index.js b/js/core/mouse/index.js new file mode 100644 index 000000000..6405cd495 --- /dev/null +++ b/js/core/mouse/index.js @@ -0,0 +1,20 @@ +// Copyright 2016-present runtime.js project authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const EventController = require('event-controller'); +exports.onMousedown = new EventController(); +exports.onMouseup = new EventController(); +exports.onMousemove = new EventController(); diff --git a/js/core/ps2/index.js b/js/core/ps2/index.js index 2c607bead..efe607419 100644 --- a/js/core/ps2/index.js +++ b/js/core/ps2/index.js @@ -26,3 +26,13 @@ exports.setKeyboardDriver = (driver) => { ioPort: driverUtils.ioPort(0x60), }); }; + +exports.setMouseDriver = (driver) => { + assert(typeutils.isFunction(driver.init)); + assert(typeutils.isFunction(driver.reset)); + + driver.init({ + irq: driverUtils.irq(12), + ioPorts: [driverUtils.ioPort(0x60), driverUtils.ioPort(0x64)], + }); +}; diff --git a/js/driver/ps2/index.js b/js/driver/ps2/index.js index e8996a609..d43208613 100644 --- a/js/driver/ps2/index.js +++ b/js/driver/ps2/index.js @@ -14,3 +14,4 @@ 'use strict'; require('./keyboard'); +require('./mouse'); diff --git a/js/driver/ps2/mouse.js b/js/driver/ps2/mouse.js new file mode 100644 index 000000000..66e1594ff --- /dev/null +++ b/js/driver/ps2/mouse.js @@ -0,0 +1,196 @@ +// Copyright 2016-present runtime.js project authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Some code modified and adapted for use by runtime.js from http://forum.osdev.org/viewtopic.php?t=10247 + +'use strict'; + +const runtime = require('../../core'); + +const flags = { + middle: false, + right: false, + left: false, +}; + +function splitByte(byte) { + const ret = []; + for (let i = 7; i >= 0; i--) ret.push(byte & (1 << i) ? 1 : 0); + return ret; +} + +function processPacket(packet) { + const split = splitByte(packet[0]); + const info = { + yOverflow: split[0], + xOverflow: split[1], + ySign: split[2], + xSign: split[3], + alwaysOne: split[4], + buttons: { + middle: split[5], + right: split[6], + left: split[7], + }, + xOffset: packet[1], + yOffset: packet[2], + }; + + // just a sanity check: + if (info.alwaysOne !== 1) return; // something's wrong with this packet + + if (flags.middle && info.buttons.middle === 0) { + flags.middle = false; + setImmediate(() => { + runtime.mouse.onMouseup.dispatch(1); + }); + } else if (!flags.middle && info.buttons.middle === 1) { + flags.middle = true; + setImmediate(() => { + runtime.mouse.onMousedown.dispatch(1); + }); + } else if (flags.right && info.buttons.right === 0) { + flags.right = false; + setImmediate(() => { + runtime.mouse.onMouseup.dispatch(2); + }); + } else if (!flags.right && info.buttons.right === 1) { + flags.right = true; + setImmediate(() => { + runtime.mouse.onMousedown.dispatch(2); + }); + } else if (flags.left && info.buttons.left === 0) { + flags.left = false; + setImmediate(() => { + runtime.mouse.onMouseup.dispatch(0); + }); + } else if (!flags.left && info.buttons.left === 1) { + flags.left = true; + setImmediate(() => { + runtime.mouse.onMousedown.dispatch(0); + }); + } + + if (info.xOffset !== 0 || info.yOffset !== 0) { + setImmediate(() => { + runtime.mouse.onMousemove.dispatch({ + x: (info.xSign) ? (info.xOffset | 0xffffff00) : info.xOffset, + y: (info.ySign) ? (info.yOffset | 0xffffff00) : info.yOffset, + }); + }); + } +} + +const driver = { + init(device) { + const irq = device.irq; + const [mainPort, port64] = device.ioPorts; + + function mouseWait(type) { + return new Promise((resolve, reject) => { + let maxIter = 1500; + function loop() { + return new Promise((resolve, reject) => { + if (maxIter === 0) return resolve(); + if (type === 0) { + if ((port64.read8() & 1) === 1) { + return resolve(); + } + } else { + if ((port64.read8() & 2) === 0) { + return resolve(); + } + } + maxIter--; + setImmediate(() => { + loop().then(resolve).catch(reject); + }); + }); + } + setImmediate(() => { + loop().then(resolve).catch(reject); + }); + }); + } + + function mouseWrite(data) { + return new Promise((resolve, reject) => { + mouseWait(1).then(() => { + port64.write8(0xd4); + return mouseWait(1); + }).then(() => { + mainPort.write8(data); + resolve(); + }).catch(reject); + }); + } + + function mouseRead() { + return new Promise((resolve, reject) => { + mouseWait(0).then(() => { + resolve(mainPort.read8()); + }).catch(reject); + }); + } + + let status; + + /* eslint-disable newline-per-chained-call */ + mouseWait(1).then(() => { + port64.write8(0xa8); // enable the aux mouse device + return mouseWait(1); + }).then(() => { + port64.write8(0x20); // use interupts + return mouseWait(0); + }).then(() => { + status = (mainPort.read8() | 2); + return mouseWait(1); + }).then(() => { + port64.write8(0x60); + return mouseWait(1); // end use interupts + }).then(() => { + mainPort.write8(status); + return mouseWrite(0xf6); // use defaults + }) + .then(() => mouseRead()) // ACK + .then(() => mouseWrite(0xf4)) // enable the mouse + .then(() => mouseRead()) // ACK + .then(() => { + let cycle = 0; + let packet = []; + irq.on(() => { + if (cycle === 0) { + packet = []; + packet[0] = mainPort.read8(); + cycle++; + } else if (cycle === 1) { + packet[1] = mainPort.read8(); + cycle++; + } else if (cycle === 2) { + packet[2] = mainPort.read8(); + cycle = 0; + ((packet) => { + setImmediate(() => { + processPacket(packet); + }); + })(packet); + } + }); + }); + /* eslint-enable newline-per-chained-call */ + }, + reset() {}, +}; + +runtime.ps2.setMouseDriver(driver); From 92db2417eb13a1ef91142a7d446f91bd34fa554c Mon Sep 17 00:00:00 2001 From: Face Kapow Date: Fri, 19 Aug 2016 19:25:13 -0400 Subject: [PATCH 2/2] eslint no-shadow, reduce use of new Promise --- .eslintrc | 1 - js/driver/ps2/mouse.js | 28 ++++++++++------------------ 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/.eslintrc b/.eslintrc index 7025bb960..e4336abdc 100644 --- a/.eslintrc +++ b/.eslintrc @@ -41,7 +41,6 @@ rules: - 2 - props: false - no-shadow: 0 parserOptions: ecmaVersion: 6 diff --git a/js/driver/ps2/mouse.js b/js/driver/ps2/mouse.js index 66e1594ff..3e78b0625 100644 --- a/js/driver/ps2/mouse.js +++ b/js/driver/ps2/mouse.js @@ -98,7 +98,7 @@ const driver = { const [mainPort, port64] = device.ioPorts; function mouseWait(type) { - return new Promise((resolve, reject) => { + return new Promise((outerResolve, outerReject) => { let maxIter = 1500; function loop() { return new Promise((resolve, reject) => { @@ -119,29 +119,21 @@ const driver = { }); } setImmediate(() => { - loop().then(resolve).catch(reject); + loop().then(outerResolve).catch(outerReject); }); }); } function mouseWrite(data) { - return new Promise((resolve, reject) => { - mouseWait(1).then(() => { - port64.write8(0xd4); - return mouseWait(1); - }).then(() => { - mainPort.write8(data); - resolve(); - }).catch(reject); - }); + return mouseWait(1).then(() => { + port64.write8(0xd4); + return mouseWait(1); + }) + .then(() => { mainPort.write8(data); }); } function mouseRead() { - return new Promise((resolve, reject) => { - mouseWait(0).then(() => { - resolve(mainPort.read8()); - }).catch(reject); - }); + return mouseWait(0).then(() => mainPort.read8()); } let status; @@ -180,9 +172,9 @@ const driver = { } else if (cycle === 2) { packet[2] = mainPort.read8(); cycle = 0; - ((packet) => { + ((packetPersistent) => { setImmediate(() => { - processPacket(packet); + processPacket(packetPersistent); }); })(packet); }