All files / src touchscreen.js

100% Statements 56/56
96.29% Branches 26/27
100% Functions 12/12
100% Lines 49/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106    38x     20x 20x         263x 263x 263x 263x 263x       358x 358x 358x 358x       48x               29x 29x 29x 29x 29x       54x 12x       10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 8x 8x 8x 8x   40x 10x       7x 7x       54x       6x       44x   10x 10x                     24x 24x   7x   3x 3x   44x      
import { Fifo } from "./binary.js";
 
const PollHz = 8; // Made up
 
function doScale(val, scale, margin) {
    val = (val - margin) / (1 - 2 * margin);
    return val * scale;
}
 
export class TouchScreen {
    constructor(scheduler, cyclesPerSecond) {
        this.scheduler = scheduler;
        this.pollCycles = cyclesPerSecond / PollHz;
        this.outBuffer = new Fifo(16);
        this.pollTask = this.scheduler.newTask(() => this.poll());
        this.reset();
    }
 
    reset() {
        this.mouse = { x: 0, y: 0, button: 0 };
        this.mode = 0;
        this.outBuffer.clear();
        this.pollTask.cancel();
    }
 
    snapshotState() {
        return {
            mode: this.mode,
            outBuffer: this.outBuffer.toArray(),
            pollTaskOffset: this.pollTask.scheduled() ? this.pollTask.expireEpoch - this.scheduler.epoch : null,
        };
    }
 
    restoreState(state) {
        this.mode = state.mode;
        this.outBuffer.clear();
        for (const byte of state.outBuffer) this.store(byte);
        this.pollTask.cancel();
        if (state.pollTaskOffset !== null) this.pollTask.schedule(state.pollTaskOffset);
    }
 
    tryReceive(rts) {
        if (this.outBuffer.size && rts) return this.outBuffer.get();
        return -1;
    }
 
    doRead() {
        const scaleX = 120,
            marginX = 0.13;
        const scaleY = 100,
            marginY = 0.03;
        const scaledX = doScale(this.mouse.x, scaleX, marginX);
        const scaledY = doScale(1 - this.mouse.y, scaleY, marginY);
        const toSend = [0x4f, 0x4f, 0x4f, 0x4f];
        const x = Math.min(255, Math.max(0, scaledX)) | 0;
        const y = Math.min(255, Math.max(0, scaledY)) | 0;
        if (this.mouse.button) {
            toSend[0] = 0x40 | ((x & 0xf0) >>> 4);
            toSend[1] = 0x40 | (x & 0x0f);
            toSend[2] = 0x40 | ((y & 0xf0) >>> 4);
            toSend[3] = 0x40 | (y & 0x0f);
        }
        for (let i = 0; i < 4; ++i) this.store(toSend[i]);
        this.store(".".charCodeAt(0));
    }
 
    poll() {
        this.doRead();
        this.pollTask.reschedule(this.pollCycles);
    }
 
    store(byte) {
        this.outBuffer.put(byte);
    }
 
    onMouse(x, y, button) {
        this.mouse = { x: x, y: y, button: button };
    }
 
    onTransmit(val) {
        switch (String.fromCharCode(val)) {
            case "M":
                this.mode = 0;
                break;
            case "0":
            case "1":
            case "2":
            case "3":
            case "4":
            case "5":
            case "6":
            case "7":
            case "8":
            case "9":
                this.mode = 10 * this.mode + val - "0".charCodeAt(0);
                break;
            case ".":
                break;
            case "?":
                Eif (this.mode === 1) this.doRead();
                break;
        }
        this.pollTask.ensureScheduled(this.mode === 129 || this.mode === 130, this.pollCycles);
    }
}