Skip to content

Commit

Permalink
Copy graphics (#242)
Browse files Browse the repository at this point in the history
When in graphics mode, copy screen image instead of screen text.
  • Loading branch information
whscullin authored Jan 1, 2025
1 parent 285e3e8 commit b7a575a
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 20 deletions.
22 changes: 17 additions & 5 deletions js/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ export class LoresPage2D implements LoresPage {
line += String.fromCharCode(charCode);
}
}
line = line.trimRight();
line = line.trimEnd();
buffer += line + '\n';
}
return buffer;
Expand Down Expand Up @@ -921,17 +921,17 @@ export class VideoModes2D implements VideoModes {
private e: boolean
) {
this._canvas = document.createElement('canvas');
const { width, height } = { width: 560, height: 384 };
this._canvas.width = width;
this._canvas.height = height;

const context = this._canvas.getContext('2d');
const screenContext = this.screen.getContext('2d');
if (!context || !screenContext) {
throw new Error('No 2d context');
}
this.context = context;

const { width, height } = { width: 560, height: 192 };
this._canvas.width = width;
this._canvas.height = height;

this._screenContext = screenContext;
this._screenContext.imageSmoothingEnabled = false;
this._left = (this.screen.width - 560) / 2;
Expand Down Expand Up @@ -1224,4 +1224,16 @@ export class VideoModes2D implements VideoModes {
getText() {
return this._grs[this.pageMode - 1].getText();
}

async getCanvasAsBlob() {
return new Promise<Blob>((resolve, reject) => {
this.screen.toBlob((blob) => {
if (blob) {
resolve(blob);
} else {
reject(new Error('Could not read canvas'));
}
});
});
}
}
4 changes: 2 additions & 2 deletions js/components/ClipboardCopy.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { h } from 'preact';
import cs from 'classnames';
import { VideoModes } from 'js/videomodes';
import { copyScreenToClipboard, VideoModes } from 'js/videomodes';

import styles from './css/ControlButton.module.scss';

Expand All @@ -12,7 +12,7 @@ export function ClipboardCopy({ vm }: ClipboardCopyProps) {
const doCopy = function () {
const asyncCopy = async function () {
if (vm) {
await navigator.clipboard.writeText(vm.getText());
await copyScreenToClipboard(vm);
}
};
void asyncCopy();
Expand Down
12 changes: 12 additions & 0 deletions js/gl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -947,4 +947,16 @@ export class VideoModesGL implements VideoModes {
getText() {
return this._grs[this.pageMode - 1].getText();
}

async getCanvasAsBlob() {
return new Promise<Blob>((resolve, reject) => {
this.screen.toBlob((blob) => {
if (blob) {
resolve(blob);
} else {
reject(new Error('Could not read canvas'));
}
});
});
}
}
9 changes: 3 additions & 6 deletions js/ui/apple2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { debug } from '../util';
import { Apple2, Stats, State as Apple2State } from '../apple2';
import DiskII from '../cards/disk2';
import { CPU6502 } from '@whscullin/cpu6502';
import { VideoModes } from '../videomodes';
import { copyScreenToClipboard, VideoModes } from '../videomodes';
import Apple2IO from '../apple2io';
import Printer from './printer';

Expand Down Expand Up @@ -848,10 +848,7 @@ export function openOptions() {
}

export function copy() {
const asyncCopy = async function () {
await navigator.clipboard.writeText(vm.getText());
};
void asyncCopy();
void copyScreenToClipboard(vm);
}

export function paste() {
Expand Down Expand Up @@ -996,8 +993,8 @@ function onLoaded(
};

const doCopy = (event: Event) => {
event.clipboardData!.setData('text/plain', vm.getText());
event.preventDefault();
void copyScreenToClipboard(vm);
};

window.addEventListener('paste', (event: Event) => {
Expand Down
26 changes: 26 additions & 0 deletions js/videomodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,32 @@ export interface VideoModes extends Restorable<VideoModesState> {
scanlines(on: boolean): void;

getText(): string;
getCanvasAsBlob(): Promise<Blob>;

ready: Promise<void>;
}

export async function copyScreenToClipboard(vm: VideoModes) {
const text = vm.getText();
if (typeof ClipboardItem !== 'undefined') {
const data: Record<string, Blob> = {};
if (!vm.textMode) {
const blob = await vm.getCanvasAsBlob();
data[blob.type] = blob;
} else {
const htmlBlob = new Blob([`<pre>${text}</pre>`], {
type: 'text/html',
});
data[htmlBlob.type] = htmlBlob;

const textBlob = new Blob([text], {
type: 'text/plain',
});
data[textBlob.type] = textBlob;
}

await navigator.clipboard.write([new ClipboardItem(data)]);
} else {
await navigator.clipboard.writeText(text);
}
}
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion submodules/apple2shader
Submodule apple2shader updated 1 files
+2 −2 screenEmu.js

0 comments on commit b7a575a

Please sign in to comment.