Very crude mouse support. (#95)
This commit is contained in:
parent
2c02567309
commit
a9885dbfbd
|
@ -0,0 +1,88 @@
|
||||||
|
;
|
||||||
|
; Minimal mouse support. Only firmware routines are supported, no
|
||||||
|
; I/O hooks or softswitches. This is enough to work with titles
|
||||||
|
; that follow the documentation's recommendations to use the
|
||||||
|
; firmware routines like Dazzle Draw and Apple II DeskTop
|
||||||
|
;
|
||||||
|
ORG $C700
|
||||||
|
|
||||||
|
; Constants for future reference
|
||||||
|
|
||||||
|
CLAMP_X_LOW EQU $478
|
||||||
|
CLAMP_Y_LOW EQU $4F8
|
||||||
|
CLAMP_X_HIGH EQU $578
|
||||||
|
CLAMP_Y_HIGH EQU $5F8
|
||||||
|
|
||||||
|
X_LOW EQU $478
|
||||||
|
Y_LOW EQU $4F8
|
||||||
|
X_HIGH EQU $578
|
||||||
|
Y_HIGH EQU $5F8
|
||||||
|
RESERVED1 EQU $678
|
||||||
|
RESERVED2 EQU $67F
|
||||||
|
STATUS EQU $778
|
||||||
|
MODE EQU $7F8
|
||||||
|
|
||||||
|
STATUS_DOWN EQU $80
|
||||||
|
STATUS_LAST EQU $40
|
||||||
|
INT_SCREEN EQU $08
|
||||||
|
INT_BUTTON EQU $04
|
||||||
|
INT_MOUSE EQU $02
|
||||||
|
|
||||||
|
ROMRTS EQU $FF58
|
||||||
|
|
||||||
|
DFB $00 ; $00
|
||||||
|
DFB $00 ; $01
|
||||||
|
DFB $00 ; $02
|
||||||
|
DFB $00 ; $03
|
||||||
|
DFB $00 ; $04
|
||||||
|
; Cx05 - Pascal ID byte
|
||||||
|
DFB $38 ; $05
|
||||||
|
DFB $00 ; $06
|
||||||
|
; Cx07 - Pascal ID byte
|
||||||
|
DFB $18 ; $07
|
||||||
|
DFB $00 ; $08
|
||||||
|
DFB $00 ; $09
|
||||||
|
DFB $00 ; $0A
|
||||||
|
; Cx0B - Generic signature byte of firmware cards
|
||||||
|
DFB $01 ; $0B
|
||||||
|
; Cx0C - 2 = X-Y pointing device; 0 = identification code
|
||||||
|
ID1 DFB $20 ; $0C
|
||||||
|
DFB $00 ; $0D
|
||||||
|
DFB $00 ; $0E
|
||||||
|
DFB $00 ; $0F
|
||||||
|
DFB $00 ; $10
|
||||||
|
DFB $00 ; $11
|
||||||
|
; The firmware routines point to individual RTS opcodes
|
||||||
|
; that are intercepted by the card implementation which
|
||||||
|
; manipulates memory and processor state directly
|
||||||
|
DFB $20 ; $12 SETMOUSE
|
||||||
|
DFB $21 ; $13 SERVEMOUSE
|
||||||
|
DFB $22 ; $14 READMOUSE
|
||||||
|
DFB $23 ; $15 CLEARMOUSE
|
||||||
|
DFB $24 ; $16 POSMOUSE
|
||||||
|
DFB $25 ; $17 CLAMPMOUSE
|
||||||
|
DFB $26 ; $18 HOMEMOUSE
|
||||||
|
DFB $27 ; $19 INITMOUSE
|
||||||
|
DFB $00 ; $1A
|
||||||
|
DFB $00 ; $1B
|
||||||
|
DFB $00 ; $1C
|
||||||
|
DFB $00 ; $1D
|
||||||
|
DFB $00 ; $1E
|
||||||
|
DFB $00 ; $1F
|
||||||
|
RTS ; $20 SETMOUSE
|
||||||
|
RTS ; $21 SERVEMOUSE
|
||||||
|
RTS ; $22 READMOUSE
|
||||||
|
RTS ; $23 CLEARMOUSE
|
||||||
|
RTS ; $24 POSMOUSE
|
||||||
|
RTS ; $25 CLAMPMOUSE
|
||||||
|
RTS ; $26 HOMEMOUSE
|
||||||
|
RTS ; $27 INITMOUSE
|
||||||
|
PADDING DS $C7FB - PADDING
|
||||||
|
ORG $C7FB
|
||||||
|
; CxFB - A mouse identification byte
|
||||||
|
ID2 DFB $D6 ; $FB
|
||||||
|
DFB $00 ; $FC
|
||||||
|
DFB $00 ; $FD
|
||||||
|
DFB $00 ; $FE
|
||||||
|
DFB $00 ; $FF
|
||||||
|
END
|
|
@ -215,6 +215,10 @@ canvas {
|
||||||
height: 416px;
|
height: 416px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#screen.mouseMode {
|
||||||
|
cursor: none;
|
||||||
|
}
|
||||||
|
|
||||||
#screen:-webkit-full-screen {
|
#screen:-webkit-full-screen {
|
||||||
background-color: black;
|
background-color: black;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|
|
@ -0,0 +1,322 @@
|
||||||
|
import { Card, byte, word, Restorable } from '../types';
|
||||||
|
import CPU6502, { CpuState } from '../cpu6502';
|
||||||
|
import { debug } from '../util';
|
||||||
|
import { rom } from '../roms/cards/mouse';
|
||||||
|
|
||||||
|
const CLAMP_MIN_LOW = 0x478;
|
||||||
|
const CLAMP_MAX_LOW = 0x4F8;
|
||||||
|
const CLAMP_MIN_HIGH = 0x578;
|
||||||
|
const CLAMP_MAX_HIGH = 0x5F8;
|
||||||
|
|
||||||
|
const X_LOW = 0x478;
|
||||||
|
const Y_LOW = 0x4F8;
|
||||||
|
const X_HIGH = 0x578;
|
||||||
|
const Y_HIGH = 0x5F8;
|
||||||
|
const STATUS = 0x778;
|
||||||
|
const MODE = 0x7F8;
|
||||||
|
|
||||||
|
const STATUS_DOWN = 0x80;
|
||||||
|
const STATUS_LAST = 0x40;
|
||||||
|
const STATUS_MOVED = 0x20;
|
||||||
|
const INT_SCREEN = 0x08;
|
||||||
|
const INT_PRESS = 0x04;
|
||||||
|
const INT_MOVE = 0x02;
|
||||||
|
|
||||||
|
const MODE_ON = 0x01;
|
||||||
|
const MODE_INT_MOVE = 0x02;
|
||||||
|
const MODE_INT_PRESS = 0x04;
|
||||||
|
const MODE_INT_VBL = 0x08;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Firmware routine offset pointers
|
||||||
|
*/
|
||||||
|
const ENTRIES = {
|
||||||
|
SET_MOUSE: 0x12,
|
||||||
|
SERVE_MOUSE: 0x13,
|
||||||
|
READ_MOUSE: 0x14,
|
||||||
|
CLEAR_MOUSE: 0x15,
|
||||||
|
POS_MOUSE: 0x16,
|
||||||
|
CLAMP_MOUSE: 0x17,
|
||||||
|
HOME_MOUSE: 0x18,
|
||||||
|
INIT_MOUSE: 0x19
|
||||||
|
};
|
||||||
|
|
||||||
|
interface MouseState {
|
||||||
|
clampXMin: word;
|
||||||
|
clampYMin: word;
|
||||||
|
clampXMax: word
|
||||||
|
clampYMax: word;
|
||||||
|
x: word;
|
||||||
|
y: word;
|
||||||
|
mode: byte;
|
||||||
|
down: boolean;
|
||||||
|
lastDown: boolean;
|
||||||
|
lastX: word;
|
||||||
|
lastY: word;
|
||||||
|
serve: byte;
|
||||||
|
shouldIntMove: boolean;
|
||||||
|
shouldIntPress: boolean;
|
||||||
|
slot: byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Mouse implements Card, Restorable<MouseState> {
|
||||||
|
/** Lowest mouse X */
|
||||||
|
private clampXMin: word = 0;
|
||||||
|
/** Lowest mouse Y */
|
||||||
|
private clampYMin: word = 0;
|
||||||
|
/** Highest mouse X */
|
||||||
|
private clampXMax: word = 0x3FF;
|
||||||
|
/** Highest mouse Y */
|
||||||
|
private clampYMax: word = 0x3FF;
|
||||||
|
/** Mouse X position */
|
||||||
|
private x: word = 0;
|
||||||
|
/** Mouse Y position */
|
||||||
|
private y: word = 0;
|
||||||
|
/** Mouse mode */
|
||||||
|
private mode: byte = 0;
|
||||||
|
/** Mouse button down state */
|
||||||
|
private down = false;
|
||||||
|
/** Last mouse button down state */
|
||||||
|
private lastDown = false;
|
||||||
|
/** Last mouse Y Position */
|
||||||
|
private lastX: word = 0;
|
||||||
|
/** Last mouse X position */
|
||||||
|
private lastY: word = 0;
|
||||||
|
/** Interrupt service flags */
|
||||||
|
private serve: byte = 0;
|
||||||
|
/** Move happened since last refresh */
|
||||||
|
private shouldIntMove = false;
|
||||||
|
/** Button press happened since last refresh */
|
||||||
|
private shouldIntPress = false;
|
||||||
|
/** Slot for screen hole indexing */
|
||||||
|
private slot = 0;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private cpu: CPU6502,
|
||||||
|
private cbs: {
|
||||||
|
setMouse: (mouse: Mouse) => void,
|
||||||
|
mouseMode: (on: boolean) => void
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
this.cbs.setMouse(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ioSwitch(_off: byte, _val?: byte) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
read(_page: byte, off: byte) {
|
||||||
|
let state = this.cpu.getState();
|
||||||
|
|
||||||
|
const holeWrite = (addr: word, val: byte) => {
|
||||||
|
this.cpu.write(addr >> 8, (addr & 0xff) + this.slot, val);
|
||||||
|
};
|
||||||
|
|
||||||
|
const holeRead = (addr: word) => {
|
||||||
|
return this.cpu.read(addr >> 8, addr & 0xff);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCarry = (state: CpuState) => {
|
||||||
|
state.s &= 0xFE;
|
||||||
|
return state;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.cpu.getSync()) {
|
||||||
|
switch (off) {
|
||||||
|
case rom[ENTRIES.SET_MOUSE]:
|
||||||
|
{
|
||||||
|
this.mode = state.a;
|
||||||
|
this.cbs.mouseMode(!!(this.mode & MODE_ON));
|
||||||
|
state = clearCarry(state);
|
||||||
|
// debug(
|
||||||
|
// 'setMouse ',
|
||||||
|
// (_mode & MODE_ON ? 'Mouse on ' : 'Mouse off '),
|
||||||
|
// (_mode & MODE_INT_MOVE ? 'Move interrupt ' : '') +
|
||||||
|
// (_mode & MODE_INT_PRESS ? 'Move press ' : '') +
|
||||||
|
// (_mode & MODE_INT_VBL ? 'Move VBL ' : '')
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case rom[ENTRIES.SERVE_MOUSE]:
|
||||||
|
// debug('serveMouse');
|
||||||
|
holeWrite(STATUS, this.serve);
|
||||||
|
state = clearCarry(state);
|
||||||
|
this.serve = 0;
|
||||||
|
break;
|
||||||
|
case rom[ENTRIES.READ_MOUSE]:
|
||||||
|
{
|
||||||
|
const moved = (this.lastX !== this.x) || (this.lastY !== this.y);
|
||||||
|
const status =
|
||||||
|
(this.down ? STATUS_DOWN : 0) |
|
||||||
|
(this.lastDown ? STATUS_LAST : 0) |
|
||||||
|
(moved ? STATUS_MOVED : 0);
|
||||||
|
const mouseXLow = this.x & 0xff;
|
||||||
|
const mouseYLow = this.y & 0xff;
|
||||||
|
const mouseXHigh = this.x >> 8;
|
||||||
|
const mouseYHigh = this.y >> 8;
|
||||||
|
|
||||||
|
// debug({ mouseXLow, mouseYLow, mouseXHigh, mouseYHigh });
|
||||||
|
|
||||||
|
holeWrite(X_LOW, mouseXLow);
|
||||||
|
holeWrite(Y_LOW, mouseYLow);
|
||||||
|
holeWrite(X_HIGH, mouseXHigh);
|
||||||
|
holeWrite(Y_HIGH, mouseYHigh);
|
||||||
|
holeWrite(STATUS, status);
|
||||||
|
holeWrite(MODE, this.mode);
|
||||||
|
|
||||||
|
this.lastDown = this.down;
|
||||||
|
this.lastX = this.x;
|
||||||
|
this.lastY = this.y;
|
||||||
|
|
||||||
|
state = clearCarry(state);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case rom[ENTRIES.CLEAR_MOUSE]:
|
||||||
|
debug('clearMouse');
|
||||||
|
state = clearCarry(state);
|
||||||
|
break;
|
||||||
|
case rom[ENTRIES.POS_MOUSE]:
|
||||||
|
debug('posMouse');
|
||||||
|
state = clearCarry(state);
|
||||||
|
break;
|
||||||
|
case rom[ENTRIES.CLAMP_MOUSE]:
|
||||||
|
{
|
||||||
|
const clampY = state.a;
|
||||||
|
if (clampY) {
|
||||||
|
this.clampYMin = holeRead(CLAMP_MIN_LOW) | (holeRead(CLAMP_MIN_HIGH) << 8);
|
||||||
|
this.clampYMax = holeRead(CLAMP_MAX_LOW) | (holeRead(CLAMP_MAX_HIGH) << 8);
|
||||||
|
debug('clampMouse Y', this.clampYMin, this.clampYMax);
|
||||||
|
} else {
|
||||||
|
this.clampXMin = holeRead(CLAMP_MIN_LOW) | (holeRead(CLAMP_MIN_HIGH) << 8);
|
||||||
|
this.clampXMax = holeRead(CLAMP_MAX_LOW) | (holeRead(CLAMP_MAX_HIGH) << 8);
|
||||||
|
debug('clampMouse X', this.clampXMin, this.clampXMax);
|
||||||
|
}
|
||||||
|
state = clearCarry(state);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case rom[ENTRIES.HOME_MOUSE]:
|
||||||
|
{
|
||||||
|
debug('homeMouse');
|
||||||
|
this.x = this.clampXMin;
|
||||||
|
this.y = this.clampYMin;
|
||||||
|
state = clearCarry(state);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case rom[ENTRIES.INIT_MOUSE]:
|
||||||
|
{
|
||||||
|
this.slot = state.y >> 4;
|
||||||
|
debug('initMouse slot', this.slot);
|
||||||
|
state = clearCarry(state);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cpu.setState(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rom[off];
|
||||||
|
}
|
||||||
|
|
||||||
|
write() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers interrupts based on activity since the last tick
|
||||||
|
*/
|
||||||
|
|
||||||
|
tick() {
|
||||||
|
if (this.mode & MODE_INT_VBL) {
|
||||||
|
this.serve |= INT_SCREEN;
|
||||||
|
}
|
||||||
|
if ((this.mode & MODE_INT_PRESS) && this.shouldIntPress) {
|
||||||
|
this.serve |= INT_PRESS;
|
||||||
|
}
|
||||||
|
if ((this.mode & MODE_INT_MOVE) && this.shouldIntMove) {
|
||||||
|
this.serve |= INT_MOVE;
|
||||||
|
}
|
||||||
|
if (this.serve) {
|
||||||
|
this.cpu.irq();
|
||||||
|
}
|
||||||
|
this.shouldIntMove = false;
|
||||||
|
this.shouldIntPress = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scales mouse position and clamps to min and max,and flags
|
||||||
|
* potential mouse state change interrupt
|
||||||
|
*
|
||||||
|
* @param x Client mouse X position
|
||||||
|
* @param y Client mouse Y position
|
||||||
|
* @param w Client width
|
||||||
|
* @param h Client height
|
||||||
|
*/
|
||||||
|
|
||||||
|
setMouseXY(x: number, y: number, w: number, h: number) {
|
||||||
|
const rangeX = this.clampXMax - this.clampXMin;
|
||||||
|
const rangeY = this.clampYMax - this.clampYMin;
|
||||||
|
this.x = (x * rangeX / w + this.clampXMin) & 0xffff;
|
||||||
|
this.y = (y * rangeY / h + this.clampYMin) & 0xffff;
|
||||||
|
this.shouldIntMove = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks mouse button state and flags potential
|
||||||
|
* mouse state change interrupt
|
||||||
|
*
|
||||||
|
* @param down Mouse button down state
|
||||||
|
*/
|
||||||
|
|
||||||
|
setMouseDown(down: boolean) {
|
||||||
|
this.shouldIntPress = this.down !== down;
|
||||||
|
this.down = down;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores saved state
|
||||||
|
*
|
||||||
|
* @param state stored state
|
||||||
|
*/
|
||||||
|
|
||||||
|
setState(state: MouseState) {
|
||||||
|
this.clampXMin = state.clampXMin;
|
||||||
|
this.clampYMin = state.clampYMin;
|
||||||
|
this.clampXMax = state.clampXMax;
|
||||||
|
this.clampYMax = state.clampYMax;
|
||||||
|
this.x = state.x;
|
||||||
|
this.y = state.y;
|
||||||
|
this.mode = state.mode;
|
||||||
|
this.down = state.down;
|
||||||
|
this.lastDown = state.lastDown;
|
||||||
|
this.lastX = state.lastX;
|
||||||
|
this.lastY = state.lastY;
|
||||||
|
this.serve = state.serve;
|
||||||
|
this.shouldIntMove = state.shouldIntMove;
|
||||||
|
this.shouldIntPress = state.shouldIntPress;
|
||||||
|
this.slot = state.slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves state for restoration
|
||||||
|
*
|
||||||
|
* @returns restorable state
|
||||||
|
*/
|
||||||
|
|
||||||
|
getState(): MouseState {
|
||||||
|
return {
|
||||||
|
clampXMin: this.clampXMin,
|
||||||
|
clampYMin: this.clampYMin,
|
||||||
|
clampXMax: this.clampXMax,
|
||||||
|
clampYMax: this.clampYMax,
|
||||||
|
x: this.x,
|
||||||
|
y: this.y,
|
||||||
|
mode: this.mode,
|
||||||
|
down: this.down,
|
||||||
|
lastDown: this.lastDown,
|
||||||
|
lastX: this.lastX,
|
||||||
|
lastY: this.lastY,
|
||||||
|
serve: this.serve,
|
||||||
|
shouldIntMove: this.shouldIntMove,
|
||||||
|
shouldIntPress: this.shouldIntPress,
|
||||||
|
slot: this.slot
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,12 +2,14 @@ import Prefs from './prefs';
|
||||||
|
|
||||||
import { driveLights, initUI, updateUI } from './ui/apple2';
|
import { driveLights, initUI, updateUI } from './ui/apple2';
|
||||||
import Printer from './ui/printer';
|
import Printer from './ui/printer';
|
||||||
|
import { MouseUI } from './ui/mouse';
|
||||||
|
|
||||||
import DiskII from './cards/disk2';
|
import DiskII from './cards/disk2';
|
||||||
import Parallel from './cards/parallel';
|
import Parallel from './cards/parallel';
|
||||||
import RAMFactor from './cards/ramfactor';
|
import RAMFactor from './cards/ramfactor';
|
||||||
import SmartPort from './cards/smartport';
|
import SmartPort from './cards/smartport';
|
||||||
import Thunderclock from './cards/thunderclock';
|
import Thunderclock from './cards/thunderclock';
|
||||||
|
import Mouse from './cards/mouse';
|
||||||
|
|
||||||
import { Apple2 } from './apple2';
|
import { Apple2 } from './apple2';
|
||||||
|
|
||||||
|
@ -54,15 +56,18 @@ apple2.ready.then(() => {
|
||||||
const cpu = apple2.getCPU();
|
const cpu = apple2.getCPU();
|
||||||
|
|
||||||
const printer = new Printer('#printer-modal .paper');
|
const printer = new Printer('#printer-modal .paper');
|
||||||
|
const mouseUI = new MouseUI('#screen');
|
||||||
|
|
||||||
const parallel = new Parallel(printer);
|
const parallel = new Parallel(printer);
|
||||||
const slinky = new RAMFactor(1024 * 1024);
|
const slinky = new RAMFactor(1024 * 1024);
|
||||||
const disk2 = new DiskII(io, driveLights);
|
const disk2 = new DiskII(io, driveLights);
|
||||||
const clock = new Thunderclock();
|
const clock = new Thunderclock();
|
||||||
const smartport = new SmartPort(cpu, { block: !enhanced });
|
const smartport = new SmartPort(cpu, { block: !enhanced });
|
||||||
|
const mouse = new Mouse(cpu, mouseUI);
|
||||||
|
|
||||||
io.setSlot(1, parallel);
|
io.setSlot(1, parallel);
|
||||||
io.setSlot(2, slinky);
|
io.setSlot(2, slinky);
|
||||||
|
io.setSlot(4, mouse);
|
||||||
io.setSlot(5, clock);
|
io.setSlot(5, clock);
|
||||||
io.setSlot(6, disk2);
|
io.setSlot(6, disk2);
|
||||||
io.setSlot(7, smartport);
|
io.setSlot(7, smartport);
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { ReadonlyUint8Array } from '../../types';
|
||||||
|
|
||||||
|
export const rom: ReadonlyUint8Array = new Uint8Array([
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x18,
|
||||||
|
0x00,0x00,0x00,0x01,0x20,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x20,0x21,0x22,0x23,0x24,0x25,
|
||||||
|
0x26,0x27,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
|
0x00,0x00,0x00,0xd6,0x00,0x00,0x00,0x00,
|
||||||
|
]);
|
||||||
|
|
|
@ -6,6 +6,12 @@ const JOYSTICK_FLIP_X_AXIS = 'flip_x';
|
||||||
const JOYSTICK_FLIP_Y_AXIS = 'flip_y';
|
const JOYSTICK_FLIP_Y_AXIS = 'flip_y';
|
||||||
const JOYSTICK_SWAP_AXIS = 'swap_x_y';
|
const JOYSTICK_SWAP_AXIS = 'swap_x_y';
|
||||||
|
|
||||||
|
let mouseMode = false;
|
||||||
|
|
||||||
|
export function enableMouseMode(on: boolean) {
|
||||||
|
mouseMode = on;
|
||||||
|
}
|
||||||
|
|
||||||
export class JoyStick implements OptionHandler {
|
export class JoyStick implements OptionHandler {
|
||||||
private disableMouseJoystick = false;
|
private disableMouseJoystick = false;
|
||||||
private flipX = false;
|
private flipX = false;
|
||||||
|
@ -17,13 +23,13 @@ export class JoyStick implements OptionHandler {
|
||||||
document.addEventListener('mousemove', this.mousemove);
|
document.addEventListener('mousemove', this.mousemove);
|
||||||
document.querySelectorAll('canvas').forEach((canvas) => {
|
document.querySelectorAll('canvas').forEach((canvas) => {
|
||||||
canvas.addEventListener('mousedown', (evt) => {
|
canvas.addEventListener('mousedown', (evt) => {
|
||||||
if (!this.gamepad) {
|
if (!this.gamepad && !mouseMode) {
|
||||||
io.buttonDown(evt.which == 1 ? 0 : 1);
|
io.buttonDown(evt.which == 1 ? 0 : 1);
|
||||||
}
|
}
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
});
|
});
|
||||||
canvas.addEventListener('mouseup', (evt) => {
|
canvas.addEventListener('mouseup', (evt) => {
|
||||||
if (!this.gamepad) {
|
if (!this.gamepad && !mouseMode) {
|
||||||
io.buttonUp(evt.which == 1 ? 0 : 1);
|
io.buttonUp(evt.which == 1 ? 0 : 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -83,7 +89,7 @@ export class JoyStick implements OptionHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private mousemove = (evt: MouseEvent) => {
|
private mousemove = (evt: MouseEvent) => {
|
||||||
if (this.gamepad || this.disableMouseJoystick) {
|
if (this.gamepad || this.disableMouseJoystick || mouseMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import type Mouse from '../cards/mouse';
|
||||||
|
import { enableMouseMode } from './joystick';
|
||||||
|
|
||||||
|
export class MouseUI {
|
||||||
|
private mouse: Mouse;
|
||||||
|
private canvas: HTMLCanvasElement;
|
||||||
|
|
||||||
|
constructor(selector: string) {
|
||||||
|
this.canvas = document.querySelector<HTMLCanvasElement>(selector)!;
|
||||||
|
|
||||||
|
this.canvas.addEventListener(
|
||||||
|
'mousemove',
|
||||||
|
(event: MouseEvent & { target: HTMLCanvasElement} ) => {
|
||||||
|
const { offsetX, offsetY, target } = event;
|
||||||
|
this.mouse.setMouseXY(
|
||||||
|
offsetX,
|
||||||
|
offsetY,
|
||||||
|
target.clientWidth,
|
||||||
|
target.clientHeight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.canvas.addEventListener('mousedown', () => {
|
||||||
|
this.mouse.setMouseDown(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.canvas.addEventListener('mouseup', () => {
|
||||||
|
this.mouse.setMouseDown(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setMouse = (mouse: Mouse) => {
|
||||||
|
this.mouse = mouse;
|
||||||
|
};
|
||||||
|
|
||||||
|
mouseMode = (on: boolean) => {
|
||||||
|
enableMouseMode(on);
|
||||||
|
if (on) {
|
||||||
|
this.canvas.classList.add('mouseMode');
|
||||||
|
} else {
|
||||||
|
this.canvas.classList.remove('mouseMode');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -101,16 +101,14 @@ export class OptionsModal {
|
||||||
const list = document.createElement('ul');
|
const list = document.createElement('ul');
|
||||||
for (const option of options) {
|
for (const option of options) {
|
||||||
const { name, label, defaultVal, type } = option;
|
const { name, label, defaultVal, type } = option;
|
||||||
const onChange = (evt: InputEvent) => {
|
const onChange = (evt: InputEvent & { target: HTMLInputElement }) => {
|
||||||
if (evt.target) {
|
const { target } = evt;
|
||||||
const inputElement = evt.target as HTMLInputElement;
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BOOLEAN_OPTION:
|
case BOOLEAN_OPTION:
|
||||||
this.setOption(name, inputElement.checked);
|
this.setOption(name, target.checked);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.setOption(name, inputElement.value);
|
this.setOption(name, target.value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue