2022-06-09 19:22:59 +00:00
|
|
|
import { h, Fragment } from 'preact';
|
|
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
|
|
|
import Apple2IO, { slot } from 'js/apple2io';
|
|
|
|
import Parallel, { ParallelOptions } from 'js/cards/parallel';
|
|
|
|
import { Modal, ModalContent, ModalFooter } from './Modal';
|
|
|
|
|
2023-01-12 02:14:44 +00:00
|
|
|
import styles from './css/Printer.module.scss';
|
2022-06-09 19:22:59 +00:00
|
|
|
import { ControlButton } from './ControlButton';
|
|
|
|
import { byte } from 'js/types';
|
|
|
|
|
|
|
|
export interface PrinterProps {
|
|
|
|
io: Apple2IO | undefined;
|
|
|
|
slot: slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const Printer = ({ io, slot }: PrinterProps) => {
|
|
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
|
|
const [content, setContent] = useState('');
|
|
|
|
const raw = useRef(new Uint8Array(1024));
|
|
|
|
const rawLength = useRef(0);
|
|
|
|
const [href, setHref] = useState('');
|
|
|
|
|
|
|
|
const cbs = useMemo<ParallelOptions>(() => ({
|
|
|
|
putChar: (val: byte) => {
|
|
|
|
const ascii = val & 0x7f;
|
|
|
|
const visible = val >= 0x20;
|
|
|
|
const char = String.fromCharCode(ascii);
|
|
|
|
|
|
|
|
if (char === '\r') {
|
|
|
|
// Skip for once
|
|
|
|
} else if (char === '\t') {
|
|
|
|
// possibly not right due to tab stops
|
|
|
|
setContent((content) => content += ' ');
|
|
|
|
} else if (ascii === 0x04) {
|
|
|
|
setContent((content) => content = content.slice(0, -1));
|
|
|
|
return;
|
|
|
|
} else if (visible) {
|
|
|
|
setContent((content) => content += char);
|
|
|
|
}
|
|
|
|
|
|
|
|
raw.current[rawLength.current++] = val;
|
|
|
|
if (rawLength.current > raw.current.length) {
|
|
|
|
const newRaw = new Uint8Array(raw.current.length * 2);
|
|
|
|
newRaw.set(raw.current);
|
|
|
|
raw.current = newRaw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}), [rawLength]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (io) {
|
|
|
|
const parallel = new Parallel(cbs);
|
|
|
|
io.setSlot(slot, parallel);
|
|
|
|
}
|
|
|
|
}, [cbs, io, slot]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (isOpen) {
|
|
|
|
const blob = new Blob(
|
|
|
|
[raw.current.slice(0, rawLength.current)],
|
|
|
|
{ type: 'application/octet-stream' }
|
|
|
|
);
|
|
|
|
const href = window.URL.createObjectURL(blob);
|
|
|
|
setHref(href);
|
|
|
|
}
|
|
|
|
}, [isOpen]);
|
|
|
|
|
|
|
|
const onClear = useCallback(() => {
|
|
|
|
setContent('');
|
|
|
|
rawLength.current = 0;
|
|
|
|
}, []);
|
|
|
|
const onClose = useCallback(() => {
|
|
|
|
setIsOpen(false);
|
|
|
|
}, []);
|
|
|
|
const onOpen = useCallback(() => {
|
|
|
|
setIsOpen(true);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<Modal isOpen={isOpen} onClose={onClose} title="Printer">
|
|
|
|
<ModalContent>
|
2022-07-17 03:50:15 +00:00
|
|
|
<pre className={styles.printer} tabIndex={-1} >
|
2022-06-09 19:22:59 +00:00
|
|
|
{content}
|
|
|
|
</pre>
|
|
|
|
</ModalContent>
|
|
|
|
<ModalFooter>
|
|
|
|
<a
|
|
|
|
download="raw_printer_output.bin"
|
|
|
|
href={href}
|
|
|
|
role="button"
|
|
|
|
>
|
|
|
|
Download Raw
|
|
|
|
</a>
|
|
|
|
<button onClick={onClear}>Clear</button>
|
|
|
|
<button onClick={onClose}>OK</button>
|
|
|
|
</ModalFooter>
|
|
|
|
</Modal>
|
|
|
|
<ControlButton
|
|
|
|
icon="print"
|
|
|
|
title="Printer"
|
|
|
|
onClick={onOpen}
|
|
|
|
active={content.length > 0}
|
|
|
|
/>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|