First round of changes to map Host touch events to Guest mouse events
This commit is contained in:
parent
94dad8cbb5
commit
d2fb395e55
170 changed files with 9084 additions and 2096 deletions
|
|
@ -169,6 +169,11 @@ Component.subclass(Mouse);
|
|||
|
||||
Mouse.ID_SERIAL = 0x4D;
|
||||
|
||||
Mouse.BUTTON = {
|
||||
LEFT: 0,
|
||||
RIGHT: 2
|
||||
};
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
|
|
@ -430,21 +435,21 @@ Mouse.prototype.captureMouse = function(control)
|
|||
control.addEventListener(
|
||||
'mousemove',
|
||||
function onMouseMove(event) {
|
||||
mouse.moveMouse(event);
|
||||
mouse.processMouseEvent(event);
|
||||
},
|
||||
false // we'll specify false for the 'useCapture' parameter for now...
|
||||
);
|
||||
control.addEventListener(
|
||||
'mousedown',
|
||||
function onMouseDown(event) {
|
||||
mouse.clickMouse(event.button, true);
|
||||
mouse.processMouseEvent(event, true);
|
||||
},
|
||||
false // we'll specify false for the 'useCapture' parameter for now...
|
||||
);
|
||||
control.addEventListener(
|
||||
'mouseup',
|
||||
function onMouseUp(event) {
|
||||
mouse.clickMouse(event.button, false);
|
||||
mouse.processMouseEvent(event, false);
|
||||
},
|
||||
false // we'll specify false for the 'useCapture' parameter for now...
|
||||
);
|
||||
|
|
@ -485,59 +490,15 @@ Mouse.prototype.releaseMouse = function(control)
|
|||
};
|
||||
|
||||
/**
|
||||
* moveMouse(event)
|
||||
*
|
||||
* MouseEvent objects contain, among other things, the following properties:
|
||||
*
|
||||
* clientX
|
||||
* clientY
|
||||
*
|
||||
* I've selected the above properties because they're widely supported, not because I need
|
||||
* client-area coordinates. In fact, layerX and layerY are probably closer to what I really want,
|
||||
* but I don't think they're available in all browsers. screenX and screenY would work as well.
|
||||
*
|
||||
* This is because all we care about are deltas. We record clientX and clientY (as xMouse and yMouse)
|
||||
* merely to calculate xDelta and yDelta.
|
||||
* processMouseEvent(event, fDown)
|
||||
*
|
||||
* @this {Mouse}
|
||||
* @param {Object} event object from a 'mousemove' event (specifically, a MouseEvent object)
|
||||
* @param {Object} event object from a 'mousemove', 'mousedown' or 'mouseup' event (specifically, a MouseEvent object)
|
||||
* @param {boolean} [fDown] (undefined if neither a down nor up event)
|
||||
*/
|
||||
Mouse.prototype.moveMouse = function(event)
|
||||
Mouse.prototype.processMouseEvent = function(event, fDown)
|
||||
{
|
||||
if (this.isActive()) {
|
||||
if (this.xMouse < 0 || this.yMouse < 0) {
|
||||
this.xMouse = event.clientX;
|
||||
this.yMouse = event.clientY;
|
||||
}
|
||||
if (this.fLocked) {
|
||||
this.xDelta = event['movementX'] || event['mozMovementX'] || event['webkitMovementX'] || 0;
|
||||
this.yDelta = event['movementY'] || event['mozMovementY'] || event['webkitMovementY'] || 0;
|
||||
} else {
|
||||
this.xDelta = event.clientX - this.xMouse;
|
||||
this.yDelta = event.clientY - this.yMouse;
|
||||
}
|
||||
if (this.xDelta || this.yDelta) {
|
||||
/*
|
||||
* As sendPacket() indicates, any x and y coordinates we supply are for diagnostic purposes only.
|
||||
* sendPacket() only cares about the xDelta and yDelta properties, which it then zeroes on completion.
|
||||
*/
|
||||
this.sendPacket(null, event.clientX, event.clientY);
|
||||
}
|
||||
this.xMouse = event.clientX;
|
||||
this.yMouse = event.clientY;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* clickMouse(iButton, fDown)
|
||||
*
|
||||
* @this {Mouse}
|
||||
* @param {number} iButton is 0 for fButton1 (the LEFT button), 2 for fButton2 (the RIGHT button)
|
||||
* @param {boolean} fDown
|
||||
*/
|
||||
Mouse.prototype.clickMouse = function(iButton, fDown)
|
||||
{
|
||||
if (this.isActive()) {
|
||||
if (fDown !== undefined) {
|
||||
if (this.fLocked === false) {
|
||||
/*
|
||||
* If there's no support for automatic pointer locking in the Video component, then notifyPointerActive()
|
||||
|
|
@ -547,15 +508,58 @@ Mouse.prototype.clickMouse = function(iButton, fDown)
|
|||
this.fLocked = null;
|
||||
}
|
||||
}
|
||||
this.clickMouse(event.button, fDown);
|
||||
} else {
|
||||
/*
|
||||
* MouseEvent objects contain, among other things, the following properties:
|
||||
*
|
||||
* clientX
|
||||
* clientY
|
||||
*
|
||||
* I've selected the above properties because they're widely supported, not because I need
|
||||
* client-area coordinates. In fact, layerX and layerY are probably closer to what I really want,
|
||||
* but I don't think they're available in all browsers. screenX and screenY would work as well.
|
||||
*
|
||||
* This is because all we care about are deltas. We record clientX and clientY (as xMouse and yMouse)
|
||||
* merely to calculate xDelta and yDelta.
|
||||
*/
|
||||
var xDelta, yDelta;
|
||||
if (this.xMouse < 0 || this.yMouse < 0) {
|
||||
this.xMouse = event.clientX;
|
||||
this.yMouse = event.clientY;
|
||||
}
|
||||
if (this.fLocked) {
|
||||
xDelta = event['movementX'] || event['mozMovementX'] || event['webkitMovementX'] || 0;
|
||||
yDelta = event['movementY'] || event['mozMovementY'] || event['webkitMovementY'] || 0;
|
||||
} else {
|
||||
xDelta = event.clientX - this.xMouse;
|
||||
yDelta = event.clientY - this.yMouse;
|
||||
}
|
||||
this.xMouse = event.clientX;
|
||||
this.yMouse = event.clientY;
|
||||
this.moveMouse(xDelta, yDelta, this.xMouse, this.yMouse);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* clickMouse(iButton, fDown)
|
||||
*
|
||||
* @this {Mouse}
|
||||
* @param {number} iButton is Mouse.BUTTON.LEFT (0) for fButton1, Mouse.BUTTON.RIGHT (2) for fButton2
|
||||
* @param {boolean} fDown
|
||||
*/
|
||||
Mouse.prototype.clickMouse = function(iButton, fDown)
|
||||
{
|
||||
if (this.isActive()) {
|
||||
var sDiag = DEBUGGER? ("mouse button" + iButton + ' ' + (fDown? "dn" : "up")) : null;
|
||||
switch (iButton) {
|
||||
case 0:
|
||||
case Mouse.BUTTON.LEFT:
|
||||
if (this.fButton1 != fDown) {
|
||||
this.fButton1 = fDown;
|
||||
this.sendPacket(sDiag);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
case Mouse.BUTTON.RIGHT:
|
||||
if (this.fButton2 != fDown) {
|
||||
this.fButton2 = fDown;
|
||||
this.sendPacket(sDiag);
|
||||
|
|
@ -567,6 +571,31 @@ Mouse.prototype.clickMouse = function(iButton, fDown)
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* moveMouse(xDelta, yDelta, xDiag, yDiag)
|
||||
*
|
||||
* @this {Mouse}
|
||||
* @param {number} xDelta
|
||||
* @param {number} yDelta
|
||||
* @param {number} [xDiag]
|
||||
* @param {number} [yDiag]
|
||||
*/
|
||||
Mouse.prototype.moveMouse = function(xDelta, yDelta, xDiag, yDiag)
|
||||
{
|
||||
if (this.isActive()) {
|
||||
if (xDelta || yDelta) {
|
||||
/*
|
||||
* As sendPacket() indicates, any x and y coordinates we supply are for diagnostic purposes only.
|
||||
* sendPacket() only cares about the xDelta and yDelta properties we provide above, which it then zeroes
|
||||
* on completion.
|
||||
*/
|
||||
this.xDelta = xDelta;
|
||||
this.yDelta = yDelta;
|
||||
this.sendPacket(null, xDiag, yDiag);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* sendPacket(sDiag, xDiag, yDiag)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ if (typeof module !== 'undefined') {
|
|||
var Messages = require("./messages");
|
||||
var ChipSet = require("./chipset");
|
||||
var Keyboard = require("./keyboard");
|
||||
var Mouse = require("./mouse");
|
||||
var State = require("./state");
|
||||
}
|
||||
|
||||
|
|
@ -59,6 +60,7 @@ if (typeof module !== 'undefined') {
|
|||
* charRows: number of character rows
|
||||
* fontROM: path to .rom file (or a JSON representation) that defines the character set
|
||||
* screenColor: background color of the screen canvas (default is black)
|
||||
* touchScreen: string specifying desired touch-screen support (default is ''); see initBus()
|
||||
* autoLock: true to (attempt to) automatically lock the mouse to the canvas (default is false)
|
||||
*
|
||||
* An EGA may specify the following additional properties:
|
||||
|
|
@ -144,13 +146,17 @@ function Video(parmsVideo, canvas, context, textarea, container)
|
|||
this.fScaleFont = parmsVideo['scale'];
|
||||
this.fDoubleFont = Math.round(this.cxScreen / this.nColsDefault) >= 12;
|
||||
|
||||
this.fTouchScreen = parmsVideo['touchScreen'];
|
||||
|
||||
this.canvasScreen = canvas;
|
||||
this.contextScreen = context;
|
||||
this.textareaScreen = textarea;
|
||||
this.inputScreen = textarea || canvas || null;
|
||||
|
||||
/*
|
||||
* initBus() will determine touch-screen support; for now, just record values and set defaults.
|
||||
*/
|
||||
this.sTouchScreen = parmsVideo['touchScreen'];
|
||||
this.nTouchConfig = Video.TOUCH.NONE;
|
||||
|
||||
/*
|
||||
* If a Mouse exists, we'll be notified when it requests our canvas, and we make a note of it
|
||||
* so that if lockPointer() is ever invoked, we can notify the Mouse.
|
||||
|
|
@ -2644,6 +2650,15 @@ Video.cardSpecs[Video.CARD.CGA] = ["CGA", Card.CGA.CRTC.INDX.PORT, 0xB8000, 0x04
|
|||
Video.cardSpecs[Video.CARD.EGA] = ["EGA", Card.CGA.CRTC.INDX.PORT, 0xB8000, 0x04000, 0x10000, ChipSet.MONITOR.EGACOLOR];
|
||||
Video.cardSpecs[Video.CARD.VGA] = ["VGA", Card.CGA.CRTC.INDX.PORT, 0xB8000, 0x04000, 0x40000, ChipSet.MONITOR.VGACOLOR];
|
||||
|
||||
/*
|
||||
* Values for nTouchConfig; a value will be selected based on the sTouchScreen configuration parameter.
|
||||
*/
|
||||
Video.TOUCH = {
|
||||
NONE: 0,
|
||||
KEYGRID: 1,
|
||||
MOUSE: 2
|
||||
};
|
||||
|
||||
/**
|
||||
* initBus(cmp, bus, cpu, dbg)
|
||||
*
|
||||
|
|
@ -2723,7 +2738,18 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
|
|||
if (this.nCard == Video.CARD.EGA) this.bEGASwitches = this.chipset.parseSwitches(this.sSwitches, this.bEGASwitches);
|
||||
}
|
||||
|
||||
if (this.kbd && this.fTouchScreen) this.captureTouch();
|
||||
/*
|
||||
* The default value for the 'touchScreen' parameter is an empty string; machine configs must explicitly
|
||||
* select one of the following values, via the 'touchscreen' attribute in the <video> element, to enable any
|
||||
* touch-screen support.
|
||||
*/
|
||||
if (this.sTouchScreen == "mouse") {
|
||||
this.mouse = cmp.getComponentByType("Mouse");
|
||||
if (this.mouse) this.captureTouch(Video.TOUCH.MOUSE);
|
||||
}
|
||||
else if (this.sTouchScreen == "keygrid") {
|
||||
if (this.kbd) this.captureTouch(Video.TOUCH.KEYGRID);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -2915,13 +2941,13 @@ Video.prototype.lockPointer = function(fLock)
|
|||
if (fLock) {
|
||||
if (this.inputScreen.lockPointer) {
|
||||
this.inputScreen.lockPointer();
|
||||
this.mouse.notifyPointerLocked(true);
|
||||
if (this.mouse) this.mouse.notifyPointerLocked(true);
|
||||
fSuccess = true;
|
||||
}
|
||||
} else {
|
||||
if (this.inputScreen.unlockPointer) {
|
||||
this.inputScreen.unlockPointer();
|
||||
this.mouse.notifyPointerLocked(false);
|
||||
if (this.mouse) this.mouse.notifyPointerLocked(false);
|
||||
fSuccess = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2962,16 +2988,17 @@ Video.prototype.notifyPointerLocked = function(fLocked)
|
|||
};
|
||||
|
||||
/**
|
||||
* captureTouch()
|
||||
* captureTouch(nTouchConfig)
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {number} nTouchConfig (must be one of the supported Video.TOUCH values)
|
||||
*/
|
||||
Video.prototype.captureTouch = function()
|
||||
Video.prototype.captureTouch = function(nTouchConfig)
|
||||
{
|
||||
var control = this.inputScreen;
|
||||
if (control) {
|
||||
var video = this;
|
||||
if (!this.fCaptured) {
|
||||
if (!this.nTouchConfig) {
|
||||
control.addEventListener(
|
||||
'touchstart',
|
||||
function onTouchStart(event) { video.onTouchStart(event); },
|
||||
|
|
@ -3009,7 +3036,8 @@ Video.prototype.captureTouch = function()
|
|||
*/
|
||||
}
|
||||
// this.log("touch events captured");
|
||||
this.fCaptured = true;
|
||||
this.nTouchConfig = nTouchConfig;
|
||||
this.xTouch = this.yTouch = this.timeTouch = -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -3066,7 +3094,7 @@ Video.prototype.onTouchStart = function(event)
|
|||
Video.prototype.onTouchMove = function(event)
|
||||
{
|
||||
if (DEBUG) this.printMessage("onTouchMove()");
|
||||
this.processTouchEvent(event, false);
|
||||
this.processTouchEvent(event);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -3078,17 +3106,29 @@ Video.prototype.onTouchMove = function(event)
|
|||
Video.prototype.onTouchEnd = function(event)
|
||||
{
|
||||
if (DEBUG) this.printMessage("onTouchEnd()");
|
||||
this.processTouchEvent(event, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* processTouchEvent(event, fStart)
|
||||
*
|
||||
* If nTouchConfig is non-zero, touch event handlers are installed, which pass their events to this function.
|
||||
*
|
||||
* What we do with those events here depends on the value of nTouchConfig. Originally, the only supported
|
||||
* configuration was the experimental conversion of touch events into arrow keys, based on an invisible grid
|
||||
* that divided the screen into thirds; that configuration is now identified as Video.TOUCH.KEYGRID.
|
||||
*
|
||||
* The new preferred configuration is Video.TOUCH.MOUSE, which does little more than allow you to "push" the
|
||||
* simulated mouse around. If Video.TOUCH.MOUSE is enabled, it's already been confirmed the machine has a mouse.
|
||||
*
|
||||
* @this {Video}
|
||||
* @param {Event} event object from a 'touch' event
|
||||
* @param {boolean} fStart if this is a 'touchstart' event
|
||||
* @param {boolean} [fStart] (true if 'touchstart', false if 'touchend', undefined if 'touchmove')
|
||||
*/
|
||||
Video.prototype.processTouchEvent = function(event, fStart)
|
||||
{
|
||||
var xTouch, yTouch;
|
||||
|
||||
// if (!event) event = window.event;
|
||||
/*
|
||||
* My thinking here is that if the canvas does NOT yet have focus, then we should actually SKIP
|
||||
|
|
@ -3133,34 +3173,73 @@ Video.prototype.processTouchEvent = function(event, fStart)
|
|||
* @name Event
|
||||
* @property {Array} targetTouches
|
||||
*/
|
||||
var xTouch, yTouch;
|
||||
if (!event.targetTouches) {
|
||||
if (!event.targetTouches || !event.targetTouches.length) {
|
||||
xTouch = event.pageX;
|
||||
yTouch = event.pageY;
|
||||
} else {
|
||||
xTouch = event.targetTouches[0].pageX;
|
||||
yTouch = event.targetTouches[0].pageY;
|
||||
}
|
||||
|
||||
xTouch = ((xTouch - xTouchOffset) * xScale);
|
||||
yTouch = ((yTouch - yTouchOffset) * yScale);
|
||||
var xThird = (xTouch / (this.cxScreen / 3)) | 0;
|
||||
var yThird = (yTouch / (this.cyScreen / 3)) | 0;
|
||||
|
||||
/*
|
||||
* At this point, xThird and yThird should both be one of 0, 1 or 2, indicating which horizontal and vertical
|
||||
* third of the virtual screen the touch event occurred.
|
||||
*/
|
||||
if (/* xThird == 1 && */ yThird != 1) {
|
||||
if (!yThird) {
|
||||
this.kbd.addActiveKey(Keyboard.CLICKCODES.UP, true);
|
||||
} else {
|
||||
this.kbd.addActiveKey(Keyboard.CLICKCODES.DOWN, true);
|
||||
if (this.nTouchConfig == Video.TOUCH.KEYGRID) {
|
||||
|
||||
var xThird = (xTouch / (this.cxScreen / 3)) | 0;
|
||||
var yThird = (yTouch / (this.cyScreen / 3)) | 0;
|
||||
|
||||
/*
|
||||
* At this point, xThird and yThird should both be one of 0, 1 or 2, indicating which horizontal and vertical
|
||||
* third of the virtual screen the touch event occurred.
|
||||
*/
|
||||
if (/* xThird == 1 && */ yThird != 1) {
|
||||
if (!yThird) {
|
||||
this.kbd.addActiveKey(Keyboard.CLICKCODES.UP, true);
|
||||
} else {
|
||||
this.kbd.addActiveKey(Keyboard.CLICKCODES.DOWN, true);
|
||||
}
|
||||
} else if (/* yThird == 1 && */ xThird != 1) {
|
||||
if (!xThird) {
|
||||
this.kbd.addActiveKey(Keyboard.CLICKCODES.LEFT, true);
|
||||
} else {
|
||||
this.kbd.addActiveKey(Keyboard.CLICKCODES.RIGHT, true);
|
||||
}
|
||||
}
|
||||
} else if (/* yThird == 1 && */ xThird != 1) {
|
||||
if (!xThird) {
|
||||
this.kbd.addActiveKey(Keyboard.CLICKCODES.LEFT, true);
|
||||
} else {
|
||||
this.kbd.addActiveKey(Keyboard.CLICKCODES.RIGHT, true);
|
||||
} else {
|
||||
if (this.mouse) {
|
||||
if (fStart === true) {
|
||||
this.timeTouch = event.timeStamp;
|
||||
}
|
||||
if (fStart === false) {
|
||||
var timeDelta = event.timeStamp - this.timeTouch;
|
||||
this.println("processTouchEvent(false," + timeDelta + ")");
|
||||
/*
|
||||
* NOTE: 200ms is merely my initial stab at a reasonable number of milliseconds to interpret a
|
||||
* start/end touch sequence as a "tap"; I also make no note of any intervening move events (ie,
|
||||
* events where fStart is undefined), and perhaps I should....
|
||||
*/
|
||||
if (timeDelta < 200) {
|
||||
this.mouse.clickMouse(Mouse.BUTTON.LEFT, true);
|
||||
this.mouse.clickMouse(Mouse.BUTTON.LEFT, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This 'touchmove" code mimics the 'mousemove' event processing in processMouseEvent() in mouse.js, with
|
||||
* one important difference: every time touching "restarts", we need to reset the variables used to calculate
|
||||
* the deltas, so that the mere act of lifting and replacing your finger doesn't generate a delta by itself.
|
||||
*/
|
||||
if (fStart || this.xTouch < 0 || this.yTouch < 0) {
|
||||
this.xTouch = xTouch;
|
||||
this.yTouch = yTouch;
|
||||
}
|
||||
var xDelta = Math.round(xTouch - this.xTouch);
|
||||
var yDelta = Math.round(yTouch - this.yTouch);
|
||||
this.xTouch = xTouch;
|
||||
this.yTouch = yTouch;
|
||||
// this.println("moveMouse(" + xDelta + "," + yDelta + ")");
|
||||
this.mouse.moveMouse(xDelta, yDelta, this.xTouch, this.yTouch);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -905,7 +905,7 @@
|
|||
<xsl:variable name="touchScreen">
|
||||
<xsl:choose>
|
||||
<xsl:when test="@touchscreen"><xsl:value-of select="@touchscreen"/></xsl:when>
|
||||
<xsl:otherwise>false</xsl:otherwise>
|
||||
<xsl:otherwise></xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:variable>
|
||||
<xsl:variable name="autoLock">
|
||||
|
|
@ -917,7 +917,7 @@
|
|||
<xsl:call-template name="component">
|
||||
<xsl:with-param name="machine" select="$machine"/>
|
||||
<xsl:with-param name="class">video</xsl:with-param>
|
||||
<xsl:with-param name="parms">,model:'<xsl:value-of select="$model"/>',mode:<xsl:value-of select="$mode"/>,screenWidth:<xsl:value-of select="$screenWidth"/>,screenHeight:<xsl:value-of select="$screenHeight"/>,memory:<xsl:value-of select="$memory"/>,switches:'<xsl:value-of select="$switches"/>',scale:<xsl:value-of select="$scale"/>,charCols:<xsl:value-of select="$charCols"/>,charRows:<xsl:value-of select="$charRows"/>,fontROM:'<xsl:value-of select="$fontROM"/>',screenColor:'<xsl:value-of select="$screenColor"/>',touchScreen:<xsl:value-of select="$touchScreen"/>,autoLock:<xsl:value-of select="$autoLock"/></xsl:with-param>
|
||||
<xsl:with-param name="parms">,model:'<xsl:value-of select="$model"/>',mode:<xsl:value-of select="$mode"/>,screenWidth:<xsl:value-of select="$screenWidth"/>,screenHeight:<xsl:value-of select="$screenHeight"/>,memory:<xsl:value-of select="$memory"/>,switches:'<xsl:value-of select="$switches"/>',scale:<xsl:value-of select="$scale"/>,charCols:<xsl:value-of select="$charCols"/>,charRows:<xsl:value-of select="$charRows"/>,fontROM:'<xsl:value-of select="$fontROM"/>',screenColor:'<xsl:value-of select="$screenColor"/>',touchScreen:'<xsl:value-of select="$touchScreen"/>',autoLock:<xsl:value-of select="$autoLock"/></xsl:with-param>
|
||||
</xsl:call-template>
|
||||
</xsl:template>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue